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 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * missing or damaged, see the license at "http://www.cups.org/".
13 * Original DBUS/colord code is Copyright 2011 Red Hat, Inc.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
19 * Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
22 * Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution.
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
31 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
41 * Include necessary headers...
45 #include <cups/ppd-private.h>
48 # include <ApplicationServices/ApplicationServices.h>
49 extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
50 # include <CoreFoundation/CoreFoundation.h>
51 #elif defined(HAVE_DBUS)
52 # include <dbus/dbus.h>
55 * Defines used by colord. See the reference docs for further details:
57 * http://colord.hughsie.com/api/ref-dbus.html
60 # define COLORD_SCOPE_NORMAL "normal"
62 # define COLORD_SCOPE_TEMP "temp" /* Process scope */
63 # define COLORD_SCOPE_DISK "disk" /* Lives forever, as stored in DB */
65 # define COLORD_RELATION_SOFT "soft" /* Mapping is not default */
66 # define COLORD_RELATION_HARD "hard" /* Explicitly mapped profile */
68 # define COLORD_SPACE_RGB "rgb" /* RGB colorspace */
69 # define COLORD_SPACE_CMYK "cmyk" /* CMYK colorspace */
70 # define COLORD_SPACE_GRAY "gray" /* Gray colorspace */
71 # define COLORD_SPACE_UNKNOWN "unknown"
72 /* Unknown colorspace */
74 # define COLORD_MODE_PHYSICAL "physical"
76 # define COLORD_MODE_VIRTUAL "virtual"
77 /* Virtual device with no hardware */
79 # define COLORD_KIND_PRINTER "printer"
80 /* printing output device */
82 # define COLORD_DBUS_SERVICE "org.freedesktop.ColorManager"
83 # define COLORD_DBUS_INTERFACE "org.freedesktop.ColorManager"
84 # define COLORD_DBUS_INTERFACE_DEVICE "org.freedesktop.ColorManager.Device"
85 # define COLORD_DBUS_PATH "/org/freedesktop/ColorManager"
86 /* Path for color management system */
87 # define COLORD_DBUS_TIMEOUT 5000 /* Timeout for connecting to colord in ms */
88 #endif /* __APPLE__ */
95 #if !defined(__APPLE__) && defined(HAVE_DBUS)
96 static DBusConnection *colord_con = NULL;
97 /* DBUS connection for colord */
98 #endif /* !__APPLE__ && HAVE_DBUS */
106 static void apple_init_profile(ppd_file_t *ppd, cups_array_t *languages,
107 CFMutableDictionaryRef profile,
108 unsigned id, const char *name,
109 const char *text, const char *iccfile);
110 static void apple_register_profiles(cupsd_printer_t *p);
111 static void apple_unregister_profiles(cupsd_printer_t *p);
113 #elif defined(HAVE_DBUS)
114 static void colord_create_device(cupsd_printer_t *p, ppd_file_t *ppd,
115 cups_array_t *profiles,
116 const char *colorspace, char **format,
117 const char *relation, const char *scope);
118 static void colord_create_profile(cups_array_t *profiles,
119 const char *printer_name,
120 const char *qualifier,
121 const char *colorspace,
122 char **format, const char *iccfile,
124 static void colord_delete_device(const char *device_id);
125 static void colord_device_add_profile(const char *device_path,
126 const char *profile_path,
127 const char *relation);
128 static void colord_dict_add_strings(DBusMessageIter *dict,
129 const char *key, const char *value);
130 static char *colord_find_device(const char *device_id);
131 static void colord_get_qualifier_format(ppd_file_t *ppd, char *format[3]);
132 static void colord_register_printer(cupsd_printer_t *p);
133 static void colord_unregister_printer(cupsd_printer_t *p);
134 #endif /* __APPLE__ */
138 * 'cupsdRegisterColor()' - Register vendor color profiles in a PPD file.
142 cupsdRegisterColor(cupsd_printer_t *p) /* I - Printer */
147 apple_unregister_profiles(p);
148 apple_register_profiles(p);
151 #elif defined(HAVE_DBUS)
154 colord_unregister_printer(p);
155 colord_register_printer(p);
157 #endif /* __APPLE__ */
162 * 'cupsdStartColor()' - Initialize color management.
166 cupsdStartColor(void)
168 #if !defined(__APPLE__) && defined(HAVE_DBUS)
169 cupsd_printer_t *p; /* Current printer */
172 colord_con = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
174 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
176 p = (cupsd_printer_t *)cupsArrayNext(Printers))
177 cupsdRegisterColor(p);
178 #endif /* !__APPLE__ && HAVE_DBUS */
183 * 'cupsdStopColor()' - Shutdown color management.
189 #if !defined(__APPLE__) && defined(HAVE_DBUS)
191 dbus_connection_unref(colord_con);
193 #endif /* !__APPLE__ && HAVE_DBUS */
198 * 'cupsdUnregisterColor()' - Unregister vendor color profiles in a PPD file.
202 cupsdUnregisterColor(cupsd_printer_t *p)/* I - Printer */
206 apple_unregister_profiles(p);
208 #elif defined(HAVE_DBUS)
210 colord_unregister_printer(p);
211 #endif /* __APPLE__ */
217 * 'apple_init_profile()' - Initialize a color profile.
222 ppd_file_t *ppd, /* I - PPD file */
223 cups_array_t *languages, /* I - Languages in the PPD file */
224 CFMutableDictionaryRef profile, /* I - Profile dictionary */
225 unsigned id, /* I - Profile ID */
226 const char *name, /* I - Profile name */
227 const char *text, /* I - Profile UI text */
228 const char *iccfile) /* I - ICC filename */
230 CFURLRef url; /* URL for profile filename */
231 CFMutableDictionaryRef dict; /* Dictionary for name */
232 char *language; /* Current language */
233 ppd_attr_t *attr; /* Profile attribute */
234 CFStringRef cflang, /* Language string */
235 cftext; /* Localized text */
241 * Build the profile name dictionary...
244 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
245 &kCFTypeDictionaryKeyCallBacks,
246 &kCFTypeDictionaryValueCallBacks);
249 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize profile \"%s\".",
254 cftext = CFStringCreateWithCString(kCFAllocatorDefault, text,
255 kCFStringEncodingUTF8);
259 CFDictionarySetValue(dict, CFSTR("en_US"), cftext);
266 * Find localized names for the color profiles...
269 cupsArraySave(ppd->sorted_attrs);
271 for (language = (char *)cupsArrayFirst(languages);
273 language = (char *)cupsArrayNext(languages))
277 if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name,
279 attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language);
282 attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language);
284 if (attr && attr->text[0])
286 cflang = CFStringCreateWithCString(kCFAllocatorDefault, language,
287 kCFStringEncodingUTF8);
288 cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text,
289 kCFStringEncodingUTF8);
291 if (cflang && cftext)
292 CFDictionarySetValue(dict, cflang, cftext);
302 cupsArrayRestore(ppd->sorted_attrs);
306 * Fill in the profile data...
309 if (iccfile && *iccfile)
311 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)iccfile, (CFIndex)strlen(iccfile), false);
315 CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url);
320 CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict);
326 * 'apple_register_profiles()' - Register color profiles for a printer.
330 apple_register_profiles(
331 cupsd_printer_t *p) /* I - Printer */
333 int i; /* Looping var */
334 char ppdfile[1024], /* PPD filename */
335 iccfile[1024], /* ICC filename */
336 selector[PPD_MAX_NAME];
337 /* Profile selection string */
338 ppd_file_t *ppd; /* PPD file */
339 ppd_attr_t *attr, /* Profile attributes */
340 *profileid_attr,/* cupsProfileID attribute */
341 *q1_attr, /* ColorModel (or other) qualifier */
342 *q2_attr, /* MediaType (or other) qualifier */
343 *q3_attr; /* Resolution (or other) qualifier */
344 char q_keyword[PPD_MAX_NAME];
345 /* Qualifier keyword */
346 const char *q1_choice, /* ColorModel (or other) choice */
347 *q2_choice, /* MediaType (or other) choice */
348 *q3_choice; /* Resolution (or other) choice */
349 ppd_option_t *cm_option; /* Color model option */
350 ppd_choice_t *cm_choice; /* Color model choice */
351 int num_profiles; /* Number of profiles */
352 OSStatus error = 0; /* Last error */
353 unsigned device_id, /* Printer device ID */
354 profile_id = 0, /* Profile ID */
355 default_profile_id = 0;
356 /* Default profile ID */
357 CFMutableDictionaryRef device_name; /* Printer device name dictionary */
358 CFStringRef printer_name; /* Printer name string */
359 cups_array_t *languages; /* Languages array */
360 CFMutableDictionaryRef profiles, /* Dictionary of profiles */
361 profile; /* Current profile info dictionary */
362 CFStringRef dict_key; /* Key in factory profile dictionary */
366 * Make sure ColorSync is available...
369 if (&ColorSyncRegisterDevice == NULL)
373 * Try opening the PPD file for this printer...
376 snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
377 if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
381 * See if we have any profiles...
384 for (num_profiles = 0, attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
386 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
387 if (attr->spec[0] && attr->value && attr->value[0])
389 if (attr->value[0] != '/')
390 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
393 strlcpy(iccfile, attr->value, sizeof(iccfile));
395 if (access(iccfile, 0))
397 cupsdLogMessage(CUPSD_LOG_ERROR,
398 "%s: ICC Profile \"%s\" does not exist.", p->name,
400 cupsdSetPrinterReasons(p, "+cups-missing-filter-warning");
408 * Create a dictionary for the factory profiles...
411 profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
412 &kCFTypeDictionaryKeyCallBacks,
413 &kCFTypeDictionaryValueCallBacks);
416 cupsdLogMessage(CUPSD_LOG_ERROR,
417 "Unable to allocate memory for factory profiles.");
423 * If we have profiles, add them...
426 if (num_profiles > 0)
429 * For CUPS PPDs, figure out the default profile selector values...
432 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL &&
433 attr->value && attr->value[0])
435 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
436 q1_attr = ppdFindAttr(ppd, q_keyword, NULL);
438 else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL)
439 q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
441 if (q1_attr && q1_attr->value && q1_attr->value[0])
442 q1_choice = q1_attr->value;
446 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
447 attr->value && attr->value[0])
449 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
450 q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
453 q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
455 if (q2_attr && q2_attr->value && q2_attr->value[0])
456 q2_choice = q2_attr->value;
460 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
461 attr->value && attr->value[0])
463 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
464 q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
467 q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
469 if (q3_attr && q3_attr->value && q3_attr->value[0])
470 q3_choice = q3_attr->value;
475 * Loop through the profiles listed in the PPD...
478 languages = _ppdGetLanguages(ppd);
480 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
482 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
483 if (attr->spec[0] && attr->value && attr->value[0])
486 * Add this profile...
489 if (attr->value[0] != '/')
490 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
493 strlcpy(iccfile, attr->value, sizeof(iccfile));
495 if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
496 cupsdLogFCMessage, p))
499 cupsArraySave(ppd->sorted_attrs);
501 if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID",
502 attr->spec)) != NULL &&
503 profileid_attr->value && isdigit(profileid_attr->value[0] & 255))
504 profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10);
506 profile_id = _ppdHashName(attr->spec);
508 cupsArrayRestore(ppd->sorted_attrs);
510 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
511 &kCFTypeDictionaryKeyCallBacks,
512 &kCFTypeDictionaryValueCallBacks);
515 cupsdLogMessage(CUPSD_LOG_ERROR,
516 "Unable to allocate memory for color profile.");
522 apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
523 attr->text[0] ? attr->text : attr->spec, iccfile);
525 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
526 CFSTR("%u"), profile_id);
529 CFDictionarySetValue(profiles, dict_key, profile);
536 * See if this is the default profile...
539 if (!default_profile_id && q1_choice && q2_choice && q3_choice)
541 snprintf(selector, sizeof(selector), "%s.%s.%s", q1_choice, q2_choice,
543 if (!strcmp(selector, attr->spec))
544 default_profile_id = profile_id;
547 if (!default_profile_id && q1_choice && q2_choice)
549 snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, q2_choice);
550 if (!strcmp(selector, attr->spec))
551 default_profile_id = profile_id;
554 if (!default_profile_id && q1_choice && q3_choice)
556 snprintf(selector, sizeof(selector), "%s..%s", q1_choice, q3_choice);
557 if (!strcmp(selector, attr->spec))
558 default_profile_id = profile_id;
561 if (!default_profile_id && q1_choice)
563 snprintf(selector, sizeof(selector), "%s..", q1_choice);
564 if (!strcmp(selector, attr->spec))
565 default_profile_id = profile_id;
568 if (!default_profile_id && q2_choice && q3_choice)
570 snprintf(selector, sizeof(selector), ".%s.%s", q2_choice, q3_choice);
571 if (!strcmp(selector, attr->spec))
572 default_profile_id = profile_id;
575 if (!default_profile_id && q2_choice)
577 snprintf(selector, sizeof(selector), ".%s.", q2_choice);
578 if (!strcmp(selector, attr->spec))
579 default_profile_id = profile_id;
582 if (!default_profile_id && q3_choice)
584 snprintf(selector, sizeof(selector), "..%s", q3_choice);
585 if (!strcmp(selector, attr->spec))
586 default_profile_id = profile_id;
590 _ppdFreeLanguages(languages);
592 else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
595 * Extract profiles from ColorModel option...
598 const char *profile_name; /* Name of generic profile */
601 num_profiles = cm_option->num_choices;
603 for (i = cm_option->num_choices, cm_choice = cm_option->choices;
607 if (!strcmp(cm_choice->choice, "Gray") ||
608 !strcmp(cm_choice->choice, "Black"))
609 profile_name = "Gray";
610 else if (!strcmp(cm_choice->choice, "RGB") ||
611 !strcmp(cm_choice->choice, "CMY"))
612 profile_name = "RGB";
613 else if (!strcmp(cm_choice->choice, "CMYK") ||
614 !strcmp(cm_choice->choice, "KCMY"))
615 profile_name = "CMYK";
617 profile_name = "DeviceN";
619 snprintf(selector, sizeof(selector), "%s..", profile_name);
620 profile_id = _ppdHashName(selector);
622 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
623 &kCFTypeDictionaryKeyCallBacks,
624 &kCFTypeDictionaryValueCallBacks);
627 cupsdLogMessage(CUPSD_LOG_ERROR,
628 "Unable to allocate memory for color profile.");
634 apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
635 cm_choice->text, NULL);
637 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
638 CFSTR("%u"), profile_id);
641 CFDictionarySetValue(profiles, dict_key, profile);
647 if (cm_choice->marked)
648 default_profile_id = profile_id;
654 * Use the default colorspace...
657 attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
659 num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2;
662 * Add the grayscale profile first. We always have a grayscale profile.
665 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
666 &kCFTypeDictionaryKeyCallBacks,
667 &kCFTypeDictionaryValueCallBacks);
671 cupsdLogMessage(CUPSD_LOG_ERROR,
672 "Unable to allocate memory for color profile.");
678 profile_id = _ppdHashName("Gray..");
679 apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL);
681 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
685 CFDictionarySetValue(profiles, dict_key, profile);
692 * Then add the RGB/CMYK/DeviceN color profile...
695 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
696 &kCFTypeDictionaryKeyCallBacks,
697 &kCFTypeDictionaryValueCallBacks);
701 cupsdLogMessage(CUPSD_LOG_ERROR,
702 "Unable to allocate memory for color profile.");
708 switch (ppd->colorspace)
713 profile_id = _ppdHashName("RGB..");
714 apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB",
720 profile_id = _ppdHashName("CMYK..");
721 apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK",
730 profile_id = _ppdHashName("DeviceN..");
731 apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN",
736 if (CFDictionaryGetCount(profile) > 0)
738 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
739 CFSTR("%u"), profile_id);
742 CFDictionarySetValue(profiles, dict_key, profile);
750 if (num_profiles > 0)
753 * Make sure we have a default profile ID...
756 if (!default_profile_id)
757 default_profile_id = profile_id; /* Last profile */
759 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
763 CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID,
769 * Get the device ID hash and pathelogical name dictionary.
772 cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"",
775 device_id = _ppdHashName(p->name);
776 device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
777 &kCFTypeDictionaryKeyCallBacks,
778 &kCFTypeDictionaryValueCallBacks);
779 printer_name = CFStringCreateWithCString(kCFAllocatorDefault,
780 p->name, kCFStringEncodingUTF8);
782 if (device_name && printer_name)
785 * Register the device with ColorSync...
788 CFTypeRef deviceDictKeys[] =
790 kColorSyncDeviceDescriptions,
791 kColorSyncFactoryProfiles,
792 kColorSyncDeviceUserScope,
793 kColorSyncDeviceHostScope
795 CFTypeRef deviceDictVals[] =
796 { /* Device values */
799 kCFPreferencesAnyUser,
800 kCFPreferencesCurrentHost
802 CFDictionaryRef deviceDict; /* Device dictionary */
803 CFUUIDRef deviceUUID; /* Device UUID */
805 CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name);
807 deviceDict = CFDictionaryCreate(kCFAllocatorDefault,
808 (const void **)deviceDictKeys,
809 (const void **)deviceDictVals,
810 sizeof(deviceDictKeys) /
811 sizeof(deviceDictKeys[0]),
812 &kCFTypeDictionaryKeyCallBacks,
813 &kCFTypeDictionaryValueCallBacks);
814 deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id);
816 if (!deviceDict || !deviceUUID ||
817 !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID,
822 CFRelease(deviceUUID);
825 CFRelease(deviceDict);
835 cupsdLogMessage(CUPSD_LOG_ERROR,
836 "Unable to register ICC color profiles for \"%s\": %d",
837 p->name, (int)error);
840 CFRelease(printer_name);
843 CFRelease(device_name);
847 * Free any memory we used...
857 * 'apple_unregister_profiles()' - Remove color profiles for the specified
862 apple_unregister_profiles(
863 cupsd_printer_t *p) /* I - Printer */
866 * Make sure ColorSync is available...
869 if (&ColorSyncUnregisterDevice != NULL)
871 CFUUIDRef deviceUUID; /* Device UUID */
873 deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name));
876 ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
877 CFRelease(deviceUUID);
883 #elif defined(HAVE_DBUS)
885 * 'colord_create_device()' - Create a device and register profiles.
889 colord_create_device(
890 cupsd_printer_t *p, /* I - Printer */
891 ppd_file_t *ppd, /* I - PPD file */
892 cups_array_t *profiles, /* I - Profiles array */
893 const char *colorspace, /* I - Device colorspace, e.g. 'rgb' */
894 char **format, /* I - Device qualifier format */
895 const char *relation, /* I - Profile relation, either 'soft'
897 const char *scope) /* I - The scope of the device, e.g.
898 'normal', 'temp' or 'disk' */
900 DBusMessage *message = NULL; /* D-Bus request */
901 DBusMessage *reply = NULL; /* D-Bus reply */
902 DBusMessageIter args; /* D-Bus method arguments */
903 DBusMessageIter dict; /* D-Bus method arguments */
904 DBusError error; /* D-Bus error */
905 const char *device_path; /* Device object path */
906 const char *profile_path; /* Profile path */
907 char *default_profile_path = NULL;
908 /* Default profile path */
909 char device_id[1024]; /* Device ID as understood by colord */
910 char format_str[1024]; /* Qualifier format as a string */
914 * Create the device...
917 snprintf(device_id, sizeof(device_id), "cups-%s", p->name);
918 device_path = device_id;
920 message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
922 COLORD_DBUS_INTERFACE,
925 dbus_message_iter_init_append(message, &args);
926 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_path);
927 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope);
929 snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1],
932 dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict);
933 colord_dict_add_strings(&dict, "Colorspace", colorspace);
934 colord_dict_add_strings(&dict, "Mode", COLORD_MODE_PHYSICAL);
935 if (ppd->manufacturer)
936 colord_dict_add_strings(&dict, "Vendor", ppd->manufacturer);
938 colord_dict_add_strings(&dict, "Model", ppd->modelname);
939 if (p->sanitized_device_uri)
940 colord_dict_add_strings(&dict, "Serial", p->sanitized_device_uri);
941 colord_dict_add_strings(&dict, "Format", format_str);
942 colord_dict_add_strings(&dict, "Kind", COLORD_KIND_PRINTER);
943 dbus_message_iter_close_container(&args, &dict);
946 * Send the CreateDevice request synchronously...
949 dbus_error_init(&error);
950 cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateDevice(%s,%s)", device_id,
952 reply = dbus_connection_send_with_reply_and_block(colord_con, message,
957 cupsdLogMessage(CUPSD_LOG_WARN, "CreateDevice failed: %s:%s", error.name,
959 dbus_error_free(&error);
967 dbus_message_iter_init(reply, &args);
968 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
970 cupsdLogMessage(CUPSD_LOG_WARN,
971 "CreateDevice failed: Incorrect reply type.");
975 dbus_message_iter_get_basic(&args, &device_path);
976 cupsdLogMessage(CUPSD_LOG_DEBUG, "Created device \"%s\".", device_path);
982 for (profile_path = cupsArrayFirst(profiles);
984 profile_path = cupsArrayNext(profiles))
986 colord_device_add_profile(device_path, profile_path, relation);
991 if (default_profile_path)
992 free(default_profile_path);
995 dbus_message_unref(message);
998 dbus_message_unref(reply);
1003 * 'colord_create_profile()' - Create a color profile for a printer.
1007 colord_create_profile(
1008 cups_array_t *profiles, /* I - Profiles array */
1009 const char *printer_name, /* I - Printer name */
1010 const char *qualifier, /* I - Profile qualifier */
1011 const char *colorspace, /* I - Profile colorspace */
1012 char **format, /* I - Profile qualifier format */
1013 const char *iccfile, /* I - ICC filename */
1014 const char *scope) /* I - The scope of the profile, e.g.
1015 'normal', 'temp' or 'disk' */
1017 DBusMessage *message = NULL; /* D-Bus request */
1018 DBusMessage *reply = NULL; /* D-Bus reply */
1019 DBusMessageIter args; /* D-Bus method arguments */
1020 DBusMessageIter dict; /* D-Bus method arguments */
1021 DBusError error; /* D-Bus error */
1022 char *idstr; /* Profile ID string */
1023 size_t idstrlen; /* Profile ID allocated length */
1024 const char *profile_path; /* Device object path */
1025 char format_str[1024]; /* Qualifier format as a string */
1029 * Create the profile...
1032 message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1034 COLORD_DBUS_INTERFACE,
1037 idstrlen = strlen(printer_name) + 1 + strlen(qualifier) + 1;
1038 if ((idstr = malloc(idstrlen)) == NULL)
1040 snprintf(idstr, idstrlen, "%s-%s", printer_name, qualifier);
1041 cupsdLogMessage(CUPSD_LOG_DEBUG, "Using profile ID \"%s\".", idstr);
1043 dbus_message_iter_init_append(message, &args);
1044 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &idstr);
1045 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope);
1047 snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1],
1050 dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict);
1051 colord_dict_add_strings(&dict, "Qualifier", qualifier);
1052 colord_dict_add_strings(&dict, "Format", format_str);
1053 colord_dict_add_strings(&dict, "Colorspace", colorspace);
1055 colord_dict_add_strings(&dict, "Filename", iccfile);
1056 dbus_message_iter_close_container(&args, &dict);
1059 * Send the CreateProfile request synchronously...
1062 dbus_error_init(&error);
1063 cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateProfile(%s,%s)", idstr,
1065 reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1066 COLORD_DBUS_TIMEOUT,
1070 cupsdLogMessage(CUPSD_LOG_WARN, "CreateProfile failed: %s:%s", error.name,
1072 dbus_error_free(&error);
1080 dbus_message_iter_init(reply, &args);
1081 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
1083 cupsdLogMessage(CUPSD_LOG_WARN,
1084 "CreateProfile failed: Incorrect reply type.");
1088 dbus_message_iter_get_basic(&args, &profile_path);
1089 cupsdLogMessage(CUPSD_LOG_DEBUG, "Created profile \"%s\".", profile_path);
1090 cupsArrayAdd(profiles, strdup(profile_path));
1095 dbus_message_unref(message);
1098 dbus_message_unref(reply);
1106 * 'colord_delete_device()' - Delete a device
1110 colord_delete_device(
1111 const char *device_id) /* I - Device ID string */
1113 DBusMessage *message = NULL; /* D-Bus request */
1114 DBusMessage *reply = NULL; /* D-Bus reply */
1115 DBusMessageIter args; /* D-Bus method arguments */
1116 DBusError error; /* D-Bus error */
1117 char *device_path; /* Device object path */
1121 * Find the device...
1124 if ((device_path = colord_find_device(device_id)) == NULL)
1128 * Delete the device...
1131 message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1133 COLORD_DBUS_INTERFACE,
1136 dbus_message_iter_init_append(message, &args);
1137 dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &device_path);
1140 * Send the DeleteDevice request synchronously...
1143 dbus_error_init(&error);
1144 cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling DeleteDevice(%s)", device_path);
1145 reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1146 COLORD_DBUS_TIMEOUT,
1150 cupsdLogMessage(CUPSD_LOG_DEBUG, "DeleteDevice failed: %s:%s", error.name,
1152 dbus_error_free(&error);
1162 dbus_message_unref(message);
1165 dbus_message_unref(reply);
1170 * 'colord_device_add_profile()' - Assign a profile to a device.
1174 colord_device_add_profile(
1175 const char *device_path, /* I - Device object path */
1176 const char *profile_path, /* I - Profile object path */
1177 const char *relation) /* I - Device relation, either
1180 DBusMessage *message = NULL; /* D-Bus request */
1181 DBusMessage *reply = NULL; /* D-Bus reply */
1182 DBusMessageIter args; /* D-Bus method arguments */
1183 DBusError error; /* D-Bus error */
1186 message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1188 COLORD_DBUS_INTERFACE_DEVICE,
1191 dbus_message_iter_init_append(message, &args);
1192 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &relation);
1193 dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &profile_path);
1194 cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling %s:AddProfile(%s) [%s]",
1195 device_path, profile_path, relation);
1198 * Send the AddProfile request synchronously...
1201 dbus_error_init(&error);
1202 reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1203 COLORD_DBUS_TIMEOUT,
1207 cupsdLogMessage(CUPSD_LOG_WARN, "AddProfile failed: %s:%s", error.name,
1209 dbus_error_free(&error);
1216 dbus_message_unref(message);
1219 dbus_message_unref(reply);
1224 * 'colord_dict_add_strings()' - Add two strings to a dictionary.
1228 colord_dict_add_strings(
1229 DBusMessageIter *dict, /* I - Dictionary */
1230 const char *key, /* I - Key string */
1231 const char *value) /* I - Value string */
1233 DBusMessageIter entry; /* Entry to add */
1236 dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
1237 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
1238 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value);
1239 dbus_message_iter_close_container(dict, &entry);
1244 * 'colord_find_device()' - Finds a device
1247 static char * /* O - Device path or NULL */
1249 const char *device_id) /* I - Device ID string */
1251 DBusMessage *message = NULL; /* D-Bus request */
1252 DBusMessage *reply = NULL; /* D-Bus reply */
1253 DBusMessageIter args; /* D-Bus method arguments */
1254 DBusError error; /* D-Bus error */
1255 const char *device_path_tmp; /* Device object path */
1256 char *device_path = NULL; /* Device object path */
1259 message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1261 COLORD_DBUS_INTERFACE,
1264 dbus_message_iter_init_append(message, &args);
1265 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_id);
1268 * Send the FindDeviceById request synchronously...
1271 dbus_error_init(&error);
1272 cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling FindDeviceById(%s)", device_id);
1273 reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1274 COLORD_DBUS_TIMEOUT,
1278 cupsdLogMessage(CUPSD_LOG_DEBUG, "FindDeviceById failed: %s:%s",
1279 error.name, error.message);
1280 dbus_error_free(&error);
1288 dbus_message_iter_init(reply, &args);
1289 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
1291 cupsdLogMessage(CUPSD_LOG_WARN,
1292 "FindDeviceById failed: Incorrect reply type.");
1296 dbus_message_iter_get_basic(&args, &device_path_tmp);
1297 if (device_path_tmp)
1298 device_path = strdup(device_path_tmp);
1303 dbus_message_unref(message);
1306 dbus_message_unref(reply);
1308 return (device_path);
1313 * 'colord_get_qualifier_format()' - Get the qualifier format.
1315 * Note: Returns a value of "ColorSpace.MediaType.Resolution" by default.
1319 colord_get_qualifier_format(
1320 ppd_file_t *ppd, /* I - PPD file data */
1321 char *format[3]) /* I - Format tuple */
1323 const char *tmp; /* Temporary string */
1324 ppd_attr_t *attr; /* Profile attributes */
1328 * Get 1st section...
1331 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL)
1333 else if (ppdFindAttr(ppd, "DefaultColorModel", NULL))
1335 else if (ppdFindAttr(ppd, "DefaultColorSpace", NULL))
1340 format[0] = strdup(tmp);
1343 * Get 2nd section...
1346 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL)
1351 format[1] = strdup(tmp);
1354 * Get 3rd section...
1357 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL)
1362 format[2] = strdup(tmp);
1367 * 'colord_register_printer()' - Register profiles for a printer.
1371 colord_register_printer(
1372 cupsd_printer_t *p) /* I - printer */
1374 char ppdfile[1024], /* PPD filename */
1375 iccfile[1024]; /* ICC filename */
1376 ppd_file_t *ppd; /* PPD file */
1377 cups_array_t *profiles; /* Profile paths array */
1378 ppd_attr_t *attr; /* Profile attributes */
1379 const char *device_colorspace; /* Device colorspace */
1380 char *format[3]; /* Qualifier format tuple */
1384 * Ensure we have a D-Bus connection...
1391 * Try opening the PPD file for this printer...
1394 snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
1395 if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
1399 * Find out the qualifier format
1402 colord_get_qualifier_format(ppd, format);
1405 * See if we have any embedded profiles...
1408 profiles = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup,
1409 (cups_afree_func_t)free);
1410 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
1412 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
1413 if (attr->spec[0] && attr->value && attr->value[0])
1415 if (attr->value[0] != '/')
1416 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
1419 strlcpy(iccfile, attr->value, sizeof(iccfile));
1421 if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
1422 cupsdLogFCMessage, p))
1425 colord_create_profile(profiles, p->name, attr->spec, COLORD_SPACE_UNKNOWN,
1426 format, iccfile, COLORD_SCOPE_TEMP);
1430 * Add the grayscale profile first. We always have a grayscale profile.
1433 colord_create_profile(profiles, p->name, "Gray..", COLORD_SPACE_GRAY,
1434 format, NULL, COLORD_SCOPE_TEMP);
1437 * Then add the RGB/CMYK/DeviceN color profile...
1440 device_colorspace = "unknown";
1441 switch (ppd->colorspace)
1445 device_colorspace = COLORD_SPACE_RGB;
1446 colord_create_profile(profiles, p->name, "RGB..", COLORD_SPACE_RGB,
1447 format, NULL, COLORD_SCOPE_TEMP);
1452 device_colorspace = COLORD_SPACE_CMYK;
1453 colord_create_profile(profiles, p->name, "CMYK..", COLORD_SPACE_CMYK,
1454 format, NULL, COLORD_SCOPE_TEMP);
1458 device_colorspace = COLORD_SPACE_GRAY;
1462 colord_create_profile(profiles, p->name, "DeviceN..",
1463 COLORD_SPACE_UNKNOWN, format, NULL,
1469 * Register the device with colord.
1472 cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\".",
1474 colord_create_device(p, ppd, profiles, device_colorspace, format,
1475 COLORD_RELATION_SOFT, COLORD_SCOPE_TEMP);
1478 * Free any memory we used...
1481 cupsArrayDelete(profiles);
1492 * 'colord_unregister_printer()' - Unregister profiles for a printer.
1496 colord_unregister_printer(
1497 cupsd_printer_t *p) /* I - printer */
1499 char device_id[1024]; /* Device ID as understood by colord */
1503 * Ensure we have a D-Bus connection...
1510 * Just delete the device itself, and leave the profiles registered
1513 snprintf(device_id, sizeof(device_id), "cups-%s", p->name);
1514 colord_delete_device(device_id);
1516 #endif /* __APPLE__ */