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