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__ */