4 * Color management routines for the CUPS scheduler.
6 * Copyright 2007-2012 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * Original DBUS/colord code is Copyright 2011 Red Hat, Inc.
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
21 * Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
24 * Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution.
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
46 * Include necessary headers...
50 #include <cups/ppd-private.h>
53 # include <ApplicationServices/ApplicationServices.h>
54 extern CFUUIDRef
ColorSyncCreateUUIDFromUInt32(unsigned id
);
55 # include <CoreFoundation/CoreFoundation.h>
56 #elif defined(HAVE_DBUS)
57 # include <dbus/dbus.h>
60 * Defines used by colord. See the reference docs for further details:
62 * http://colord.hughsie.com/api/ref-dbus.html
65 # define COLORD_SCOPE_NORMAL "normal"
67 # define COLORD_SCOPE_TEMP "temp" /* Process scope */
68 # define COLORD_SCOPE_DISK "disk" /* Lives forever, as stored in DB */
70 # define COLORD_RELATION_SOFT "soft" /* Mapping is not default */
71 # define COLORD_RELATION_HARD "hard" /* Explicitly mapped profile */
73 # define COLORD_SPACE_RGB "rgb" /* RGB colorspace */
74 # define COLORD_SPACE_CMYK "cmyk" /* CMYK colorspace */
75 # define COLORD_SPACE_GRAY "gray" /* Gray colorspace */
76 # define COLORD_SPACE_UNKNOWN "unknown"
77 /* Unknown colorspace */
79 # define COLORD_MODE_PHYSICAL "physical"
81 # define COLORD_MODE_VIRTUAL "virtual"
82 /* Virtual device with no hardware */
84 # define COLORD_KIND_PRINTER "printer"
85 /* printing output device */
87 # define COLORD_DBUS_MSG(p,m) dbus_message_new_method_call(\
88 "org.freedesktop.ColorManager", (p),\
89 "org.freedesktop.ColorManager", (m))
90 /* Macro to make new colord messages */
91 # define COLORD_DBUS_PATH "/org/freedesktop/ColorManager"
92 /* Path for color management system */
93 # define COLORD_DBUS_TIMEOUT 5000 /* Timeout for connecting to colord in ms */
94 #endif /* __APPLE__ */
101 #if !defined(__APPLE__) && defined(HAVE_DBUS)
102 static DBusConnection
*colord_con
= NULL
;
103 /* DBUS connection for colord */
104 #endif /* !__APPLE__ && HAVE_DBUS */
112 static void apple_init_profile(ppd_file_t
*ppd
, cups_array_t
*languages
,
113 CFMutableDictionaryRef profile
,
114 unsigned id
, const char *name
,
115 const char *text
, const char *iccfile
);
116 static void apple_register_profiles(cupsd_printer_t
*p
);
117 static void apple_unregister_profiles(cupsd_printer_t
*p
);
119 #elif defined(HAVE_DBUS)
120 static void colord_create_device(cupsd_printer_t
*p
, ppd_file_t
*ppd
,
121 cups_array_t
*profiles
,
122 const char *colorspace
, char **format
,
123 const char *relation
, const char *scope
);
124 static void colord_create_profile(cups_array_t
*profiles
,
125 const char *printer_name
,
126 const char *qualifier
,
127 const char *colorspace
,
128 char **format
, const char *iccfile
,
130 static void colord_delete_device(const char *device_id
);
131 static void colord_device_add_profile(const char *device_path
,
132 const char *profile_path
,
133 const char *relation
);
134 static void colord_dict_add_strings(DBusMessageIter
*dict
,
135 const char *key
, const char *value
);
136 static char *colord_find_device(const char *device_id
);
137 static void colord_get_qualifier_format(ppd_file_t
*ppd
, char *format
[3]);
138 static void colord_register_printer(cupsd_printer_t
*p
);
139 static void colord_unregister_printer(cupsd_printer_t
*p
);
140 #endif /* __APPLE__ */
144 * 'cupsdRegisterColor()' - Register vendor color profiles in a PPD file.
148 cupsdRegisterColor(cupsd_printer_t
*p
) /* I - Printer */
153 apple_unregister_profiles(p
);
154 apple_register_profiles(p
);
157 #elif defined(HAVE_DBUS)
158 colord_unregister_printer(p
);
159 colord_register_printer(p
);
160 #endif /* __APPLE__ */
165 * 'cupsdStartColor()' - Initialize color management.
169 cupsdStartColor(void)
171 #if !defined(__APPLE__) && defined(HAVE_DBUS)
172 cupsd_printer_t
*p
; /* Current printer */
175 colord_con
= dbus_bus_get(DBUS_BUS_SYSTEM
, NULL
);
177 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
179 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
180 cupsdRegisterColor(p
);
181 #endif /* !__APPLE__ && HAVE_DBUS */
186 * 'cupsdStopColor()' - Shutdown color management.
192 #if !defined(__APPLE__) && defined(HAVE_DBUS)
193 dbus_connection_unref(colord_con
);
195 #endif /* !__APPLE__ && HAVE_DBUS */
200 * 'cupsdUnregisterColor()' - Unregister vendor color profiles in a PPD file.
204 cupsdUnregisterColor(cupsd_printer_t
*p
)/* I - Printer */
208 apple_unregister_profiles(p
);
210 #elif defined(HAVE_DBUS)
211 colord_unregister_printer(p
);
212 #endif /* __APPLE__ */
218 * 'apple_init_profile()' - Initialize a color profile.
223 ppd_file_t
*ppd
, /* I - PPD file */
224 cups_array_t
*languages
, /* I - Languages in the PPD file */
225 CFMutableDictionaryRef profile
, /* I - Profile dictionary */
226 unsigned id
, /* I - Profile ID */
227 const char *name
, /* I - Profile name */
228 const char *text
, /* I - Profile UI text */
229 const char *iccfile
) /* I - ICC filename */
231 CFURLRef url
; /* URL for profile filename */
232 CFMutableDictionaryRef dict
; /* Dictionary for name */
233 char *language
; /* Current language */
234 ppd_attr_t
*attr
; /* Profile attribute */
235 CFStringRef cflang
, /* Language string */
236 cftext
; /* Localized text */
242 * Build the profile name dictionary...
245 dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
246 &kCFTypeDictionaryKeyCallBacks
,
247 &kCFTypeDictionaryValueCallBacks
);
250 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to initialize profile \"%s\".",
255 cftext
= CFStringCreateWithCString(kCFAllocatorDefault
, text
,
256 kCFStringEncodingUTF8
);
260 CFDictionarySetValue(dict
, CFSTR("en_US"), cftext
);
267 * Find localized names for the color profiles...
270 cupsArraySave(ppd
->sorted_attrs
);
272 for (language
= (char *)cupsArrayFirst(languages
);
274 language
= (char *)cupsArrayNext(languages
))
278 if ((attr
= _ppdLocalizedAttr(ppd
, "cupsICCProfile", name
,
280 attr
= _ppdLocalizedAttr(ppd
, "APTiogaProfile", name
, language
);
283 attr
= _ppdLocalizedAttr(ppd
, "ColorModel", name
, language
);
285 if (attr
&& attr
->text
[0])
287 cflang
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
288 kCFStringEncodingUTF8
);
289 cftext
= CFStringCreateWithCString(kCFAllocatorDefault
, attr
->text
,
290 kCFStringEncodingUTF8
);
292 if (cflang
&& cftext
)
293 CFDictionarySetValue(dict
, cflang
, cftext
);
303 cupsArrayRestore(ppd
->sorted_attrs
);
307 * Fill in the profile data...
312 url
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
313 (const UInt8
*)iccfile
,
314 strlen(iccfile
), false);
318 CFDictionarySetValue(profile
, kColorSyncDeviceProfileURL
, url
);
323 CFDictionarySetValue(profile
, kColorSyncDeviceModeDescriptions
, dict
);
329 * 'apple_register_profiles()' - Register color profiles for a printer.
333 apple_register_profiles(
334 cupsd_printer_t
*p
) /* I - Printer */
336 int i
; /* Looping var */
337 char ppdfile
[1024], /* PPD filename */
338 iccfile
[1024], /* ICC filename */
339 selector
[PPD_MAX_NAME
];
340 /* Profile selection string */
341 ppd_file_t
*ppd
; /* PPD file */
342 ppd_attr_t
*attr
, /* Profile attributes */
343 *profileid_attr
,/* cupsProfileID attribute */
344 *q1_attr
, /* ColorModel (or other) qualifier */
345 *q2_attr
, /* MediaType (or other) qualifier */
346 *q3_attr
; /* Resolution (or other) qualifier */
347 char q_keyword
[PPD_MAX_NAME
];
348 /* Qualifier keyword */
349 const char *q1_choice
, /* ColorModel (or other) choice */
350 *q2_choice
, /* MediaType (or other) choice */
351 *q3_choice
; /* Resolution (or other) choice */
352 ppd_option_t
*cm_option
; /* Color model option */
353 ppd_choice_t
*cm_choice
; /* Color model choice */
354 int num_profiles
; /* Number of profiles */
355 OSStatus error
= 0; /* Last error */
356 unsigned device_id
, /* Printer device ID */
357 profile_id
= 0, /* Profile ID */
358 default_profile_id
= 0;
359 /* Default profile ID */
360 CFMutableDictionaryRef device_name
; /* Printer device name dictionary */
361 CFStringRef printer_name
; /* Printer name string */
362 cups_array_t
*languages
; /* Languages array */
363 CFMutableDictionaryRef profiles
, /* Dictionary of profiles */
364 profile
; /* Current profile info dictionary */
365 CFStringRef dict_key
; /* Key in factory profile dictionary */
369 * Make sure ColorSync is available...
372 if (ColorSyncRegisterDevice
== NULL
)
376 * Try opening the PPD file for this printer...
379 snprintf(ppdfile
, sizeof(ppdfile
), "%s/ppd/%s.ppd", ServerRoot
, p
->name
);
380 if ((ppd
= _ppdOpenFile(ppdfile
, _PPD_LOCALIZATION_ICC_PROFILES
)) == NULL
)
384 * See if we have any profiles...
387 for (num_profiles
= 0, attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
389 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
390 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
392 if (attr
->value
[0] != '/')
393 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
396 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
398 if (access(iccfile
, 0))
400 cupsdLogMessage(CUPSD_LOG_ERROR
,
401 "%s: ICC Profile \"%s\" does not exist.", p
->name
,
403 cupsdSetPrinterReasons(p
, "+cups-missing-filter-warning");
411 * Create a dictionary for the factory profiles...
414 profiles
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
415 &kCFTypeDictionaryKeyCallBacks
,
416 &kCFTypeDictionaryValueCallBacks
);
419 cupsdLogMessage(CUPSD_LOG_ERROR
,
420 "Unable to allocate memory for factory profiles.");
426 * If we have profiles, add them...
429 if (num_profiles
> 0)
432 * For CUPS PPDs, figure out the default profile selector values...
435 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier1", NULL
)) != NULL
&&
436 attr
->value
&& attr
->value
[0])
438 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
439 q1_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
441 else if ((q1_attr
= ppdFindAttr(ppd
, "DefaultColorModel", NULL
)) == NULL
)
442 q1_attr
= ppdFindAttr(ppd
, "DefaultColorSpace", NULL
);
444 if (q1_attr
&& q1_attr
->value
&& q1_attr
->value
[0])
445 q1_choice
= q1_attr
->value
;
449 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier2", NULL
)) != NULL
&&
450 attr
->value
&& attr
->value
[0])
452 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
453 q2_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
456 q2_attr
= ppdFindAttr(ppd
, "DefaultMediaType", NULL
);
458 if (q2_attr
&& q2_attr
->value
&& q2_attr
->value
[0])
459 q2_choice
= q2_attr
->value
;
463 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier3", NULL
)) != NULL
&&
464 attr
->value
&& attr
->value
[0])
466 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
467 q3_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
470 q3_attr
= ppdFindAttr(ppd
, "DefaultResolution", NULL
);
472 if (q3_attr
&& q3_attr
->value
&& q3_attr
->value
[0])
473 q3_choice
= q3_attr
->value
;
478 * Loop through the profiles listed in the PPD...
481 languages
= _ppdGetLanguages(ppd
);
483 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
485 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
486 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
489 * Add this profile...
492 if (attr
->value
[0] != '/')
493 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
496 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
498 if (_cupsFileCheck(iccfile
, _CUPS_FILE_CHECK_FILE
, !RunUser
,
499 cupsdLogFCMessage
, p
))
502 cupsArraySave(ppd
->sorted_attrs
);
504 if ((profileid_attr
= ppdFindAttr(ppd
, "cupsProfileID",
505 attr
->spec
)) != NULL
&&
506 profileid_attr
->value
&& isdigit(profileid_attr
->value
[0] & 255))
507 profile_id
= (unsigned)strtoul(profileid_attr
->value
, NULL
, 10);
509 profile_id
= _ppdHashName(attr
->spec
);
511 cupsArrayRestore(ppd
->sorted_attrs
);
513 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
514 &kCFTypeDictionaryKeyCallBacks
,
515 &kCFTypeDictionaryValueCallBacks
);
518 cupsdLogMessage(CUPSD_LOG_ERROR
,
519 "Unable to allocate memory for color profile.");
525 apple_init_profile(ppd
, languages
, profile
, profile_id
, attr
->spec
,
526 attr
->text
[0] ? attr
->text
: attr
->spec
, iccfile
);
528 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
529 CFSTR("%u"), profile_id
);
532 CFDictionarySetValue(profiles
, dict_key
, profile
);
539 * See if this is the default profile...
542 if (!default_profile_id
&& q1_choice
&& q2_choice
&& q3_choice
)
544 snprintf(selector
, sizeof(selector
), "%s.%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
&& q2_choice
)
552 snprintf(selector
, sizeof(selector
), "%s.%s.", q1_choice
, q2_choice
);
553 if (!strcmp(selector
, attr
->spec
))
554 default_profile_id
= profile_id
;
557 if (!default_profile_id
&& q1_choice
&& q3_choice
)
559 snprintf(selector
, sizeof(selector
), "%s..%s", q1_choice
, q3_choice
);
560 if (!strcmp(selector
, attr
->spec
))
561 default_profile_id
= profile_id
;
564 if (!default_profile_id
&& q1_choice
)
566 snprintf(selector
, sizeof(selector
), "%s..", q1_choice
);
567 if (!strcmp(selector
, attr
->spec
))
568 default_profile_id
= profile_id
;
571 if (!default_profile_id
&& q2_choice
&& q3_choice
)
573 snprintf(selector
, sizeof(selector
), ".%s.%s", q2_choice
, q3_choice
);
574 if (!strcmp(selector
, attr
->spec
))
575 default_profile_id
= profile_id
;
578 if (!default_profile_id
&& q2_choice
)
580 snprintf(selector
, sizeof(selector
), ".%s.", q2_choice
);
581 if (!strcmp(selector
, attr
->spec
))
582 default_profile_id
= profile_id
;
585 if (!default_profile_id
&& q3_choice
)
587 snprintf(selector
, sizeof(selector
), "..%s", q3_choice
);
588 if (!strcmp(selector
, attr
->spec
))
589 default_profile_id
= profile_id
;
593 _ppdFreeLanguages(languages
);
595 else if ((cm_option
= ppdFindOption(ppd
, "ColorModel")) != NULL
)
598 * Extract profiles from ColorModel option...
601 const char *profile_name
; /* Name of generic profile */
604 num_profiles
= cm_option
->num_choices
;
606 for (i
= cm_option
->num_choices
, cm_choice
= cm_option
->choices
;
610 if (!strcmp(cm_choice
->choice
, "Gray") ||
611 !strcmp(cm_choice
->choice
, "Black"))
612 profile_name
= "Gray";
613 else if (!strcmp(cm_choice
->choice
, "RGB") ||
614 !strcmp(cm_choice
->choice
, "CMY"))
615 profile_name
= "RGB";
616 else if (!strcmp(cm_choice
->choice
, "CMYK") ||
617 !strcmp(cm_choice
->choice
, "KCMY"))
618 profile_name
= "CMYK";
620 profile_name
= "DeviceN";
622 snprintf(selector
, sizeof(selector
), "%s..", profile_name
);
623 profile_id
= _ppdHashName(selector
);
625 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
626 &kCFTypeDictionaryKeyCallBacks
,
627 &kCFTypeDictionaryValueCallBacks
);
630 cupsdLogMessage(CUPSD_LOG_ERROR
,
631 "Unable to allocate memory for color profile.");
637 apple_init_profile(ppd
, NULL
, profile
, profile_id
, cm_choice
->choice
,
638 cm_choice
->text
, NULL
);
640 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
641 CFSTR("%u"), profile_id
);
644 CFDictionarySetValue(profiles
, dict_key
, profile
);
650 if (cm_choice
->marked
)
651 default_profile_id
= profile_id
;
657 * Use the default colorspace...
660 attr
= ppdFindAttr(ppd
, "DefaultColorSpace", NULL
);
662 num_profiles
= (attr
&& ppd
->colorspace
== PPD_CS_GRAY
) ? 1 : 2;
665 * Add the grayscale profile first. We always have a grayscale profile.
668 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
669 &kCFTypeDictionaryKeyCallBacks
,
670 &kCFTypeDictionaryValueCallBacks
);
674 cupsdLogMessage(CUPSD_LOG_ERROR
,
675 "Unable to allocate memory for color profile.");
681 profile_id
= _ppdHashName("Gray..");
682 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "Gray", "Gray", NULL
);
684 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%u"),
688 CFDictionarySetValue(profiles
, dict_key
, profile
);
695 * Then add the RGB/CMYK/DeviceN color profile...
698 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
699 &kCFTypeDictionaryKeyCallBacks
,
700 &kCFTypeDictionaryValueCallBacks
);
704 cupsdLogMessage(CUPSD_LOG_ERROR
,
705 "Unable to allocate memory for color profile.");
711 switch (ppd
->colorspace
)
716 profile_id
= _ppdHashName("RGB..");
717 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "RGB", "RGB",
723 profile_id
= _ppdHashName("CMYK..");
724 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "CMYK", "CMYK",
733 profile_id
= _ppdHashName("DeviceN..");
734 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "DeviceN",
739 if (CFDictionaryGetCount(profile
) > 0)
741 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
742 CFSTR("%u"), profile_id
);
745 CFDictionarySetValue(profiles
, dict_key
, profile
);
753 if (num_profiles
> 0)
756 * Make sure we have a default profile ID...
759 if (!default_profile_id
)
760 default_profile_id
= profile_id
; /* Last profile */
762 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%u"),
766 CFDictionarySetValue(profiles
, kColorSyncDeviceDefaultProfileID
,
772 * Get the device ID hash and pathelogical name dictionary.
775 cupsdLogMessage(CUPSD_LOG_INFO
, "Registering ICC color profiles for \"%s\"",
778 device_id
= _ppdHashName(p
->name
);
779 device_name
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
780 &kCFTypeDictionaryKeyCallBacks
,
781 &kCFTypeDictionaryValueCallBacks
);
782 printer_name
= CFStringCreateWithCString(kCFAllocatorDefault
,
783 p
->name
, kCFStringEncodingUTF8
);
785 if (device_name
&& printer_name
)
788 * Register the device with ColorSync...
791 CFTypeRef deviceDictKeys
[] =
793 kColorSyncDeviceDescriptions
,
794 kColorSyncFactoryProfiles
,
795 kColorSyncDeviceUserScope
,
796 kColorSyncDeviceHostScope
798 CFTypeRef deviceDictVals
[] =
799 { /* Device values */
802 kCFPreferencesAnyUser
,
803 kCFPreferencesCurrentHost
805 CFDictionaryRef deviceDict
; /* Device dictionary */
806 CFUUIDRef deviceUUID
; /* Device UUID */
808 CFDictionarySetValue(device_name
, CFSTR("en_US"), printer_name
);
810 deviceDict
= CFDictionaryCreate(kCFAllocatorDefault
,
811 (const void **)deviceDictKeys
,
812 (const void **)deviceDictVals
,
813 sizeof(deviceDictKeys
) /
814 sizeof(deviceDictKeys
[0]),
815 &kCFTypeDictionaryKeyCallBacks
,
816 &kCFTypeDictionaryValueCallBacks
);
817 deviceUUID
= ColorSyncCreateUUIDFromUInt32(device_id
);
819 if (!deviceDict
|| !deviceUUID
||
820 !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass
, deviceUUID
,
825 CFRelease(deviceUUID
);
828 CFRelease(deviceDict
);
838 cupsdLogMessage(CUPSD_LOG_ERROR
,
839 "Unable to register ICC color profiles for \"%s\": %d",
840 p
->name
, (int)error
);
843 CFRelease(printer_name
);
846 CFRelease(device_name
);
850 * Free any memory we used...
860 * 'apple_unregister_profiles()' - Remove color profiles for the specified
865 apple_unregister_profiles(
866 cupsd_printer_t
*p
) /* I - Printer */
869 * Make sure ColorSync is available...
872 if (ColorSyncUnregisterDevice
!= NULL
)
874 CFUUIDRef deviceUUID
; /* Device UUID */
876 deviceUUID
= ColorSyncCreateUUIDFromUInt32(_ppdHashName(p
->name
));
879 ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass
, deviceUUID
);
880 CFRelease(deviceUUID
);
886 #elif defined(HAVE_DBUS)
888 * 'colord_create_device()' - Create a device and register profiles.
892 colord_create_device(
893 cupsd_printer_t
*p
, /* I - Printer */
894 ppd_file_t
*ppd
, /* I - PPD file */
895 cups_array_t
*profiles
, /* I - Profiles array */
896 const char *colorspace
, /* I - Device colorspace, e.g. 'rgb' */
897 char **format
, /* I - Device qualifier format */
898 const char *relation
, /* I - Profile relation, either 'soft'
900 const char *scope
) /* I - The scope of the device, e.g.
901 'normal', 'temp' or 'disk' */
903 DBusMessage
*message
= NULL
; /* D-Bus request */
904 DBusMessage
*reply
= NULL
; /* D-Bus reply */
905 DBusMessageIter args
; /* D-Bus method arguments */
906 DBusMessageIter dict
; /* D-Bus method arguments */
907 DBusError error
; /* D-Bus error */
908 const char *device_path
; /* Device object path */
909 const char *profile_path
; /* Profile path */
910 char *default_profile_path
= NULL
;
911 /* Default profile path */
912 char device_id
[1024]; /* Device ID as understood by colord */
913 char format_str
[1024]; /* Qualifier format as a string */
917 * Create the device...
920 snprintf(device_id
, sizeof(device_id
), "cups-%s", p
->name
);
921 device_path
= device_id
;
923 message
= COLORD_DBUS_MSG(COLORD_DBUS_PATH
, "CreateDevice");
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
= COLORD_DBUS_MSG(COLORD_DBUS_PATH
, "CreateProfile");
1034 idstrlen
= strlen(printer_name
) + 1 + strlen(qualifier
) + 1;
1035 if ((idstr
= malloc(idstrlen
)) == NULL
)
1037 snprintf(idstr
, idstrlen
, "%s-%s", printer_name
, qualifier
);
1038 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Using profile ID \"%s\".", idstr
);
1040 dbus_message_iter_init_append(message
, &args
);
1041 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &idstr
);
1042 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &scope
);
1044 snprintf(format_str
, sizeof(format_str
), "%s.%s.%s", format
[0], format
[1],
1047 dbus_message_iter_open_container(&args
, DBUS_TYPE_ARRAY
, "{ss}", &dict
);
1048 colord_dict_add_strings(&dict
, "Qualifier", qualifier
);
1049 colord_dict_add_strings(&dict
, "Format", format_str
);
1050 colord_dict_add_strings(&dict
, "Colorspace", colorspace
);
1052 colord_dict_add_strings(&dict
, "Filename", iccfile
);
1053 dbus_message_iter_close_container(&args
, &dict
);
1056 * Send the CreateProfile request synchronously...
1059 dbus_error_init(&error
);
1060 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling CreateProfile(%s,%s)", idstr
,
1062 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1063 COLORD_DBUS_TIMEOUT
,
1067 cupsdLogMessage(CUPSD_LOG_WARN
, "CreateProfile failed: %s:%s", error
.name
,
1069 dbus_error_free(&error
);
1077 dbus_message_iter_init(reply
, &args
);
1078 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
1080 cupsdLogMessage(CUPSD_LOG_WARN
,
1081 "CreateProfile failed: Incorrect reply type.");
1085 dbus_message_iter_get_basic(&args
, &profile_path
);
1086 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Created profile \"%s\".", profile_path
);
1087 cupsArrayAdd(profiles
, strdup(profile_path
));
1092 dbus_message_unref(message
);
1095 dbus_message_unref(reply
);
1103 * 'colord_delete_device()' - Delete a device
1107 colord_delete_device(
1108 const char *device_id
) /* I - Device ID string */
1110 DBusMessage
*message
= NULL
; /* D-Bus request */
1111 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1112 DBusMessageIter args
; /* D-Bus method arguments */
1113 DBusError error
; /* D-Bus error */
1114 char *device_path
; /* Device object path */
1118 * Find the device...
1121 if ((device_path
= colord_find_device(device_id
)) == NULL
)
1125 * Delete the device...
1128 message
= COLORD_DBUS_MSG(COLORD_DBUS_PATH
, "DeleteDevice");
1130 dbus_message_iter_init_append(message
, &args
);
1131 dbus_message_iter_append_basic(&args
, DBUS_TYPE_OBJECT_PATH
, &device_path
);
1134 * Send the DeleteDevice request synchronously...
1137 dbus_error_init(&error
);
1138 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling DeleteDevice(%s)", device_path
);
1139 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1140 COLORD_DBUS_TIMEOUT
,
1144 cupsdLogMessage(CUPSD_LOG_DEBUG
, "DeleteDevice failed: %s:%s", error
.name
,
1146 dbus_error_free(&error
);
1156 dbus_message_unref(message
);
1159 dbus_message_unref(reply
);
1164 * 'colord_device_add_profile()' - Assign a profile to a device.
1168 colord_device_add_profile(
1169 const char *device_path
, /* I - Device object path */
1170 const char *profile_path
, /* I - Profile object path */
1171 const char *relation
) /* I - Device relation, either
1174 DBusMessage
*message
= NULL
; /* D-Bus request */
1175 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1176 DBusMessageIter args
; /* D-Bus method arguments */
1177 DBusError error
; /* D-Bus error */
1180 message
= COLORD_DBUS_MSG(device_path
, "AddProfile");
1182 dbus_message_iter_init_append(message
, &args
);
1183 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &relation
);
1184 dbus_message_iter_append_basic(&args
, DBUS_TYPE_OBJECT_PATH
, &profile_path
);
1185 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling %s:AddProfile(%s) [%s]",
1186 device_path
, profile_path
, relation
);
1189 * Send the AddProfile request synchronously...
1192 dbus_error_init(&error
);
1193 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1194 COLORD_DBUS_TIMEOUT
,
1198 cupsdLogMessage(CUPSD_LOG_WARN
, "AddProfile failed: %s:%s", error
.name
,
1200 dbus_error_free(&error
);
1207 dbus_message_unref(message
);
1210 dbus_message_unref(reply
);
1215 * 'colord_dict_add_strings()' - Add two strings to a dictionary.
1219 colord_dict_add_strings(
1220 DBusMessageIter
*dict
, /* I - Dictionary */
1221 const char *key
, /* I - Key string */
1222 const char *value
) /* I - Value string */
1224 DBusMessageIter entry
; /* Entry to add */
1227 dbus_message_iter_open_container(dict
, DBUS_TYPE_DICT_ENTRY
, NULL
, &entry
);
1228 dbus_message_iter_append_basic(&entry
, DBUS_TYPE_STRING
, &key
);
1229 dbus_message_iter_append_basic(&entry
, DBUS_TYPE_STRING
, &value
);
1230 dbus_message_iter_close_container(dict
, &entry
);
1235 * 'colord_find_device()' - Finds a device
1238 static char * /* O - Device path or NULL */
1240 const char *device_id
) /* I - Device ID string */
1242 DBusMessage
*message
= NULL
; /* D-Bus request */
1243 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1244 DBusMessageIter args
; /* D-Bus method arguments */
1245 DBusError error
; /* D-Bus error */
1246 const char *device_path_tmp
; /* Device object path */
1247 char *device_path
= NULL
; /* Device object path */
1250 message
= COLORD_DBUS_MSG(COLORD_DBUS_PATH
, "FindDeviceById");
1252 dbus_message_iter_init_append(message
, &args
);
1253 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &device_id
);
1256 * Send the FindDeviceById request synchronously...
1259 dbus_error_init(&error
);
1260 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling FindDeviceById(%s)", device_id
);
1261 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1262 COLORD_DBUS_TIMEOUT
,
1266 cupsdLogMessage(CUPSD_LOG_DEBUG
, "FindDeviceById failed: %s:%s",
1267 error
.name
, error
.message
);
1268 dbus_error_free(&error
);
1276 dbus_message_iter_init(reply
, &args
);
1277 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
1279 cupsdLogMessage(CUPSD_LOG_WARN
,
1280 "FindDeviceById failed: Incorrect reply type.");
1284 dbus_message_iter_get_basic(&args
, &device_path_tmp
);
1285 if (device_path_tmp
)
1286 device_path
= strdup(device_path_tmp
);
1291 dbus_message_unref(message
);
1294 dbus_message_unref(reply
);
1296 return (device_path
);
1301 * 'colord_get_qualifier_format()' - Get the qualifier format.
1303 * Note: Returns a value of "ColorSpace.MediaType.Resolution" by default.
1307 colord_get_qualifier_format(
1308 ppd_file_t
*ppd
, /* I - PPD file data */
1309 char *format
[3]) /* I - Format tuple */
1311 const char *tmp
; /* Temporary string */
1312 ppd_attr_t
*attr
; /* Profile attributes */
1316 * Get 1st section...
1319 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier1", NULL
)) != NULL
)
1321 else if (ppdFindAttr(ppd
, "DefaultColorModel", NULL
))
1323 else if (ppdFindAttr(ppd
, "DefaultColorSpace", NULL
))
1328 format
[0] = strdup(tmp
);
1331 * Get 2nd section...
1334 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier2", NULL
)) != NULL
)
1339 format
[1] = strdup(tmp
);
1342 * Get 3rd section...
1345 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier3", NULL
)) != NULL
)
1350 format
[2] = strdup(tmp
);
1355 * 'colord_register_printer()' - Register profiles for a printer.
1359 colord_register_printer(
1360 cupsd_printer_t
*p
) /* I - printer */
1362 char ppdfile
[1024], /* PPD filename */
1363 iccfile
[1024]; /* ICC filename */
1364 ppd_file_t
*ppd
; /* PPD file */
1365 cups_array_t
*profiles
; /* Profile paths array */
1366 ppd_attr_t
*attr
; /* Profile attributes */
1367 const char *device_colorspace
; /* Device colorspace */
1368 char *format
[3]; /* Qualifier format tuple */
1372 * Ensure we have a D-Bus connection...
1379 * Try opening the PPD file for this printer...
1382 snprintf(ppdfile
, sizeof(ppdfile
), "%s/ppd/%s.ppd", ServerRoot
, p
->name
);
1383 if ((ppd
= _ppdOpenFile(ppdfile
, _PPD_LOCALIZATION_ICC_PROFILES
)) == NULL
)
1387 * Find out the qualifier format
1390 colord_get_qualifier_format(ppd
, format
);
1393 * See if we have any embedded profiles...
1396 profiles
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
1397 (cups_afree_func_t
)free
);
1398 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
1400 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
1401 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
1403 if (attr
->value
[0] != '/')
1404 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
1407 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
1409 if (_cupsFileCheck(iccfile
, _CUPS_FILE_CHECK_FILE
, !RunUser
,
1410 cupsdLogFCMessage
, p
))
1413 colord_create_profile(profiles
, p
->name
, attr
->spec
, COLORD_SPACE_UNKNOWN
,
1414 format
, iccfile
, COLORD_SCOPE_TEMP
);
1418 * Add the grayscale profile first. We always have a grayscale profile.
1421 colord_create_profile(profiles
, p
->name
, "Gray..", COLORD_SPACE_GRAY
,
1422 format
, NULL
, COLORD_SCOPE_TEMP
);
1425 * Then add the RGB/CMYK/DeviceN color profile...
1428 device_colorspace
= "unknown";
1429 switch (ppd
->colorspace
)
1433 device_colorspace
= COLORD_SPACE_RGB
;
1434 colord_create_profile(profiles
, p
->name
, "RGB..", COLORD_SPACE_RGB
,
1435 format
, NULL
, COLORD_SCOPE_TEMP
);
1440 device_colorspace
= COLORD_SPACE_CMYK
;
1441 colord_create_profile(profiles
, p
->name
, "CMYK..", COLORD_SPACE_CMYK
,
1442 format
, NULL
, COLORD_SCOPE_TEMP
);
1446 device_colorspace
= COLORD_SPACE_GRAY
;
1450 colord_create_profile(profiles
, p
->name
, "DeviceN..",
1451 COLORD_SPACE_UNKNOWN
, format
, NULL
,
1457 * Register the device with colord.
1460 cupsdLogMessage(CUPSD_LOG_INFO
, "Registering ICC color profiles for \"%s\".",
1462 colord_create_device(p
, ppd
, profiles
, device_colorspace
, format
,
1463 COLORD_RELATION_SOFT
, COLORD_SCOPE_TEMP
);
1466 * Free any memory we used...
1469 cupsArrayDelete(profiles
);
1480 * 'colord_unregister_printer()' - Unregister profiles for a printer.
1484 colord_unregister_printer(
1485 cupsd_printer_t
*p
) /* I - printer */
1487 char device_id
[1024]; /* Device ID as understood by colord */
1491 * Ensure we have a D-Bus connection...
1498 * Just delete the device itself, and leave the profiles registered
1501 snprintf(device_id
, sizeof(device_id
), "cups-%s", p
->name
);
1502 colord_delete_device(device_id
);
1504 #endif /* __APPLE__ */