2 * "$Id: colorman.c 12226 2014-10-21 13:36:05Z msweet $"
4 * Color management routines for the CUPS scheduler.
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * Original DBUS/colord code is Copyright 2011 Red Hat, Inc.
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
21 * Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
24 * Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution.
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
43 * Include necessary headers...
47 #include <cups/ppd-private.h>
50 # include <ApplicationServices/ApplicationServices.h>
51 extern CFUUIDRef
ColorSyncCreateUUIDFromUInt32(unsigned id
);
52 # include <CoreFoundation/CoreFoundation.h>
53 #elif defined(HAVE_DBUS)
54 # include <dbus/dbus.h>
57 * Defines used by colord. See the reference docs for further details:
59 * http://colord.hughsie.com/api/ref-dbus.html
62 # define COLORD_SCOPE_NORMAL "normal"
64 # define COLORD_SCOPE_TEMP "temp" /* Process scope */
65 # define COLORD_SCOPE_DISK "disk" /* Lives forever, as stored in DB */
67 # define COLORD_RELATION_SOFT "soft" /* Mapping is not default */
68 # define COLORD_RELATION_HARD "hard" /* Explicitly mapped profile */
70 # define COLORD_SPACE_RGB "rgb" /* RGB colorspace */
71 # define COLORD_SPACE_CMYK "cmyk" /* CMYK colorspace */
72 # define COLORD_SPACE_GRAY "gray" /* Gray colorspace */
73 # define COLORD_SPACE_UNKNOWN "unknown"
74 /* Unknown colorspace */
76 # define COLORD_MODE_PHYSICAL "physical"
78 # define COLORD_MODE_VIRTUAL "virtual"
79 /* Virtual device with no hardware */
81 # define COLORD_KIND_PRINTER "printer"
82 /* printing output device */
84 # define COLORD_DBUS_SERVICE "org.freedesktop.ColorManager"
85 # define COLORD_DBUS_INTERFACE "org.freedesktop.ColorManager"
86 # define COLORD_DBUS_INTERFACE_DEVICE "org.freedesktop.ColorManager.Device"
87 # define COLORD_DBUS_PATH "/org/freedesktop/ColorManager"
88 /* Path for color management system */
89 # define COLORD_DBUS_TIMEOUT 5000 /* Timeout for connecting to colord in ms */
90 #endif /* __APPLE__ */
97 #if !defined(__APPLE__) && defined(HAVE_DBUS)
98 static DBusConnection
*colord_con
= NULL
;
99 /* DBUS connection for colord */
100 #endif /* !__APPLE__ && HAVE_DBUS */
108 static void apple_init_profile(ppd_file_t
*ppd
, cups_array_t
*languages
,
109 CFMutableDictionaryRef profile
,
110 unsigned id
, const char *name
,
111 const char *text
, const char *iccfile
);
112 static void apple_register_profiles(cupsd_printer_t
*p
);
113 static void apple_unregister_profiles(cupsd_printer_t
*p
);
115 #elif defined(HAVE_DBUS)
116 static void colord_create_device(cupsd_printer_t
*p
, ppd_file_t
*ppd
,
117 cups_array_t
*profiles
,
118 const char *colorspace
, char **format
,
119 const char *relation
, const char *scope
);
120 static void colord_create_profile(cups_array_t
*profiles
,
121 const char *printer_name
,
122 const char *qualifier
,
123 const char *colorspace
,
124 char **format
, const char *iccfile
,
126 static void colord_delete_device(const char *device_id
);
127 static void colord_device_add_profile(const char *device_path
,
128 const char *profile_path
,
129 const char *relation
);
130 static void colord_dict_add_strings(DBusMessageIter
*dict
,
131 const char *key
, const char *value
);
132 static char *colord_find_device(const char *device_id
);
133 static void colord_get_qualifier_format(ppd_file_t
*ppd
, char *format
[3]);
134 static void colord_register_printer(cupsd_printer_t
*p
);
135 static void colord_unregister_printer(cupsd_printer_t
*p
);
136 #endif /* __APPLE__ */
140 * 'cupsdRegisterColor()' - Register vendor color profiles in a PPD file.
144 cupsdRegisterColor(cupsd_printer_t
*p
) /* I - Printer */
149 apple_unregister_profiles(p
);
150 apple_register_profiles(p
);
153 #elif defined(HAVE_DBUS)
154 colord_unregister_printer(p
);
155 colord_register_printer(p
);
156 #endif /* __APPLE__ */
161 * 'cupsdStartColor()' - Initialize color management.
165 cupsdStartColor(void)
167 #if !defined(__APPLE__) && defined(HAVE_DBUS)
168 cupsd_printer_t
*p
; /* Current printer */
171 colord_con
= dbus_bus_get(DBUS_BUS_SYSTEM
, NULL
);
173 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
175 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
176 cupsdRegisterColor(p
);
177 #endif /* !__APPLE__ && HAVE_DBUS */
182 * 'cupsdStopColor()' - Shutdown color management.
188 #if !defined(__APPLE__) && defined(HAVE_DBUS)
190 dbus_connection_unref(colord_con
);
192 #endif /* !__APPLE__ && HAVE_DBUS */
197 * 'cupsdUnregisterColor()' - Unregister vendor color profiles in a PPD file.
201 cupsdUnregisterColor(cupsd_printer_t
*p
)/* I - Printer */
205 apple_unregister_profiles(p
);
207 #elif defined(HAVE_DBUS)
208 colord_unregister_printer(p
);
209 #endif /* __APPLE__ */
215 * 'apple_init_profile()' - Initialize a color profile.
220 ppd_file_t
*ppd
, /* I - PPD file */
221 cups_array_t
*languages
, /* I - Languages in the PPD file */
222 CFMutableDictionaryRef profile
, /* I - Profile dictionary */
223 unsigned id
, /* I - Profile ID */
224 const char *name
, /* I - Profile name */
225 const char *text
, /* I - Profile UI text */
226 const char *iccfile
) /* I - ICC filename */
228 CFURLRef url
; /* URL for profile filename */
229 CFMutableDictionaryRef dict
; /* Dictionary for name */
230 char *language
; /* Current language */
231 ppd_attr_t
*attr
; /* Profile attribute */
232 CFStringRef cflang
, /* Language string */
233 cftext
; /* Localized text */
239 * Build the profile name dictionary...
242 dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
243 &kCFTypeDictionaryKeyCallBacks
,
244 &kCFTypeDictionaryValueCallBacks
);
247 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to initialize profile \"%s\".",
252 cftext
= CFStringCreateWithCString(kCFAllocatorDefault
, text
,
253 kCFStringEncodingUTF8
);
257 CFDictionarySetValue(dict
, CFSTR("en_US"), cftext
);
264 * Find localized names for the color profiles...
267 cupsArraySave(ppd
->sorted_attrs
);
269 for (language
= (char *)cupsArrayFirst(languages
);
271 language
= (char *)cupsArrayNext(languages
))
275 if ((attr
= _ppdLocalizedAttr(ppd
, "cupsICCProfile", name
,
277 attr
= _ppdLocalizedAttr(ppd
, "APTiogaProfile", name
, language
);
280 attr
= _ppdLocalizedAttr(ppd
, "ColorModel", name
, language
);
282 if (attr
&& attr
->text
[0])
284 cflang
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
285 kCFStringEncodingUTF8
);
286 cftext
= CFStringCreateWithCString(kCFAllocatorDefault
, attr
->text
,
287 kCFStringEncodingUTF8
);
289 if (cflang
&& cftext
)
290 CFDictionarySetValue(dict
, cflang
, cftext
);
300 cupsArrayRestore(ppd
->sorted_attrs
);
304 * Fill in the profile data...
307 if (iccfile
&& *iccfile
)
309 url
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, (const UInt8
*)iccfile
, (CFIndex
)strlen(iccfile
), false);
313 CFDictionarySetValue(profile
, kColorSyncDeviceProfileURL
, url
);
318 CFDictionarySetValue(profile
, kColorSyncDeviceModeDescriptions
, dict
);
324 * 'apple_register_profiles()' - Register color profiles for a printer.
328 apple_register_profiles(
329 cupsd_printer_t
*p
) /* I - Printer */
331 int i
; /* Looping var */
332 char ppdfile
[1024], /* PPD filename */
333 iccfile
[1024], /* ICC filename */
334 selector
[PPD_MAX_NAME
];
335 /* Profile selection string */
336 ppd_file_t
*ppd
; /* PPD file */
337 ppd_attr_t
*attr
, /* Profile attributes */
338 *profileid_attr
,/* cupsProfileID attribute */
339 *q1_attr
, /* ColorModel (or other) qualifier */
340 *q2_attr
, /* MediaType (or other) qualifier */
341 *q3_attr
; /* Resolution (or other) qualifier */
342 char q_keyword
[PPD_MAX_NAME
];
343 /* Qualifier keyword */
344 const char *q1_choice
, /* ColorModel (or other) choice */
345 *q2_choice
, /* MediaType (or other) choice */
346 *q3_choice
; /* Resolution (or other) choice */
347 ppd_option_t
*cm_option
; /* Color model option */
348 ppd_choice_t
*cm_choice
; /* Color model choice */
349 int num_profiles
; /* Number of profiles */
350 OSStatus error
= 0; /* Last error */
351 unsigned device_id
, /* Printer device ID */
352 profile_id
= 0, /* Profile ID */
353 default_profile_id
= 0;
354 /* Default profile ID */
355 CFMutableDictionaryRef device_name
; /* Printer device name dictionary */
356 CFStringRef printer_name
; /* Printer name string */
357 cups_array_t
*languages
; /* Languages array */
358 CFMutableDictionaryRef profiles
, /* Dictionary of profiles */
359 profile
; /* Current profile info dictionary */
360 CFStringRef dict_key
; /* Key in factory profile dictionary */
364 * Make sure ColorSync is available...
367 if (ColorSyncRegisterDevice
== NULL
)
371 * Try opening the PPD file for this printer...
374 snprintf(ppdfile
, sizeof(ppdfile
), "%s/ppd/%s.ppd", ServerRoot
, p
->name
);
375 if ((ppd
= _ppdOpenFile(ppdfile
, _PPD_LOCALIZATION_ICC_PROFILES
)) == NULL
)
379 * See if we have any profiles...
382 for (num_profiles
= 0, attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
384 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
385 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
387 if (attr
->value
[0] != '/')
388 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
391 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
393 if (access(iccfile
, 0))
395 cupsdLogMessage(CUPSD_LOG_ERROR
,
396 "%s: ICC Profile \"%s\" does not exist.", p
->name
,
398 cupsdSetPrinterReasons(p
, "+cups-missing-filter-warning");
406 * Create a dictionary for the factory profiles...
409 profiles
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
410 &kCFTypeDictionaryKeyCallBacks
,
411 &kCFTypeDictionaryValueCallBacks
);
414 cupsdLogMessage(CUPSD_LOG_ERROR
,
415 "Unable to allocate memory for factory profiles.");
421 * If we have profiles, add them...
424 if (num_profiles
> 0)
427 * For CUPS PPDs, figure out the default profile selector values...
430 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier1", NULL
)) != NULL
&&
431 attr
->value
&& attr
->value
[0])
433 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
434 q1_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
436 else if ((q1_attr
= ppdFindAttr(ppd
, "DefaultColorModel", NULL
)) == NULL
)
437 q1_attr
= ppdFindAttr(ppd
, "DefaultColorSpace", NULL
);
439 if (q1_attr
&& q1_attr
->value
&& q1_attr
->value
[0])
440 q1_choice
= q1_attr
->value
;
444 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier2", NULL
)) != NULL
&&
445 attr
->value
&& attr
->value
[0])
447 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
448 q2_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
451 q2_attr
= ppdFindAttr(ppd
, "DefaultMediaType", NULL
);
453 if (q2_attr
&& q2_attr
->value
&& q2_attr
->value
[0])
454 q2_choice
= q2_attr
->value
;
458 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier3", NULL
)) != NULL
&&
459 attr
->value
&& attr
->value
[0])
461 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
462 q3_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
465 q3_attr
= ppdFindAttr(ppd
, "DefaultResolution", NULL
);
467 if (q3_attr
&& q3_attr
->value
&& q3_attr
->value
[0])
468 q3_choice
= q3_attr
->value
;
473 * Loop through the profiles listed in the PPD...
476 languages
= _ppdGetLanguages(ppd
);
478 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
480 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
481 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
484 * Add this profile...
487 if (attr
->value
[0] != '/')
488 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
491 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
493 if (_cupsFileCheck(iccfile
, _CUPS_FILE_CHECK_FILE
, !RunUser
,
494 cupsdLogFCMessage
, p
))
497 cupsArraySave(ppd
->sorted_attrs
);
499 if ((profileid_attr
= ppdFindAttr(ppd
, "cupsProfileID",
500 attr
->spec
)) != NULL
&&
501 profileid_attr
->value
&& isdigit(profileid_attr
->value
[0] & 255))
502 profile_id
= (unsigned)strtoul(profileid_attr
->value
, NULL
, 10);
504 profile_id
= _ppdHashName(attr
->spec
);
506 cupsArrayRestore(ppd
->sorted_attrs
);
508 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
509 &kCFTypeDictionaryKeyCallBacks
,
510 &kCFTypeDictionaryValueCallBacks
);
513 cupsdLogMessage(CUPSD_LOG_ERROR
,
514 "Unable to allocate memory for color profile.");
520 apple_init_profile(ppd
, languages
, profile
, profile_id
, attr
->spec
,
521 attr
->text
[0] ? attr
->text
: attr
->spec
, iccfile
);
523 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
524 CFSTR("%u"), profile_id
);
527 CFDictionarySetValue(profiles
, dict_key
, profile
);
534 * See if this is the default profile...
537 if (!default_profile_id
&& q1_choice
&& q2_choice
&& q3_choice
)
539 snprintf(selector
, sizeof(selector
), "%s.%s.%s", q1_choice
, q2_choice
,
541 if (!strcmp(selector
, attr
->spec
))
542 default_profile_id
= profile_id
;
545 if (!default_profile_id
&& q1_choice
&& q2_choice
)
547 snprintf(selector
, sizeof(selector
), "%s.%s.", q1_choice
, q2_choice
);
548 if (!strcmp(selector
, attr
->spec
))
549 default_profile_id
= profile_id
;
552 if (!default_profile_id
&& q1_choice
&& q3_choice
)
554 snprintf(selector
, sizeof(selector
), "%s..%s", q1_choice
, q3_choice
);
555 if (!strcmp(selector
, attr
->spec
))
556 default_profile_id
= profile_id
;
559 if (!default_profile_id
&& q1_choice
)
561 snprintf(selector
, sizeof(selector
), "%s..", q1_choice
);
562 if (!strcmp(selector
, attr
->spec
))
563 default_profile_id
= profile_id
;
566 if (!default_profile_id
&& q2_choice
&& q3_choice
)
568 snprintf(selector
, sizeof(selector
), ".%s.%s", q2_choice
, q3_choice
);
569 if (!strcmp(selector
, attr
->spec
))
570 default_profile_id
= profile_id
;
573 if (!default_profile_id
&& q2_choice
)
575 snprintf(selector
, sizeof(selector
), ".%s.", q2_choice
);
576 if (!strcmp(selector
, attr
->spec
))
577 default_profile_id
= profile_id
;
580 if (!default_profile_id
&& q3_choice
)
582 snprintf(selector
, sizeof(selector
), "..%s", q3_choice
);
583 if (!strcmp(selector
, attr
->spec
))
584 default_profile_id
= profile_id
;
588 _ppdFreeLanguages(languages
);
590 else if ((cm_option
= ppdFindOption(ppd
, "ColorModel")) != NULL
)
593 * Extract profiles from ColorModel option...
596 const char *profile_name
; /* Name of generic profile */
599 num_profiles
= cm_option
->num_choices
;
601 for (i
= cm_option
->num_choices
, cm_choice
= cm_option
->choices
;
605 if (!strcmp(cm_choice
->choice
, "Gray") ||
606 !strcmp(cm_choice
->choice
, "Black"))
607 profile_name
= "Gray";
608 else if (!strcmp(cm_choice
->choice
, "RGB") ||
609 !strcmp(cm_choice
->choice
, "CMY"))
610 profile_name
= "RGB";
611 else if (!strcmp(cm_choice
->choice
, "CMYK") ||
612 !strcmp(cm_choice
->choice
, "KCMY"))
613 profile_name
= "CMYK";
615 profile_name
= "DeviceN";
617 snprintf(selector
, sizeof(selector
), "%s..", profile_name
);
618 profile_id
= _ppdHashName(selector
);
620 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
621 &kCFTypeDictionaryKeyCallBacks
,
622 &kCFTypeDictionaryValueCallBacks
);
625 cupsdLogMessage(CUPSD_LOG_ERROR
,
626 "Unable to allocate memory for color profile.");
632 apple_init_profile(ppd
, NULL
, profile
, profile_id
, cm_choice
->choice
,
633 cm_choice
->text
, NULL
);
635 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
636 CFSTR("%u"), profile_id
);
639 CFDictionarySetValue(profiles
, dict_key
, profile
);
645 if (cm_choice
->marked
)
646 default_profile_id
= profile_id
;
652 * Use the default colorspace...
655 attr
= ppdFindAttr(ppd
, "DefaultColorSpace", NULL
);
657 num_profiles
= (attr
&& ppd
->colorspace
== PPD_CS_GRAY
) ? 1 : 2;
660 * Add the grayscale profile first. We always have a grayscale profile.
663 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
664 &kCFTypeDictionaryKeyCallBacks
,
665 &kCFTypeDictionaryValueCallBacks
);
669 cupsdLogMessage(CUPSD_LOG_ERROR
,
670 "Unable to allocate memory for color profile.");
676 profile_id
= _ppdHashName("Gray..");
677 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "Gray", "Gray", NULL
);
679 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%u"),
683 CFDictionarySetValue(profiles
, dict_key
, profile
);
690 * Then add the RGB/CMYK/DeviceN color profile...
693 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
694 &kCFTypeDictionaryKeyCallBacks
,
695 &kCFTypeDictionaryValueCallBacks
);
699 cupsdLogMessage(CUPSD_LOG_ERROR
,
700 "Unable to allocate memory for color profile.");
706 switch (ppd
->colorspace
)
711 profile_id
= _ppdHashName("RGB..");
712 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "RGB", "RGB",
718 profile_id
= _ppdHashName("CMYK..");
719 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "CMYK", "CMYK",
728 profile_id
= _ppdHashName("DeviceN..");
729 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "DeviceN",
734 if (CFDictionaryGetCount(profile
) > 0)
736 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
737 CFSTR("%u"), profile_id
);
740 CFDictionarySetValue(profiles
, dict_key
, profile
);
748 if (num_profiles
> 0)
751 * Make sure we have a default profile ID...
754 if (!default_profile_id
)
755 default_profile_id
= profile_id
; /* Last profile */
757 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%u"),
761 CFDictionarySetValue(profiles
, kColorSyncDeviceDefaultProfileID
,
767 * Get the device ID hash and pathelogical name dictionary.
770 cupsdLogMessage(CUPSD_LOG_INFO
, "Registering ICC color profiles for \"%s\"",
773 device_id
= _ppdHashName(p
->name
);
774 device_name
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
775 &kCFTypeDictionaryKeyCallBacks
,
776 &kCFTypeDictionaryValueCallBacks
);
777 printer_name
= CFStringCreateWithCString(kCFAllocatorDefault
,
778 p
->name
, kCFStringEncodingUTF8
);
780 if (device_name
&& printer_name
)
783 * Register the device with ColorSync...
786 CFTypeRef deviceDictKeys
[] =
788 kColorSyncDeviceDescriptions
,
789 kColorSyncFactoryProfiles
,
790 kColorSyncDeviceUserScope
,
791 kColorSyncDeviceHostScope
793 CFTypeRef deviceDictVals
[] =
794 { /* Device values */
797 kCFPreferencesAnyUser
,
798 kCFPreferencesCurrentHost
800 CFDictionaryRef deviceDict
; /* Device dictionary */
801 CFUUIDRef deviceUUID
; /* Device UUID */
803 CFDictionarySetValue(device_name
, CFSTR("en_US"), printer_name
);
805 deviceDict
= CFDictionaryCreate(kCFAllocatorDefault
,
806 (const void **)deviceDictKeys
,
807 (const void **)deviceDictVals
,
808 sizeof(deviceDictKeys
) /
809 sizeof(deviceDictKeys
[0]),
810 &kCFTypeDictionaryKeyCallBacks
,
811 &kCFTypeDictionaryValueCallBacks
);
812 deviceUUID
= ColorSyncCreateUUIDFromUInt32(device_id
);
814 if (!deviceDict
|| !deviceUUID
||
815 !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass
, deviceUUID
,
820 CFRelease(deviceUUID
);
823 CFRelease(deviceDict
);
833 cupsdLogMessage(CUPSD_LOG_ERROR
,
834 "Unable to register ICC color profiles for \"%s\": %d",
835 p
->name
, (int)error
);
838 CFRelease(printer_name
);
841 CFRelease(device_name
);
845 * Free any memory we used...
855 * 'apple_unregister_profiles()' - Remove color profiles for the specified
860 apple_unregister_profiles(
861 cupsd_printer_t
*p
) /* I - Printer */
864 * Make sure ColorSync is available...
867 if (ColorSyncUnregisterDevice
!= NULL
)
869 CFUUIDRef deviceUUID
; /* Device UUID */
871 deviceUUID
= ColorSyncCreateUUIDFromUInt32(_ppdHashName(p
->name
));
874 ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass
, deviceUUID
);
875 CFRelease(deviceUUID
);
881 #elif defined(HAVE_DBUS)
883 * 'colord_create_device()' - Create a device and register profiles.
887 colord_create_device(
888 cupsd_printer_t
*p
, /* I - Printer */
889 ppd_file_t
*ppd
, /* I - PPD file */
890 cups_array_t
*profiles
, /* I - Profiles array */
891 const char *colorspace
, /* I - Device colorspace, e.g. 'rgb' */
892 char **format
, /* I - Device qualifier format */
893 const char *relation
, /* I - Profile relation, either 'soft'
895 const char *scope
) /* I - The scope of the device, e.g.
896 'normal', 'temp' or 'disk' */
898 DBusMessage
*message
= NULL
; /* D-Bus request */
899 DBusMessage
*reply
= NULL
; /* D-Bus reply */
900 DBusMessageIter args
; /* D-Bus method arguments */
901 DBusMessageIter dict
; /* D-Bus method arguments */
902 DBusError error
; /* D-Bus error */
903 const char *device_path
; /* Device object path */
904 const char *profile_path
; /* Profile path */
905 char *default_profile_path
= NULL
;
906 /* Default profile path */
907 char device_id
[1024]; /* Device ID as understood by colord */
908 char format_str
[1024]; /* Qualifier format as a string */
912 * Create the device...
915 snprintf(device_id
, sizeof(device_id
), "cups-%s", p
->name
);
916 device_path
= device_id
;
918 message
= dbus_message_new_method_call(COLORD_DBUS_SERVICE
,
920 COLORD_DBUS_INTERFACE
,
923 dbus_message_iter_init_append(message
, &args
);
924 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &device_path
);
925 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &scope
);
927 snprintf(format_str
, sizeof(format_str
), "%s.%s.%s", format
[0], format
[1],
930 dbus_message_iter_open_container(&args
, DBUS_TYPE_ARRAY
, "{ss}", &dict
);
931 colord_dict_add_strings(&dict
, "Colorspace", colorspace
);
932 colord_dict_add_strings(&dict
, "Mode", COLORD_MODE_PHYSICAL
);
933 if (ppd
->manufacturer
)
934 colord_dict_add_strings(&dict
, "Vendor", ppd
->manufacturer
);
936 colord_dict_add_strings(&dict
, "Model", ppd
->modelname
);
937 if (p
->sanitized_device_uri
)
938 colord_dict_add_strings(&dict
, "Serial", p
->sanitized_device_uri
);
939 colord_dict_add_strings(&dict
, "Format", format_str
);
940 colord_dict_add_strings(&dict
, "Kind", COLORD_KIND_PRINTER
);
941 dbus_message_iter_close_container(&args
, &dict
);
944 * Send the CreateDevice request synchronously...
947 dbus_error_init(&error
);
948 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling CreateDevice(%s,%s)", device_id
,
950 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
955 cupsdLogMessage(CUPSD_LOG_WARN
, "CreateDevice failed: %s:%s", error
.name
,
957 dbus_error_free(&error
);
965 dbus_message_iter_init(reply
, &args
);
966 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
968 cupsdLogMessage(CUPSD_LOG_WARN
,
969 "CreateDevice failed: Incorrect reply type.");
973 dbus_message_iter_get_basic(&args
, &device_path
);
974 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Created device \"%s\".", device_path
);
980 for (profile_path
= cupsArrayFirst(profiles
);
982 profile_path
= cupsArrayNext(profiles
))
984 colord_device_add_profile(device_path
, profile_path
, relation
);
989 if (default_profile_path
)
990 free(default_profile_path
);
993 dbus_message_unref(message
);
996 dbus_message_unref(reply
);
1001 * 'colord_create_profile()' - Create a color profile for a printer.
1005 colord_create_profile(
1006 cups_array_t
*profiles
, /* I - Profiles array */
1007 const char *printer_name
, /* I - Printer name */
1008 const char *qualifier
, /* I - Profile qualifier */
1009 const char *colorspace
, /* I - Profile colorspace */
1010 char **format
, /* I - Profile qualifier format */
1011 const char *iccfile
, /* I - ICC filename */
1012 const char *scope
) /* I - The scope of the profile, e.g.
1013 'normal', 'temp' or 'disk' */
1015 DBusMessage
*message
= NULL
; /* D-Bus request */
1016 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1017 DBusMessageIter args
; /* D-Bus method arguments */
1018 DBusMessageIter dict
; /* D-Bus method arguments */
1019 DBusError error
; /* D-Bus error */
1020 char *idstr
; /* Profile ID string */
1021 size_t idstrlen
; /* Profile ID allocated length */
1022 const char *profile_path
; /* Device object path */
1023 char format_str
[1024]; /* Qualifier format as a string */
1027 * Create the profile...
1030 message
= dbus_message_new_method_call(COLORD_DBUS_SERVICE
,
1032 COLORD_DBUS_INTERFACE
,
1035 idstrlen
= strlen(printer_name
) + 1 + strlen(qualifier
) + 1;
1036 if ((idstr
= malloc(idstrlen
)) == NULL
)
1038 snprintf(idstr
, idstrlen
, "%s-%s", printer_name
, qualifier
);
1039 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Using profile ID \"%s\".", idstr
);
1041 dbus_message_iter_init_append(message
, &args
);
1042 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &idstr
);
1043 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &scope
);
1045 snprintf(format_str
, sizeof(format_str
), "%s.%s.%s", format
[0], format
[1],
1048 dbus_message_iter_open_container(&args
, DBUS_TYPE_ARRAY
, "{ss}", &dict
);
1049 colord_dict_add_strings(&dict
, "Qualifier", qualifier
);
1050 colord_dict_add_strings(&dict
, "Format", format_str
);
1051 colord_dict_add_strings(&dict
, "Colorspace", colorspace
);
1053 colord_dict_add_strings(&dict
, "Filename", iccfile
);
1054 dbus_message_iter_close_container(&args
, &dict
);
1057 * Send the CreateProfile request synchronously...
1060 dbus_error_init(&error
);
1061 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling CreateProfile(%s,%s)", idstr
,
1063 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1064 COLORD_DBUS_TIMEOUT
,
1068 cupsdLogMessage(CUPSD_LOG_WARN
, "CreateProfile failed: %s:%s", error
.name
,
1070 dbus_error_free(&error
);
1078 dbus_message_iter_init(reply
, &args
);
1079 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
1081 cupsdLogMessage(CUPSD_LOG_WARN
,
1082 "CreateProfile failed: Incorrect reply type.");
1086 dbus_message_iter_get_basic(&args
, &profile_path
);
1087 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Created profile \"%s\".", profile_path
);
1088 cupsArrayAdd(profiles
, strdup(profile_path
));
1093 dbus_message_unref(message
);
1096 dbus_message_unref(reply
);
1104 * 'colord_delete_device()' - Delete a device
1108 colord_delete_device(
1109 const char *device_id
) /* I - Device ID string */
1111 DBusMessage
*message
= NULL
; /* D-Bus request */
1112 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1113 DBusMessageIter args
; /* D-Bus method arguments */
1114 DBusError error
; /* D-Bus error */
1115 char *device_path
; /* Device object path */
1119 * Find the device...
1122 if ((device_path
= colord_find_device(device_id
)) == NULL
)
1126 * Delete the device...
1129 message
= dbus_message_new_method_call(COLORD_DBUS_SERVICE
,
1131 COLORD_DBUS_INTERFACE
,
1134 dbus_message_iter_init_append(message
, &args
);
1135 dbus_message_iter_append_basic(&args
, DBUS_TYPE_OBJECT_PATH
, &device_path
);
1138 * Send the DeleteDevice request synchronously...
1141 dbus_error_init(&error
);
1142 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling DeleteDevice(%s)", device_path
);
1143 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1144 COLORD_DBUS_TIMEOUT
,
1148 cupsdLogMessage(CUPSD_LOG_DEBUG
, "DeleteDevice failed: %s:%s", error
.name
,
1150 dbus_error_free(&error
);
1160 dbus_message_unref(message
);
1163 dbus_message_unref(reply
);
1168 * 'colord_device_add_profile()' - Assign a profile to a device.
1172 colord_device_add_profile(
1173 const char *device_path
, /* I - Device object path */
1174 const char *profile_path
, /* I - Profile object path */
1175 const char *relation
) /* I - Device relation, either
1178 DBusMessage
*message
= NULL
; /* D-Bus request */
1179 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1180 DBusMessageIter args
; /* D-Bus method arguments */
1181 DBusError error
; /* D-Bus error */
1184 message
= dbus_message_new_method_call(COLORD_DBUS_SERVICE
,
1186 COLORD_DBUS_INTERFACE_DEVICE
,
1189 dbus_message_iter_init_append(message
, &args
);
1190 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &relation
);
1191 dbus_message_iter_append_basic(&args
, DBUS_TYPE_OBJECT_PATH
, &profile_path
);
1192 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling %s:AddProfile(%s) [%s]",
1193 device_path
, profile_path
, relation
);
1196 * Send the AddProfile request synchronously...
1199 dbus_error_init(&error
);
1200 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1201 COLORD_DBUS_TIMEOUT
,
1205 cupsdLogMessage(CUPSD_LOG_WARN
, "AddProfile failed: %s:%s", error
.name
,
1207 dbus_error_free(&error
);
1214 dbus_message_unref(message
);
1217 dbus_message_unref(reply
);
1222 * 'colord_dict_add_strings()' - Add two strings to a dictionary.
1226 colord_dict_add_strings(
1227 DBusMessageIter
*dict
, /* I - Dictionary */
1228 const char *key
, /* I - Key string */
1229 const char *value
) /* I - Value string */
1231 DBusMessageIter entry
; /* Entry to add */
1234 dbus_message_iter_open_container(dict
, DBUS_TYPE_DICT_ENTRY
, NULL
, &entry
);
1235 dbus_message_iter_append_basic(&entry
, DBUS_TYPE_STRING
, &key
);
1236 dbus_message_iter_append_basic(&entry
, DBUS_TYPE_STRING
, &value
);
1237 dbus_message_iter_close_container(dict
, &entry
);
1242 * 'colord_find_device()' - Finds a device
1245 static char * /* O - Device path or NULL */
1247 const char *device_id
) /* I - Device ID string */
1249 DBusMessage
*message
= NULL
; /* D-Bus request */
1250 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1251 DBusMessageIter args
; /* D-Bus method arguments */
1252 DBusError error
; /* D-Bus error */
1253 const char *device_path_tmp
; /* Device object path */
1254 char *device_path
= NULL
; /* Device object path */
1257 message
= dbus_message_new_method_call(COLORD_DBUS_SERVICE
,
1259 COLORD_DBUS_INTERFACE
,
1262 dbus_message_iter_init_append(message
, &args
);
1263 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &device_id
);
1266 * Send the FindDeviceById request synchronously...
1269 dbus_error_init(&error
);
1270 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling FindDeviceById(%s)", device_id
);
1271 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1272 COLORD_DBUS_TIMEOUT
,
1276 cupsdLogMessage(CUPSD_LOG_DEBUG
, "FindDeviceById failed: %s:%s",
1277 error
.name
, error
.message
);
1278 dbus_error_free(&error
);
1286 dbus_message_iter_init(reply
, &args
);
1287 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
1289 cupsdLogMessage(CUPSD_LOG_WARN
,
1290 "FindDeviceById failed: Incorrect reply type.");
1294 dbus_message_iter_get_basic(&args
, &device_path_tmp
);
1295 if (device_path_tmp
)
1296 device_path
= strdup(device_path_tmp
);
1301 dbus_message_unref(message
);
1304 dbus_message_unref(reply
);
1306 return (device_path
);
1311 * 'colord_get_qualifier_format()' - Get the qualifier format.
1313 * Note: Returns a value of "ColorSpace.MediaType.Resolution" by default.
1317 colord_get_qualifier_format(
1318 ppd_file_t
*ppd
, /* I - PPD file data */
1319 char *format
[3]) /* I - Format tuple */
1321 const char *tmp
; /* Temporary string */
1322 ppd_attr_t
*attr
; /* Profile attributes */
1326 * Get 1st section...
1329 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier1", NULL
)) != NULL
)
1331 else if (ppdFindAttr(ppd
, "DefaultColorModel", NULL
))
1333 else if (ppdFindAttr(ppd
, "DefaultColorSpace", NULL
))
1338 format
[0] = strdup(tmp
);
1341 * Get 2nd section...
1344 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier2", NULL
)) != NULL
)
1349 format
[1] = strdup(tmp
);
1352 * Get 3rd section...
1355 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier3", NULL
)) != NULL
)
1360 format
[2] = strdup(tmp
);
1365 * 'colord_register_printer()' - Register profiles for a printer.
1369 colord_register_printer(
1370 cupsd_printer_t
*p
) /* I - printer */
1372 char ppdfile
[1024], /* PPD filename */
1373 iccfile
[1024]; /* ICC filename */
1374 ppd_file_t
*ppd
; /* PPD file */
1375 cups_array_t
*profiles
; /* Profile paths array */
1376 ppd_attr_t
*attr
; /* Profile attributes */
1377 const char *device_colorspace
; /* Device colorspace */
1378 char *format
[3]; /* Qualifier format tuple */
1382 * Ensure we have a D-Bus connection...
1389 * Try opening the PPD file for this printer...
1392 snprintf(ppdfile
, sizeof(ppdfile
), "%s/ppd/%s.ppd", ServerRoot
, p
->name
);
1393 if ((ppd
= _ppdOpenFile(ppdfile
, _PPD_LOCALIZATION_ICC_PROFILES
)) == NULL
)
1397 * Find out the qualifier format
1400 colord_get_qualifier_format(ppd
, format
);
1403 * See if we have any embedded profiles...
1406 profiles
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
1407 (cups_afree_func_t
)free
);
1408 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
1410 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
1411 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
1413 if (attr
->value
[0] != '/')
1414 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
1417 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
1419 if (_cupsFileCheck(iccfile
, _CUPS_FILE_CHECK_FILE
, !RunUser
,
1420 cupsdLogFCMessage
, p
))
1423 colord_create_profile(profiles
, p
->name
, attr
->spec
, COLORD_SPACE_UNKNOWN
,
1424 format
, iccfile
, COLORD_SCOPE_TEMP
);
1428 * Add the grayscale profile first. We always have a grayscale profile.
1431 colord_create_profile(profiles
, p
->name
, "Gray..", COLORD_SPACE_GRAY
,
1432 format
, NULL
, COLORD_SCOPE_TEMP
);
1435 * Then add the RGB/CMYK/DeviceN color profile...
1438 device_colorspace
= "unknown";
1439 switch (ppd
->colorspace
)
1443 device_colorspace
= COLORD_SPACE_RGB
;
1444 colord_create_profile(profiles
, p
->name
, "RGB..", COLORD_SPACE_RGB
,
1445 format
, NULL
, COLORD_SCOPE_TEMP
);
1450 device_colorspace
= COLORD_SPACE_CMYK
;
1451 colord_create_profile(profiles
, p
->name
, "CMYK..", COLORD_SPACE_CMYK
,
1452 format
, NULL
, COLORD_SCOPE_TEMP
);
1456 device_colorspace
= COLORD_SPACE_GRAY
;
1460 colord_create_profile(profiles
, p
->name
, "DeviceN..",
1461 COLORD_SPACE_UNKNOWN
, format
, NULL
,
1467 * Register the device with colord.
1470 cupsdLogMessage(CUPSD_LOG_INFO
, "Registering ICC color profiles for \"%s\".",
1472 colord_create_device(p
, ppd
, profiles
, device_colorspace
, format
,
1473 COLORD_RELATION_SOFT
, COLORD_SCOPE_TEMP
);
1476 * Free any memory we used...
1479 cupsArrayDelete(profiles
);
1490 * 'colord_unregister_printer()' - Unregister profiles for a printer.
1494 colord_unregister_printer(
1495 cupsd_printer_t
*p
) /* I - printer */
1497 char device_id
[1024]; /* Device ID as understood by colord */
1501 * Ensure we have a D-Bus connection...
1508 * Just delete the device itself, and leave the profiles registered
1511 snprintf(device_id
, sizeof(device_id
), "cups-%s", p
->name
);
1512 colord_delete_device(device_id
);
1514 #endif /* __APPLE__ */
1518 * End of "$Id: colorman.c 12226 2014-10-21 13:36:05Z msweet $".