2 * Color management routines for the CUPS scheduler.
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
9 * Original DBUS/colord code is Copyright 2011 Red Hat, Inc.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
15 * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
18 * Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33 * OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Include necessary headers...
41 #include <cups/ppd-private.h>
44 # include <ApplicationServices/ApplicationServices.h>
45 extern CFUUIDRef
ColorSyncCreateUUIDFromUInt32(unsigned id
);
46 # include <CoreFoundation/CoreFoundation.h>
47 #elif defined(HAVE_DBUS)
48 # include <dbus/dbus.h>
51 * Defines used by colord. See the reference docs for further details:
53 * http://colord.hughsie.com/api/ref-dbus.html
56 # define COLORD_SCOPE_NORMAL "normal"
58 # define COLORD_SCOPE_TEMP "temp" /* Process scope */
59 # define COLORD_SCOPE_DISK "disk" /* Lives forever, as stored in DB */
61 # define COLORD_RELATION_SOFT "soft" /* Mapping is not default */
62 # define COLORD_RELATION_HARD "hard" /* Explicitly mapped profile */
64 # define COLORD_SPACE_RGB "rgb" /* RGB colorspace */
65 # define COLORD_SPACE_CMYK "cmyk" /* CMYK colorspace */
66 # define COLORD_SPACE_GRAY "gray" /* Gray colorspace */
67 # define COLORD_SPACE_UNKNOWN "unknown"
68 /* Unknown colorspace */
70 # define COLORD_MODE_PHYSICAL "physical"
72 # define COLORD_MODE_VIRTUAL "virtual"
73 /* Virtual device with no hardware */
75 # define COLORD_KIND_PRINTER "printer"
76 /* printing output device */
78 # define COLORD_DBUS_SERVICE "org.freedesktop.ColorManager"
79 # define COLORD_DBUS_INTERFACE "org.freedesktop.ColorManager"
80 # define COLORD_DBUS_INTERFACE_DEVICE "org.freedesktop.ColorManager.Device"
81 # define COLORD_DBUS_PATH "/org/freedesktop/ColorManager"
82 /* Path for color management system */
83 # define COLORD_DBUS_TIMEOUT 5000 /* Timeout for connecting to colord in ms */
84 #endif /* __APPLE__ */
91 #if !defined(__APPLE__) && defined(HAVE_DBUS)
92 static DBusConnection
*colord_con
= NULL
;
93 /* DBUS connection for colord */
94 #endif /* !__APPLE__ && HAVE_DBUS */
102 static void apple_init_profile(ppd_file_t
*ppd
, cups_array_t
*languages
,
103 CFMutableDictionaryRef profile
,
104 unsigned id
, const char *name
,
105 const char *text
, const char *iccfile
);
106 static void apple_register_profiles(cupsd_printer_t
*p
);
107 static void apple_unregister_profiles(cupsd_printer_t
*p
);
109 #elif defined(HAVE_DBUS)
110 static void colord_create_device(cupsd_printer_t
*p
, ppd_file_t
*ppd
,
111 cups_array_t
*profiles
,
112 const char *colorspace
, char **format
,
113 const char *relation
, const char *scope
);
114 static void colord_create_profile(cups_array_t
*profiles
,
115 const char *printer_name
,
116 const char *qualifier
,
117 const char *colorspace
,
118 char **format
, const char *iccfile
,
120 static void colord_delete_device(const char *device_id
);
121 static void colord_device_add_profile(const char *device_path
,
122 const char *profile_path
,
123 const char *relation
);
124 static void colord_dict_add_strings(DBusMessageIter
*dict
,
125 const char *key
, const char *value
);
126 static char *colord_find_device(const char *device_id
);
127 static void colord_get_qualifier_format(ppd_file_t
*ppd
, char *format
[3]);
128 static void colord_register_printer(cupsd_printer_t
*p
);
129 static void colord_unregister_printer(cupsd_printer_t
*p
);
130 #endif /* __APPLE__ */
134 * 'cupsdRegisterColor()' - Register vendor color profiles in a PPD file.
138 cupsdRegisterColor(cupsd_printer_t
*p
) /* I - Printer */
143 apple_unregister_profiles(p
);
144 apple_register_profiles(p
);
147 #elif defined(HAVE_DBUS)
150 colord_unregister_printer(p
);
151 colord_register_printer(p
);
153 #endif /* __APPLE__ */
158 * 'cupsdStartColor()' - Initialize color management.
162 cupsdStartColor(void)
164 #if !defined(__APPLE__) && defined(HAVE_DBUS)
165 cupsd_printer_t
*p
; /* Current printer */
168 colord_con
= dbus_bus_get(DBUS_BUS_SYSTEM
, NULL
);
170 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
172 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
173 cupsdRegisterColor(p
);
174 #endif /* !__APPLE__ && HAVE_DBUS */
179 * 'cupsdStopColor()' - Shutdown color management.
185 #if !defined(__APPLE__) && defined(HAVE_DBUS)
187 dbus_connection_unref(colord_con
);
189 #endif /* !__APPLE__ && HAVE_DBUS */
194 * 'cupsdUnregisterColor()' - Unregister vendor color profiles in a PPD file.
198 cupsdUnregisterColor(cupsd_printer_t
*p
)/* I - Printer */
202 apple_unregister_profiles(p
);
204 #elif defined(HAVE_DBUS)
206 colord_unregister_printer(p
);
207 #endif /* __APPLE__ */
213 * 'apple_init_profile()' - Initialize a color profile.
218 ppd_file_t
*ppd
, /* I - PPD file */
219 cups_array_t
*languages
, /* I - Languages in the PPD file */
220 CFMutableDictionaryRef profile
, /* I - Profile dictionary */
221 unsigned id
, /* I - Profile ID */
222 const char *name
, /* I - Profile name */
223 const char *text
, /* I - Profile UI text */
224 const char *iccfile
) /* I - ICC filename */
226 CFURLRef url
; /* URL for profile filename */
227 CFMutableDictionaryRef dict
; /* Dictionary for name */
228 char *language
; /* Current language */
229 ppd_attr_t
*attr
; /* Profile attribute */
230 CFStringRef cflang
, /* Language string */
231 cftext
; /* Localized text */
237 * Build the profile name dictionary...
240 dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
241 &kCFTypeDictionaryKeyCallBacks
,
242 &kCFTypeDictionaryValueCallBacks
);
245 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to initialize profile \"%s\".",
250 cftext
= CFStringCreateWithCString(kCFAllocatorDefault
, text
,
251 kCFStringEncodingUTF8
);
255 CFDictionarySetValue(dict
, CFSTR("en_US"), cftext
);
262 * Find localized names for the color profiles...
265 cupsArraySave(ppd
->sorted_attrs
);
267 for (language
= (char *)cupsArrayFirst(languages
);
269 language
= (char *)cupsArrayNext(languages
))
273 if ((attr
= _ppdLocalizedAttr(ppd
, "cupsICCProfile", name
,
275 attr
= _ppdLocalizedAttr(ppd
, "APTiogaProfile", name
, language
);
278 attr
= _ppdLocalizedAttr(ppd
, "ColorModel", name
, language
);
280 if (attr
&& attr
->text
[0])
282 cflang
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
283 kCFStringEncodingUTF8
);
284 cftext
= CFStringCreateWithCString(kCFAllocatorDefault
, attr
->text
,
285 kCFStringEncodingUTF8
);
287 if (cflang
&& cftext
)
288 CFDictionarySetValue(dict
, cflang
, cftext
);
298 cupsArrayRestore(ppd
->sorted_attrs
);
302 * Fill in the profile data...
305 if (iccfile
&& *iccfile
)
307 url
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, (const UInt8
*)iccfile
, (CFIndex
)strlen(iccfile
), false);
311 CFDictionarySetValue(profile
, kColorSyncDeviceProfileURL
, url
);
316 CFDictionarySetValue(profile
, kColorSyncDeviceModeDescriptions
, dict
);
322 * 'apple_register_profiles()' - Register color profiles for a printer.
326 apple_register_profiles(
327 cupsd_printer_t
*p
) /* I - Printer */
329 int i
; /* Looping var */
330 char ppdfile
[1024], /* PPD filename */
331 iccfile
[1024], /* ICC filename */
332 selector
[PPD_MAX_NAME
];
333 /* Profile selection string */
334 ppd_file_t
*ppd
; /* PPD file */
335 ppd_attr_t
*attr
, /* Profile attributes */
336 *profileid_attr
,/* cupsProfileID attribute */
337 *q1_attr
, /* ColorModel (or other) qualifier */
338 *q2_attr
, /* MediaType (or other) qualifier */
339 *q3_attr
; /* Resolution (or other) qualifier */
340 char q_keyword
[PPD_MAX_NAME
];
341 /* Qualifier keyword */
342 const char *q1_choice
, /* ColorModel (or other) choice */
343 *q2_choice
, /* MediaType (or other) choice */
344 *q3_choice
; /* Resolution (or other) choice */
345 ppd_option_t
*cm_option
; /* Color model option */
346 ppd_choice_t
*cm_choice
; /* Color model choice */
347 int num_profiles
; /* Number of profiles */
348 OSStatus error
= 0; /* Last error */
349 unsigned device_id
, /* Printer device ID */
350 profile_id
= 0, /* Profile ID */
351 default_profile_id
= 0;
352 /* Default profile ID */
353 CFMutableDictionaryRef device_name
; /* Printer device name dictionary */
354 CFStringRef printer_name
; /* Printer name string */
355 cups_array_t
*languages
; /* Languages array */
356 CFMutableDictionaryRef profiles
, /* Dictionary of profiles */
357 profile
; /* Current profile info dictionary */
358 CFStringRef dict_key
; /* Key in factory profile dictionary */
362 * Make sure ColorSync is available...
365 if (&ColorSyncRegisterDevice
== NULL
)
369 * Try opening the PPD file for this printer...
372 snprintf(ppdfile
, sizeof(ppdfile
), "%s/ppd/%s.ppd", ServerRoot
, p
->name
);
373 if ((ppd
= _ppdOpenFile(ppdfile
, _PPD_LOCALIZATION_ICC_PROFILES
)) == NULL
)
377 * See if we have any profiles...
380 for (num_profiles
= 0, attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
382 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
383 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
385 if (attr
->value
[0] != '/')
386 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
389 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
391 if (access(iccfile
, 0))
393 cupsdLogMessage(CUPSD_LOG_ERROR
,
394 "%s: ICC Profile \"%s\" does not exist.", p
->name
,
396 cupsdSetPrinterReasons(p
, "+cups-missing-filter-warning");
404 * Create a dictionary for the factory profiles...
407 profiles
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
408 &kCFTypeDictionaryKeyCallBacks
,
409 &kCFTypeDictionaryValueCallBacks
);
412 cupsdLogMessage(CUPSD_LOG_ERROR
,
413 "Unable to allocate memory for factory profiles.");
419 * If we have profiles, add them...
422 if (num_profiles
> 0)
425 * For CUPS PPDs, figure out the default profile selector values...
428 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier1", NULL
)) != NULL
&&
429 attr
->value
&& attr
->value
[0])
431 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
432 q1_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
434 else if ((q1_attr
= ppdFindAttr(ppd
, "DefaultColorModel", NULL
)) == NULL
)
435 q1_attr
= ppdFindAttr(ppd
, "DefaultColorSpace", NULL
);
437 if (q1_attr
&& q1_attr
->value
&& q1_attr
->value
[0])
438 q1_choice
= q1_attr
->value
;
442 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier2", NULL
)) != NULL
&&
443 attr
->value
&& attr
->value
[0])
445 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
446 q2_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
449 q2_attr
= ppdFindAttr(ppd
, "DefaultMediaType", NULL
);
451 if (q2_attr
&& q2_attr
->value
&& q2_attr
->value
[0])
452 q2_choice
= q2_attr
->value
;
456 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier3", NULL
)) != NULL
&&
457 attr
->value
&& attr
->value
[0])
459 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
460 q3_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
463 q3_attr
= ppdFindAttr(ppd
, "DefaultResolution", NULL
);
465 if (q3_attr
&& q3_attr
->value
&& q3_attr
->value
[0])
466 q3_choice
= q3_attr
->value
;
471 * Loop through the profiles listed in the PPD...
474 languages
= _ppdGetLanguages(ppd
);
476 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
478 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
479 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
482 * Add this profile...
485 if (attr
->value
[0] != '/')
486 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
489 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
491 if (_cupsFileCheck(iccfile
, _CUPS_FILE_CHECK_FILE
, !RunUser
,
492 cupsdLogFCMessage
, p
))
495 cupsArraySave(ppd
->sorted_attrs
);
497 if ((profileid_attr
= ppdFindAttr(ppd
, "cupsProfileID",
498 attr
->spec
)) != NULL
&&
499 profileid_attr
->value
&& isdigit(profileid_attr
->value
[0] & 255))
500 profile_id
= (unsigned)strtoul(profileid_attr
->value
, NULL
, 10);
502 profile_id
= _ppdHashName(attr
->spec
);
504 cupsArrayRestore(ppd
->sorted_attrs
);
506 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
507 &kCFTypeDictionaryKeyCallBacks
,
508 &kCFTypeDictionaryValueCallBacks
);
511 cupsdLogMessage(CUPSD_LOG_ERROR
,
512 "Unable to allocate memory for color profile.");
518 apple_init_profile(ppd
, languages
, profile
, profile_id
, attr
->spec
,
519 attr
->text
[0] ? attr
->text
: attr
->spec
, iccfile
);
521 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
522 CFSTR("%u"), profile_id
);
525 CFDictionarySetValue(profiles
, dict_key
, profile
);
532 * See if this is the default profile...
535 if (!default_profile_id
&& q1_choice
&& q2_choice
&& q3_choice
)
537 snprintf(selector
, sizeof(selector
), "%s.%s.%s", q1_choice
, q2_choice
,
539 if (!strcmp(selector
, attr
->spec
))
540 default_profile_id
= profile_id
;
543 if (!default_profile_id
&& q1_choice
&& q2_choice
)
545 snprintf(selector
, sizeof(selector
), "%s.%s.", q1_choice
, q2_choice
);
546 if (!strcmp(selector
, attr
->spec
))
547 default_profile_id
= profile_id
;
550 if (!default_profile_id
&& q1_choice
&& q3_choice
)
552 snprintf(selector
, sizeof(selector
), "%s..%s", q1_choice
, q3_choice
);
553 if (!strcmp(selector
, attr
->spec
))
554 default_profile_id
= profile_id
;
557 if (!default_profile_id
&& q1_choice
)
559 snprintf(selector
, sizeof(selector
), "%s..", q1_choice
);
560 if (!strcmp(selector
, attr
->spec
))
561 default_profile_id
= profile_id
;
564 if (!default_profile_id
&& q2_choice
&& q3_choice
)
566 snprintf(selector
, sizeof(selector
), ".%s.%s", q2_choice
, q3_choice
);
567 if (!strcmp(selector
, attr
->spec
))
568 default_profile_id
= profile_id
;
571 if (!default_profile_id
&& q2_choice
)
573 snprintf(selector
, sizeof(selector
), ".%s.", q2_choice
);
574 if (!strcmp(selector
, attr
->spec
))
575 default_profile_id
= profile_id
;
578 if (!default_profile_id
&& q3_choice
)
580 snprintf(selector
, sizeof(selector
), "..%s", q3_choice
);
581 if (!strcmp(selector
, attr
->spec
))
582 default_profile_id
= profile_id
;
586 _ppdFreeLanguages(languages
);
588 else if ((cm_option
= ppdFindOption(ppd
, "ColorModel")) != NULL
)
591 * Extract profiles from ColorModel option...
594 const char *profile_name
; /* Name of generic profile */
597 num_profiles
= cm_option
->num_choices
;
599 for (i
= cm_option
->num_choices
, cm_choice
= cm_option
->choices
;
603 if (!strcmp(cm_choice
->choice
, "Gray") ||
604 !strcmp(cm_choice
->choice
, "Black"))
605 profile_name
= "Gray";
606 else if (!strcmp(cm_choice
->choice
, "RGB") ||
607 !strcmp(cm_choice
->choice
, "CMY"))
608 profile_name
= "RGB";
609 else if (!strcmp(cm_choice
->choice
, "CMYK") ||
610 !strcmp(cm_choice
->choice
, "KCMY"))
611 profile_name
= "CMYK";
613 profile_name
= "DeviceN";
615 snprintf(selector
, sizeof(selector
), "%s..", profile_name
);
616 profile_id
= _ppdHashName(selector
);
618 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
619 &kCFTypeDictionaryKeyCallBacks
,
620 &kCFTypeDictionaryValueCallBacks
);
623 cupsdLogMessage(CUPSD_LOG_ERROR
,
624 "Unable to allocate memory for color profile.");
630 apple_init_profile(ppd
, NULL
, profile
, profile_id
, cm_choice
->choice
,
631 cm_choice
->text
, NULL
);
633 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
634 CFSTR("%u"), profile_id
);
637 CFDictionarySetValue(profiles
, dict_key
, profile
);
643 if (cm_choice
->marked
)
644 default_profile_id
= profile_id
;
650 * Use the default colorspace...
653 attr
= ppdFindAttr(ppd
, "DefaultColorSpace", NULL
);
655 num_profiles
= (attr
&& ppd
->colorspace
== PPD_CS_GRAY
) ? 1 : 2;
658 * Add the grayscale profile first. We always have a grayscale profile.
661 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
662 &kCFTypeDictionaryKeyCallBacks
,
663 &kCFTypeDictionaryValueCallBacks
);
667 cupsdLogMessage(CUPSD_LOG_ERROR
,
668 "Unable to allocate memory for color profile.");
674 profile_id
= _ppdHashName("Gray..");
675 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "Gray", "Gray", NULL
);
677 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%u"),
681 CFDictionarySetValue(profiles
, dict_key
, profile
);
688 * Then add the RGB/CMYK/DeviceN color profile...
691 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
692 &kCFTypeDictionaryKeyCallBacks
,
693 &kCFTypeDictionaryValueCallBacks
);
697 cupsdLogMessage(CUPSD_LOG_ERROR
,
698 "Unable to allocate memory for color profile.");
704 switch (ppd
->colorspace
)
709 profile_id
= _ppdHashName("RGB..");
710 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "RGB", "RGB",
716 profile_id
= _ppdHashName("CMYK..");
717 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "CMYK", "CMYK",
726 profile_id
= _ppdHashName("DeviceN..");
727 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "DeviceN",
732 if (CFDictionaryGetCount(profile
) > 0)
734 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
735 CFSTR("%u"), profile_id
);
738 CFDictionarySetValue(profiles
, dict_key
, profile
);
746 if (num_profiles
> 0)
749 * Make sure we have a default profile ID...
752 if (!default_profile_id
)
753 default_profile_id
= profile_id
; /* Last profile */
755 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%u"),
759 CFDictionarySetValue(profiles
, kColorSyncDeviceDefaultProfileID
,
765 * Get the device ID hash and pathelogical name dictionary.
768 cupsdLogMessage(CUPSD_LOG_INFO
, "Registering ICC color profiles for \"%s\"",
771 device_id
= _ppdHashName(p
->name
);
772 device_name
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
773 &kCFTypeDictionaryKeyCallBacks
,
774 &kCFTypeDictionaryValueCallBacks
);
775 printer_name
= CFStringCreateWithCString(kCFAllocatorDefault
,
776 p
->name
, kCFStringEncodingUTF8
);
778 if (device_name
&& printer_name
)
781 * Register the device with ColorSync...
784 CFTypeRef deviceDictKeys
[] =
786 kColorSyncDeviceDescriptions
,
787 kColorSyncFactoryProfiles
,
788 kColorSyncDeviceUserScope
,
789 kColorSyncDeviceHostScope
791 CFTypeRef deviceDictVals
[] =
792 { /* Device values */
795 kCFPreferencesAnyUser
,
796 kCFPreferencesCurrentHost
798 CFDictionaryRef deviceDict
; /* Device dictionary */
799 CFUUIDRef deviceUUID
; /* Device UUID */
801 CFDictionarySetValue(device_name
, CFSTR("en_US"), printer_name
);
803 deviceDict
= CFDictionaryCreate(kCFAllocatorDefault
,
804 (const void **)deviceDictKeys
,
805 (const void **)deviceDictVals
,
806 sizeof(deviceDictKeys
) /
807 sizeof(deviceDictKeys
[0]),
808 &kCFTypeDictionaryKeyCallBacks
,
809 &kCFTypeDictionaryValueCallBacks
);
810 deviceUUID
= ColorSyncCreateUUIDFromUInt32(device_id
);
812 if (!deviceDict
|| !deviceUUID
||
813 !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass
, deviceUUID
,
818 CFRelease(deviceUUID
);
821 CFRelease(deviceDict
);
831 cupsdLogMessage(CUPSD_LOG_ERROR
,
832 "Unable to register ICC color profiles for \"%s\": %d",
833 p
->name
, (int)error
);
836 CFRelease(printer_name
);
839 CFRelease(device_name
);
843 * Free any memory we used...
853 * 'apple_unregister_profiles()' - Remove color profiles for the specified
858 apple_unregister_profiles(
859 cupsd_printer_t
*p
) /* I - Printer */
862 * Make sure ColorSync is available...
865 if (&ColorSyncUnregisterDevice
!= NULL
)
867 CFUUIDRef deviceUUID
; /* Device UUID */
869 deviceUUID
= ColorSyncCreateUUIDFromUInt32(_ppdHashName(p
->name
));
872 ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass
, deviceUUID
);
873 CFRelease(deviceUUID
);
879 #elif defined(HAVE_DBUS)
881 * 'colord_create_device()' - Create a device and register profiles.
885 colord_create_device(
886 cupsd_printer_t
*p
, /* I - Printer */
887 ppd_file_t
*ppd
, /* I - PPD file */
888 cups_array_t
*profiles
, /* I - Profiles array */
889 const char *colorspace
, /* I - Device colorspace, e.g. 'rgb' */
890 char **format
, /* I - Device qualifier format */
891 const char *relation
, /* I - Profile relation, either 'soft'
893 const char *scope
) /* I - The scope of the device, e.g.
894 'normal', 'temp' or 'disk' */
896 DBusMessage
*message
= NULL
; /* D-Bus request */
897 DBusMessage
*reply
= NULL
; /* D-Bus reply */
898 DBusMessageIter args
; /* D-Bus method arguments */
899 DBusMessageIter dict
; /* D-Bus method arguments */
900 DBusError error
; /* D-Bus error */
901 const char *device_path
; /* Device object path */
902 const char *profile_path
; /* Profile path */
903 char *default_profile_path
= NULL
;
904 /* Default profile path */
905 char device_id
[1024]; /* Device ID as understood by colord */
906 char format_str
[1024]; /* Qualifier format as a string */
910 * Create the device...
913 snprintf(device_id
, sizeof(device_id
), "cups-%s", p
->name
);
914 device_path
= device_id
;
916 message
= dbus_message_new_method_call(COLORD_DBUS_SERVICE
,
918 COLORD_DBUS_INTERFACE
,
921 dbus_message_iter_init_append(message
, &args
);
922 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &device_path
);
923 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &scope
);
925 snprintf(format_str
, sizeof(format_str
), "%s.%s.%s", format
[0], format
[1],
928 dbus_message_iter_open_container(&args
, DBUS_TYPE_ARRAY
, "{ss}", &dict
);
929 colord_dict_add_strings(&dict
, "Colorspace", colorspace
);
930 colord_dict_add_strings(&dict
, "Mode", COLORD_MODE_PHYSICAL
);
931 if (ppd
->manufacturer
)
932 colord_dict_add_strings(&dict
, "Vendor", ppd
->manufacturer
);
934 colord_dict_add_strings(&dict
, "Model", ppd
->modelname
);
935 if (p
->sanitized_device_uri
)
936 colord_dict_add_strings(&dict
, "Serial", p
->sanitized_device_uri
);
937 colord_dict_add_strings(&dict
, "Format", format_str
);
938 colord_dict_add_strings(&dict
, "Kind", COLORD_KIND_PRINTER
);
939 dbus_message_iter_close_container(&args
, &dict
);
942 * Send the CreateDevice request synchronously...
945 dbus_error_init(&error
);
946 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling CreateDevice(%s,%s)", device_id
,
948 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
953 cupsdLogMessage(CUPSD_LOG_WARN
, "CreateDevice failed: %s:%s", error
.name
,
955 dbus_error_free(&error
);
963 dbus_message_iter_init(reply
, &args
);
964 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
966 cupsdLogMessage(CUPSD_LOG_WARN
,
967 "CreateDevice failed: Incorrect reply type.");
971 dbus_message_iter_get_basic(&args
, &device_path
);
972 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Created device \"%s\".", device_path
);
978 for (profile_path
= cupsArrayFirst(profiles
);
980 profile_path
= cupsArrayNext(profiles
))
982 colord_device_add_profile(device_path
, profile_path
, relation
);
987 if (default_profile_path
)
988 free(default_profile_path
);
991 dbus_message_unref(message
);
994 dbus_message_unref(reply
);
999 * 'colord_create_profile()' - Create a color profile for a printer.
1003 colord_create_profile(
1004 cups_array_t
*profiles
, /* I - Profiles array */
1005 const char *printer_name
, /* I - Printer name */
1006 const char *qualifier
, /* I - Profile qualifier */
1007 const char *colorspace
, /* I - Profile colorspace */
1008 char **format
, /* I - Profile qualifier format */
1009 const char *iccfile
, /* I - ICC filename */
1010 const char *scope
) /* I - The scope of the profile, e.g.
1011 'normal', 'temp' or 'disk' */
1013 DBusMessage
*message
= NULL
; /* D-Bus request */
1014 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1015 DBusMessageIter args
; /* D-Bus method arguments */
1016 DBusMessageIter dict
; /* D-Bus method arguments */
1017 DBusError error
; /* D-Bus error */
1018 char *idstr
; /* Profile ID string */
1019 size_t idstrlen
; /* Profile ID allocated length */
1020 const char *profile_path
; /* Device object path */
1021 char format_str
[1024]; /* Qualifier format as a string */
1025 * Create the profile...
1028 message
= dbus_message_new_method_call(COLORD_DBUS_SERVICE
,
1030 COLORD_DBUS_INTERFACE
,
1033 idstrlen
= strlen(printer_name
) + 1 + strlen(qualifier
) + 1;
1034 if ((idstr
= malloc(idstrlen
)) == NULL
)
1036 snprintf(idstr
, idstrlen
, "%s-%s", printer_name
, qualifier
);
1037 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Using profile ID \"%s\".", idstr
);
1039 dbus_message_iter_init_append(message
, &args
);
1040 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &idstr
);
1041 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &scope
);
1043 snprintf(format_str
, sizeof(format_str
), "%s.%s.%s", format
[0], format
[1],
1046 dbus_message_iter_open_container(&args
, DBUS_TYPE_ARRAY
, "{ss}", &dict
);
1047 colord_dict_add_strings(&dict
, "Qualifier", qualifier
);
1048 colord_dict_add_strings(&dict
, "Format", format_str
);
1049 colord_dict_add_strings(&dict
, "Colorspace", colorspace
);
1051 colord_dict_add_strings(&dict
, "Filename", iccfile
);
1052 dbus_message_iter_close_container(&args
, &dict
);
1055 * Send the CreateProfile request synchronously...
1058 dbus_error_init(&error
);
1059 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling CreateProfile(%s,%s)", idstr
,
1061 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1062 COLORD_DBUS_TIMEOUT
,
1066 cupsdLogMessage(CUPSD_LOG_WARN
, "CreateProfile failed: %s:%s", error
.name
,
1068 dbus_error_free(&error
);
1076 dbus_message_iter_init(reply
, &args
);
1077 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
1079 cupsdLogMessage(CUPSD_LOG_WARN
,
1080 "CreateProfile failed: Incorrect reply type.");
1084 dbus_message_iter_get_basic(&args
, &profile_path
);
1085 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Created profile \"%s\".", profile_path
);
1086 cupsArrayAdd(profiles
, strdup(profile_path
));
1091 dbus_message_unref(message
);
1094 dbus_message_unref(reply
);
1102 * 'colord_delete_device()' - Delete a device
1106 colord_delete_device(
1107 const char *device_id
) /* I - Device ID string */
1109 DBusMessage
*message
= NULL
; /* D-Bus request */
1110 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1111 DBusMessageIter args
; /* D-Bus method arguments */
1112 DBusError error
; /* D-Bus error */
1113 char *device_path
; /* Device object path */
1117 * Find the device...
1120 if ((device_path
= colord_find_device(device_id
)) == NULL
)
1124 * Delete the device...
1127 message
= dbus_message_new_method_call(COLORD_DBUS_SERVICE
,
1129 COLORD_DBUS_INTERFACE
,
1132 dbus_message_iter_init_append(message
, &args
);
1133 dbus_message_iter_append_basic(&args
, DBUS_TYPE_OBJECT_PATH
, &device_path
);
1136 * Send the DeleteDevice request synchronously...
1139 dbus_error_init(&error
);
1140 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling DeleteDevice(%s)", device_path
);
1141 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1142 COLORD_DBUS_TIMEOUT
,
1146 cupsdLogMessage(CUPSD_LOG_DEBUG
, "DeleteDevice failed: %s:%s", error
.name
,
1148 dbus_error_free(&error
);
1158 dbus_message_unref(message
);
1161 dbus_message_unref(reply
);
1166 * 'colord_device_add_profile()' - Assign a profile to a device.
1170 colord_device_add_profile(
1171 const char *device_path
, /* I - Device object path */
1172 const char *profile_path
, /* I - Profile object path */
1173 const char *relation
) /* I - Device relation, either
1176 DBusMessage
*message
= NULL
; /* D-Bus request */
1177 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1178 DBusMessageIter args
; /* D-Bus method arguments */
1179 DBusError error
; /* D-Bus error */
1182 message
= dbus_message_new_method_call(COLORD_DBUS_SERVICE
,
1184 COLORD_DBUS_INTERFACE_DEVICE
,
1187 dbus_message_iter_init_append(message
, &args
);
1188 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &relation
);
1189 dbus_message_iter_append_basic(&args
, DBUS_TYPE_OBJECT_PATH
, &profile_path
);
1190 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling %s:AddProfile(%s) [%s]",
1191 device_path
, profile_path
, relation
);
1194 * Send the AddProfile request synchronously...
1197 dbus_error_init(&error
);
1198 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1199 COLORD_DBUS_TIMEOUT
,
1203 cupsdLogMessage(CUPSD_LOG_WARN
, "AddProfile failed: %s:%s", error
.name
,
1205 dbus_error_free(&error
);
1212 dbus_message_unref(message
);
1215 dbus_message_unref(reply
);
1220 * 'colord_dict_add_strings()' - Add two strings to a dictionary.
1224 colord_dict_add_strings(
1225 DBusMessageIter
*dict
, /* I - Dictionary */
1226 const char *key
, /* I - Key string */
1227 const char *value
) /* I - Value string */
1229 DBusMessageIter entry
; /* Entry to add */
1232 dbus_message_iter_open_container(dict
, DBUS_TYPE_DICT_ENTRY
, NULL
, &entry
);
1233 dbus_message_iter_append_basic(&entry
, DBUS_TYPE_STRING
, &key
);
1234 dbus_message_iter_append_basic(&entry
, DBUS_TYPE_STRING
, &value
);
1235 dbus_message_iter_close_container(dict
, &entry
);
1240 * 'colord_find_device()' - Finds a device
1243 static char * /* O - Device path or NULL */
1245 const char *device_id
) /* I - Device ID string */
1247 DBusMessage
*message
= NULL
; /* D-Bus request */
1248 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1249 DBusMessageIter args
; /* D-Bus method arguments */
1250 DBusError error
; /* D-Bus error */
1251 const char *device_path_tmp
; /* Device object path */
1252 char *device_path
= NULL
; /* Device object path */
1255 message
= dbus_message_new_method_call(COLORD_DBUS_SERVICE
,
1257 COLORD_DBUS_INTERFACE
,
1260 dbus_message_iter_init_append(message
, &args
);
1261 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &device_id
);
1264 * Send the FindDeviceById request synchronously...
1267 dbus_error_init(&error
);
1268 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling FindDeviceById(%s)", device_id
);
1269 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1270 COLORD_DBUS_TIMEOUT
,
1274 cupsdLogMessage(CUPSD_LOG_DEBUG
, "FindDeviceById failed: %s:%s",
1275 error
.name
, error
.message
);
1276 dbus_error_free(&error
);
1284 dbus_message_iter_init(reply
, &args
);
1285 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
1287 cupsdLogMessage(CUPSD_LOG_WARN
,
1288 "FindDeviceById failed: Incorrect reply type.");
1292 dbus_message_iter_get_basic(&args
, &device_path_tmp
);
1293 if (device_path_tmp
)
1294 device_path
= strdup(device_path_tmp
);
1299 dbus_message_unref(message
);
1302 dbus_message_unref(reply
);
1304 return (device_path
);
1309 * 'colord_get_qualifier_format()' - Get the qualifier format.
1311 * Note: Returns a value of "ColorSpace.MediaType.Resolution" by default.
1315 colord_get_qualifier_format(
1316 ppd_file_t
*ppd
, /* I - PPD file data */
1317 char *format
[3]) /* I - Format tuple */
1319 const char *tmp
; /* Temporary string */
1320 ppd_attr_t
*attr
; /* Profile attributes */
1324 * Get 1st section...
1327 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier1", NULL
)) != NULL
)
1329 else if (ppdFindAttr(ppd
, "DefaultColorModel", NULL
))
1331 else if (ppdFindAttr(ppd
, "DefaultColorSpace", NULL
))
1336 format
[0] = strdup(tmp
);
1339 * Get 2nd section...
1342 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier2", NULL
)) != NULL
)
1347 format
[1] = strdup(tmp
);
1350 * Get 3rd section...
1353 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier3", NULL
)) != NULL
)
1358 format
[2] = strdup(tmp
);
1363 * 'colord_register_printer()' - Register profiles for a printer.
1367 colord_register_printer(
1368 cupsd_printer_t
*p
) /* I - printer */
1370 char ppdfile
[1024], /* PPD filename */
1371 iccfile
[1024]; /* ICC filename */
1372 ppd_file_t
*ppd
; /* PPD file */
1373 cups_array_t
*profiles
; /* Profile paths array */
1374 ppd_attr_t
*attr
; /* Profile attributes */
1375 const char *device_colorspace
; /* Device colorspace */
1376 char *format
[3]; /* Qualifier format tuple */
1380 * Ensure we have a D-Bus connection...
1387 * Try opening the PPD file for this printer...
1390 snprintf(ppdfile
, sizeof(ppdfile
), "%s/ppd/%s.ppd", ServerRoot
, p
->name
);
1391 if ((ppd
= _ppdOpenFile(ppdfile
, _PPD_LOCALIZATION_ICC_PROFILES
)) == NULL
)
1395 * Find out the qualifier format
1398 colord_get_qualifier_format(ppd
, format
);
1401 * See if we have any embedded profiles...
1404 profiles
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
1405 (cups_afree_func_t
)free
);
1406 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
1408 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
1409 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
1411 if (attr
->value
[0] != '/')
1412 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
1415 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
1417 if (_cupsFileCheck(iccfile
, _CUPS_FILE_CHECK_FILE
, !RunUser
,
1418 cupsdLogFCMessage
, p
))
1421 colord_create_profile(profiles
, p
->name
, attr
->spec
, COLORD_SPACE_UNKNOWN
,
1422 format
, iccfile
, COLORD_SCOPE_TEMP
);
1426 * Add the grayscale profile first. We always have a grayscale profile.
1429 colord_create_profile(profiles
, p
->name
, "Gray..", COLORD_SPACE_GRAY
,
1430 format
, NULL
, COLORD_SCOPE_TEMP
);
1433 * Then add the RGB/CMYK/DeviceN color profile...
1436 device_colorspace
= "unknown";
1437 switch (ppd
->colorspace
)
1441 device_colorspace
= COLORD_SPACE_RGB
;
1442 colord_create_profile(profiles
, p
->name
, "RGB..", COLORD_SPACE_RGB
,
1443 format
, NULL
, COLORD_SCOPE_TEMP
);
1448 device_colorspace
= COLORD_SPACE_CMYK
;
1449 colord_create_profile(profiles
, p
->name
, "CMYK..", COLORD_SPACE_CMYK
,
1450 format
, NULL
, COLORD_SCOPE_TEMP
);
1454 device_colorspace
= COLORD_SPACE_GRAY
;
1458 colord_create_profile(profiles
, p
->name
, "DeviceN..",
1459 COLORD_SPACE_UNKNOWN
, format
, NULL
,
1465 * Register the device with colord.
1468 cupsdLogMessage(CUPSD_LOG_INFO
, "Registering ICC color profiles for \"%s\".",
1470 colord_create_device(p
, ppd
, profiles
, device_colorspace
, format
,
1471 COLORD_RELATION_SOFT
, COLORD_SCOPE_TEMP
);
1474 * Free any memory we used...
1477 cupsArrayDelete(profiles
);
1488 * 'colord_unregister_printer()' - Unregister profiles for a printer.
1492 colord_unregister_printer(
1493 cupsd_printer_t
*p
) /* I - printer */
1495 char device_id
[1024]; /* Device ID as understood by colord */
1499 * Ensure we have a D-Bus connection...
1506 * Just delete the device itself, and leave the profiles registered
1509 snprintf(device_id
, sizeof(device_id
), "cups-%s", p
->name
);
1510 colord_delete_device(device_id
);
1512 #endif /* __APPLE__ */