]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/colorman.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / scheduler / colorman.c
1 /*
2 * Color management routines for the CUPS scheduler.
3 *
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
8 *
9 * Original DBUS/colord code is Copyright 2011 Red Hat, Inc.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 *
18 * Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /*
37 * Include necessary headers...
38 */
39
40 #include "cupsd.h"
41 #include <cups/ppd-private.h>
42
43 #ifdef __APPLE__
44 # include <ApplicationServices/ApplicationServices.h>
45 extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
46 # include <CoreFoundation/CoreFoundation.h>
47 #elif defined(HAVE_DBUS)
48 # include <dbus/dbus.h>
49
50 /*
51 * Defines used by colord. See the reference docs for further details:
52 *
53 * http://colord.hughsie.com/api/ref-dbus.html
54 */
55
56 # define COLORD_SCOPE_NORMAL "normal"
57 /* System scope */
58 # define COLORD_SCOPE_TEMP "temp" /* Process scope */
59 # define COLORD_SCOPE_DISK "disk" /* Lives forever, as stored in DB */
60
61 # define COLORD_RELATION_SOFT "soft" /* Mapping is not default */
62 # define COLORD_RELATION_HARD "hard" /* Explicitly mapped profile */
63
64 # define COLORD_SPACE_RGB "rgb" /* RGB colorspace */
65 # define COLORD_SPACE_CMYK "cmyk" /* CMYK colorspace */
66 # define COLORD_SPACE_GRAY "gray" /* Gray colorspace */
67 # define COLORD_SPACE_UNKNOWN "unknown"
68 /* Unknown colorspace */
69
70 # define COLORD_MODE_PHYSICAL "physical"
71 /* Actual device */
72 # define COLORD_MODE_VIRTUAL "virtual"
73 /* Virtual device with no hardware */
74
75 # define COLORD_KIND_PRINTER "printer"
76 /* printing output device */
77
78 # define COLORD_DBUS_SERVICE "org.freedesktop.ColorManager"
79 # define COLORD_DBUS_INTERFACE "org.freedesktop.ColorManager"
80 # define COLORD_DBUS_INTERFACE_DEVICE "org.freedesktop.ColorManager.Device"
81 # define COLORD_DBUS_PATH "/org/freedesktop/ColorManager"
82 /* Path for color management system */
83 # define COLORD_DBUS_TIMEOUT 5000 /* Timeout for connecting to colord in ms */
84 #endif /* __APPLE__ */
85
86
87 /*
88 * Local globals...
89 */
90
91 #if !defined(__APPLE__) && defined(HAVE_DBUS)
92 static DBusConnection *colord_con = NULL;
93 /* DBUS connection for colord */
94 #endif /* !__APPLE__ && HAVE_DBUS */
95
96
97 /*
98 * Local functions...
99 */
100
101 #ifdef __APPLE__
102 static void apple_init_profile(ppd_file_t *ppd, cups_array_t *languages,
103 CFMutableDictionaryRef profile,
104 unsigned id, const char *name,
105 const char *text, const char *iccfile);
106 static void apple_register_profiles(cupsd_printer_t *p);
107 static void apple_unregister_profiles(cupsd_printer_t *p);
108
109 #elif defined(HAVE_DBUS)
110 static void colord_create_device(cupsd_printer_t *p, ppd_file_t *ppd,
111 cups_array_t *profiles,
112 const char *colorspace, char **format,
113 const char *relation, const char *scope);
114 static void colord_create_profile(cups_array_t *profiles,
115 const char *printer_name,
116 const char *qualifier,
117 const char *colorspace,
118 char **format, const char *iccfile,
119 const char *scope);
120 static void colord_delete_device(const char *device_id);
121 static void colord_device_add_profile(const char *device_path,
122 const char *profile_path,
123 const char *relation);
124 static void colord_dict_add_strings(DBusMessageIter *dict,
125 const char *key, const char *value);
126 static char *colord_find_device(const char *device_id);
127 static void colord_get_qualifier_format(ppd_file_t *ppd, char *format[3]);
128 static void colord_register_printer(cupsd_printer_t *p);
129 static void colord_unregister_printer(cupsd_printer_t *p);
130 #endif /* __APPLE__ */
131
132
133 /*
134 * 'cupsdRegisterColor()' - Register vendor color profiles in a PPD file.
135 */
136
137 void
138 cupsdRegisterColor(cupsd_printer_t *p) /* I - Printer */
139 {
140 #ifdef __APPLE__
141 if (!RunUser)
142 {
143 apple_unregister_profiles(p);
144 apple_register_profiles(p);
145 }
146
147 #elif defined(HAVE_DBUS)
148 if (!RunUser)
149 {
150 colord_unregister_printer(p);
151 colord_register_printer(p);
152 }
153 #endif /* __APPLE__ */
154 }
155
156
157 /*
158 * 'cupsdStartColor()' - Initialize color management.
159 */
160
161 void
162 cupsdStartColor(void)
163 {
164 #if !defined(__APPLE__) && defined(HAVE_DBUS)
165 cupsd_printer_t *p; /* Current printer */
166
167
168 colord_con = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
169
170 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
171 p;
172 p = (cupsd_printer_t *)cupsArrayNext(Printers))
173 cupsdRegisterColor(p);
174 #endif /* !__APPLE__ && HAVE_DBUS */
175 }
176
177
178 /*
179 * 'cupsdStopColor()' - Shutdown color management.
180 */
181
182 void
183 cupsdStopColor(void)
184 {
185 #if !defined(__APPLE__) && defined(HAVE_DBUS)
186 if (colord_con)
187 dbus_connection_unref(colord_con);
188 colord_con = NULL;
189 #endif /* !__APPLE__ && HAVE_DBUS */
190 }
191
192
193 /*
194 * 'cupsdUnregisterColor()' - Unregister vendor color profiles in a PPD file.
195 */
196
197 void
198 cupsdUnregisterColor(cupsd_printer_t *p)/* I - Printer */
199 {
200 #ifdef __APPLE__
201 if (!RunUser)
202 apple_unregister_profiles(p);
203
204 #elif defined(HAVE_DBUS)
205 if (!RunUser)
206 colord_unregister_printer(p);
207 #endif /* __APPLE__ */
208 }
209
210
211 #ifdef __APPLE__
212 /*
213 * 'apple_init_profile()' - Initialize a color profile.
214 */
215
216 static void
217 apple_init_profile(
218 ppd_file_t *ppd, /* I - PPD file */
219 cups_array_t *languages, /* I - Languages in the PPD file */
220 CFMutableDictionaryRef profile, /* I - Profile dictionary */
221 unsigned id, /* I - Profile ID */
222 const char *name, /* I - Profile name */
223 const char *text, /* I - Profile UI text */
224 const char *iccfile) /* I - ICC filename */
225 {
226 CFURLRef url; /* URL for profile filename */
227 CFMutableDictionaryRef dict; /* Dictionary for name */
228 char *language; /* Current language */
229 ppd_attr_t *attr; /* Profile attribute */
230 CFStringRef cflang, /* Language string */
231 cftext; /* Localized text */
232
233
234 (void)id;
235
236 /*
237 * Build the profile name dictionary...
238 */
239
240 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
241 &kCFTypeDictionaryKeyCallBacks,
242 &kCFTypeDictionaryValueCallBacks);
243 if (!dict)
244 {
245 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize profile \"%s\".",
246 iccfile);
247 return;
248 }
249
250 cftext = CFStringCreateWithCString(kCFAllocatorDefault, text,
251 kCFStringEncodingUTF8);
252
253 if (cftext)
254 {
255 CFDictionarySetValue(dict, CFSTR("en_US"), cftext);
256 CFRelease(cftext);
257 }
258
259 if (languages)
260 {
261 /*
262 * Find localized names for the color profiles...
263 */
264
265 cupsArraySave(ppd->sorted_attrs);
266
267 for (language = (char *)cupsArrayFirst(languages);
268 language;
269 language = (char *)cupsArrayNext(languages))
270 {
271 if (iccfile)
272 {
273 if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name,
274 language)) == NULL)
275 attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language);
276 }
277 else
278 attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language);
279
280 if (attr && attr->text[0])
281 {
282 cflang = CFStringCreateWithCString(kCFAllocatorDefault, language,
283 kCFStringEncodingUTF8);
284 cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text,
285 kCFStringEncodingUTF8);
286
287 if (cflang && cftext)
288 CFDictionarySetValue(dict, cflang, cftext);
289
290 if (cflang)
291 CFRelease(cflang);
292
293 if (cftext)
294 CFRelease(cftext);
295 }
296 }
297
298 cupsArrayRestore(ppd->sorted_attrs);
299 }
300
301 /*
302 * Fill in the profile data...
303 */
304
305 if (iccfile && *iccfile)
306 {
307 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)iccfile, (CFIndex)strlen(iccfile), false);
308
309 if (url)
310 {
311 CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url);
312 CFRelease(url);
313 }
314 }
315
316 CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict);
317 CFRelease(dict);
318 }
319
320
321 /*
322 * 'apple_register_profiles()' - Register color profiles for a printer.
323 */
324
325 static void
326 apple_register_profiles(
327 cupsd_printer_t *p) /* I - Printer */
328 {
329 int i; /* Looping var */
330 char ppdfile[1024], /* PPD filename */
331 iccfile[1024], /* ICC filename */
332 selector[PPD_MAX_NAME];
333 /* Profile selection string */
334 ppd_file_t *ppd; /* PPD file */
335 ppd_attr_t *attr, /* Profile attributes */
336 *profileid_attr,/* cupsProfileID attribute */
337 *q1_attr, /* ColorModel (or other) qualifier */
338 *q2_attr, /* MediaType (or other) qualifier */
339 *q3_attr; /* Resolution (or other) qualifier */
340 char q_keyword[PPD_MAX_NAME];
341 /* Qualifier keyword */
342 const char *q1_choice, /* ColorModel (or other) choice */
343 *q2_choice, /* MediaType (or other) choice */
344 *q3_choice; /* Resolution (or other) choice */
345 ppd_option_t *cm_option; /* Color model option */
346 ppd_choice_t *cm_choice; /* Color model choice */
347 int num_profiles; /* Number of profiles */
348 OSStatus error = 0; /* Last error */
349 unsigned device_id, /* Printer device ID */
350 profile_id = 0, /* Profile ID */
351 default_profile_id = 0;
352 /* Default profile ID */
353 CFMutableDictionaryRef device_name; /* Printer device name dictionary */
354 CFStringRef printer_name; /* Printer name string */
355 cups_array_t *languages; /* Languages array */
356 CFMutableDictionaryRef profiles, /* Dictionary of profiles */
357 profile; /* Current profile info dictionary */
358 CFStringRef dict_key; /* Key in factory profile dictionary */
359
360
361 /*
362 * Make sure ColorSync is available...
363 */
364
365 if (&ColorSyncRegisterDevice == NULL)
366 return;
367
368 /*
369 * Try opening the PPD file for this printer...
370 */
371
372 snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
373 if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
374 return;
375
376 /*
377 * See if we have any profiles...
378 */
379
380 for (num_profiles = 0, attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
381 attr;
382 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
383 if (attr->spec[0] && attr->value && attr->value[0])
384 {
385 if (attr->value[0] != '/')
386 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
387 attr->value);
388 else
389 strlcpy(iccfile, attr->value, sizeof(iccfile));
390
391 if (access(iccfile, 0))
392 {
393 cupsdLogMessage(CUPSD_LOG_ERROR,
394 "%s: ICC Profile \"%s\" does not exist.", p->name,
395 iccfile);
396 cupsdSetPrinterReasons(p, "+cups-missing-filter-warning");
397 continue;
398 }
399
400 num_profiles ++;
401 }
402
403 /*
404 * Create a dictionary for the factory profiles...
405 */
406
407 profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
408 &kCFTypeDictionaryKeyCallBacks,
409 &kCFTypeDictionaryValueCallBacks);
410 if (!profiles)
411 {
412 cupsdLogMessage(CUPSD_LOG_ERROR,
413 "Unable to allocate memory for factory profiles.");
414 ppdClose(ppd);
415 return;
416 }
417
418 /*
419 * If we have profiles, add them...
420 */
421
422 if (num_profiles > 0)
423 {
424 /*
425 * For CUPS PPDs, figure out the default profile selector values...
426 */
427
428 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL &&
429 attr->value && attr->value[0])
430 {
431 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
432 q1_attr = ppdFindAttr(ppd, q_keyword, NULL);
433 }
434 else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL)
435 q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
436
437 if (q1_attr && q1_attr->value && q1_attr->value[0])
438 q1_choice = q1_attr->value;
439 else
440 q1_choice = "";
441
442 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
443 attr->value && attr->value[0])
444 {
445 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
446 q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
447 }
448 else
449 q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
450
451 if (q2_attr && q2_attr->value && q2_attr->value[0])
452 q2_choice = q2_attr->value;
453 else
454 q2_choice = NULL;
455
456 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
457 attr->value && attr->value[0])
458 {
459 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
460 q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
461 }
462 else
463 q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
464
465 if (q3_attr && q3_attr->value && q3_attr->value[0])
466 q3_choice = q3_attr->value;
467 else
468 q3_choice = NULL;
469
470 /*
471 * Loop through the profiles listed in the PPD...
472 */
473
474 languages = _ppdGetLanguages(ppd);
475
476 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
477 attr;
478 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
479 if (attr->spec[0] && attr->value && attr->value[0])
480 {
481 /*
482 * Add this profile...
483 */
484
485 if (attr->value[0] != '/')
486 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
487 attr->value);
488 else
489 strlcpy(iccfile, attr->value, sizeof(iccfile));
490
491 if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
492 cupsdLogFCMessage, p))
493 iccfile[0] = '\0';
494
495 cupsArraySave(ppd->sorted_attrs);
496
497 if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID",
498 attr->spec)) != NULL &&
499 profileid_attr->value && isdigit(profileid_attr->value[0] & 255))
500 profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10);
501 else
502 profile_id = _ppdHashName(attr->spec);
503
504 cupsArrayRestore(ppd->sorted_attrs);
505
506 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
507 &kCFTypeDictionaryKeyCallBacks,
508 &kCFTypeDictionaryValueCallBacks);
509 if (!profile)
510 {
511 cupsdLogMessage(CUPSD_LOG_ERROR,
512 "Unable to allocate memory for color profile.");
513 CFRelease(profiles);
514 ppdClose(ppd);
515 return;
516 }
517
518 apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
519 attr->text[0] ? attr->text : attr->spec, iccfile);
520
521 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
522 CFSTR("%u"), profile_id);
523 if (dict_key)
524 {
525 CFDictionarySetValue(profiles, dict_key, profile);
526 CFRelease(dict_key);
527 }
528
529 CFRelease(profile);
530
531 /*
532 * See if this is the default profile...
533 */
534
535 if (!default_profile_id && q1_choice && q2_choice && q3_choice)
536 {
537 snprintf(selector, sizeof(selector), "%s.%s.%s", q1_choice, q2_choice,
538 q3_choice);
539 if (!strcmp(selector, attr->spec))
540 default_profile_id = profile_id;
541 }
542
543 if (!default_profile_id && q1_choice && q2_choice)
544 {
545 snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, q2_choice);
546 if (!strcmp(selector, attr->spec))
547 default_profile_id = profile_id;
548 }
549
550 if (!default_profile_id && q1_choice && q3_choice)
551 {
552 snprintf(selector, sizeof(selector), "%s..%s", q1_choice, q3_choice);
553 if (!strcmp(selector, attr->spec))
554 default_profile_id = profile_id;
555 }
556
557 if (!default_profile_id && q1_choice)
558 {
559 snprintf(selector, sizeof(selector), "%s..", q1_choice);
560 if (!strcmp(selector, attr->spec))
561 default_profile_id = profile_id;
562 }
563
564 if (!default_profile_id && q2_choice && q3_choice)
565 {
566 snprintf(selector, sizeof(selector), ".%s.%s", q2_choice, q3_choice);
567 if (!strcmp(selector, attr->spec))
568 default_profile_id = profile_id;
569 }
570
571 if (!default_profile_id && q2_choice)
572 {
573 snprintf(selector, sizeof(selector), ".%s.", q2_choice);
574 if (!strcmp(selector, attr->spec))
575 default_profile_id = profile_id;
576 }
577
578 if (!default_profile_id && q3_choice)
579 {
580 snprintf(selector, sizeof(selector), "..%s", q3_choice);
581 if (!strcmp(selector, attr->spec))
582 default_profile_id = profile_id;
583 }
584 }
585
586 _ppdFreeLanguages(languages);
587 }
588 else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
589 {
590 /*
591 * Extract profiles from ColorModel option...
592 */
593
594 const char *profile_name; /* Name of generic profile */
595
596
597 num_profiles = cm_option->num_choices;
598
599 for (i = cm_option->num_choices, cm_choice = cm_option->choices;
600 i > 0;
601 i --, cm_choice ++)
602 {
603 if (!strcmp(cm_choice->choice, "Gray") ||
604 !strcmp(cm_choice->choice, "Black"))
605 profile_name = "Gray";
606 else if (!strcmp(cm_choice->choice, "RGB") ||
607 !strcmp(cm_choice->choice, "CMY"))
608 profile_name = "RGB";
609 else if (!strcmp(cm_choice->choice, "CMYK") ||
610 !strcmp(cm_choice->choice, "KCMY"))
611 profile_name = "CMYK";
612 else
613 profile_name = "DeviceN";
614
615 snprintf(selector, sizeof(selector), "%s..", profile_name);
616 profile_id = _ppdHashName(selector);
617
618 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
619 &kCFTypeDictionaryKeyCallBacks,
620 &kCFTypeDictionaryValueCallBacks);
621 if (!profile)
622 {
623 cupsdLogMessage(CUPSD_LOG_ERROR,
624 "Unable to allocate memory for color profile.");
625 CFRelease(profiles);
626 ppdClose(ppd);
627 return;
628 }
629
630 apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
631 cm_choice->text, NULL);
632
633 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
634 CFSTR("%u"), profile_id);
635 if (dict_key)
636 {
637 CFDictionarySetValue(profiles, dict_key, profile);
638 CFRelease(dict_key);
639 }
640
641 CFRelease(profile);
642
643 if (cm_choice->marked)
644 default_profile_id = profile_id;
645 }
646 }
647 else
648 {
649 /*
650 * Use the default colorspace...
651 */
652
653 attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
654
655 num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2;
656
657 /*
658 * Add the grayscale profile first. We always have a grayscale profile.
659 */
660
661 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
662 &kCFTypeDictionaryKeyCallBacks,
663 &kCFTypeDictionaryValueCallBacks);
664
665 if (!profile)
666 {
667 cupsdLogMessage(CUPSD_LOG_ERROR,
668 "Unable to allocate memory for color profile.");
669 CFRelease(profiles);
670 ppdClose(ppd);
671 return;
672 }
673
674 profile_id = _ppdHashName("Gray..");
675 apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL);
676
677 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
678 profile_id);
679 if (dict_key)
680 {
681 CFDictionarySetValue(profiles, dict_key, profile);
682 CFRelease(dict_key);
683 }
684
685 CFRelease(profile);
686
687 /*
688 * Then add the RGB/CMYK/DeviceN color profile...
689 */
690
691 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
692 &kCFTypeDictionaryKeyCallBacks,
693 &kCFTypeDictionaryValueCallBacks);
694
695 if (!profile)
696 {
697 cupsdLogMessage(CUPSD_LOG_ERROR,
698 "Unable to allocate memory for color profile.");
699 CFRelease(profiles);
700 ppdClose(ppd);
701 return;
702 }
703
704 switch (ppd->colorspace)
705 {
706 default :
707 case PPD_CS_RGB :
708 case PPD_CS_CMY :
709 profile_id = _ppdHashName("RGB..");
710 apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB",
711 NULL);
712 break;
713
714 case PPD_CS_RGBK :
715 case PPD_CS_CMYK :
716 profile_id = _ppdHashName("CMYK..");
717 apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK",
718 NULL);
719 break;
720
721 case PPD_CS_GRAY :
722 if (attr)
723 break;
724
725 case PPD_CS_N :
726 profile_id = _ppdHashName("DeviceN..");
727 apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN",
728 "DeviceN", NULL);
729 break;
730 }
731
732 if (CFDictionaryGetCount(profile) > 0)
733 {
734 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
735 CFSTR("%u"), profile_id);
736 if (dict_key)
737 {
738 CFDictionarySetValue(profiles, dict_key, profile);
739 CFRelease(dict_key);
740 }
741 }
742
743 CFRelease(profile);
744 }
745
746 if (num_profiles > 0)
747 {
748 /*
749 * Make sure we have a default profile ID...
750 */
751
752 if (!default_profile_id)
753 default_profile_id = profile_id; /* Last profile */
754
755 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
756 default_profile_id);
757 if (dict_key)
758 {
759 CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID,
760 dict_key);
761 CFRelease(dict_key);
762 }
763
764 /*
765 * Get the device ID hash and pathelogical name dictionary.
766 */
767
768 cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"",
769 p->name);
770
771 device_id = _ppdHashName(p->name);
772 device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
773 &kCFTypeDictionaryKeyCallBacks,
774 &kCFTypeDictionaryValueCallBacks);
775 printer_name = CFStringCreateWithCString(kCFAllocatorDefault,
776 p->name, kCFStringEncodingUTF8);
777
778 if (device_name && printer_name)
779 {
780 /*
781 * Register the device with ColorSync...
782 */
783
784 CFTypeRef deviceDictKeys[] =
785 { /* Device keys */
786 kColorSyncDeviceDescriptions,
787 kColorSyncFactoryProfiles,
788 kColorSyncDeviceUserScope,
789 kColorSyncDeviceHostScope
790 };
791 CFTypeRef deviceDictVals[] =
792 { /* Device values */
793 device_name,
794 profiles,
795 kCFPreferencesAnyUser,
796 kCFPreferencesCurrentHost
797 };
798 CFDictionaryRef deviceDict; /* Device dictionary */
799 CFUUIDRef deviceUUID; /* Device UUID */
800
801 CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name);
802
803 deviceDict = CFDictionaryCreate(kCFAllocatorDefault,
804 (const void **)deviceDictKeys,
805 (const void **)deviceDictVals,
806 sizeof(deviceDictKeys) /
807 sizeof(deviceDictKeys[0]),
808 &kCFTypeDictionaryKeyCallBacks,
809 &kCFTypeDictionaryValueCallBacks);
810 deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id);
811
812 if (!deviceDict || !deviceUUID ||
813 !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID,
814 deviceDict))
815 error = 1001;
816
817 if (deviceUUID)
818 CFRelease(deviceUUID);
819
820 if (deviceDict)
821 CFRelease(deviceDict);
822 }
823 else
824 error = 1000;
825
826 /*
827 * Clean up...
828 */
829
830 if (error != noErr)
831 cupsdLogMessage(CUPSD_LOG_ERROR,
832 "Unable to register ICC color profiles for \"%s\": %d",
833 p->name, (int)error);
834
835 if (printer_name)
836 CFRelease(printer_name);
837
838 if (device_name)
839 CFRelease(device_name);
840 }
841
842 /*
843 * Free any memory we used...
844 */
845
846 CFRelease(profiles);
847
848 ppdClose(ppd);
849 }
850
851
852 /*
853 * 'apple_unregister_profiles()' - Remove color profiles for the specified
854 * printer.
855 */
856
857 static void
858 apple_unregister_profiles(
859 cupsd_printer_t *p) /* I - Printer */
860 {
861 /*
862 * Make sure ColorSync is available...
863 */
864
865 if (&ColorSyncUnregisterDevice != NULL)
866 {
867 CFUUIDRef deviceUUID; /* Device UUID */
868
869 deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name));
870 if (deviceUUID)
871 {
872 ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
873 CFRelease(deviceUUID);
874 }
875 }
876 }
877
878
879 #elif defined(HAVE_DBUS)
880 /*
881 * 'colord_create_device()' - Create a device and register profiles.
882 */
883
884 static void
885 colord_create_device(
886 cupsd_printer_t *p, /* I - Printer */
887 ppd_file_t *ppd, /* I - PPD file */
888 cups_array_t *profiles, /* I - Profiles array */
889 const char *colorspace, /* I - Device colorspace, e.g. 'rgb' */
890 char **format, /* I - Device qualifier format */
891 const char *relation, /* I - Profile relation, either 'soft'
892 or 'hard' */
893 const char *scope) /* I - The scope of the device, e.g.
894 'normal', 'temp' or 'disk' */
895 {
896 DBusMessage *message = NULL; /* D-Bus request */
897 DBusMessage *reply = NULL; /* D-Bus reply */
898 DBusMessageIter args; /* D-Bus method arguments */
899 DBusMessageIter dict; /* D-Bus method arguments */
900 DBusError error; /* D-Bus error */
901 const char *device_path; /* Device object path */
902 const char *profile_path; /* Profile path */
903 char *default_profile_path = NULL;
904 /* Default profile path */
905 char device_id[1024]; /* Device ID as understood by colord */
906 char format_str[1024]; /* Qualifier format as a string */
907
908
909 /*
910 * Create the device...
911 */
912
913 snprintf(device_id, sizeof(device_id), "cups-%s", p->name);
914 device_path = device_id;
915
916 message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
917 COLORD_DBUS_PATH,
918 COLORD_DBUS_INTERFACE,
919 "CreateDevice");
920
921 dbus_message_iter_init_append(message, &args);
922 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_path);
923 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope);
924
925 snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1],
926 format[2]);
927
928 dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict);
929 colord_dict_add_strings(&dict, "Colorspace", colorspace);
930 colord_dict_add_strings(&dict, "Mode", COLORD_MODE_PHYSICAL);
931 if (ppd->manufacturer)
932 colord_dict_add_strings(&dict, "Vendor", ppd->manufacturer);
933 if (ppd->modelname)
934 colord_dict_add_strings(&dict, "Model", ppd->modelname);
935 if (p->sanitized_device_uri)
936 colord_dict_add_strings(&dict, "Serial", p->sanitized_device_uri);
937 colord_dict_add_strings(&dict, "Format", format_str);
938 colord_dict_add_strings(&dict, "Kind", COLORD_KIND_PRINTER);
939 dbus_message_iter_close_container(&args, &dict);
940
941 /*
942 * Send the CreateDevice request synchronously...
943 */
944
945 dbus_error_init(&error);
946 cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateDevice(%s,%s)", device_id,
947 scope);
948 reply = dbus_connection_send_with_reply_and_block(colord_con, message,
949 COLORD_DBUS_TIMEOUT,
950 &error);
951 if (!reply)
952 {
953 cupsdLogMessage(CUPSD_LOG_WARN, "CreateDevice failed: %s:%s", error.name,
954 error.message);
955 dbus_error_free(&error);
956 goto out;
957 }
958
959 /*
960 * Get reply data...
961 */
962
963 dbus_message_iter_init(reply, &args);
964 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
965 {
966 cupsdLogMessage(CUPSD_LOG_WARN,
967 "CreateDevice failed: Incorrect reply type.");
968 goto out;
969 }
970
971 dbus_message_iter_get_basic(&args, &device_path);
972 cupsdLogMessage(CUPSD_LOG_DEBUG, "Created device \"%s\".", device_path);
973
974 /*
975 * Add profiles...
976 */
977
978 for (profile_path = cupsArrayFirst(profiles);
979 profile_path;
980 profile_path = cupsArrayNext(profiles))
981 {
982 colord_device_add_profile(device_path, profile_path, relation);
983 }
984
985 out:
986
987 if (default_profile_path)
988 free(default_profile_path);
989
990 if (message)
991 dbus_message_unref(message);
992
993 if (reply)
994 dbus_message_unref(reply);
995 }
996
997
998 /*
999 * 'colord_create_profile()' - Create a color profile for a printer.
1000 */
1001
1002 static void
1003 colord_create_profile(
1004 cups_array_t *profiles, /* I - Profiles array */
1005 const char *printer_name, /* I - Printer name */
1006 const char *qualifier, /* I - Profile qualifier */
1007 const char *colorspace, /* I - Profile colorspace */
1008 char **format, /* I - Profile qualifier format */
1009 const char *iccfile, /* I - ICC filename */
1010 const char *scope) /* I - The scope of the profile, e.g.
1011 'normal', 'temp' or 'disk' */
1012 {
1013 DBusMessage *message = NULL; /* D-Bus request */
1014 DBusMessage *reply = NULL; /* D-Bus reply */
1015 DBusMessageIter args; /* D-Bus method arguments */
1016 DBusMessageIter dict; /* D-Bus method arguments */
1017 DBusError error; /* D-Bus error */
1018 char *idstr; /* Profile ID string */
1019 size_t idstrlen; /* Profile ID allocated length */
1020 const char *profile_path; /* Device object path */
1021 char format_str[1024]; /* Qualifier format as a string */
1022
1023
1024 /*
1025 * Create the profile...
1026 */
1027
1028 message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1029 COLORD_DBUS_PATH,
1030 COLORD_DBUS_INTERFACE,
1031 "CreateProfile");
1032
1033 idstrlen = strlen(printer_name) + 1 + strlen(qualifier) + 1;
1034 if ((idstr = malloc(idstrlen)) == NULL)
1035 goto out;
1036 snprintf(idstr, idstrlen, "%s-%s", printer_name, qualifier);
1037 cupsdLogMessage(CUPSD_LOG_DEBUG, "Using profile ID \"%s\".", idstr);
1038
1039 dbus_message_iter_init_append(message, &args);
1040 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &idstr);
1041 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope);
1042
1043 snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1],
1044 format[2]);
1045
1046 dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict);
1047 colord_dict_add_strings(&dict, "Qualifier", qualifier);
1048 colord_dict_add_strings(&dict, "Format", format_str);
1049 colord_dict_add_strings(&dict, "Colorspace", colorspace);
1050 if (iccfile)
1051 colord_dict_add_strings(&dict, "Filename", iccfile);
1052 dbus_message_iter_close_container(&args, &dict);
1053
1054 /*
1055 * Send the CreateProfile request synchronously...
1056 */
1057
1058 dbus_error_init(&error);
1059 cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateProfile(%s,%s)", idstr,
1060 scope);
1061 reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1062 COLORD_DBUS_TIMEOUT,
1063 &error);
1064 if (!reply)
1065 {
1066 cupsdLogMessage(CUPSD_LOG_WARN, "CreateProfile failed: %s:%s", error.name,
1067 error.message);
1068 dbus_error_free(&error);
1069 goto out;
1070 }
1071
1072 /*
1073 * Get reply data...
1074 */
1075
1076 dbus_message_iter_init(reply, &args);
1077 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
1078 {
1079 cupsdLogMessage(CUPSD_LOG_WARN,
1080 "CreateProfile failed: Incorrect reply type.");
1081 goto out;
1082 }
1083
1084 dbus_message_iter_get_basic(&args, &profile_path);
1085 cupsdLogMessage(CUPSD_LOG_DEBUG, "Created profile \"%s\".", profile_path);
1086 cupsArrayAdd(profiles, strdup(profile_path));
1087
1088 out:
1089
1090 if (message)
1091 dbus_message_unref(message);
1092
1093 if (reply)
1094 dbus_message_unref(reply);
1095
1096 if (idstr)
1097 free(idstr);
1098 }
1099
1100
1101 /*
1102 * 'colord_delete_device()' - Delete a device
1103 */
1104
1105 static void
1106 colord_delete_device(
1107 const char *device_id) /* I - Device ID string */
1108 {
1109 DBusMessage *message = NULL; /* D-Bus request */
1110 DBusMessage *reply = NULL; /* D-Bus reply */
1111 DBusMessageIter args; /* D-Bus method arguments */
1112 DBusError error; /* D-Bus error */
1113 char *device_path; /* Device object path */
1114
1115
1116 /*
1117 * Find the device...
1118 */
1119
1120 if ((device_path = colord_find_device(device_id)) == NULL)
1121 goto out;
1122
1123 /*
1124 * Delete the device...
1125 */
1126
1127 message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1128 COLORD_DBUS_PATH,
1129 COLORD_DBUS_INTERFACE,
1130 "DeleteDevice");
1131
1132 dbus_message_iter_init_append(message, &args);
1133 dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &device_path);
1134
1135 /*
1136 * Send the DeleteDevice request synchronously...
1137 */
1138
1139 dbus_error_init(&error);
1140 cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling DeleteDevice(%s)", device_path);
1141 reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1142 COLORD_DBUS_TIMEOUT,
1143 &error);
1144 if (!reply)
1145 {
1146 cupsdLogMessage(CUPSD_LOG_DEBUG, "DeleteDevice failed: %s:%s", error.name,
1147 error.message);
1148 dbus_error_free(&error);
1149 goto out;
1150 }
1151
1152 out:
1153
1154 if (device_path)
1155 free(device_path);
1156
1157 if (message)
1158 dbus_message_unref(message);
1159
1160 if (reply)
1161 dbus_message_unref(reply);
1162 }
1163
1164
1165 /*
1166 * 'colord_device_add_profile()' - Assign a profile to a device.
1167 */
1168
1169 static void
1170 colord_device_add_profile(
1171 const char *device_path, /* I - Device object path */
1172 const char *profile_path, /* I - Profile object path */
1173 const char *relation) /* I - Device relation, either
1174 'soft' or 'hard' */
1175 {
1176 DBusMessage *message = NULL; /* D-Bus request */
1177 DBusMessage *reply = NULL; /* D-Bus reply */
1178 DBusMessageIter args; /* D-Bus method arguments */
1179 DBusError error; /* D-Bus error */
1180
1181
1182 message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1183 device_path,
1184 COLORD_DBUS_INTERFACE_DEVICE,
1185 "AddProfile");
1186
1187 dbus_message_iter_init_append(message, &args);
1188 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &relation);
1189 dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &profile_path);
1190 cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling %s:AddProfile(%s) [%s]",
1191 device_path, profile_path, relation);
1192
1193 /*
1194 * Send the AddProfile request synchronously...
1195 */
1196
1197 dbus_error_init(&error);
1198 reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1199 COLORD_DBUS_TIMEOUT,
1200 &error);
1201 if (!reply)
1202 {
1203 cupsdLogMessage(CUPSD_LOG_WARN, "AddProfile failed: %s:%s", error.name,
1204 error.message);
1205 dbus_error_free(&error);
1206 goto out;
1207 }
1208
1209 out:
1210
1211 if (message)
1212 dbus_message_unref(message);
1213
1214 if (reply)
1215 dbus_message_unref(reply);
1216 }
1217
1218
1219 /*
1220 * 'colord_dict_add_strings()' - Add two strings to a dictionary.
1221 */
1222
1223 static void
1224 colord_dict_add_strings(
1225 DBusMessageIter *dict, /* I - Dictionary */
1226 const char *key, /* I - Key string */
1227 const char *value) /* I - Value string */
1228 {
1229 DBusMessageIter entry; /* Entry to add */
1230
1231
1232 dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
1233 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
1234 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value);
1235 dbus_message_iter_close_container(dict, &entry);
1236 }
1237
1238
1239 /*
1240 * 'colord_find_device()' - Finds a device
1241 */
1242
1243 static char * /* O - Device path or NULL */
1244 colord_find_device(
1245 const char *device_id) /* I - Device ID string */
1246 {
1247 DBusMessage *message = NULL; /* D-Bus request */
1248 DBusMessage *reply = NULL; /* D-Bus reply */
1249 DBusMessageIter args; /* D-Bus method arguments */
1250 DBusError error; /* D-Bus error */
1251 const char *device_path_tmp; /* Device object path */
1252 char *device_path = NULL; /* Device object path */
1253
1254
1255 message = dbus_message_new_method_call(COLORD_DBUS_SERVICE,
1256 COLORD_DBUS_PATH,
1257 COLORD_DBUS_INTERFACE,
1258 "FindDeviceById");
1259
1260 dbus_message_iter_init_append(message, &args);
1261 dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_id);
1262
1263 /*
1264 * Send the FindDeviceById request synchronously...
1265 */
1266
1267 dbus_error_init(&error);
1268 cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling FindDeviceById(%s)", device_id);
1269 reply = dbus_connection_send_with_reply_and_block(colord_con, message,
1270 COLORD_DBUS_TIMEOUT,
1271 &error);
1272 if (!reply)
1273 {
1274 cupsdLogMessage(CUPSD_LOG_DEBUG, "FindDeviceById failed: %s:%s",
1275 error.name, error.message);
1276 dbus_error_free(&error);
1277 goto out;
1278 }
1279
1280 /*
1281 * Get reply data...
1282 */
1283
1284 dbus_message_iter_init(reply, &args);
1285 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
1286 {
1287 cupsdLogMessage(CUPSD_LOG_WARN,
1288 "FindDeviceById failed: Incorrect reply type.");
1289 goto out;
1290 }
1291
1292 dbus_message_iter_get_basic(&args, &device_path_tmp);
1293 if (device_path_tmp)
1294 device_path = strdup(device_path_tmp);
1295
1296 out:
1297
1298 if (message)
1299 dbus_message_unref(message);
1300
1301 if (reply)
1302 dbus_message_unref(reply);
1303
1304 return (device_path);
1305 }
1306
1307
1308 /*
1309 * 'colord_get_qualifier_format()' - Get the qualifier format.
1310 *
1311 * Note: Returns a value of "ColorSpace.MediaType.Resolution" by default.
1312 */
1313
1314 static void
1315 colord_get_qualifier_format(
1316 ppd_file_t *ppd, /* I - PPD file data */
1317 char *format[3]) /* I - Format tuple */
1318 {
1319 const char *tmp; /* Temporary string */
1320 ppd_attr_t *attr; /* Profile attributes */
1321
1322
1323 /*
1324 * Get 1st section...
1325 */
1326
1327 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL)
1328 tmp = attr->value;
1329 else if (ppdFindAttr(ppd, "DefaultColorModel", NULL))
1330 tmp = "ColorModel";
1331 else if (ppdFindAttr(ppd, "DefaultColorSpace", NULL))
1332 tmp = "ColorSpace";
1333 else
1334 tmp = "";
1335
1336 format[0] = strdup(tmp);
1337
1338 /*
1339 * Get 2nd section...
1340 */
1341
1342 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL)
1343 tmp = attr->value;
1344 else
1345 tmp = "MediaType";
1346
1347 format[1] = strdup(tmp);
1348
1349 /*
1350 * Get 3rd section...
1351 */
1352
1353 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL)
1354 tmp = attr->value;
1355 else
1356 tmp = "Resolution";
1357
1358 format[2] = strdup(tmp);
1359 }
1360
1361
1362 /*
1363 * 'colord_register_printer()' - Register profiles for a printer.
1364 */
1365
1366 static void
1367 colord_register_printer(
1368 cupsd_printer_t *p) /* I - printer */
1369 {
1370 char ppdfile[1024], /* PPD filename */
1371 iccfile[1024]; /* ICC filename */
1372 ppd_file_t *ppd; /* PPD file */
1373 cups_array_t *profiles; /* Profile paths array */
1374 ppd_attr_t *attr; /* Profile attributes */
1375 const char *device_colorspace; /* Device colorspace */
1376 char *format[3]; /* Qualifier format tuple */
1377
1378
1379 /*
1380 * Ensure we have a D-Bus connection...
1381 */
1382
1383 if (!colord_con)
1384 return;
1385
1386 /*
1387 * Try opening the PPD file for this printer...
1388 */
1389
1390 snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
1391 if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
1392 return;
1393
1394 /*
1395 * Find out the qualifier format
1396 */
1397
1398 colord_get_qualifier_format(ppd, format);
1399
1400 /*
1401 * See if we have any embedded profiles...
1402 */
1403
1404 profiles = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup,
1405 (cups_afree_func_t)free);
1406 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
1407 attr;
1408 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
1409 if (attr->spec[0] && attr->value && attr->value[0])
1410 {
1411 if (attr->value[0] != '/')
1412 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
1413 attr->value);
1414 else
1415 strlcpy(iccfile, attr->value, sizeof(iccfile));
1416
1417 if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
1418 cupsdLogFCMessage, p))
1419 continue;
1420
1421 colord_create_profile(profiles, p->name, attr->spec, COLORD_SPACE_UNKNOWN,
1422 format, iccfile, COLORD_SCOPE_TEMP);
1423 }
1424
1425 /*
1426 * Add the grayscale profile first. We always have a grayscale profile.
1427 */
1428
1429 colord_create_profile(profiles, p->name, "Gray..", COLORD_SPACE_GRAY,
1430 format, NULL, COLORD_SCOPE_TEMP);
1431
1432 /*
1433 * Then add the RGB/CMYK/DeviceN color profile...
1434 */
1435
1436 device_colorspace = "unknown";
1437 switch (ppd->colorspace)
1438 {
1439 case PPD_CS_RGB :
1440 case PPD_CS_CMY :
1441 device_colorspace = COLORD_SPACE_RGB;
1442 colord_create_profile(profiles, p->name, "RGB..", COLORD_SPACE_RGB,
1443 format, NULL, COLORD_SCOPE_TEMP);
1444 break;
1445
1446 case PPD_CS_RGBK :
1447 case PPD_CS_CMYK :
1448 device_colorspace = COLORD_SPACE_CMYK;
1449 colord_create_profile(profiles, p->name, "CMYK..", COLORD_SPACE_CMYK,
1450 format, NULL, COLORD_SCOPE_TEMP);
1451 break;
1452
1453 case PPD_CS_GRAY :
1454 device_colorspace = COLORD_SPACE_GRAY;
1455 break;
1456
1457 case PPD_CS_N :
1458 colord_create_profile(profiles, p->name, "DeviceN..",
1459 COLORD_SPACE_UNKNOWN, format, NULL,
1460 COLORD_SCOPE_TEMP);
1461 break;
1462 }
1463
1464 /*
1465 * Register the device with colord.
1466 */
1467
1468 cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\".",
1469 p->name);
1470 colord_create_device(p, ppd, profiles, device_colorspace, format,
1471 COLORD_RELATION_SOFT, COLORD_SCOPE_TEMP);
1472
1473 /*
1474 * Free any memory we used...
1475 */
1476
1477 cupsArrayDelete(profiles);
1478
1479 free(format[0]);
1480 free(format[1]);
1481 free(format[2]);
1482
1483 ppdClose(ppd);
1484 }
1485
1486
1487 /*
1488 * 'colord_unregister_printer()' - Unregister profiles for a printer.
1489 */
1490
1491 static void
1492 colord_unregister_printer(
1493 cupsd_printer_t *p) /* I - printer */
1494 {
1495 char device_id[1024]; /* Device ID as understood by colord */
1496
1497
1498 /*
1499 * Ensure we have a D-Bus connection...
1500 */
1501
1502 if (!colord_con)
1503 return;
1504
1505 /*
1506 * Just delete the device itself, and leave the profiles registered
1507 */
1508
1509 snprintf(device_id, sizeof(device_id), "cups-%s", p->name);
1510 colord_delete_device(device_id);
1511 }
1512 #endif /* __APPLE__ */