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.
43 * cupsdRegisterColor() - Register vendor color profiles in a PPD
45 * cupsdStartColor() - Initialize color management.
46 * cupsdStopColor() - Shutdown color management.
47 * cupsdUnregisterColor() - Unregister vendor color profiles in a PPD
49 * apple_init_profile() - Initialize a color profile.
50 * apple_register_profiles() - Register color profiles for a printer.
51 * apple_unregister_profiles() - Remove color profiles for the specified
53 * colord_create_device() - Create a device and register profiles.
54 * colord_create_profile() - Create a color profile for a printer.
55 * colord_delete_device() - Delete a device
56 * colord_device_add_profile() - Assign a profile to a device.
57 * colord_dict_add_strings() - Add two strings to a dictionary.
58 * colord_find_device() - Finds a device
59 * colord_get_qualifier_format() - Get the qualifier format.
60 * colord_register_printer() - Register profiles for a printer.
61 * colord_unregister_printer() - Unregister profiles for a printer.
65 * Include necessary headers...
69 #include <cups/ppd-private.h>
72 # include <ApplicationServices/ApplicationServices.h>
73 extern CFUUIDRef
ColorSyncCreateUUIDFromUInt32(unsigned id
);
74 # include <CoreFoundation/CoreFoundation.h>
75 #elif defined(HAVE_DBUS)
76 # include <dbus/dbus.h>
79 * Defines used by colord. See the reference docs for further details:
81 * http://colord.hughsie.com/api/ref-dbus.html
84 # define COLORD_SCOPE_NORMAL "normal"
86 # define COLORD_SCOPE_TEMP "temp" /* Process scope */
87 # define COLORD_SCOPE_DISK "disk" /* Lives forever, as stored in DB */
89 # define COLORD_RELATION_SOFT "soft" /* Mapping is not default */
90 # define COLORD_RELATION_HARD "hard" /* Explicitly mapped profile */
92 # define COLORD_SPACE_RGB "rgb" /* RGB colorspace */
93 # define COLORD_SPACE_CMYK "cmyk" /* CMYK colorspace */
94 # define COLORD_SPACE_GRAY "gray" /* Gray colorspace */
95 # define COLORD_SPACE_UNKNOWN "unknown"
96 /* Unknown colorspace */
98 # define COLORD_MODE_PHYSICAL "physical"
100 # define COLORD_MODE_VIRTUAL "virtual"
101 /* Virtual device with no hardware */
103 # define COLORD_KIND_PRINTER "printer"
104 /* printing output device */
106 # define COLORD_DBUS_MSG(p,m) dbus_message_new_method_call(\
107 "org.freedesktop.ColorManager", (p),\
108 "org.freedesktop.ColorManager", (m))
109 /* Macro to make new colord messages */
110 # define COLORD_DBUS_PATH "/org/freedesktop/ColorManager"
111 /* Path for color management system */
112 # define COLORD_DBUS_TIMEOUT 5000 /* Timeout for connecting to colord in ms */
113 #endif /* __APPLE__ */
120 #if !defined(__APPLE__) && defined(HAVE_DBUS)
121 static DBusConnection
*colord_con
= NULL
;
122 /* DBUS connection for colord */
123 #endif /* !__APPLE__ && HAVE_DBUS */
131 static void apple_init_profile(ppd_file_t
*ppd
, cups_array_t
*languages
,
132 CFMutableDictionaryRef profile
,
133 unsigned id
, const char *name
,
134 const char *text
, const char *iccfile
);
135 static void apple_register_profiles(cupsd_printer_t
*p
);
136 static void apple_unregister_profiles(cupsd_printer_t
*p
);
138 #elif defined(HAVE_DBUS)
139 static void colord_create_device(cupsd_printer_t
*p
, ppd_file_t
*ppd
,
140 cups_array_t
*profiles
,
141 const char *colorspace
, char **format
,
142 const char *relation
, const char *scope
);
143 static void colord_create_profile(cups_array_t
*profiles
,
144 const char *printer_name
,
145 const char *qualifier
,
146 const char *colorspace
,
147 char **format
, const char *iccfile
,
149 static void colord_delete_device(const char *device_id
);
150 static void colord_device_add_profile(const char *device_path
,
151 const char *profile_path
,
152 const char *relation
);
153 static void colord_dict_add_strings(DBusMessageIter
*dict
,
154 const char *key
, const char *value
);
155 static char *colord_find_device(const char *device_id
);
156 static void colord_get_qualifier_format(ppd_file_t
*ppd
, char *format
[3]);
157 static void colord_register_printer(cupsd_printer_t
*p
);
158 static void colord_unregister_printer(cupsd_printer_t
*p
);
159 #endif /* __APPLE__ */
163 * 'cupsdRegisterColor()' - Register vendor color profiles in a PPD file.
167 cupsdRegisterColor(cupsd_printer_t
*p
) /* I - Printer */
172 apple_unregister_profiles(p
);
173 apple_register_profiles(p
);
176 #elif defined(HAVE_DBUS)
177 colord_unregister_printer(p
);
178 colord_register_printer(p
);
179 #endif /* __APPLE__ */
184 * 'cupsdStartColor()' - Initialize color management.
188 cupsdStartColor(void)
190 #if !defined(__APPLE__) && defined(HAVE_DBUS)
191 cupsd_printer_t
*p
; /* Current printer */
194 colord_con
= dbus_bus_get(DBUS_BUS_SYSTEM
, NULL
);
196 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
198 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
199 cupsdRegisterColor(p
);
200 #endif /* !__APPLE__ && HAVE_DBUS */
205 * 'cupsdStopColor()' - Shutdown color management.
211 #if !defined(__APPLE__) && defined(HAVE_DBUS)
212 dbus_connection_unref(colord_con
);
214 #endif /* !__APPLE__ && HAVE_DBUS */
219 * 'cupsdUnregisterColor()' - Unregister vendor color profiles in a PPD file.
223 cupsdUnregisterColor(cupsd_printer_t
*p
)/* I - Printer */
227 apple_unregister_profiles(p
);
229 #elif defined(HAVE_DBUS)
230 colord_unregister_printer(p
);
231 #endif /* __APPLE__ */
237 * 'apple_init_profile()' - Initialize a color profile.
242 ppd_file_t
*ppd
, /* I - PPD file */
243 cups_array_t
*languages
, /* I - Languages in the PPD file */
244 CFMutableDictionaryRef profile
, /* I - Profile dictionary */
245 unsigned id
, /* I - Profile ID */
246 const char *name
, /* I - Profile name */
247 const char *text
, /* I - Profile UI text */
248 const char *iccfile
) /* I - ICC filename */
250 CFURLRef url
; /* URL for profile filename */
251 CFMutableDictionaryRef dict
; /* Dictionary for name */
252 char *language
; /* Current language */
253 ppd_attr_t
*attr
; /* Profile attribute */
254 CFStringRef cflang
, /* Language string */
255 cftext
; /* Localized text */
261 * Build the profile name dictionary...
264 dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
265 &kCFTypeDictionaryKeyCallBacks
,
266 &kCFTypeDictionaryValueCallBacks
);
269 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to initialize profile \"%s\".",
274 cftext
= CFStringCreateWithCString(kCFAllocatorDefault
, text
,
275 kCFStringEncodingUTF8
);
279 CFDictionarySetValue(dict
, CFSTR("en_US"), cftext
);
286 * Find localized names for the color profiles...
289 cupsArraySave(ppd
->sorted_attrs
);
291 for (language
= (char *)cupsArrayFirst(languages
);
293 language
= (char *)cupsArrayNext(languages
))
297 if ((attr
= _ppdLocalizedAttr(ppd
, "cupsICCProfile", name
,
299 attr
= _ppdLocalizedAttr(ppd
, "APTiogaProfile", name
, language
);
302 attr
= _ppdLocalizedAttr(ppd
, "ColorModel", name
, language
);
304 if (attr
&& attr
->text
[0])
306 cflang
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
307 kCFStringEncodingUTF8
);
308 cftext
= CFStringCreateWithCString(kCFAllocatorDefault
, attr
->text
,
309 kCFStringEncodingUTF8
);
311 if (cflang
&& cftext
)
312 CFDictionarySetValue(dict
, cflang
, cftext
);
322 cupsArrayRestore(ppd
->sorted_attrs
);
326 * Fill in the profile data...
329 if (iccfile
&& *iccfile
)
331 url
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
332 (const UInt8
*)iccfile
,
333 strlen(iccfile
), false);
337 CFDictionarySetValue(profile
, kColorSyncDeviceProfileURL
, url
);
342 CFDictionarySetValue(profile
, kColorSyncDeviceModeDescriptions
, dict
);
348 * 'apple_register_profiles()' - Register color profiles for a printer.
352 apple_register_profiles(
353 cupsd_printer_t
*p
) /* I - Printer */
355 int i
; /* Looping var */
356 char ppdfile
[1024], /* PPD filename */
357 iccfile
[1024], /* ICC filename */
358 selector
[PPD_MAX_NAME
];
359 /* Profile selection string */
360 ppd_file_t
*ppd
; /* PPD file */
361 ppd_attr_t
*attr
, /* Profile attributes */
362 *profileid_attr
,/* cupsProfileID attribute */
363 *q1_attr
, /* ColorModel (or other) qualifier */
364 *q2_attr
, /* MediaType (or other) qualifier */
365 *q3_attr
; /* Resolution (or other) qualifier */
366 char q_keyword
[PPD_MAX_NAME
];
367 /* Qualifier keyword */
368 const char *q1_choice
, /* ColorModel (or other) choice */
369 *q2_choice
, /* MediaType (or other) choice */
370 *q3_choice
; /* Resolution (or other) choice */
371 ppd_option_t
*cm_option
; /* Color model option */
372 ppd_choice_t
*cm_choice
; /* Color model choice */
373 int num_profiles
; /* Number of profiles */
374 OSStatus error
= 0; /* Last error */
375 unsigned device_id
, /* Printer device ID */
376 profile_id
= 0, /* Profile ID */
377 default_profile_id
= 0;
378 /* Default profile ID */
379 CFMutableDictionaryRef device_name
; /* Printer device name dictionary */
380 CFStringRef printer_name
; /* Printer name string */
381 cups_array_t
*languages
; /* Languages array */
382 CFMutableDictionaryRef profiles
, /* Dictionary of profiles */
383 profile
; /* Current profile info dictionary */
384 CFStringRef dict_key
; /* Key in factory profile dictionary */
388 * Make sure ColorSync is available...
391 if (ColorSyncRegisterDevice
== NULL
)
395 * Try opening the PPD file for this printer...
398 snprintf(ppdfile
, sizeof(ppdfile
), "%s/ppd/%s.ppd", ServerRoot
, p
->name
);
399 if ((ppd
= _ppdOpenFile(ppdfile
, _PPD_LOCALIZATION_ICC_PROFILES
)) == NULL
)
403 * See if we have any profiles...
406 for (num_profiles
= 0, attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
408 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
409 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
411 if (attr
->value
[0] != '/')
412 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
415 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
417 if (access(iccfile
, 0))
419 cupsdLogMessage(CUPSD_LOG_ERROR
,
420 "%s: ICC Profile \"%s\" does not exist.", p
->name
,
422 cupsdSetPrinterReasons(p
, "+cups-missing-filter-warning");
430 * Create a dictionary for the factory profiles...
433 profiles
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
434 &kCFTypeDictionaryKeyCallBacks
,
435 &kCFTypeDictionaryValueCallBacks
);
438 cupsdLogMessage(CUPSD_LOG_ERROR
,
439 "Unable to allocate memory for factory profiles.");
445 * If we have profiles, add them...
448 if (num_profiles
> 0)
451 * For CUPS PPDs, figure out the default profile selector values...
454 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier1", NULL
)) != NULL
&&
455 attr
->value
&& attr
->value
[0])
457 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
458 q1_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
460 else if ((q1_attr
= ppdFindAttr(ppd
, "DefaultColorModel", NULL
)) == NULL
)
461 q1_attr
= ppdFindAttr(ppd
, "DefaultColorSpace", NULL
);
463 if (q1_attr
&& q1_attr
->value
&& q1_attr
->value
[0])
464 q1_choice
= q1_attr
->value
;
468 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier2", NULL
)) != NULL
&&
469 attr
->value
&& attr
->value
[0])
471 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
472 q2_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
475 q2_attr
= ppdFindAttr(ppd
, "DefaultMediaType", NULL
);
477 if (q2_attr
&& q2_attr
->value
&& q2_attr
->value
[0])
478 q2_choice
= q2_attr
->value
;
482 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier3", NULL
)) != NULL
&&
483 attr
->value
&& attr
->value
[0])
485 snprintf(q_keyword
, sizeof(q_keyword
), "Default%s", attr
->value
);
486 q3_attr
= ppdFindAttr(ppd
, q_keyword
, NULL
);
489 q3_attr
= ppdFindAttr(ppd
, "DefaultResolution", NULL
);
491 if (q3_attr
&& q3_attr
->value
&& q3_attr
->value
[0])
492 q3_choice
= q3_attr
->value
;
497 * Loop through the profiles listed in the PPD...
500 languages
= _ppdGetLanguages(ppd
);
502 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
504 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
505 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
508 * Add this profile...
511 if (attr
->value
[0] != '/')
512 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
515 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
517 if (_cupsFileCheck(iccfile
, _CUPS_FILE_CHECK_FILE
, !RunUser
,
518 cupsdLogFCMessage
, p
))
521 cupsArraySave(ppd
->sorted_attrs
);
523 if ((profileid_attr
= ppdFindAttr(ppd
, "cupsProfileID",
524 attr
->spec
)) != NULL
&&
525 profileid_attr
->value
&& isdigit(profileid_attr
->value
[0] & 255))
526 profile_id
= (unsigned)strtoul(profileid_attr
->value
, NULL
, 10);
528 profile_id
= _ppdHashName(attr
->spec
);
530 cupsArrayRestore(ppd
->sorted_attrs
);
532 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
533 &kCFTypeDictionaryKeyCallBacks
,
534 &kCFTypeDictionaryValueCallBacks
);
537 cupsdLogMessage(CUPSD_LOG_ERROR
,
538 "Unable to allocate memory for color profile.");
544 apple_init_profile(ppd
, languages
, profile
, profile_id
, attr
->spec
,
545 attr
->text
[0] ? attr
->text
: attr
->spec
, iccfile
);
547 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
548 CFSTR("%u"), profile_id
);
551 CFDictionarySetValue(profiles
, dict_key
, profile
);
558 * See if this is the default profile...
561 if (!default_profile_id
&& q1_choice
&& q2_choice
&& q3_choice
)
563 snprintf(selector
, sizeof(selector
), "%s.%s.%s", q1_choice
, q2_choice
,
565 if (!strcmp(selector
, attr
->spec
))
566 default_profile_id
= profile_id
;
569 if (!default_profile_id
&& q1_choice
&& q2_choice
)
571 snprintf(selector
, sizeof(selector
), "%s.%s.", q1_choice
, q2_choice
);
572 if (!strcmp(selector
, attr
->spec
))
573 default_profile_id
= profile_id
;
576 if (!default_profile_id
&& q1_choice
&& q3_choice
)
578 snprintf(selector
, sizeof(selector
), "%s..%s", q1_choice
, q3_choice
);
579 if (!strcmp(selector
, attr
->spec
))
580 default_profile_id
= profile_id
;
583 if (!default_profile_id
&& q1_choice
)
585 snprintf(selector
, sizeof(selector
), "%s..", q1_choice
);
586 if (!strcmp(selector
, attr
->spec
))
587 default_profile_id
= profile_id
;
590 if (!default_profile_id
&& q2_choice
&& q3_choice
)
592 snprintf(selector
, sizeof(selector
), ".%s.%s", q2_choice
, q3_choice
);
593 if (!strcmp(selector
, attr
->spec
))
594 default_profile_id
= profile_id
;
597 if (!default_profile_id
&& q2_choice
)
599 snprintf(selector
, sizeof(selector
), ".%s.", q2_choice
);
600 if (!strcmp(selector
, attr
->spec
))
601 default_profile_id
= profile_id
;
604 if (!default_profile_id
&& q3_choice
)
606 snprintf(selector
, sizeof(selector
), "..%s", q3_choice
);
607 if (!strcmp(selector
, attr
->spec
))
608 default_profile_id
= profile_id
;
612 _ppdFreeLanguages(languages
);
614 else if ((cm_option
= ppdFindOption(ppd
, "ColorModel")) != NULL
)
617 * Extract profiles from ColorModel option...
620 const char *profile_name
; /* Name of generic profile */
623 num_profiles
= cm_option
->num_choices
;
625 for (i
= cm_option
->num_choices
, cm_choice
= cm_option
->choices
;
629 if (!strcmp(cm_choice
->choice
, "Gray") ||
630 !strcmp(cm_choice
->choice
, "Black"))
631 profile_name
= "Gray";
632 else if (!strcmp(cm_choice
->choice
, "RGB") ||
633 !strcmp(cm_choice
->choice
, "CMY"))
634 profile_name
= "RGB";
635 else if (!strcmp(cm_choice
->choice
, "CMYK") ||
636 !strcmp(cm_choice
->choice
, "KCMY"))
637 profile_name
= "CMYK";
639 profile_name
= "DeviceN";
641 snprintf(selector
, sizeof(selector
), "%s..", profile_name
);
642 profile_id
= _ppdHashName(selector
);
644 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
645 &kCFTypeDictionaryKeyCallBacks
,
646 &kCFTypeDictionaryValueCallBacks
);
649 cupsdLogMessage(CUPSD_LOG_ERROR
,
650 "Unable to allocate memory for color profile.");
656 apple_init_profile(ppd
, NULL
, profile
, profile_id
, cm_choice
->choice
,
657 cm_choice
->text
, NULL
);
659 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
660 CFSTR("%u"), profile_id
);
663 CFDictionarySetValue(profiles
, dict_key
, profile
);
669 if (cm_choice
->marked
)
670 default_profile_id
= profile_id
;
676 * Use the default colorspace...
679 attr
= ppdFindAttr(ppd
, "DefaultColorSpace", NULL
);
681 num_profiles
= (attr
&& ppd
->colorspace
== PPD_CS_GRAY
) ? 1 : 2;
684 * Add the grayscale profile first. We always have a grayscale profile.
687 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
688 &kCFTypeDictionaryKeyCallBacks
,
689 &kCFTypeDictionaryValueCallBacks
);
693 cupsdLogMessage(CUPSD_LOG_ERROR
,
694 "Unable to allocate memory for color profile.");
700 profile_id
= _ppdHashName("Gray..");
701 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "Gray", "Gray", NULL
);
703 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%u"),
707 CFDictionarySetValue(profiles
, dict_key
, profile
);
714 * Then add the RGB/CMYK/DeviceN color profile...
717 profile
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
718 &kCFTypeDictionaryKeyCallBacks
,
719 &kCFTypeDictionaryValueCallBacks
);
723 cupsdLogMessage(CUPSD_LOG_ERROR
,
724 "Unable to allocate memory for color profile.");
730 switch (ppd
->colorspace
)
735 profile_id
= _ppdHashName("RGB..");
736 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "RGB", "RGB",
742 profile_id
= _ppdHashName("CMYK..");
743 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "CMYK", "CMYK",
752 profile_id
= _ppdHashName("DeviceN..");
753 apple_init_profile(ppd
, NULL
, profile
, profile_id
, "DeviceN",
758 if (CFDictionaryGetCount(profile
) > 0)
760 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
761 CFSTR("%u"), profile_id
);
764 CFDictionarySetValue(profiles
, dict_key
, profile
);
772 if (num_profiles
> 0)
775 * Make sure we have a default profile ID...
778 if (!default_profile_id
)
779 default_profile_id
= profile_id
; /* Last profile */
781 dict_key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%u"),
785 CFDictionarySetValue(profiles
, kColorSyncDeviceDefaultProfileID
,
791 * Get the device ID hash and pathelogical name dictionary.
794 cupsdLogMessage(CUPSD_LOG_INFO
, "Registering ICC color profiles for \"%s\"",
797 device_id
= _ppdHashName(p
->name
);
798 device_name
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
799 &kCFTypeDictionaryKeyCallBacks
,
800 &kCFTypeDictionaryValueCallBacks
);
801 printer_name
= CFStringCreateWithCString(kCFAllocatorDefault
,
802 p
->name
, kCFStringEncodingUTF8
);
804 if (device_name
&& printer_name
)
807 * Register the device with ColorSync...
810 CFTypeRef deviceDictKeys
[] =
812 kColorSyncDeviceDescriptions
,
813 kColorSyncFactoryProfiles
,
814 kColorSyncDeviceUserScope
,
815 kColorSyncDeviceHostScope
817 CFTypeRef deviceDictVals
[] =
818 { /* Device values */
821 kCFPreferencesAnyUser
,
822 kCFPreferencesCurrentHost
824 CFDictionaryRef deviceDict
; /* Device dictionary */
825 CFUUIDRef deviceUUID
; /* Device UUID */
827 CFDictionarySetValue(device_name
, CFSTR("en_US"), printer_name
);
829 deviceDict
= CFDictionaryCreate(kCFAllocatorDefault
,
830 (const void **)deviceDictKeys
,
831 (const void **)deviceDictVals
,
832 sizeof(deviceDictKeys
) /
833 sizeof(deviceDictKeys
[0]),
834 &kCFTypeDictionaryKeyCallBacks
,
835 &kCFTypeDictionaryValueCallBacks
);
836 deviceUUID
= ColorSyncCreateUUIDFromUInt32(device_id
);
838 if (!deviceDict
|| !deviceUUID
||
839 !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass
, deviceUUID
,
844 CFRelease(deviceUUID
);
847 CFRelease(deviceDict
);
857 cupsdLogMessage(CUPSD_LOG_ERROR
,
858 "Unable to register ICC color profiles for \"%s\": %d",
859 p
->name
, (int)error
);
862 CFRelease(printer_name
);
865 CFRelease(device_name
);
869 * Free any memory we used...
879 * 'apple_unregister_profiles()' - Remove color profiles for the specified
884 apple_unregister_profiles(
885 cupsd_printer_t
*p
) /* I - Printer */
888 * Make sure ColorSync is available...
891 if (ColorSyncUnregisterDevice
!= NULL
)
893 CFUUIDRef deviceUUID
; /* Device UUID */
895 deviceUUID
= ColorSyncCreateUUIDFromUInt32(_ppdHashName(p
->name
));
898 ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass
, deviceUUID
);
899 CFRelease(deviceUUID
);
905 #elif defined(HAVE_DBUS)
907 * 'colord_create_device()' - Create a device and register profiles.
911 colord_create_device(
912 cupsd_printer_t
*p
, /* I - Printer */
913 ppd_file_t
*ppd
, /* I - PPD file */
914 cups_array_t
*profiles
, /* I - Profiles array */
915 const char *colorspace
, /* I - Device colorspace, e.g. 'rgb' */
916 char **format
, /* I - Device qualifier format */
917 const char *relation
, /* I - Profile relation, either 'soft'
919 const char *scope
) /* I - The scope of the device, e.g.
920 'normal', 'temp' or 'disk' */
922 DBusMessage
*message
= NULL
; /* D-Bus request */
923 DBusMessage
*reply
= NULL
; /* D-Bus reply */
924 DBusMessageIter args
; /* D-Bus method arguments */
925 DBusMessageIter dict
; /* D-Bus method arguments */
926 DBusError error
; /* D-Bus error */
927 const char *device_path
; /* Device object path */
928 const char *profile_path
; /* Profile path */
929 char *default_profile_path
= NULL
;
930 /* Default profile path */
931 char device_id
[1024]; /* Device ID as understood by colord */
932 char format_str
[1024]; /* Qualifier format as a string */
936 * Create the device...
939 snprintf(device_id
, sizeof(device_id
), "cups-%s", p
->name
);
940 device_path
= device_id
;
942 message
= COLORD_DBUS_MSG(COLORD_DBUS_PATH
, "CreateDevice");
944 dbus_message_iter_init_append(message
, &args
);
945 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &device_path
);
946 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &scope
);
948 snprintf(format_str
, sizeof(format_str
), "%s.%s.%s", format
[0], format
[1],
951 dbus_message_iter_open_container(&args
, DBUS_TYPE_ARRAY
, "{ss}", &dict
);
952 colord_dict_add_strings(&dict
, "Colorspace", colorspace
);
953 colord_dict_add_strings(&dict
, "Mode", COLORD_MODE_PHYSICAL
);
954 if (ppd
->manufacturer
)
955 colord_dict_add_strings(&dict
, "Vendor", ppd
->manufacturer
);
957 colord_dict_add_strings(&dict
, "Model", ppd
->modelname
);
958 if (p
->sanitized_device_uri
)
959 colord_dict_add_strings(&dict
, "Serial", p
->sanitized_device_uri
);
960 colord_dict_add_strings(&dict
, "Format", format_str
);
961 colord_dict_add_strings(&dict
, "Kind", COLORD_KIND_PRINTER
);
962 dbus_message_iter_close_container(&args
, &dict
);
965 * Send the CreateDevice request synchronously...
968 dbus_error_init(&error
);
969 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling CreateDevice(%s,%s)", device_id
,
971 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
976 cupsdLogMessage(CUPSD_LOG_WARN
, "CreateDevice failed: %s:%s", error
.name
,
978 dbus_error_free(&error
);
986 dbus_message_iter_init(reply
, &args
);
987 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
989 cupsdLogMessage(CUPSD_LOG_WARN
,
990 "CreateDevice failed: Incorrect reply type.");
994 dbus_message_iter_get_basic(&args
, &device_path
);
995 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Created device \"%s\".", device_path
);
1001 for (profile_path
= cupsArrayFirst(profiles
);
1003 profile_path
= cupsArrayNext(profiles
))
1005 colord_device_add_profile(device_path
, profile_path
, relation
);
1010 if (default_profile_path
)
1011 free(default_profile_path
);
1014 dbus_message_unref(message
);
1017 dbus_message_unref(reply
);
1022 * 'colord_create_profile()' - Create a color profile for a printer.
1026 colord_create_profile(
1027 cups_array_t
*profiles
, /* I - Profiles array */
1028 const char *printer_name
, /* I - Printer name */
1029 const char *qualifier
, /* I - Profile qualifier */
1030 const char *colorspace
, /* I - Profile colorspace */
1031 char **format
, /* I - Profile qualifier format */
1032 const char *iccfile
, /* I - ICC filename */
1033 const char *scope
) /* I - The scope of the profile, e.g.
1034 'normal', 'temp' or 'disk' */
1036 DBusMessage
*message
= NULL
; /* D-Bus request */
1037 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1038 DBusMessageIter args
; /* D-Bus method arguments */
1039 DBusMessageIter dict
; /* D-Bus method arguments */
1040 DBusError error
; /* D-Bus error */
1041 char *idstr
; /* Profile ID string */
1042 size_t idstrlen
; /* Profile ID allocated length */
1043 const char *profile_path
; /* Device object path */
1044 char format_str
[1024]; /* Qualifier format as a string */
1048 * Create the profile...
1051 message
= COLORD_DBUS_MSG(COLORD_DBUS_PATH
, "CreateProfile");
1053 idstrlen
= strlen(printer_name
) + 1 + strlen(qualifier
) + 1;
1054 if ((idstr
= malloc(idstrlen
)) == NULL
)
1056 snprintf(idstr
, idstrlen
, "%s-%s", printer_name
, qualifier
);
1057 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Using profile ID \"%s\".", idstr
);
1059 dbus_message_iter_init_append(message
, &args
);
1060 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &idstr
);
1061 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &scope
);
1063 snprintf(format_str
, sizeof(format_str
), "%s.%s.%s", format
[0], format
[1],
1066 dbus_message_iter_open_container(&args
, DBUS_TYPE_ARRAY
, "{ss}", &dict
);
1067 colord_dict_add_strings(&dict
, "Qualifier", qualifier
);
1068 colord_dict_add_strings(&dict
, "Format", format_str
);
1069 colord_dict_add_strings(&dict
, "Colorspace", colorspace
);
1071 colord_dict_add_strings(&dict
, "Filename", iccfile
);
1072 dbus_message_iter_close_container(&args
, &dict
);
1075 * Send the CreateProfile request synchronously...
1078 dbus_error_init(&error
);
1079 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling CreateProfile(%s,%s)", idstr
,
1081 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1082 COLORD_DBUS_TIMEOUT
,
1086 cupsdLogMessage(CUPSD_LOG_WARN
, "CreateProfile failed: %s:%s", error
.name
,
1088 dbus_error_free(&error
);
1096 dbus_message_iter_init(reply
, &args
);
1097 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
1099 cupsdLogMessage(CUPSD_LOG_WARN
,
1100 "CreateProfile failed: Incorrect reply type.");
1104 dbus_message_iter_get_basic(&args
, &profile_path
);
1105 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Created profile \"%s\".", profile_path
);
1106 cupsArrayAdd(profiles
, strdup(profile_path
));
1111 dbus_message_unref(message
);
1114 dbus_message_unref(reply
);
1122 * 'colord_delete_device()' - Delete a device
1126 colord_delete_device(
1127 const char *device_id
) /* I - Device ID string */
1129 DBusMessage
*message
= NULL
; /* D-Bus request */
1130 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1131 DBusMessageIter args
; /* D-Bus method arguments */
1132 DBusError error
; /* D-Bus error */
1133 char *device_path
; /* Device object path */
1137 * Find the device...
1140 if ((device_path
= colord_find_device(device_id
)) == NULL
)
1144 * Delete the device...
1147 message
= COLORD_DBUS_MSG(COLORD_DBUS_PATH
, "DeleteDevice");
1149 dbus_message_iter_init_append(message
, &args
);
1150 dbus_message_iter_append_basic(&args
, DBUS_TYPE_OBJECT_PATH
, &device_path
);
1153 * Send the DeleteDevice request synchronously...
1156 dbus_error_init(&error
);
1157 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling DeleteDevice(%s)", device_path
);
1158 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1159 COLORD_DBUS_TIMEOUT
,
1163 cupsdLogMessage(CUPSD_LOG_DEBUG
, "DeleteDevice failed: %s:%s", error
.name
,
1165 dbus_error_free(&error
);
1175 dbus_message_unref(message
);
1178 dbus_message_unref(reply
);
1183 * 'colord_device_add_profile()' - Assign a profile to a device.
1187 colord_device_add_profile(
1188 const char *device_path
, /* I - Device object path */
1189 const char *profile_path
, /* I - Profile object path */
1190 const char *relation
) /* I - Device relation, either
1193 DBusMessage
*message
= NULL
; /* D-Bus request */
1194 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1195 DBusMessageIter args
; /* D-Bus method arguments */
1196 DBusError error
; /* D-Bus error */
1199 message
= COLORD_DBUS_MSG(device_path
, "AddProfile");
1201 dbus_message_iter_init_append(message
, &args
);
1202 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &relation
);
1203 dbus_message_iter_append_basic(&args
, DBUS_TYPE_OBJECT_PATH
, &profile_path
);
1204 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling %s:AddProfile(%s) [%s]",
1205 device_path
, profile_path
, relation
);
1208 * Send the AddProfile request synchronously...
1211 dbus_error_init(&error
);
1212 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1213 COLORD_DBUS_TIMEOUT
,
1217 cupsdLogMessage(CUPSD_LOG_WARN
, "AddProfile failed: %s:%s", error
.name
,
1219 dbus_error_free(&error
);
1226 dbus_message_unref(message
);
1229 dbus_message_unref(reply
);
1234 * 'colord_dict_add_strings()' - Add two strings to a dictionary.
1238 colord_dict_add_strings(
1239 DBusMessageIter
*dict
, /* I - Dictionary */
1240 const char *key
, /* I - Key string */
1241 const char *value
) /* I - Value string */
1243 DBusMessageIter entry
; /* Entry to add */
1246 dbus_message_iter_open_container(dict
, DBUS_TYPE_DICT_ENTRY
, NULL
, &entry
);
1247 dbus_message_iter_append_basic(&entry
, DBUS_TYPE_STRING
, &key
);
1248 dbus_message_iter_append_basic(&entry
, DBUS_TYPE_STRING
, &value
);
1249 dbus_message_iter_close_container(dict
, &entry
);
1254 * 'colord_find_device()' - Finds a device
1257 static char * /* O - Device path or NULL */
1259 const char *device_id
) /* I - Device ID string */
1261 DBusMessage
*message
= NULL
; /* D-Bus request */
1262 DBusMessage
*reply
= NULL
; /* D-Bus reply */
1263 DBusMessageIter args
; /* D-Bus method arguments */
1264 DBusError error
; /* D-Bus error */
1265 const char *device_path_tmp
; /* Device object path */
1266 char *device_path
= NULL
; /* Device object path */
1269 message
= COLORD_DBUS_MSG(COLORD_DBUS_PATH
, "FindDeviceById");
1271 dbus_message_iter_init_append(message
, &args
);
1272 dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
, &device_id
);
1275 * Send the FindDeviceById request synchronously...
1278 dbus_error_init(&error
);
1279 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Calling FindDeviceById(%s)", device_id
);
1280 reply
= dbus_connection_send_with_reply_and_block(colord_con
, message
,
1281 COLORD_DBUS_TIMEOUT
,
1285 cupsdLogMessage(CUPSD_LOG_DEBUG
, "FindDeviceById failed: %s:%s",
1286 error
.name
, error
.message
);
1287 dbus_error_free(&error
);
1295 dbus_message_iter_init(reply
, &args
);
1296 if (dbus_message_iter_get_arg_type(&args
) != DBUS_TYPE_OBJECT_PATH
)
1298 cupsdLogMessage(CUPSD_LOG_WARN
,
1299 "FindDeviceById failed: Incorrect reply type.");
1303 dbus_message_iter_get_basic(&args
, &device_path_tmp
);
1304 if (device_path_tmp
)
1305 device_path
= strdup(device_path_tmp
);
1310 dbus_message_unref(message
);
1313 dbus_message_unref(reply
);
1315 return (device_path
);
1320 * 'colord_get_qualifier_format()' - Get the qualifier format.
1322 * Note: Returns a value of "ColorSpace.MediaType.Resolution" by default.
1326 colord_get_qualifier_format(
1327 ppd_file_t
*ppd
, /* I - PPD file data */
1328 char *format
[3]) /* I - Format tuple */
1330 const char *tmp
; /* Temporary string */
1331 ppd_attr_t
*attr
; /* Profile attributes */
1335 * Get 1st section...
1338 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier1", NULL
)) != NULL
)
1340 else if (ppdFindAttr(ppd
, "DefaultColorModel", NULL
))
1342 else if (ppdFindAttr(ppd
, "DefaultColorSpace", NULL
))
1347 format
[0] = strdup(tmp
);
1350 * Get 2nd section...
1353 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier2", NULL
)) != NULL
)
1358 format
[1] = strdup(tmp
);
1361 * Get 3rd section...
1364 if ((attr
= ppdFindAttr(ppd
, "cupsICCQualifier3", NULL
)) != NULL
)
1369 format
[2] = strdup(tmp
);
1374 * 'colord_register_printer()' - Register profiles for a printer.
1378 colord_register_printer(
1379 cupsd_printer_t
*p
) /* I - printer */
1381 char ppdfile
[1024], /* PPD filename */
1382 iccfile
[1024]; /* ICC filename */
1383 ppd_file_t
*ppd
; /* PPD file */
1384 cups_array_t
*profiles
; /* Profile paths array */
1385 ppd_attr_t
*attr
; /* Profile attributes */
1386 const char *device_colorspace
; /* Device colorspace */
1387 char *format
[3]; /* Qualifier format tuple */
1391 * Ensure we have a D-Bus connection...
1398 * Try opening the PPD file for this printer...
1401 snprintf(ppdfile
, sizeof(ppdfile
), "%s/ppd/%s.ppd", ServerRoot
, p
->name
);
1402 if ((ppd
= _ppdOpenFile(ppdfile
, _PPD_LOCALIZATION_ICC_PROFILES
)) == NULL
)
1406 * Find out the qualifier format
1409 colord_get_qualifier_format(ppd
, format
);
1412 * See if we have any embedded profiles...
1415 profiles
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
1416 (cups_afree_func_t
)free
);
1417 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
1419 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
1420 if (attr
->spec
[0] && attr
->value
&& attr
->value
[0])
1422 if (attr
->value
[0] != '/')
1423 snprintf(iccfile
, sizeof(iccfile
), "%s/profiles/%s", DataDir
,
1426 strlcpy(iccfile
, attr
->value
, sizeof(iccfile
));
1428 if (_cupsFileCheck(iccfile
, _CUPS_FILE_CHECK_FILE
, !RunUser
,
1429 cupsdLogFCMessage
, p
))
1432 colord_create_profile(profiles
, p
->name
, attr
->spec
, COLORD_SPACE_UNKNOWN
,
1433 format
, iccfile
, COLORD_SCOPE_TEMP
);
1437 * Add the grayscale profile first. We always have a grayscale profile.
1440 colord_create_profile(profiles
, p
->name
, "Gray..", COLORD_SPACE_GRAY
,
1441 format
, NULL
, COLORD_SCOPE_TEMP
);
1444 * Then add the RGB/CMYK/DeviceN color profile...
1447 device_colorspace
= "unknown";
1448 switch (ppd
->colorspace
)
1452 device_colorspace
= COLORD_SPACE_RGB
;
1453 colord_create_profile(profiles
, p
->name
, "RGB..", COLORD_SPACE_RGB
,
1454 format
, NULL
, COLORD_SCOPE_TEMP
);
1459 device_colorspace
= COLORD_SPACE_CMYK
;
1460 colord_create_profile(profiles
, p
->name
, "CMYK..", COLORD_SPACE_CMYK
,
1461 format
, NULL
, COLORD_SCOPE_TEMP
);
1465 device_colorspace
= COLORD_SPACE_GRAY
;
1469 colord_create_profile(profiles
, p
->name
, "DeviceN..",
1470 COLORD_SPACE_UNKNOWN
, format
, NULL
,
1476 * Register the device with colord.
1479 cupsdLogMessage(CUPSD_LOG_INFO
, "Registering ICC color profiles for \"%s\".",
1481 colord_create_device(p
, ppd
, profiles
, device_colorspace
, format
,
1482 COLORD_RELATION_SOFT
, COLORD_SCOPE_TEMP
);
1485 * Free any memory we used...
1488 cupsArrayDelete(profiles
);
1499 * 'colord_unregister_printer()' - Unregister profiles for a printer.
1503 colord_unregister_printer(
1504 cupsd_printer_t
*p
) /* I - printer */
1506 char device_id
[1024]; /* Device ID as understood by colord */
1510 * Ensure we have a D-Bus connection...
1517 * Just delete the device itself, and leave the profiles registered
1520 snprintf(device_id
, sizeof(device_id
), "cups-%s", p
->name
);
1521 colord_delete_device(device_id
);
1523 #endif /* __APPLE__ */