1 /*
2 * PWG Raster/Apple Raster/PCLm/PDF/IPP legacy PPD generator
3 *
4 * Copyright 2016-2019 by Till Kamppeter.
5 * Copyright 2017-2019 by Sahil Arora.
6 * Copyright 2018-2019 by Deepak Patankar.
7 *
8 * The PPD generator is based on the PPD generator for the CUPS
9 * "lpadmin -m everywhere" functionality in the cups/ppd-cache.c
10 * file. The copyright of this file is:
11 *
12 * Copyright 2010-2016 by Apple Inc.
13 *
14 * These coded instructions, statements, and computer programs are the
15 * property of Apple Inc. and are protected by Federal copyright
16 * law. Distribution and use rights are outlined in the file "COPYING"
17 * which should have been included with this file.
18 */
19
20 #include <config.h>
21 #include <limits.h>
22 #include <cups/cups.h>
23 #include <cups/dir.h>
24 #include <cupsfilters/ppdgenerator.h>
25 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
26 #define HAVE_CUPS_1_6 1
27 #endif
28 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 6)
29 #define HAVE_CUPS_1_7 1
30 #endif
31
32
33 /*
34 * Include necessary headers.
35 */
36
37 #include <errno.h>
38 #include "driver.h"
39 #include <string.h>
40 #include <ctype.h>
41 #ifdef HAVE_CUPS_1_7
42 #include <cups/pwg.h>
43 #endif /* HAVE_CUPS_1_7 */
44
45
46 /*
47 * Macros to work around typos in older libcups version
48 */
49
50 #if (CUPS_VERSION_MAJOR < 2) || ((CUPS_VERSION_MAJOR == 2) && ((CUPS_VERSION_MINOR < 3) || ((CUPS_VERSION_MINOR == 3) && (CUPS_VERSION_PATCH < 1))))
51 #define IPP_FINISHINGS_CUPS_FOLD_ACCORDION IPP_FINISHINGS_CUPS_FOLD_ACCORDIAN
52 #define IPP_FINISHINGS_FOLD_ACCORDION IPP_FINISHINGS_FOLD_ACCORDIAN
53 #endif
54
55
56 #ifdef HAVE_CUPS_1_6
57 /* The following code uses a lot of CUPS >= 1.6 specific stuff.
58 It needed for create_local_queue() in cups-browsed
59 to set up local queues for non-CUPS printer broadcasts
60 that is disabled in create_local_queue() for older CUPS <= 1.5.4.
61 Accordingly the following code is also disabled here for CUPS < 1.6. */
62
63 /*
64 * The code below is borrowed from the CUPS 2.2.x upstream repository
65 * (via patches attached to https://www.cups.org/str.php?L4258). This
66 * allows for automatic PPD generation already with CUPS versions older
67 * than CUPS 2.2.x. We have also an additional test and development
68 * platform for this code. Taken from cups/ppd-cache.c,
69 * cups/string-private.h, cups/string.c.
70 *
71 * The advantage of PPD generation instead of working with System V
72 * interface scripts is that the print dialogs of the clients do not
73 * need to ask the printer for its options via IPP. So we have access
74 * to the options with the current PPD-based dialogs and can even share
75 * the automatically created print queue to other CUPS-based machines
76 * without problems.
77 */
78
79
80 cups_array_t *opt_strings_catalog = NULL;
81 char ppdgenerator_msg[1024];
82
83 typedef struct _pwg_finishings_s /**** PWG finishings mapping data ****/
84 {
85 ipp_finishings_t value; /* finishings value */
86 int num_options; /* Number of options to apply */
87 cups_option_t *options; /* Options to apply */
88 } _pwg_finishings_t;
89
90 #define _PWG_EQUIVALENT(x, y) (abs((x)-(y)) < 2)
91
92 static void pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
93 static void pwg_ppdize_resolution(ipp_attribute_t *attr, int element,
94 int *xres, int *yres, char *name, size_t namesize);
95
96 /*
97 * '_cupsSetError()' - Set the last PPD generator status-message.
98 *
99 * This function replaces the original _cupsSetError() of the private
100 * API of the CUPS library. The #define and the renamed function prevent
101 * from the linker using the original function of the CUPS library instead
102 * of this replacement function.
103 */
104
105 #define _cupsSetError(x, y, z) _CFcupsSetError(x, y, z)
106
107 void
_CFcupsSetError(ipp_status_t status,const char * message,int localize)108 _CFcupsSetError(ipp_status_t status, /* I - IPP status code
109 (for compatibility, ignored) */
110 const char *message, /* I - status-message value */
111 int localize) /* I - Localize the message?
112 (for compatibility, ignored) */
113 {
114 (void)status;
115 (void)localize;
116
117 if (!message && errno)
118 message = strerror(errno);
119
120 if (message)
121 snprintf(ppdgenerator_msg, sizeof(ppdgenerator_msg), "%s", message);
122 }
123
124 int /* O - 1 on match, 0 otherwise */
_cups_isalnum(int ch)125 _cups_isalnum(int ch) /* I - Character to test */
126 {
127 return ((ch >= '0' && ch <= '9') ||
128 (ch >= 'A' && ch <= 'Z') ||
129 (ch >= 'a' && ch <= 'z'));
130 }
131
132 int /* O - 1 on match, 0 otherwise */
_cups_isalpha(int ch)133 _cups_isalpha(int ch) /* I - Character to test */
134 {
135 return ((ch >= 'A' && ch <= 'Z') ||
136 (ch >= 'a' && ch <= 'z'));
137 }
138
139 int /* O - 1 on match, 0 otherwise */
_cups_islower(int ch)140 _cups_islower(int ch) /* I - Character to test */
141 {
142 return (ch >= 'a' && ch <= 'z');
143 }
144
145 int /* O - 1 on match, 0 otherwise */
_cups_isspace(int ch)146 _cups_isspace(int ch) /* I - Character to test */
147 {
148 return (ch == ' ' || ch == '\f' || ch == '\n' || ch == '\r' || ch == '\t' ||
149 ch == '\v');
150 }
151
152 int /* O - 1 on match, 0 otherwise */
_cups_isupper(int ch)153 _cups_isupper(int ch) /* I - Character to test */
154 {
155 return (ch >= 'A' && ch <= 'Z');
156 }
157
158 int /* O - Converted character */
_cups_tolower(int ch)159 _cups_tolower(int ch) /* I - Character to convert */
160 {
161 return (_cups_isupper(ch) ? ch - 'A' + 'a' : ch);
162 }
163
164 int /* O - Converted character */
_cups_toupper(int ch)165 _cups_toupper(int ch) /* I - Character to convert */
166 {
167 return (_cups_islower(ch) ? ch - 'a' + 'A' : ch);
168 }
169
170 #ifndef HAVE_STRLCPY
171 /*
172 * '_cups_strlcpy()' - Safely copy two strings.
173 */
174
175 size_t /* O - Length of string */
strlcpy(char * dst,const char * src,size_t size)176 strlcpy(char *dst, /* O - Destination string */
177 const char *src, /* I - Source string */
178 size_t size) /* I - Size of destination string buffer */
179 {
180 size_t srclen; /* Length of source string */
181
182
183 /*
184 * Figure out how much room is needed...
185 */
186
187 size --;
188
189 srclen = strlen(src);
190
191 /*
192 * Copy the appropriate amount...
193 */
194
195 if (srclen > size)
196 srclen = size;
197
198 memmove(dst, src, srclen);
199 dst[srclen] = '\0';
200
201 return (srclen);
202 }
203 #endif /* !HAVE_STRLCPY */
204
205 /*
206 * '_cupsStrFormatd()' - Format a floating-point number.
207 */
208
209 char * /* O - Pointer to end of string */
_cupsStrFormatd(char * buf,char * bufend,double number,struct lconv * loc)210 _cupsStrFormatd(char *buf, /* I - String */
211 char *bufend, /* I - End of string buffer */
212 double number, /* I - Number to format */
213 struct lconv *loc) /* I - Locale data */
214 {
215 char *bufptr, /* Pointer into buffer */
216 temp[1024], /* Temporary string */
217 *tempdec, /* Pointer to decimal point */
218 *tempptr; /* Pointer into temporary string */
219 const char *dec; /* Decimal point */
220 int declen; /* Length of decimal point */
221
222
223 /*
224 * Format the number using the "%.12f" format and then eliminate
225 * unnecessary trailing 0's.
226 */
227
228 snprintf(temp, sizeof(temp), "%.12f", number);
229 for (tempptr = temp + strlen(temp) - 1;
230 tempptr > temp && *tempptr == '0';
231 *tempptr-- = '\0');
232
233 /*
234 * Next, find the decimal point...
235 */
236
237 if (loc && loc->decimal_point) {
238 dec = loc->decimal_point;
239 declen = (int)strlen(dec);
240 } else {
241 dec = ".";
242 declen = 1;
243 }
244
245 if (declen == 1)
246 tempdec = strchr(temp, *dec);
247 else
248 tempdec = strstr(temp, dec);
249
250 /*
251 * Copy everything up to the decimal point...
252 */
253
254 if (tempdec) {
255 for (tempptr = temp, bufptr = buf;
256 tempptr < tempdec && bufptr < bufend;
257 *bufptr++ = *tempptr++);
258
259 tempptr += declen;
260
261 if (*tempptr && bufptr < bufend) {
262 *bufptr++ = '.';
263
264 while (*tempptr && bufptr < bufend)
265 *bufptr++ = *tempptr++;
266 }
267
268 *bufptr = '\0';
269 } else {
270 strlcpy(buf, temp, (size_t)(bufend - buf + 1));
271 bufptr = buf + strlen(buf);
272 }
273
274 return (bufptr);
275 }
276
277
278 /*
279 * '_cups_strcasecmp()' - Do a case-insensitive comparison.
280 */
281
282 int /* O - Result of comparison (-1, 0, or 1) */
_cups_strcasecmp(const char * s,const char * t)283 _cups_strcasecmp(const char *s, /* I - First string */
284 const char *t) /* I - Second string */
285 {
286 while (*s != '\0' && *t != '\0') {
287 if (_cups_tolower(*s) < _cups_tolower(*t))
288 return (-1);
289 else if (_cups_tolower(*s) > _cups_tolower(*t))
290 return (1);
291
292 s ++;
293 t ++;
294 }
295
296 if (*s == '\0' && *t == '\0')
297 return (0);
298 else if (*s != '\0')
299 return (1);
300 else
301 return (-1);
302 }
303
304 /*
305 * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
306 */
307
308 int /* O - Result of comparison (-1, 0, or 1) */
_cups_strncasecmp(const char * s,const char * t,size_t n)309 _cups_strncasecmp(const char *s, /* I - First string */
310 const char *t, /* I - Second string */
311 size_t n) /* I - Maximum number of characters to
312 compare */
313 {
314 while (*s != '\0' && *t != '\0' && n > 0) {
315 if (_cups_tolower(*s) < _cups_tolower(*t))
316 return (-1);
317 else if (_cups_tolower(*s) > _cups_tolower(*t))
318 return (1);
319
320 s ++;
321 t ++;
322 n --;
323 }
324
325 if (n == 0)
326 return (0);
327 else if (*s == '\0' && *t == '\0')
328 return (0);
329 else if (*s != '\0')
330 return (1);
331 else
332 return (-1);
333 }
334
335
336 /*
337 * 'pwg_compare_sizes()' - Compare two media sizes...
338 */
339
340 static int /* O - Result of comparison */
pwg_compare_sizes(cups_size_t * a,cups_size_t * b)341 pwg_compare_sizes(cups_size_t *a, /* I - First media size */
342 cups_size_t *b) /* I - Second media size */
343 {
344 return (strcmp(a->media, b->media));
345 }
346
347
348 /*
349 * 'pwg_copy_size()' - Copy a media size.
350 */
351
352 static cups_size_t * /* O - New media size */
pwg_copy_size(cups_size_t * size)353 pwg_copy_size(cups_size_t *size) /* I - Media size to copy */
354 {
355 cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t));
356 /* New media size */
357
358 if (newsize)
359 memcpy(newsize, size, sizeof(cups_size_t));
360
361 return (newsize);
362 }
363
364 static int /* O - 1 on success, 0 on failure */
get_url(const char * url,char * name,size_t namesize)365 get_url(const char *url, /* I - URL to get */
366 char *name, /* I - Temporary filename */
367 size_t namesize) /* I - Size of temporary filename
368 buffer */
369 {
370 http_t *http = NULL;
371 char scheme[32], /* URL scheme */
372 userpass[256], /* URL username:password */
373 host[256], /* URL host */
374 resource[256]; /* URL resource */
375 int port; /* URL port */
376 http_encryption_t encryption; /* Type of encryption to use */
377 http_status_t status; /* Status of GET request */
378 int fd; /* Temporary file */
379
380
381 if (httpSeparateURI(HTTP_URI_CODING_ALL, url, scheme, sizeof(scheme),
382 userpass, sizeof(userpass), host, sizeof(host), &port,
383 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
384 return (0);
385
386 if (port == 443 || !strcmp(scheme, "https"))
387 encryption = HTTP_ENCRYPTION_ALWAYS;
388 else
389 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
390
391 http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 5000, NULL);
392
393 if (!http)
394 return (0);
395
396 if ((fd = cupsTempFd(name, (int)namesize)) < 0)
397 return (0);
398
399 status = cupsGetFd(http, resource, fd);
400
401 close(fd);
402 httpClose(http);
403
404 if (status != HTTP_STATUS_OK) {
405 unlink(name);
406 *name = '\0';
407 return (0);
408 }
409
410 return (1);
411 }
412
413 /*
414 * '_()' - Simplify copying the ppdCreateFromIPP() function from CUPS,
415 * as we do not do translations of UI strings in cups-browsed
416 */
417
418 #define _(s) s
419
420 /*
421 * '_cupsLangString()' - Simplify copying the ppdCreateFromIPP() function
422 * from CUPS, as we do not do translations of UI strings
423 * in cups-browsed
424 */
425
426 const char *
_cupsLangString(cups_lang_t * l,const char * s)427 _cupsLangString(cups_lang_t *l, const char *s)
428 {
429 return s;
430 }
431
432 /*
433 * '_findCUPSMessageCatalog()' - Find a CUPS message catalog file
434 * containing human-readable standard
435 * option and choice names for IPP
436 * printers
437 */
438
439 const char *
_searchDirForCatalog(const char * dirname)440 _searchDirForCatalog(const char *dirname)
441 {
442 const char *catalog = NULL, *c1, *c2;
443 cups_dir_t *dir = NULL, *subdir;
444 cups_dentry_t *subdirentry, *catalogentry;
445 char subdirpath[1024], catalogpath[2048], lang[8];
446 int i;
447
448 if (dirname == NULL)
449 return NULL;
450
451 /* Check first whether we have an English file and prefer this */
452 snprintf(catalogpath, sizeof(catalogpath), "%s/en/cups_en.po", dirname);
453 if (access(catalogpath, R_OK) == 0) {
454 /* Found */
455 catalog = strdup(catalogpath);
456 return catalog;
457 }
458
459 if ((dir = cupsDirOpen(dirname)) == NULL)
460 return NULL;
461
462 while ((subdirentry = cupsDirRead(dir)) != NULL) {
463 /* Do we actually have a subdir? */
464 if (!S_ISDIR(subdirentry->fileinfo.st_mode))
465 continue;
466 /* Check format of subdir name */
467 c1 = subdirentry->filename;
468 if (c1[0] < 'a' || c1[0] > 'z' || c1[1] < 'a' || c1[1] > 'z')
469 continue;
470 if (c1[2] >= 'a' && c1[2] <= 'z')
471 i = 3;
472 else
473 i = 2;
474 if (c1[i] == '_') {
475 i ++;
476 if (c1[i] < 'A' || c1[i] > 'Z' || c1[i+1] < 'A' || c1[i+1] > 'Z')
477 continue;
478 i += 2;
479 if (c1[i] >= 'A' && c1[i] <= 'Z')
480 i ++;
481 }
482 if (c1[i] != '\0' && c1[i] != '@')
483 continue;
484 strncpy(lang, c1, i);
485 lang[i] = '\0';
486 snprintf(subdirpath, sizeof(subdirpath), "%s/%s", dirname, c1);
487 if ((subdir = cupsDirOpen(subdirpath)) != NULL) {
488 while ((catalogentry = cupsDirRead(subdir)) != NULL) {
489 /* Do we actually have a regular file? */
490 if (!S_ISREG(catalogentry->fileinfo.st_mode))
491 continue;
492 /* Check format of catalog name */
493 c2 = catalogentry->filename;
494 if (strlen(c2) < 10 || strncmp(c2, "cups_", 5) != 0 ||
495 strncmp(c2 + 5, lang, i) != 0 ||
496 strcmp(c2 + strlen(c2) - 3, ".po"))
497 continue;
498 /* Is catalog readable ? */
499 snprintf(catalogpath, sizeof(catalogpath), "%s/%s", subdirpath, c2);
500 if (access(catalogpath, R_OK) != 0)
501 continue;
502 /* Found */
503 catalog = strdup(catalogpath);
504 break;
505 }
506 cupsDirClose(subdir);
507 subdir = NULL;
508 if (catalog != NULL)
509 break;
510 }
511 }
512
513 cupsDirClose(dir);
514 return catalog;
515 }
516
517 const char *
_findCUPSMessageCatalog(const char * preferreddir)518 _findCUPSMessageCatalog(const char *preferreddir)
519 {
520 const char *catalog = NULL, *c;
521 char buf[1024];
522
523 /* Directory supplied by calling program, from config file,
524 environment variable, ... */
525 if ((catalog = _searchDirForCatalog(preferreddir)) != NULL)
526 goto found;
527
528 /* Directory supplied by environment variable CUPS_LOCALEDIR */
529 if ((catalog = _searchDirForCatalog(getenv("CUPS_LOCALEDIR"))) != NULL)
530 goto found;
531
532 /* Determine CUPS datadir (usually /usr/share/cups) */
533 if ((c = getenv("CUPS_DATADIR")) == NULL)
534 c = CUPS_DATADIR;
535
536 /* Search /usr/share/cups/locale/ (location which
537 Debian/Ubuntu package of CUPS is using) */
538 snprintf(buf, sizeof(buf), "%s/locale", c);
539 if ((catalog = _searchDirForCatalog(buf)) != NULL)
540 goto found;
541
542 /* Search /usr/(local/)share/locale/ (standard location
543 which CUPS is using on Linux) */
544 snprintf(buf, sizeof(buf), "%s/../locale", c);
545 if ((catalog = _searchDirForCatalog(buf)) != NULL)
546 goto found;
547
548 /* Search /usr/(local/)lib/locale/ (standard location
549 which CUPS is using on many non-Linux systems) */
550 snprintf(buf, sizeof(buf), "%s/../../lib/locale", c);
551 if ((catalog = _searchDirForCatalog(buf)) != NULL)
552 goto found;
553
554 found:
555 return catalog;
556 }
557
558 /* Data structure for IPP choice name and human-readable string */
559 typedef struct ipp_choice_strings_s {
560 char *name, *human_readable;
561 } ipp_choice_strings_t;
562
563 /* Data structure for IPP option name, human-readable string, and choice list */
564 typedef struct ipp_opt_strings_s {
565 char *name, *human_readable;
566 cups_array_t *choices;
567 } ipp_opt_strings_t;
568
569 int
compare_choices(void * a,void * b,void * user_data)570 compare_choices(void *a, void *b, void *user_data)
571 {
572 return strcasecmp(((ipp_choice_strings_t *)a)->name,
573 ((ipp_choice_strings_t *)b)->name);
574 }
575
576 int
compare_options(void * a,void * b,void * user_data)577 compare_options(void *a, void *b, void *user_data)
578 {
579 return strcasecmp(((ipp_opt_strings_t *)a)->name,
580 ((ipp_opt_strings_t *)b)->name);
581 }
582
583 void
free_choice_strings(void * entry,void * user_data)584 free_choice_strings(void* entry, void* user_data)
585 {
586 ipp_choice_strings_t *entry_rec = (ipp_choice_strings_t *)entry;
587
588 if (entry_rec) {
589 if (entry_rec->name) free(entry_rec->name);
590 if (entry_rec->human_readable) free(entry_rec->human_readable);
591 free(entry_rec);
592 }
593 }
594
595 void
free_opt_strings(void * entry,void * user_data)596 free_opt_strings(void* entry, void* user_data)
597 {
598 ipp_opt_strings_t *entry_rec = (ipp_opt_strings_t *)entry;
599
600 if (entry_rec) {
601 if (entry_rec->name) free(entry_rec->name);
602 if (entry_rec->human_readable) free(entry_rec->human_readable);
603 if (entry_rec->choices) cupsArrayDelete(entry_rec->choices);
604 free(entry_rec);
605 }
606 }
607
608 cups_array_t *
optArrayNew()609 optArrayNew()
610 {
611 return cupsArrayNew3(compare_options, NULL, NULL, 0,
612 NULL, free_opt_strings);
613 }
614
615 ipp_opt_strings_t *
find_opt_in_array(cups_array_t * options,char * name)616 find_opt_in_array(cups_array_t *options, char *name)
617 {
618 ipp_opt_strings_t opt;
619
620 if (!name || !options)
621 return NULL;
622
623 opt.name = name;
624 return cupsArrayFind(options, &opt);
625 }
626
627 ipp_choice_strings_t *
find_choice_in_array(cups_array_t * choices,char * name)628 find_choice_in_array(cups_array_t *choices, char *name)
629 {
630 ipp_choice_strings_t choice;
631
632 if (!name || !choices)
633 return NULL;
634
635 choice.name = name;
636 return cupsArrayFind(choices, &choice);
637 }
638
639 ipp_opt_strings_t *
add_opt_to_array(char * name,char * human_readable,cups_array_t * options)640 add_opt_to_array(char *name, char *human_readable, cups_array_t *options)
641 {
642 ipp_opt_strings_t *opt = NULL;
643
644 if (!name || !options)
645 return NULL;
646
647 if ((opt = find_opt_in_array(options, name)) == NULL) {
648 opt = calloc(1, sizeof(ipp_opt_strings_t));
649 if (!opt) return NULL;
650 opt->human_readable = NULL;
651 opt->choices = cupsArrayNew3(compare_choices, NULL, NULL, 0,
652 NULL, free_choice_strings);
653 if (!opt->choices) {
654 free(opt);
655 return NULL;
656 }
657 opt->name = strdup(name);
658 if (!cupsArrayAdd(options, opt)) {
659 free_opt_strings(opt, NULL);
660 return NULL;
661 }
662 }
663
664 if (human_readable)
665 opt->human_readable = strdup(human_readable);
666
667 return opt;
668 }
669
670 ipp_choice_strings_t *
add_choice_to_array(char * name,char * human_readable,char * opt_name,cups_array_t * options)671 add_choice_to_array(char *name, char *human_readable, char *opt_name,
672 cups_array_t *options)
673 {
674 ipp_choice_strings_t *choice = NULL;
675 ipp_opt_strings_t *opt;
676
677 if (!name || !human_readable || !opt_name || !options)
678 return NULL;
679
680 opt = add_opt_to_array(opt_name, NULL, options);
681 if (!opt) return NULL;
682
683 if ((choice = find_choice_in_array(opt->choices, name)) == NULL) {
684 choice = calloc(1, sizeof(ipp_choice_strings_t));
685 if (!choice) return NULL;
686 choice->human_readable = NULL;
687 choice->name = strdup(name);
688 if (!cupsArrayAdd(opt->choices, choice)) {
689 free_choice_strings(choice, NULL);
690 return NULL;
691 }
692 }
693
694 if (human_readable)
695 choice->human_readable = strdup(human_readable);
696
697 return choice;
698
699 }
700
701 char *
lookup_option(char * name,cups_array_t * options,cups_array_t * printer_options)702 lookup_option(char *name, cups_array_t *options,
703 cups_array_t *printer_options)
704 {
705 ipp_opt_strings_t *opt = NULL;
706
707 if (!name || !options)
708 return NULL;
709
710 if (printer_options &&
711 (opt = find_opt_in_array(printer_options, name)) != NULL)
712 return opt->human_readable;
713 if ((opt = find_opt_in_array(options, name)) != NULL)
714 return opt->human_readable;
715 else
716 return NULL;
717 }
718
719 char *
lookup_choice(char * name,char * opt_name,cups_array_t * options,cups_array_t * printer_options)720 lookup_choice(char *name, char *opt_name, cups_array_t *options,
721 cups_array_t *printer_options)
722 {
723 ipp_opt_strings_t *opt = NULL;
724 ipp_choice_strings_t *choice = NULL;
725
726 if (!name || !opt_name || !options)
727 return NULL;
728
729 if (printer_options &&
730 (opt = find_opt_in_array(printer_options, opt_name)) != NULL &&
731 (choice = find_choice_in_array(opt->choices, name)) != NULL)
732 return choice->human_readable;
733 else if ((opt = find_opt_in_array(options, opt_name)) != NULL &&
734 (choice = find_choice_in_array(opt->choices, name)) != NULL)
735 return choice->human_readable;
736 else
737 return NULL;
738 }
739
740 void
load_opt_strings_catalog(const char * location,cups_array_t * options)741 load_opt_strings_catalog(const char *location, cups_array_t *options)
742 {
743 char tmpfile[1024];
744 const char *filename = NULL;
745 struct stat statbuf;
746 cups_file_t *fp;
747 char line[65536];
748 char *ptr, *start, *start2, *end, *end2, *sep;
749 char *opt_name = NULL, *choice_name = NULL,
750 *human_readable = NULL;
751 int part = -1; /* -1: before first "msgid" or invalid
752 line
753 0: "msgid"
754 1: "msgstr"
755 2: "..." = "..."
756 10: EOF, save last entry */
757 int digit;
758 int found_in_catalog = 0;
759
760 if (location == NULL || (strncasecmp(location, "http:", 5) &&
761 strncasecmp(location, "https:", 6))) {
762 if (location == NULL ||
763 (stat(location, &statbuf) == 0 &&
764 S_ISDIR(statbuf.st_mode))) /* directory? */
765 {
766 filename = _findCUPSMessageCatalog(location);
767 if (filename)
768 found_in_catalog = 1;
769 }
770 else
771 filename = location;
772 } else {
773 if (get_url(location, tmpfile, sizeof(tmpfile)))
774 filename = tmpfile;
775 }
776 if (!filename)
777 return;
778
779 if ((fp = cupsFileOpen(filename, "r")) == NULL)
780 return;
781
782 while (cupsFileGets(fp, line, sizeof(line)) || (part = 10)) {
783 /* Find a pair of quotes delimiting a string in each line
784 and optional "msgid" or "msgstr" keywords, or a
785 "..." = "..." pair. Skip comments ('#') and empty lines. */
786 if (part < 10) {
787 ptr = line;
788 while (isspace(*ptr)) ptr ++;
789 if (*ptr == '#' || *ptr == '\0') continue;
790 if ((start = strchr(ptr, '\"')) == NULL) continue;
791 if ((end = strrchr(ptr, '\"')) == start) continue;
792 if (*(end - 1) == '\\') continue;
793 start2 = NULL;
794 end2 = NULL;
795 if (start > ptr) {
796 if (*(start - 1) == '\\') continue;
797 if (strncasecmp(ptr, "msgid", 5) == 0) part = 0;
798 if (strncasecmp(ptr, "msgstr", 6) == 0) part = 1;
799 } else {
800 start2 = ptr;
801 while ((start2 = strchr(start2 + 1, '\"')) < end &&
802 *(start2 - 1) == '\\');
803 if (start2 < end) {
804 /* Line with "..." = "..." of text/strings format */
805 end2 = end;
806 end = start2;
807 start2 ++;
808 while (isspace(*start2)) start2 ++;
809 if (*start2 != '=') continue;
810 start2 ++;
811 while (isspace(*start2)) start2 ++;
812 if (*start2 != '\"') continue;
813 start2 ++;
814 *end2 = '\0';
815 part = 2;
816 } else
817 /* Continuation line in message catalog file */
818 start2 = NULL;
819 }
820 start ++;
821 *end = '\0';
822 }
823 /* Read out the strings between the quotes and save entries */
824 if (part == 0 || part == 2 || part == 10) {
825 /* Save previous attribute */
826 if (human_readable) {
827 if (opt_name) {
828 if (choice_name) {
829 add_choice_to_array(choice_name, human_readable,
830 opt_name, options);
831 free(choice_name);
832 } else
833 add_opt_to_array(opt_name, human_readable, options);
834 free(opt_name);
835 }
836 free(human_readable);
837 opt_name = NULL;
838 choice_name = NULL;
839 human_readable = NULL;
840 }
841 /* Stop the loop after saving the last entry */
842 if (part == 10)
843 break;
844 /* IPP attribute has to be defined with a single msgid line,
845 no continuation lines */
846 if (opt_name) {
847 free (opt_name);
848 opt_name = NULL;
849 if (choice_name) {
850 free (choice_name);
851 choice_name = NULL;
852 }
853 part = -1;
854 continue;
855 }
856 /* No continuation line in text/strings format */
857 if (part == 2 && (start2 == NULL || end2 == NULL)) {
858 part = -1;
859 continue;
860 }
861 /* Check line if it is a valid IPP attribute:
862 No spaces, only lowercase letters, digits, '-', '_',
863 "option" or "option.choice" */
864 for (ptr = start, sep = NULL; ptr < end; ptr ++)
865 if (*ptr == '.') { /* Separator between option and choice */
866 if (!sep) { /* Only the first '.' counts */
867 sep = ptr + 1;
868 *ptr = '\0';
869 }
870 } else if (!((*ptr >= 'a' && *ptr <= 'z') ||
871 (*ptr >= '0' && *ptr <= '9') ||
872 *ptr == '-' || *ptr == '_'))
873 break;
874 if (ptr < end) { /* Illegal character found */
875 part = -1;
876 continue;
877 }
878 if (strlen(start) > 0) /* Option name found */
879 opt_name = strdup(start);
880 else { /* Empty option name */
881 part = -1;
882 continue;
883 }
884 if (sep && strlen(sep) > 0) /* Choice name found */
885 choice_name = strdup(sep);
886 else /* Empty choice name */
887 choice_name = NULL;
888 if (part == 2) { /* Human-readable string in the same line */
889 start = start2;
890 end = end2;
891 }
892 }
893 if (part == 1 || part == 2) {
894 /* msgid was not for an IPP attribute, ignore this msgstr */
895 if (!opt_name) continue;
896 /* Empty string */
897 if (start == end) continue;
898 /* Unquote string */
899 ptr = start;
900 end = start;
901 while (*ptr) {
902 if (*ptr == '\\') {
903 ptr ++;
904 if (isdigit(*ptr)) {
905 digit = 0;
906 *end = 0;
907 while (isdigit(*ptr) && digit < 3) {
908 *end = *end * 8 + *ptr - '0';
909 digit ++;
910 ptr ++;
911 }
912 end ++;
913 } else {
914 if (*ptr == 'n')
915 *end ++ = '\n';
916 else if (*ptr == 'r')
917 *end ++ = '\r';
918 else if (*ptr == 't')
919 *end ++ = '\t';
920 else
921 *end ++ = *ptr;
922 ptr ++;
923 }
924 } else
925 *end ++ = *ptr ++;
926 }
927 *end = '\0';
928 /* Did the unquoting make the string empty? */
929 if (strlen(start) == 0) continue;
930 /* Add the string to our human-readable string */
931 if (human_readable) { /* Continuation line */
932 human_readable = realloc(human_readable,
933 sizeof(char) *
934 (strlen(human_readable) +
935 strlen(start) + 2));
936 ptr = human_readable + strlen(human_readable);
937 *ptr = ' ';
938 strlcpy(ptr + 1, start, strlen(start) + 1);
939 } else { /* First line */
940 human_readable = malloc(sizeof(char) *
941 (strlen(start) + 1));
942 strlcpy(human_readable, start, strlen(start) + 1);
943 }
944 }
945 }
946 cupsFileClose(fp);
947 if (choice_name != NULL)
948 free(choice_name);
949 if (opt_name != NULL)
950 free(opt_name);
951 if (filename == tmpfile)
952 unlink(filename);
953 if (found_in_catalog)
954 free((char *)filename);
955 }
956
957
958 int
compare_resolutions(void * resolution_a,void * resolution_b,void * user_data)959 compare_resolutions(void *resolution_a, void *resolution_b,
960 void *user_data)
961 {
962 res_t *res_a = (res_t *)resolution_a;
963 res_t *res_b = (res_t *)resolution_b;
964 int i, a, b;
965
966 /* Compare the pixels per square inch */
967 a = res_a->x * res_a->y;
968 b = res_b->x * res_b->y;
969 i = (a > b) - (a < b);
970 if (i) return i;
971
972 /* Compare how much the pixel shape deviates from a square, the
973 more, the worse */
974 a = 100 * res_a->y / res_a->x;
975 if (a > 100) a = 10000 / a;
976 b = 100 * res_b->y / res_b->x;
977 if (b > 100) b = 10000 / b;
978 return (a > b) - (a < b);
979 }
980
981 void *
copy_resolution(void * resolution,void * user_data)982 copy_resolution(void *resolution, void *user_data)
983 {
984 res_t *res = (res_t *)resolution;
985 res_t *copy;
986
987 copy = (res_t *)calloc(1, sizeof(res_t));
988 if (copy) {
989 copy->x = res->x;
990 copy->y = res->y;
991 }
992
993 return copy;
994 }
995
996 void
free_resolution(void * resolution,void * user_data)997 free_resolution(void *resolution, void *user_data)
998 {
999 res_t *res = (res_t *)resolution;
1000
1001 if (res) free(res);
1002 }
1003
1004 cups_array_t *
resolutionArrayNew()1005 resolutionArrayNew()
1006 {
1007 return cupsArrayNew3(compare_resolutions, NULL, NULL, 0,
1008 copy_resolution, free_resolution);
1009 }
1010
1011 res_t *
resolutionNew(int x,int y)1012 resolutionNew(int x, int y)
1013 {
1014 res_t *res = (res_t *)calloc(1, sizeof(res_t));
1015 if (res) {
1016 res->x = x;
1017 res->y = y;
1018 }
1019 return res;
1020 }
1021
1022 /* Read a single resolution from an IPP attribute, take care of
1023 obviously wrong entries (printer firmware bugs), ignoring
1024 resolutions of less than 75 dpi in at least one dimension and
1025 fixing Brother's "600x2dpi" resolutions. */
1026 res_t *
ippResolutionToRes(ipp_attribute_t * attr,int index)1027 ippResolutionToRes(ipp_attribute_t *attr, int index)
1028 {
1029 res_t *res = NULL;
1030 int x = 0, y = 0;
1031
1032 if (attr) {
1033 ipp_tag_t tag = ippGetValueTag(attr);
1034 int count = ippGetCount(attr);
1035
1036 if (tag == IPP_TAG_RESOLUTION && index < count) {
1037 pwg_ppdize_resolution(attr, index, &x, &y, NULL, 0);
1038 if (y == 2) y = x; /* Brother quirk ("600x2dpi") */
1039 if (x >= 75 && y >= 75)
1040 res = resolutionNew(x, y);
1041 }
1042 }
1043
1044 return res;
1045 }
1046
1047 cups_array_t *
ippResolutionListToArray(ipp_attribute_t * attr)1048 ippResolutionListToArray(ipp_attribute_t *attr)
1049 {
1050 cups_array_t *res_array = NULL;
1051 res_t *res;
1052 int i;
1053
1054 if (attr) {
1055 ipp_tag_t tag = ippGetValueTag(attr);
1056 int count = ippGetCount(attr);
1057
1058 if (tag == IPP_TAG_RESOLUTION && count > 0) {
1059 res_array = resolutionArrayNew();
1060 if (res_array) {
1061 for (i = 0; i < count; i ++)
1062 if ((res = ippResolutionToRes(attr, i)) != NULL) {
1063 if (cupsArrayFind(res_array, res) == NULL)
1064 cupsArrayAdd(res_array, res);
1065 free_resolution(res, NULL);
1066 }
1067 }
1068 if (cupsArrayCount(res_array) == 0) {
1069 cupsArrayDelete(res_array);
1070 res_array = NULL;
1071 }
1072 }
1073 }
1074
1075 return res_array;
1076 }
1077
1078 /* Build up an array of common resolutions and most desirable default
1079 resolution from multiple arrays of resolutions with an optional
1080 default resolution.
1081 Call this function with each resolution array you find as "new", and
1082 in "current" an array of the common resolutions will be built up.
1083 You do not need to create an empty array for "current" before
1084 starting. Initialize it with NULL.
1085 "current_default" holds the default resolution of the array "current".
1086 It will get replaced by "new_default" if "current_default" is either
1087 NULL or a resolution which is not in "current" any more.
1088 "new" and "new_default" will be deleted/freed and set to NULL after
1089 each, successful or unsuccssful operation.
1090 Note that when calling this function the addresses of the pointers
1091 to the resolution arrays and default resolutions have to be given
1092 (call by reference) as all will get modified by the function. */
1093
1094 int /* 1 on success, 0 on failure */
joinResolutionArrays(cups_array_t ** current,cups_array_t ** new,res_t ** current_default,res_t ** new_default)1095 joinResolutionArrays(cups_array_t **current, cups_array_t **new,
1096 res_t **current_default, res_t **new_default)
1097 {
1098 res_t *res;
1099 int retval;
1100
1101 if (current == NULL || new == NULL || *new == NULL ||
1102 cupsArrayCount(*new) == 0) {
1103 retval = 0;
1104 goto finish;
1105 }
1106
1107 if (*current == NULL) {
1108 /* We are adding the very first resolution array, simply make it
1109 our common resolutions array */
1110 *current = *new;
1111 if (current_default) {
1112 if (*current_default)
1113 free(*current_default);
1114 *current_default = (new_default ? *new_default : NULL);
1115 }
1116 return 1;
1117 } else if (cupsArrayCount(*current) == 0) {
1118 retval = 1;
1119 goto finish;
1120 }
1121
1122 /* Dry run: Check whether the two array have at least one resolution
1123 in common, if not, do not touch the original array */
1124 for (res = cupsArrayFirst(*current);
1125 res; res = cupsArrayNext(*current))
1126 if (cupsArrayFind(*new, res))
1127 break;
1128
1129 if (res) {
1130 /* Reduce the original array to the resolutions which are in both
1131 the original and the new array, at least one resolution will
1132 remain. */
1133 for (res = cupsArrayFirst(*current);
1134 res; res = cupsArrayNext(*current))
1135 if (!cupsArrayFind(*new, res))
1136 cupsArrayRemove(*current, res);
1137 if (current_default) {
1138 /* Replace the current default by the new one if the current default
1139 is not in the array any more or if it is NULL. If the new default
1140 is not in the list or NULL in such a case, set the current default
1141 to NULL */
1142 if (*current_default && !cupsArrayFind(*current, *current_default)) {
1143 free(*current_default);
1144 *current_default = NULL;
1145 }
1146 if (*current_default == NULL && new_default && *new_default &&
1147 cupsArrayFind(*current, *new_default))
1148 *current_default = copy_resolution(*new_default, NULL);
1149 }
1150 retval = 1;
1151 } else
1152 retval = 0;
1153
1154 finish:
1155 if (new && *new) {
1156 cupsArrayDelete(*new);
1157 *new = NULL;
1158 }
1159 if (new_default && *new_default) {
1160 free(*new_default);
1161 *new_default = NULL;
1162 }
1163 return retval;
1164 }
1165
generate_sizes(ipp_t * response,ipp_attribute_t ** defattr,int * min_length,int * min_width,int * max_length,int * max_width,int * bottom,int * left,int * right,int * top,char * ppdname)1166 cups_array_t* generate_sizes(ipp_t *response,
1167 ipp_attribute_t **defattr,
1168 int* min_length,
1169 int* min_width,
1170 int* max_length,
1171 int* max_width,
1172 int* bottom,
1173 int* left,
1174 int* right,
1175 int* top,
1176 char* ppdname)
1177 {
1178 cups_array_t *sizes; /* Media sizes we've added */
1179 ipp_attribute_t *attr, /* xxx-supported */
1180 *x_dim, *y_dim; /* Media dimensions */
1181 ipp_t *media_col, /* Media collection */
1182 *media_size; /* Media size collection */
1183 int i, count = 0;
1184 pwg_media_t *pwg; /* PWG media size */
1185 int left_def, right_def, bottom_def, top_def;
1186 ipp_attribute_t *margin; /* media-xxx-margin attribute */
1187 const char *psname;
1188
1189 if ((attr = ippFindAttribute(response, "media-bottom-margin-supported",
1190 IPP_TAG_INTEGER)) != NULL) {
1191 for (i = 1, *bottom = ippGetInteger(attr, 0), count = ippGetCount(attr);
1192 i < count; i ++)
1193 if (i == 1 || ippGetInteger(attr, i) < *bottom)
1194 *bottom = ippGetInteger(attr, i);
1195 } else
1196 *bottom = 1270;
1197
1198 if ((attr = ippFindAttribute(response, "media-left-margin-supported",
1199 IPP_TAG_INTEGER)) != NULL) {
1200 for (i = 1, *left = ippGetInteger(attr, 0), count = ippGetCount(attr);
1201 i < count; i ++)
1202 if (i == 1 || ippGetInteger(attr, i) < *left)
1203 *left = ippGetInteger(attr, i);
1204 } else
1205 *left = 635;
1206
1207 if ((attr = ippFindAttribute(response, "media-right-margin-supported",
1208 IPP_TAG_INTEGER)) != NULL) {
1209 for (i = 1, *right = ippGetInteger(attr, 0), count = ippGetCount(attr);
1210 i < count; i ++)
1211 if (i == 1 || ippGetInteger(attr, i) < *right)
1212 *right = ippGetInteger(attr, i);
1213 } else
1214 *right = 635;
1215
1216 if ((attr = ippFindAttribute(response, "media-top-margin-supported",
1217 IPP_TAG_INTEGER)) != NULL) {
1218 for (i = 1, *top = ippGetInteger(attr, 0), count = ippGetCount(attr);
1219 i < count; i ++)
1220 if (i == 1 || ippGetInteger(attr, i) < *top)
1221 *top = ippGetInteger(attr, i);
1222 } else
1223 *top = 1270;
1224
1225 if ((*defattr = ippFindAttribute(response, "media-col-default",
1226 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
1227 if ((attr = ippFindAttribute(ippGetCollection(*defattr, 0), "media-size",
1228 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
1229 media_size = ippGetCollection(attr, 0);
1230 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
1231 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
1232
1233 if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0),
1234 "media-bottom-margin", IPP_TAG_INTEGER))
1235 != NULL)
1236 bottom_def = ippGetInteger(margin, 0);
1237 else
1238 bottom_def = *bottom;
1239
1240 if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0),
1241 "media-left-margin", IPP_TAG_INTEGER))
1242 != NULL)
1243 left_def = ippGetInteger(margin, 0);
1244 else
1245 left_def = *left;
1246
1247 if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0),
1248 "media-right-margin", IPP_TAG_INTEGER))
1249 != NULL)
1250 right_def = ippGetInteger(margin, 0);
1251 else
1252 right_def = *right;
1253
1254 if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0),
1255 "media-top-margin", IPP_TAG_INTEGER))
1256 != NULL)
1257 top_def = ippGetInteger(margin, 0);
1258 else
1259 top_def = *top;
1260
1261 if (x_dim && y_dim &&
1262 (pwg = pwgMediaForSize(ippGetInteger(x_dim, 0),
1263 ippGetInteger(y_dim, 0))) != NULL) {
1264 psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg);
1265 if (bottom_def == 0 && left_def == 0 && right_def == 0 && top_def == 0)
1266 snprintf(ppdname, PPD_MAX_NAME, "%s.Borderless", psname);
1267 else
1268 strlcpy(ppdname, psname, PPD_MAX_NAME);
1269 } else
1270 strlcpy(ppdname, "Unknown", PPD_MAX_NAME);
1271 } else
1272 strlcpy(ppdname, "Unknown", PPD_MAX_NAME);
1273 } else if ((pwg =
1274 pwgMediaForPWG(ippGetString(ippFindAttribute(response,
1275 "media-default",
1276 IPP_TAG_ZERO), 0,
1277 NULL))) != NULL) {
1278 psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg);
1279 strlcpy(ppdname, psname, PPD_MAX_NAME);
1280 } else
1281 strlcpy(ppdname, "Unknown", PPD_MAX_NAME);
1282
1283 sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes, NULL, NULL, 0,
1284 (cups_acopy_func_t)pwg_copy_size,
1285 (cups_afree_func_t)free);
1286
1287 if ((attr = ippFindAttribute(response, "media-col-database",
1288 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
1289 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1290 cups_size_t temp; /* Current size */
1291
1292 media_col = ippGetCollection(attr, i);
1293 media_size =
1294 ippGetCollection(ippFindAttribute(media_col, "media-size",
1295 IPP_TAG_BEGIN_COLLECTION), 0);
1296 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
1297 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
1298 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0),
1299 ippGetInteger(y_dim, 0));
1300
1301 if (pwg) {
1302 temp.width = pwg->width;
1303 temp.length = pwg->length;
1304
1305 if ((margin = ippFindAttribute(media_col, "media-bottom-margin",
1306 IPP_TAG_INTEGER)) != NULL)
1307 temp.bottom = ippGetInteger(margin, 0);
1308 else
1309 temp.bottom = *bottom;
1310
1311 if ((margin = ippFindAttribute(media_col, "media-left-margin",
1312 IPP_TAG_INTEGER)) != NULL)
1313 temp.left = ippGetInteger(margin, 0);
1314 else
1315 temp.left = *left;
1316
1317 if ((margin = ippFindAttribute(media_col, "media-right-margin",
1318 IPP_TAG_INTEGER)) != NULL)
1319 temp.right = ippGetInteger(margin, 0);
1320 else
1321 temp.right = *right;
1322
1323 if ((margin = ippFindAttribute(media_col, "media-top-margin",
1324 IPP_TAG_INTEGER)) != NULL)
1325 temp.top = ippGetInteger(margin, 0);
1326 else
1327 temp.top = *top;
1328
1329 psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg);
1330 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 &&
1331 temp.top == 0)
1332 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname);
1333 else
1334 strlcpy(temp.media, psname, sizeof(temp.media));
1335
1336 if (!cupsArrayFind(sizes, &temp))
1337 cupsArrayAdd(sizes, &temp);
1338 } else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE ||
1339 ippGetValueTag(y_dim) == IPP_TAG_RANGE) {
1340 /*
1341 * Custom size - record the min/max values...
1342 */
1343
1344 int lower, upper; /* Range values */
1345
1346 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
1347 lower = ippGetRange(x_dim, 0, &upper);
1348 else
1349 lower = upper = ippGetInteger(x_dim, 0);
1350
1351 if (lower < *min_width)
1352 *min_width = lower;
1353 if (upper > *max_width)
1354 *max_width = upper;
1355
1356 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
1357 lower = ippGetRange(y_dim, 0, &upper);
1358 else
1359 lower = upper = ippGetInteger(y_dim, 0);
1360
1361 if (lower < *min_length)
1362 *min_length = lower;
1363 if (upper > *max_length)
1364 *max_length = upper;
1365 }
1366 }
1367 }
1368 if ((attr = ippFindAttribute(response, "media-size-supported",
1369 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
1370 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1371 cups_size_t temp; /* Current size */
1372
1373 media_size = ippGetCollection(attr, i);
1374 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
1375 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
1376 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0),
1377 ippGetInteger(y_dim, 0));
1378
1379 if (pwg) {
1380 temp.width = pwg->width;
1381 temp.length = pwg->length;
1382 temp.bottom = *bottom;
1383 temp.left = *left;
1384 temp.right = *right;
1385 temp.top = *top;
1386
1387 psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg);
1388 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 &&
1389 temp.top == 0)
1390 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname);
1391 else
1392 strlcpy(temp.media, psname, sizeof(temp.media));
1393
1394 if (!cupsArrayFind(sizes, &temp))
1395 cupsArrayAdd(sizes, &temp);
1396 } else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE ||
1397 ippGetValueTag(y_dim) == IPP_TAG_RANGE) {
1398 /*
1399 * Custom size - record the min/max values...
1400 */
1401
1402 int lower, upper; /* Range values */
1403
1404 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
1405 lower = ippGetRange(x_dim, 0, &upper);
1406 else
1407 lower = upper = ippGetInteger(x_dim, 0);
1408
1409 if (lower < *min_width)
1410 *min_width = lower;
1411 if (upper > *max_width)
1412 *max_width = upper;
1413
1414 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
1415 lower = ippGetRange(y_dim, 0, &upper);
1416 else
1417 lower = upper = ippGetInteger(y_dim, 0);
1418
1419 if (lower < *min_length)
1420 *min_length = lower;
1421 if (upper > *max_length)
1422 *max_length = upper;
1423 }
1424 }
1425 }
1426 if ((attr = ippFindAttribute(response, "media-supported", IPP_TAG_ZERO))
1427 != NULL) {
1428 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1429 const char *pwg_size = ippGetString(attr, i, NULL);
1430 /* PWG size name */
1431 cups_size_t temp, *temp2; /* Current size, found size */
1432
1433 if ((pwg = pwgMediaForPWG(pwg_size)) != NULL) {
1434 if (strstr(pwg_size, "_max_") || strstr(pwg_size, "_max.")) {
1435 if (pwg->width > *max_width)
1436 *max_width = pwg->width;
1437 if (pwg->length > *max_length)
1438 *max_length = pwg->length;
1439 } else if (strstr(pwg_size, "_min_") || strstr(pwg_size, "_min.")) {
1440 if (pwg->width < *min_width)
1441 *min_width = pwg->width;
1442 if (pwg->length < *min_length)
1443 *min_length = pwg->length;
1444 } else {
1445 temp.width = pwg->width;
1446 temp.length = pwg->length;
1447 temp.bottom = *bottom;
1448 temp.left = *left;
1449 temp.right = *right;
1450 temp.top = *top;
1451
1452 psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg);
1453 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 &&
1454 temp.top == 0)
1455 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname);
1456 else
1457 strlcpy(temp.media, psname, sizeof(temp.media));
1458
1459 /* Add the printer's original IPP name to an already found size */
1460 if ((temp2 = cupsArrayFind(sizes, &temp)) != NULL) {
1461 snprintf(temp2->media + strlen(temp2->media),
1462 sizeof(temp2->media) - strlen(temp2->media),
1463 " %s", pwg_size);
1464 /* Check if we have also a borderless version of the size and add
1465 the original IPP name also there */
1466 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname);
1467 if ((temp2 = cupsArrayFind(sizes, &temp)) != NULL)
1468 snprintf(temp2->media + strlen(temp2->media),
1469 sizeof(temp2->media) - strlen(temp2->media),
1470 " %s", pwg_size);
1471 } else
1472 cupsArrayAdd(sizes, &temp);
1473 }
1474 }
1475 }
1476 }
1477 return sizes;
1478 }
1479
is_colordevice(const char * keyword,ipp_attribute_t * attr)1480 int is_colordevice(const char *keyword,ipp_attribute_t *attr)
1481 {
1482 if (!strcasecmp(keyword, "sgray_16") || !strncmp(keyword, "W8-16", 5) ||
1483 !strncmp(keyword, "W16", 3))
1484 return 1;
1485 else if (!strcasecmp(keyword, "srgb_8") || !strncmp(keyword, "SRGB24", 6) ||
1486 !strcmp(keyword, "color"))
1487 return 1;
1488 else if ((!strcasecmp(keyword, "srgb_16") ||
1489 !strncmp(keyword, "SRGB48", 6)) &&
1490 !ippContainsString(attr, "srgb_8"))
1491 return 1;
1492 else if (!strcasecmp(keyword, "adobe-rgb_16") ||
1493 !strncmp(keyword, "ADOBERGB48", 10) ||
1494 !strncmp(keyword, "ADOBERGB24-48", 13))
1495 return 1;
1496 else if ((!strcasecmp(keyword, "adobe-rgb_8") ||
1497 !strcmp(keyword, "ADOBERGB24")) &&
1498 !ippContainsString(attr, "adobe-rgb_16"))
1499 return 1;
1500 else if ((!strcasecmp(keyword, "cmyk_8") &&
1501 !ippContainsString(attr, "cmyk_16")) ||
1502 !strcmp(keyword, "DEVCMYK32"))
1503 return 1;
1504 else if (!strcasecmp(keyword, "cmyk_16") ||
1505 !strcmp(keyword, "DEVCMYK32-64") ||
1506 !strcmp(keyword, "DEVCMYK64"))
1507 return 1;
1508 else if ((!strcasecmp(keyword, "rgb_8") &&
1509 !ippContainsString(attr, "rgb_16"))
1510 || !strcmp(keyword, "DEVRGB24"))
1511 return 1;
1512 else if (!strcasecmp(keyword, "rgb_16") ||
1513 !strcmp(keyword, "DEVRGB24-48") ||
1514 !strcmp(keyword, "DEVRGB48"))
1515 return 1;
1516 return 0;
1517 }
1518
1519 /*
1520 * 'ppdCreateFromIPP()' - Create a PPD file describing the capabilities
1521 * of an IPP printer (legacy interface).
1522 */
1523
1524 char * /* O - PPD filename or NULL on
1525 error */
ppdCreateFromIPP(char * buffer,size_t bufsize,ipp_t * response,const char * make_model,const char * pdl,int color,int duplex)1526 ppdCreateFromIPP (char *buffer, /* I - Filename buffer */
1527 size_t bufsize, /* I - Size of filename
1528 buffer */
1529 ipp_t *response, /* I - Get-Printer-Attributes
1530 response */
1531 const char *make_model, /* I - Make and model from
1532 DNS-SD */
1533 const char *pdl, /* I - List of PDLs from
1534 DNS-SD */
1535 int color, /* I - Color printer? (from
1536 DNS-SD) */
1537 int duplex) /* I - Duplex printer? (from
1538 DNS-SD) */
1539 {
1540 return ppdCreateFromIPP2(buffer, bufsize, response, make_model, pdl,
1541 color, duplex, NULL, NULL, NULL, NULL);
1542 }
1543
1544 /*
1545 * 'ppdCreateFromIPP2()' - Create a PPD file describing the capabilities
1546 * of an IPP printer.
1547 */
1548
1549 char * /* O - PPD filename or NULL on
1550 error */
ppdCreateFromIPP2(char * buffer,size_t bufsize,ipp_t * response,const char * make_model,const char * pdl,int color,int duplex,cups_array_t * conflicts,cups_array_t * sizes,char * default_pagesize,const char * default_cluster_color)1551 ppdCreateFromIPP2(char *buffer, /* I - Filename buffer */
1552 size_t bufsize, /* I - Size of filename
1553 buffer */
1554 ipp_t *response, /* I - Get-Printer-Attributes
1555 response */
1556 const char *make_model, /* I - Make and model from
1557 DNS-SD */
1558 const char *pdl, /* I - List of PDLs from
1559 DNS-SD */
1560 int color, /* I - Color printer? (from
1561 DNS-SD) */
1562 int duplex, /* I - Duplex printer? (from
1563 DNS-SD) */
1564 cups_array_t *conflicts, /* I - Array of constraints */
1565 cups_array_t *sizes, /* I - Media sizes we've
1566 added */
1567 char* default_pagesize, /* I - Default page size*/
1568 const char *default_cluster_color) /* I - cluster def
1569 color (if cluster's
1570 attributes are
1571 returned) */
1572 {
1573 cups_file_t *fp; /* PPD file */
1574 cups_array_t *printer_sizes; /* Media sizes we've added */
1575 cups_size_t *size; /* Current media size */
1576 ipp_attribute_t *attr, /* xxx-supported */
1577 *attr2,
1578 *defattr, /* xxx-default */
1579 *quality, /* print-quality-supported */
1580 *x_dim, *y_dim; /* Media dimensions */
1581 ipp_t *media_col, /* Media collection */
1582 *media_size; /* Media size collection */
1583 char make[256], /* Make and model */
1584 *model, /* Model name */
1585 ppdname[PPD_MAX_NAME];
1586 /* PPD keyword */
1587 int i, j, /* Looping vars */
1588 count = 0, /* Number of values */
1589 bottom, /* Largest bottom margin */
1590 left, /* Largest left margin */
1591 right, /* Largest right margin */
1592 top, /* Largest top margin */
1593 max_length = 0, /* Maximum custom size */
1594 max_width = 0,
1595 min_length = INT_MAX,
1596 /* Minimum custom size */
1597 min_width = INT_MAX,
1598 is_apple = 0, /* Does the printer support Apple
1599 Raster? */
1600 is_pwg = 0, /* Does the printer support PWG
1601 Raster? */
1602 is_pclm = 0, /* Does the printer support PCLm? */
1603 is_pdf = 0; /* Does the printer support PDF? */
1604 pwg_media_t *pwg; /* PWG media size */
1605 int xres, yres; /* Resolution values */
1606 cups_array_t *common_res, /* Common resolutions of all PDLs */
1607 *current_res, /* Resolutions of current PDL */
1608 *pdl_list; /* List of PDLs */
1609 res_t *common_def, /* Common default resolution */
1610 *current_def, /* Default resolution of current PDL */
1611 *min_res, /* Minimum common resolution */
1612 *max_res; /* Maximum common resolution */
1613 cups_lang_t *lang = cupsLangDefault();
1614 /* Localization info */
1615 struct lconv *loc = localeconv();
1616 /* Locale data */
1617 cups_array_t *printer_opt_strings_catalog = NULL;
1618 /* Printer-specific option UI strings */
1619 char *human_readable,
1620 *human_readable2;
1621 const char *keyword; /* Keyword value */
1622 cups_array_t *fin_options = NULL;
1623 /* Finishing options */
1624 char buf[256],
1625 filter_path[1024];
1626 /* Path to filter executable */
1627 const char *cups_serverbin;/* CUPS_SERVERBIN environment
1628 variable */
1629 char *defaultoutbin = NULL;
1630 const char *outbin;
1631 char outbin_properties[1024];
1632 int octet_str_len;
1633 void *outbin_properties_octet;
1634 int outputorderinfofound = 0,
1635 faceupdown = 1,
1636 firsttolast = 1;
1637 int manual_copies = -1,
1638 is_fax = 0;
1639
1640 /*
1641 * Range check input...
1642 */
1643
1644 if (buffer)
1645 *buffer = '\0';
1646
1647 if (!buffer || bufsize < 1) {
1648 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1649 return (NULL);
1650 }
1651
1652 if (!response) {
1653 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No IPP attributes."), 1);
1654 return (NULL);
1655 }
1656
1657 /*
1658 * Open a temporary file for the PPD...
1659 */
1660
1661 if ((fp = cupsTempFile2(buffer, (int)bufsize)) == NULL) {
1662 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
1663 return (NULL);
1664 }
1665
1666 /*
1667 * Standard stuff for PPD file...
1668 */
1669
1670 cupsFilePuts(fp, "*PPD-Adobe: \"4.3\"\n");
1671 cupsFilePuts(fp, "*FormatVersion: \"4.3\"\n");
1672 cupsFilePrintf(fp, "*FileVersion: \"%s\"\n", VERSION);
1673 cupsFilePuts(fp, "*LanguageVersion: English\n");
1674 cupsFilePuts(fp, "*LanguageEncoding: ISOLatin1\n");
1675 cupsFilePuts(fp, "*PSVersion: \"(3010.000) 0\"\n");
1676 cupsFilePuts(fp, "*LanguageLevel: \"3\"\n");
1677 cupsFilePuts(fp, "*FileSystem: False\n");
1678 cupsFilePuts(fp, "*PCFileName: \"drvless.ppd\"\n");
1679
1680 if ((attr = ippFindAttribute(response, "ipp-features-supported",
1681 IPP_TAG_KEYWORD)) != NULL &&
1682 ippContainsString(attr, "faxout"))
1683 {
1684 attr = ippFindAttribute(response, "printer-uri-supported",
1685 IPP_TAG_URI);
1686 if (attr)
1687 {
1688 ippAttributeString(attr, buf, sizeof(buf));
1689 if (strcasestr(buf, "faxout"))
1690 is_fax = 1;
1691 }
1692 }
1693
1694 if ((attr = ippFindAttribute(response, "printer-make-and-model",
1695 IPP_TAG_TEXT)) != NULL)
1696 strlcpy(make, ippGetString(attr, 0, NULL), sizeof(make));
1697 else if (make_model && make_model[0] != '\0')
1698 strlcpy(make, make_model, sizeof(make));
1699 else
1700 strlcpy(make, "Unknown Printer", sizeof(make));
1701
1702 if (!_cups_strncasecmp(make, "Hewlett Packard ", 16) ||
1703 !_cups_strncasecmp(make, "Hewlett-Packard ", 16)) {
1704 model = make + 16;
1705 strlcpy(make, "HP", sizeof(make));
1706 }
1707 else if ((model = strchr(make, ' ')) != NULL)
1708 *model++ = '\0';
1709 else
1710 model = make;
1711
1712 cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make);
1713 cupsFilePrintf(fp, "*ModelName: \"%s %s\"\n", make, model);
1714 cupsFilePrintf(fp, "*Product: \"(%s %s)\"\n", make, model);
1715 cupsFilePrintf(fp, "*NickName: \"%s %s, %sdriverless, cups-filters %s\"\n",
1716 make, model, (is_fax ? "Fax, " : ""), VERSION);
1717 cupsFilePrintf(fp, "*ShortNickName: \"%s %s\"\n", make, model);
1718
1719 /* Which is the default output bin? */
1720 if ((attr = ippFindAttribute(response, "output-bin-default", IPP_TAG_ZERO))
1721 != NULL)
1722 defaultoutbin = strdup(ippGetString(attr, 0, NULL));
1723 /* Find out on which position of the list of output bins the default one is,
1724 if there is no default bin, take the first of this list */
1725 i = 0;
1726 if ((attr = ippFindAttribute(response, "output-bin-supported",
1727 IPP_TAG_ZERO)) != NULL) {
1728 count = ippGetCount(attr);
1729 for (i = 0; i < count; i ++) {
1730 outbin = ippGetString(attr, i, NULL);
1731 if (outbin == NULL)
1732 continue;
1733 if (defaultoutbin == NULL) {
1734 defaultoutbin = strdup(outbin);
1735 break;
1736 } else if (strcasecmp(outbin, defaultoutbin) == 0)
1737 break;
1738 }
1739 }
1740 if ((attr = ippFindAttribute(response, "printer-output-tray",
1741 IPP_TAG_STRING)) != NULL &&
1742 i < ippGetCount(attr)) {
1743 outbin_properties_octet = ippGetOctetString(attr, i, &octet_str_len);
1744 memset(outbin_properties, 0, sizeof(outbin_properties));
1745 memcpy(outbin_properties, outbin_properties_octet,
1746 ((size_t)octet_str_len < sizeof(outbin_properties) - 1 ?
1747 (size_t)octet_str_len : sizeof(outbin_properties) - 1));
1748 if (strcasestr(outbin_properties, "pagedelivery=faceUp")) {
1749 outputorderinfofound = 1;
1750 faceupdown = -1;
1751 }
1752 if (strcasestr(outbin_properties, "stackingorder=lastToFirst"))
1753 firsttolast = -1;
1754 }
1755 if (outputorderinfofound == 0 && defaultoutbin &&
1756 strcasestr(defaultoutbin, "face-up"))
1757 faceupdown = -1;
1758 if (defaultoutbin)
1759 free (defaultoutbin);
1760 if (firsttolast * faceupdown < 0)
1761 cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n");
1762 else
1763 cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n");
1764
1765 /* To decide whether the printer is coloured or not we see the various
1766 colormodel supported by the printer*/
1767 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD))
1768 == NULL)
1769 if ((attr = ippFindAttribute(response,
1770 "pwg-raster-document-type-supported",
1771 IPP_TAG_KEYWORD)) == NULL)
1772 if ((attr = ippFindAttribute(response, "print-color-mode-supported",
1773 IPP_TAG_KEYWORD)) == NULL)
1774 attr = ippFindAttribute(response, "output-mode-supported",
1775 IPP_TAG_KEYWORD);
1776 if (attr==NULL || !ippGetCount(attr)) {
1777 if ((attr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN))
1778 != NULL) {
1779 if (ippGetBoolean(attr, 0))
1780 cupsFilePuts(fp, "*ColorDevice: True\n");
1781 else
1782 cupsFilePuts(fp, "*ColorDevice: False\n");
1783 } else {
1784 if(color)
1785 cupsFilePuts(fp, "*ColorDevice: True\n");
1786 else
1787 cupsFilePuts(fp, "*ColorDevice: False\n");
1788 }
1789 } else {
1790 int colordevice = 0;
1791 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1792 keyword = ippGetString(attr, i, NULL);
1793 colordevice = is_colordevice(keyword,attr);
1794 if (colordevice) {
1795 cupsFilePuts(fp, "*ColorDevice: True\n");
1796 break;
1797 }
1798 }
1799 if (colordevice == 0)
1800 cupsFilePuts(fp, "*ColorDevice: False\n");
1801 }
1802
1803 cupsFilePrintf(fp, "*cupsVersion: %d.%d\n", CUPS_VERSION_MAJOR,
1804 CUPS_VERSION_MINOR);
1805 cupsFilePuts(fp, "*cupsSNMPSupplies: False\n");
1806 cupsFilePuts(fp, "*cupsLanguages: \"en\"\n");
1807
1808 if ((attr = ippFindAttribute(response, "printer-more-info", IPP_TAG_URI)) !=
1809 NULL)
1810 cupsFilePrintf(fp, "*APSupplies: \"%s\"\n", ippGetString(attr, 0, NULL));
1811
1812 if ((attr = ippFindAttribute(response, "printer-charge-info-uri",
1813 IPP_TAG_URI)) != NULL)
1814 cupsFilePrintf(fp, "*cupsChargeInfoURI: \"%s\"\n", ippGetString(attr, 0,
1815 NULL));
1816
1817 /* Message catalogs for UI strings */
1818 if (opt_strings_catalog == NULL) {
1819 opt_strings_catalog = optArrayNew();
1820 load_opt_strings_catalog(NULL, opt_strings_catalog);
1821 }
1822 if ((attr = ippFindAttribute(response, "printer-strings-uri",
1823 IPP_TAG_URI)) != NULL) {
1824 printer_opt_strings_catalog = optArrayNew();
1825 load_opt_strings_catalog(ippGetString(attr, 0, NULL),
1826 printer_opt_strings_catalog);
1827 if (printer_opt_strings_catalog)
1828 cupsFilePrintf(fp, "*cupsStringsURI: \"%s\"\n", ippGetString(attr, 0,
1829 NULL));
1830 }
1831
1832 /*
1833 * Accounting...
1834 */
1835
1836 if (ippGetBoolean(ippFindAttribute(response, "job-account-id-supported",
1837 IPP_TAG_BOOLEAN), 0))
1838 cupsFilePuts(fp, "*cupsJobAccountId: True\n");
1839
1840 if (ippGetBoolean(ippFindAttribute(response,
1841 "job-accounting-user-id-supported",
1842 IPP_TAG_BOOLEAN), 0))
1843 cupsFilePuts(fp, "*cupsJobAccountingUserId: True\n");
1844
1845 /*
1846 * Password/PIN printing...
1847 */
1848
1849 if ((attr = ippFindAttribute(response, "job-password-supported",
1850 IPP_TAG_INTEGER)) != NULL) {
1851 char pattern[33]; /* Password pattern */
1852 int maxlen = ippGetInteger(attr, 0);
1853 /* Maximum length */
1854 const char *repertoire =
1855 ippGetString(ippFindAttribute(response,
1856 "job-password-repertoire-configured",
1857 IPP_TAG_KEYWORD), 0, NULL);
1858 /* Type of password */
1859
1860 if (maxlen > (int)(sizeof(pattern) - 1))
1861 maxlen = (int)sizeof(pattern) - 1;
1862
1863 if (!repertoire || !strcmp(repertoire, "iana_us-ascii_digits"))
1864 memset(pattern, '1', (size_t)maxlen);
1865 else if (!strcmp(repertoire, "iana_us-ascii_letters"))
1866 memset(pattern, 'A', (size_t)maxlen);
1867 else if (!strcmp(repertoire, "iana_us-ascii_complex"))
1868 memset(pattern, 'C', (size_t)maxlen);
1869 else if (!strcmp(repertoire, "iana_us-ascii_any"))
1870 memset(pattern, '.', (size_t)maxlen);
1871 else if (!strcmp(repertoire, "iana_utf-8_digits"))
1872 memset(pattern, 'N', (size_t)maxlen);
1873 else if (!strcmp(repertoire, "iana_utf-8_letters"))
1874 memset(pattern, 'U', (size_t)maxlen);
1875 else
1876 memset(pattern, '*', (size_t)maxlen);
1877
1878 pattern[maxlen] = '\0';
1879
1880 cupsFilePrintf(fp, "*cupsJobPassword: \"%s\"\n", pattern);
1881 }
1882
1883
1884
1885 /*
1886 * PDLs and common resolutions ...
1887 */
1888
1889 common_res = NULL;
1890 current_res = NULL;
1891 common_def = NULL;
1892 current_def = NULL;
1893 min_res = NULL;
1894 max_res = NULL;
1895 /* Put all available PDls into a simple case-insensitevely searchable
1896 sorted string list */
1897 if ((pdl_list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1898 (cups_acopy_func_t)strdup,
1899 (cups_afree_func_t)free)) == NULL)
1900 goto bad_ppd;
1901 int formatfound = 0;
1902
1903 if (((attr = ippFindAttribute(response, "document-format-supported",
1904 IPP_TAG_MIMETYPE)) != NULL) ||
1905 (pdl && pdl[0] != '\0')) {
1906 const char *format = pdl;
1907 i = 0;
1908 count = ippGetCount(attr);
1909 while ((attr && i < count) || /* Go through formats in attribute */
1910 (!attr && pdl && pdl[0] != '\0' && format[0] != '\0')) {
1911 /* Go through formats in pdl string (from DNS-SD record) */
1912
1913 /* Pick next format from attribute */
1914 if (attr) format = ippGetString(attr, i, NULL);
1915 /* Add format to list of supported PDLs, skip duplicates */
1916 if (!cupsArrayFind(pdl_list, (void *)format))
1917 cupsArrayAdd(pdl_list, (void *)format);
1918 if (attr)
1919 /* Next format in attribute */
1920 i ++;
1921 else {
1922 /* Find the next format in the string pdl, if there is none left,
1923 go to the terminating zero */
1924 while (!isspace(*format) && *format != ',' && *format != '\0')
1925 format ++;
1926 while ((isspace(*format) || *format == ',') && *format != '\0')
1927 format ++;
1928 }
1929 }
1930 }
1931
1932 /*
1933 * Fax
1934 */
1935
1936 if (is_fax)
1937 cupsFilePuts(fp, "*cupsIPPFaxOut: True\n");
1938
1939 /* Check for each CUPS/cups-filters-supported PDL, starting with the
1940 most desirable going to the least desirable. If a PDL requires a
1941 certain set of resolutions (the raster-based PDLs), find the
1942 resolutions and find out which are the common resolutions of all
1943 supported PDLs. Choose the default resolution from the most
1944 desirable of all resolution-requiring PDLs if it is common in all
1945 of them. Skip a resolution-requiring PDL if its resolution list
1946 attribute is missing or contains only broken entries. Use the
1947 general resolution list and default resolution of the printer
1948 only if it does not support any resolution-requiring PDL. Use 300
1949 dpi if there is no resolution info at all in the attributes.
1950 In case of PDF as PDL check whether also the
1951 "application/vnd.cups-pdf" MIME type is accepted. In this case
1952 our printer is a remote CUPS queue which already runs the
1953 pdftopdf filter on the server, so let the PPD take
1954 "application/pdf" as input format so that pdftopdf does not also
1955 get executed on the client, applying option settings twice. See
1956 https://github.com/apple/cups/issues/5361 */
1957 if (cupsArrayFind(pdl_list, "application/vnd.cups-pdf")) {
1958 cupsFilePuts(fp, "*cupsFilter2: \"application/pdf application/pdf 0 -\"\n");
1959 manual_copies = 0;
1960 formatfound = 1;
1961 is_pdf = 1;
1962 } else if (cupsArrayFind(pdl_list, "application/pdf")) {
1963 cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 200 -\"\n");
1964 manual_copies = 0;
1965 formatfound = 1;
1966 is_pdf = 1;
1967 }
1968 #ifdef CUPS_RASTER_HAVE_APPLERASTER
1969 if (cupsArrayFind(pdl_list, "image/urf")) {
1970 if ((attr = ippFindAttribute(response, "urf-supported",
1971 IPP_TAG_KEYWORD)) != NULL) {
1972 int lowdpi = 0, hidpi = 0; /* Lower and higher resolution */
1973 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1974 const char *rs = ippGetString(attr, i, NULL); /* RS value */
1975 if (_cups_strncasecmp(rs, "RS", 2))
1976 continue;
1977 lowdpi = atoi(rs + 2);
1978 if ((rs = strrchr(rs, '-')) != NULL)
1979 hidpi = atoi(rs + 1);
1980 else
1981 hidpi = lowdpi;
1982 break;
1983 }
1984 if (lowdpi == 0) {
1985 /* Invalid "urf-supported" value... */
1986 goto bad_ppd;
1987 } else {
1988 if ((current_res = resolutionArrayNew()) != NULL) {
1989 if ((current_def = resolutionNew(lowdpi, lowdpi)) != NULL)
1990 {
1991 cupsArrayAdd(current_res, current_def);
1992 free_resolution(current_def, NULL);
1993 }
1994 if (hidpi != lowdpi &&
1995 (current_def = resolutionNew(hidpi, hidpi)) != NULL)
1996 {
1997 cupsArrayAdd(current_res, current_def);
1998 free_resolution(current_def, NULL);
1999 }
2000 current_def = NULL;
2001 if (cupsArrayCount(current_res) > 0 &&
2002 joinResolutionArrays(&common_res, ¤t_res, &common_def,
2003 ¤t_def)) {
2004 cupsFilePuts(fp, "*cupsFilter2: \"image/urf image/urf 0 -\"\n");
2005 if (formatfound == 0) manual_copies = 1;
2006 formatfound = 1;
2007 is_apple = 1;
2008 }
2009 }
2010 }
2011 }
2012 }
2013 #endif
2014 if (is_apple == 0 && cupsArrayFind(pdl_list, "image/pwg-raster")) {
2015 if ((attr = ippFindAttribute(response,
2016 "pwg-raster-document-resolution-supported",
2017 IPP_TAG_RESOLUTION)) != NULL) {
2018 current_def = NULL;
2019 if ((current_res = ippResolutionListToArray(attr)) != NULL &&
2020 joinResolutionArrays(&common_res, ¤t_res, &common_def,
2021 ¤t_def)) {
2022 cupsFilePuts(fp, "*cupsFilter2: \"image/pwg-raster image/pwg-raster 10 -\"\n");
2023 if (formatfound == 0) manual_copies = 1;
2024 formatfound = 1;
2025 is_pwg = 1;
2026 }
2027 }
2028 }
2029 #ifdef QPDF_HAVE_PCLM
2030 if (cupsArrayFind(pdl_list, "application/PCLm")) {
2031 if ((attr = ippFindAttribute(response, "pclm-source-resolution-supported",
2032 IPP_TAG_RESOLUTION)) != NULL) {
2033 if ((defattr = ippFindAttribute(response,
2034 "pclm-source-resolution-default",
2035 IPP_TAG_RESOLUTION)) != NULL)
2036 current_def = ippResolutionToRes(defattr, 0);
2037 else
2038 current_def = NULL;
2039 if ((current_res = ippResolutionListToArray(attr)) != NULL &&
2040 joinResolutionArrays(&common_res, ¤t_res, &common_def,
2041 ¤t_def)) {
2042 cupsFilePuts(fp, "*cupsFilter2: \"application/PCLm application/PCLm 300 -\"\n");
2043 if (formatfound == 0) manual_copies = 1;
2044 formatfound = 1;
2045 is_pclm = 1;
2046 }
2047 }
2048 }
2049 #endif
2050 if (cupsArrayFind(pdl_list, "application/vnd.hp-pclxl")) {
2051 /* Check whether the gstopxl filter is installed,
2052 otherwise ignore the PCL-XL support of the printer */
2053 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
2054 cups_serverbin = CUPS_SERVERBIN;
2055 snprintf(filter_path, sizeof(filter_path), "%s/filter/gstopxl",
2056 cups_serverbin);
2057 if (access(filter_path, X_OK) == 0) {
2058 /* We put a high cost factor here as if a printer supports also
2059 another format, like PWG or Apple Raster, we prefer it, as some
2060 PCL-XL printers have bugs in their PCL-XL interpreters */
2061 cupsFilePrintf(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/vnd.hp-pclxl 400 gstopxl\"\n");
2062 if (formatfound == 0) manual_copies = 1;
2063 formatfound = 1;
2064 }
2065 }
2066 if (cupsArrayFind(pdl_list, "application/postscript")) {
2067 /* We put a high cost factor here as if a printer supports also
2068 another format, like PWG or Apple Raster, we prefer it, as many
2069 PostScript printers have bugs in their PostScript interpreters */
2070 cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-postscript application/postscript 600 -\"\n");
2071 if (formatfound == 0) manual_copies = 0;
2072 formatfound = 1;
2073 }
2074 if (cupsArrayFind(pdl_list, "application/vnd.hp-pcl")) {
2075 /* We put a high cost factor here as if a printer supports also
2076 another format, like PWG or Apple Raster, we prefer it, as there
2077 are some printers, like HP inkjets which report to accept PCL
2078 but do not support PCL 5c/e or PCL-XL */
2079 cupsFilePrintf(fp, "*cupsFilter2: \"application/vnd.cups-raster application/vnd.hp-pcl 800 rastertopclx\"\n");
2080 if (formatfound == 0) manual_copies = 1;
2081 formatfound = 1;
2082 }
2083 if (cupsArrayFind(pdl_list, "image/jpeg"))
2084 cupsFilePuts(fp, "*cupsFilter2: \"image/jpeg image/jpeg 0 -\"\n");
2085 if (cupsArrayFind(pdl_list, "image/png"))
2086 cupsFilePuts(fp, "*cupsFilter2: \"image/png image/png 0 -\"\n");
2087 cupsArrayDelete(pdl_list);
2088 if (manual_copies < 0) manual_copies = 1;
2089 if (formatfound == 0)
2090 goto bad_ppd;
2091
2092 /* For the case that we will print in a raster format and not in a high-level
2093 format, we need to create multiple copies on the client. We add a line to
2094 the PPD which tells the pdftopdf filter to generate the copies */
2095 if (manual_copies == 1)
2096 cupsFilePuts(fp, "*cupsManualCopies: True\n");
2097
2098 /* No resolution requirements by any of the supported PDLs?
2099 Use "printer-resolution-supported" attribute */
2100 if (common_res == NULL) {
2101 if ((attr = ippFindAttribute(response, "printer-resolution-supported",
2102 IPP_TAG_RESOLUTION)) != NULL) {
2103 if ((defattr = ippFindAttribute(response, "printer-resolution-default",
2104 IPP_TAG_RESOLUTION)) != NULL)
2105 current_def = ippResolutionToRes(defattr, 0);
2106 else
2107 current_def = NULL;
2108 if ((current_res = ippResolutionListToArray(attr)) != NULL)
2109 joinResolutionArrays(&common_res, ¤t_res, &common_def,
2110 ¤t_def);
2111 }
2112 }
2113 /* Still no resolution found? Default to 300 dpi */
2114 if (common_res == NULL) {
2115 if ((common_res = resolutionArrayNew()) != NULL) {
2116 if ((current_def = resolutionNew(300, 300)) != NULL)
2117 {
2118 cupsArrayAdd(common_res, current_def);
2119 free_resolution(current_def, NULL);
2120 }
2121 current_def = NULL;
2122 } else
2123 goto bad_ppd;
2124 }
2125 /* No default resolution determined yet */
2126 if (common_def == NULL) {
2127 if ((defattr = ippFindAttribute(response, "printer-resolution-default",
2128 IPP_TAG_RESOLUTION)) != NULL) {
2129 common_def = ippResolutionToRes(defattr, 0);
2130 if (!cupsArrayFind(common_res, common_def)) {
2131 free(common_def);
2132 common_def = NULL;
2133 }
2134 }
2135 if (common_def == NULL) {
2136 count = cupsArrayCount(common_res);
2137 common_def = copy_resolution(cupsArrayIndex(common_res, count / 2), NULL);
2138 }
2139 }
2140 /* Get minimum and maximum resolution */
2141 min_res = copy_resolution(cupsArrayFirst(common_res), NULL);
2142 max_res = copy_resolution(cupsArrayLast(common_res), NULL);
2143 cupsArrayDelete(common_res);
2144
2145 #ifdef QPDF_HAVE_PCLM
2146 /*
2147 * Generically check for PCLm attributes in IPP response
2148 * and ppdize them one by one
2149 */
2150
2151 if (is_pclm) {
2152 attr = ippFirstAttribute(response); /* first attribute */
2153 while (attr) { /* loop through all the attributes */
2154 if (_cups_strncasecmp(ippGetName(attr), "pclm", 4) == 0) {
2155 pwg_ppdize_name(ippGetName(attr), ppdname, sizeof(ppdname));
2156 cupsFilePrintf(fp, "*cups%s: ", ppdname);
2157 ipp_tag_t tag = ippGetValueTag(attr);
2158 count = ippGetCount(attr);
2159
2160 if (tag == IPP_TAG_RESOLUTION) { /* ppdize values of type resolution */
2161 if ((current_res = ippResolutionListToArray(attr)) != NULL) {
2162 count = cupsArrayCount(current_res);
2163 if (count > 1)
2164 cupsFilePuts(fp, "\"");
2165 for (i = 0, current_def = cupsArrayFirst(current_res);
2166 current_def;
2167 i ++, current_def = cupsArrayNext(current_res)) {
2168 int x = current_def->x;
2169 int y = current_def->y;
2170 if (x == y)
2171 cupsFilePrintf(fp, "%ddpi", x);
2172 else
2173 cupsFilePrintf(fp, "%dx%ddpi", x, y);
2174 if (i < count - 1)
2175 cupsFilePuts(fp, ",");
2176 }
2177 if (count > 1)
2178 cupsFilePuts(fp, "\"");
2179 cupsFilePuts(fp, "\n");
2180 } else
2181 cupsFilePuts(fp, "\"\"\n");
2182 cupsArrayDelete(current_res);
2183 } else {
2184 ippAttributeString(attr, ppdname, sizeof(ppdname));
2185 if (count > 1 || /* quotes around multi-valued and string
2186 attributes */
2187 tag == IPP_TAG_STRING ||
2188 tag == IPP_TAG_TEXT ||
2189 tag == IPP_TAG_TEXTLANG)
2190 cupsFilePrintf(fp, "\"%s\"\n", ppdname);
2191 else
2192 cupsFilePrintf(fp, "%s\n", ppdname);
2193 }
2194 }
2195 attr = ippNextAttribute(response);
2196 }
2197 }
2198 #endif
2199
2200 /*
2201 * PageSize/PageRegion/ImageableArea/PaperDimension
2202 */
2203 printer_sizes = generate_sizes(response, &defattr, &min_length, &min_width,
2204 &max_length, &max_width,
2205 &bottom, &left, &right, &top, ppdname);
2206 if (sizes==NULL) {
2207 sizes = printer_sizes;
2208 } else
2209 strcpy(ppdname, default_pagesize);
2210
2211 if (cupsArrayCount(sizes) > 0) {
2212 /*
2213 * List all of the standard sizes...
2214 */
2215
2216 char tleft[256], /* Left string */
2217 tbottom[256], /* Bottom string */
2218 tright[256], /* Right string */
2219 ttop[256], /* Top string */
2220 twidth[256], /* Width string */
2221 tlength[256], /* Length string */
2222 ppdsizename[128];
2223 char *ippsizename,
2224 *suffix;
2225 int all_borderless = 1;
2226
2227 /* Find a page size without ".Borderless" suffix */
2228 /* (if all are ".Borderless" we drop the suffix in the PPD) */
2229 for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2230 size = (cups_size_t *)cupsArrayNext(sizes))
2231 if (strcasestr(size->media, ".Borderless") == NULL)
2232 break;
2233 if (size)
2234 all_borderless = 0;
2235
2236 if (all_borderless) {
2237 suffix = strcasestr(ppdname, ".Borderless");
2238 if (suffix)
2239 *suffix = '\0';
2240 }
2241
2242 cupsFilePrintf(fp, "*OpenUI *PageSize/%s: PickOne\n"
2243 "*OrderDependency: 10 AnySetup *PageSize\n"
2244 "*DefaultPageSize: %s\n", "Media Size", ppdname);
2245 for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2246 size = (cups_size_t *)cupsArrayNext(sizes)) {
2247 _cupsStrFormatd(twidth, twidth + sizeof(twidth),
2248 size->width * 72.0 / 2540.0, loc);
2249 _cupsStrFormatd(tlength, tlength + sizeof(tlength),
2250 size->length * 72.0 / 2540.0, loc);
2251 strlcpy(ppdsizename, size->media, sizeof(ppdsizename));
2252 if ((ippsizename = strchr(ppdsizename, ' ')) != NULL) {
2253 *ippsizename = '\0';
2254 ippsizename ++;
2255 }
2256
2257 if (ippsizename)
2258 human_readable = lookup_choice(ippsizename, "media",
2259 opt_strings_catalog,
2260 printer_opt_strings_catalog);
2261 else
2262 human_readable = NULL;
2263 if (!human_readable) {
2264 pwg = pwgMediaForSize(size->width, size->length);
2265 if (pwg)
2266 human_readable = lookup_choice((char *)pwg->pwg, "media",
2267 opt_strings_catalog,
2268 printer_opt_strings_catalog);
2269 }
2270
2271 if (all_borderless) {
2272 suffix = strcasestr(ppdsizename, ".Borderless");
2273 if (suffix)
2274 *suffix = '\0';
2275 }
2276
2277 cupsFilePrintf(fp, "*PageSize %s%s%s%s: \"<</PageSize[%s %s]>>setpagedevice\"\n",
2278 ppdsizename,
2279 (human_readable ? "/" : ""),
2280 (human_readable ? human_readable : ""),
2281 (human_readable && strstr(ppdsizename, ".Borderless") ?
2282 " (Borderless)" : ""),
2283 twidth, tlength);
2284 }
2285 cupsFilePuts(fp, "*CloseUI: *PageSize\n");
2286
2287 cupsFilePrintf(fp, "*OpenUI *PageRegion/%s: PickOne\n"
2288 "*OrderDependency: 10 AnySetup *PageRegion\n"
2289 "*DefaultPageRegion: %s\n", "Media Size", ppdname);
2290 for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2291 size = (cups_size_t *)cupsArrayNext(sizes)) {
2292 _cupsStrFormatd(twidth, twidth + sizeof(twidth),
2293 size->width * 72.0 / 2540.0, loc);
2294 _cupsStrFormatd(tlength, tlength + sizeof(tlength),
2295 size->length * 72.0 / 2540.0, loc);
2296 strlcpy(ppdsizename, size->media, sizeof(ppdsizename));
2297 if ((ippsizename = strchr(ppdsizename, ' ')) != NULL) {
2298 *ippsizename = '\0';
2299 ippsizename ++;
2300 }
2301
2302 if (ippsizename)
2303 human_readable = lookup_choice(ippsizename, "media",
2304 opt_strings_catalog,
2305 printer_opt_strings_catalog);
2306 else
2307 human_readable = NULL;
2308 if (!human_readable) {
2309 pwg = pwgMediaForSize(size->width, size->length);
2310 if (pwg)
2311 human_readable = lookup_choice((char *)pwg->pwg, "media",
2312 opt_strings_catalog,
2313 printer_opt_strings_catalog);
2314 }
2315
2316 if (all_borderless) {
2317 suffix = strcasestr(ppdsizename, ".Borderless");
2318 if (suffix)
2319 *suffix = '\0';
2320 }
2321
2322 cupsFilePrintf(fp, "*PageRegion %s%s%s%s: \"<</PageSize[%s %s]>>setpagedevice\"\n",
2323 ppdsizename,
2324 (human_readable ? "/" : ""),
2325 (human_readable ? human_readable : ""),
2326 (human_readable && strstr(ppdsizename, ".Borderless") ?
2327 " (Borderless)" : ""),
2328 twidth, tlength);
2329 }
2330 cupsFilePuts(fp, "*CloseUI: *PageRegion\n");
2331
2332 cupsFilePrintf(fp, "*DefaultImageableArea: %s\n"
2333 "*DefaultPaperDimension: %s\n", ppdname, ppdname);
2334
2335 for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2336 size = (cups_size_t *)cupsArrayNext(sizes)) {
2337 _cupsStrFormatd(tleft, tleft + sizeof(tleft),
2338 size->left * 72.0 / 2540.0, loc);
2339 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom),
2340 size->bottom * 72.0 / 2540.0, loc);
2341 _cupsStrFormatd(tright, tright + sizeof(tright),
2342 (size->width - size->right) * 72.0 / 2540.0, loc);
2343 _cupsStrFormatd(ttop, ttop + sizeof(ttop),
2344 (size->length - size->top) * 72.0 / 2540.0, loc);
2345 _cupsStrFormatd(twidth, twidth + sizeof(twidth),
2346 size->width * 72.0 / 2540.0, loc);
2347 _cupsStrFormatd(tlength, tlength + sizeof(tlength),
2348 size->length * 72.0 / 2540.0, loc);
2349 strlcpy(ppdsizename, size->media, sizeof(ppdsizename));
2350 if ((ippsizename = strchr(ppdsizename, ' ')) != NULL)
2351 *ippsizename = '\0';
2352
2353 if (all_borderless) {
2354 suffix = strcasestr(ppdsizename, ".Borderless");
2355 if (suffix)
2356 *suffix = '\0';
2357 }
2358
2359 cupsFilePrintf(fp, "*ImageableArea %s: \"%s %s %s %s\"\n", ppdsizename,
2360 tleft, tbottom, tright, ttop);
2361 cupsFilePrintf(fp, "*PaperDimension %s: \"%s %s\"\n", ppdsizename,
2362 twidth, tlength);
2363 }
2364
2365 /*
2366 * Custom size support...
2367 */
2368
2369 if (max_width > 0 && min_width < INT_MAX && max_length > 0 &&
2370 min_length < INT_MAX) {
2371 char tmax[256], tmin[256]; /* Min/max values */
2372
2373 _cupsStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc);
2374 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom),
2375 bottom * 72.0 / 2540.0, loc);
2376 _cupsStrFormatd(tright, tright + sizeof(tright), right * 72.0 / 2540.0,
2377 loc);
2378 _cupsStrFormatd(ttop, ttop + sizeof(ttop), top * 72.0 / 2540.0, loc);
2379
2380 cupsFilePrintf(fp, "*HWMargins: \"%s %s %s %s\"\n", tleft, tbottom,
2381 tright, ttop);
2382
2383 _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_width * 72.0 / 2540.0,
2384 loc);
2385 _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_width * 72.0 / 2540.0,
2386 loc);
2387 cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s\n", tmin,
2388 tmax);
2389
2390 _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_length * 72.0 / 2540.0,
2391 loc);
2392 _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_length * 72.0 / 2540.0,
2393 loc);
2394 cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s\n", tmin,
2395 tmax);
2396
2397 cupsFilePuts(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0\n");
2398 cupsFilePuts(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0\n");
2399 cupsFilePuts(fp, "*ParamCustomPageSize Orientation: 5 int 0 3\n");
2400 cupsFilePuts(fp, "*CustomPageSize True: \"pop pop pop <</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\"\n");
2401 }
2402 } else {
2403 cupsFilePrintf(fp,
2404 "*%% Printer did not supply page size info via IPP, using defaults\n"
2405 "*OpenUI *PageSize/Media Size: PickOne\n"
2406 "*OrderDependency: 10 AnySetup *PageSize\n"
2407 "*DefaultPageSize: Letter\n"
2408 "*PageSize Letter/US Letter: \"<</PageSize[612 792]>>setpagedevice\"\n"
2409 "*PageSize Legal/US Legal: \"<</PageSize[612 1008]>>setpagedevice\"\n"
2410 "*PageSize Executive/Executive: \"<</PageSize[522 756]>>setpagedevice\"\n"
2411 "*PageSize Tabloid/Tabloid: \"<</PageSize[792 1224]>>setpagedevice\"\n"
2412 "*PageSize A3/A3: \"<</PageSize[842 1191]>>setpagedevice\"\n"
2413 "*PageSize A4/A4: \"<</PageSize[595 842]>>setpagedevice\"\n"
2414 "*PageSize A5/A5: \"<</PageSize[420 595]>>setpagedevice\"\n"
2415 "*PageSize B5/JIS B5: \"<</PageSize[516 729]>>setpagedevice\"\n"
2416 "*PageSize EnvISOB5/Envelope B5: \"<</PageSize[499 709]>>setpagedevice\"\n"
2417 "*PageSize Env10/Envelope #10 : \"<</PageSize[297 684]>>setpagedevice\"\n"
2418 "*PageSize EnvC5/Envelope C5: \"<</PageSize[459 649]>>setpagedevice\"\n"
2419 "*PageSize EnvDL/Envelope DL: \"<</PageSize[312 624]>>setpagedevice\"\n"
2420 "*PageSize EnvMonarch/Envelope Monarch: \"<</PageSize[279 540]>>setpagedevice\"\n"
2421 "*CloseUI: *PageSize\n"
2422 "*OpenUI *PageRegion/Media Size: PickOne\n"
2423 "*OrderDependency: 10 AnySetup *PageRegion\n"
2424 "*DefaultPageRegion: Letter\n"
2425 "*PageRegion Letter/US Letter: \"<</PageSize[612 792]>>setpagedevice\"\n"
2426 "*PageRegion Legal/US Legal: \"<</PageSize[612 1008]>>setpagedevice\"\n"
2427 "*PageRegion Executive/Executive: \"<</PageSize[522 756]>>setpagedevice\"\n"
2428 "*PageRegion Tabloid/Tabloid: \"<</PageSize[792 1224]>>setpagedevice\"\n"
2429 "*PageRegion A3/A3: \"<</PageSize[842 1191]>>setpagedevice\"\n"
2430 "*PageRegion A4/A4: \"<</PageSize[595 842]>>setpagedevice\"\n"
2431 "*PageRegion A5/A5: \"<</PageSize[420 595]>>setpagedevice\"\n"
2432 "*PageRegion B5/JIS B5: \"<</PageSize[516 729]>>setpagedevice\"\n"
2433 "*PageRegion EnvISOB5/Envelope B5: \"<</PageSize[499 709]>>setpagedevice\"\n"
2434 "*PageRegion Env10/Envelope #10 : \"<</PageSize[297 684]>>setpagedevice\"\n"
2435 "*PageRegion EnvC5/Envelope C5: \"<</PageSize[459 649]>>setpagedevice\"\n"
2436 "*PageRegion EnvDL/Envelope DL: \"<</PageSize[312 624]>>setpagedevice\"\n"
2437 "*PageRegion EnvMonarch/Envelope Monarch: \"<</PageSize[279 540]>>setpagedevice\"\n"
2438 "*CloseUI: *PageSize\n"
2439 "*DefaultImageableArea: Letter\n"
2440 "*ImageableArea Letter/US Letter: \"18 12 594 780\"\n"
2441 "*ImageableArea Legal/US Legal: \"18 12 594 996\"\n"
2442 "*ImageableArea Executive/Executive: \"18 12 504 744\"\n"
2443 "*ImageableArea Tabloid/Tabloid: \"18 12 774 1212\"\n"
2444 "*ImageableArea A3/A3: \"18 12 824 1179\"\n"
2445 "*ImageableArea A4/A4: \"18 12 577 830\"\n"
2446 "*ImageableArea A5/A5: \"18 12 402 583\"\n"
2447 "*ImageableArea B5/JIS B5: \"18 12 498 717\"\n"
2448 "*ImageableArea EnvISOB5/Envelope B5: \"18 12 481 697\"\n"
2449 "*ImageableArea Env10/Envelope #10 : \"18 12 279 672\"\n"
2450 "*ImageableArea EnvC5/Envelope C5: \"18 12 441 637\"\n"
2451 "*ImageableArea EnvDL/Envelope DL: \"18 12 294 612\"\n"
2452 "*ImageableArea EnvMonarch/Envelope Monarch: \"18 12 261 528\"\n"
2453 "*DefaultPaperDimension: Letter\n"
2454 "*PaperDimension Letter/US Letter: \"612 792\"\n"
2455 "*PaperDimension Legal/US Legal: \"612 1008\"\n"
2456 "*PaperDimension Executive/Executive: \"522 756\"\n"
2457 "*PaperDimension Tabloid/Tabloid: \"792 1224\"\n"
2458 "*PaperDimension A3/A3: \"842 1191\"\n"
2459 "*PaperDimension A4/A4: \"595 842\"\n"
2460 "*PaperDimension A5/A5: \"420 595\"\n"
2461 "*PaperDimension B5/JIS B5: \"516 729\"\n"
2462 "*PaperDimension EnvISOB5/Envelope B5: \"499 709\"\n"
2463 "*PaperDimension Env10/Envelope #10 : \"297 684\"\n"
2464 "*PaperDimension EnvC5/Envelope C5: \"459 649\"\n"
2465 "*PaperDimension EnvDL/Envelope DL: \"312 624\"\n"
2466 "*PaperDimension EnvMonarch/Envelope Monarch: \"279 540\"\n");
2467 }
2468
2469 cupsArrayDelete(printer_sizes);
2470
2471 /*
2472 * InputSlot...
2473 */
2474
2475 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source",
2476 IPP_TAG_KEYWORD)) != NULL)
2477 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
2478 else
2479 ppdname[0] = '\0';
2480
2481 if ((attr = ippFindAttribute(response, "media-source-supported",
2482 IPP_TAG_KEYWORD)) != NULL &&
2483 (count = ippGetCount(attr)) > 1) {
2484 int have_default = ppdname[0] != '\0';
2485 /* Do we have a default InputSlot? */
2486 static const char * const sources[][2] =
2487 { /* "media-source" strings */
2488 { "Auto", _("Automatic") },
2489 { "Main", _("Main") },
2490 { "Alternate", _("Alternate") },
2491 { "LargeCapacity", _("Large Capacity") },
2492 { "Manual", _("Manual") },
2493 { "Envelope", _("Envelope") },
2494 { "Disc", _("Disc") },
2495 { "Photo", _("Photo") },
2496 { "Hagaki", _("Hagaki") },
2497 { "MainRoll", _("Main Roll") },
2498 { "AlternateRoll", _("Alternate Roll") },
2499 { "Top", _("Top") },
2500 { "Middle", _("Middle") },
2501 { "Bottom", _("Bottom") },
2502 { "Side", _("Side") },
2503 { "Left", _("Left") },
2504 { "Right", _("Right") },
2505 { "Center", _("Center") },
2506 { "Rear", _("Rear") },
2507 { "ByPassTray", _("Multipurpose") },
2508 { "Tray1", _("Tray 1") },
2509 { "Tray2", _("Tray 2") },
2510 { "Tray3", _("Tray 3") },
2511 { "Tray4", _("Tray 4") },
2512 { "Tray5", _("Tray 5") },
2513 { "Tray6", _("Tray 6") },
2514 { "Tray7", _("Tray 7") },
2515 { "Tray8", _("Tray 8") },
2516 { "Tray9", _("Tray 9") },
2517 { "Tray10", _("Tray 10") },
2518 { "Tray11", _("Tray 11") },
2519 { "Tray12", _("Tray 12") },
2520 { "Tray13", _("Tray 13") },
2521 { "Tray14", _("Tray 14") },
2522 { "Tray15", _("Tray 15") },
2523 { "Tray16", _("Tray 16") },
2524 { "Tray17", _("Tray 17") },
2525 { "Tray18", _("Tray 18") },
2526 { "Tray19", _("Tray 19") },
2527 { "Tray20", _("Tray 20") },
2528 { "Roll1", _("Roll 1") },
2529 { "Roll2", _("Roll 2") },
2530 { "Roll3", _("Roll 3") },
2531 { "Roll4", _("Roll 4") },
2532 { "Roll5", _("Roll 5") },
2533 { "Roll6", _("Roll 6") },
2534 { "Roll7", _("Roll 7") },
2535 { "Roll8", _("Roll 8") },
2536 { "Roll9", _("Roll 9") },
2537 { "Roll10", _("Roll 10") }
2538 };
2539
2540 human_readable = lookup_option("media-source", opt_strings_catalog,
2541 printer_opt_strings_catalog);
2542 cupsFilePrintf(fp, "*OpenUI *InputSlot/%s: PickOne\n"
2543 "*OrderDependency: 10 AnySetup *InputSlot\n",
2544 (human_readable ? human_readable : "Media Source"));
2545 if (have_default)
2546 cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
2547 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2548 keyword = ippGetString(attr, i, NULL);
2549
2550 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
2551
2552 if (i == 0 && !have_default)
2553 cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
2554
2555 human_readable = lookup_choice((char *)keyword, "media-source",
2556 opt_strings_catalog,
2557 printer_opt_strings_catalog);
2558 for (j = (int)(sizeof(sources) / sizeof(sources[0])) - 1; j >= 0; j --)
2559 if (!strcmp(sources[j][0], ppdname)) {
2560 if (human_readable == NULL)
2561 human_readable = (char *)_cupsLangString(lang, sources[j][1]);
2562 break;
2563 }
2564 if (j >= 0)
2565 cupsFilePrintf(fp, "*InputSlot %s/%s: \"<</MediaPosition %d>>setpagedevice\"\n",
2566 ppdname, human_readable, j);
2567 else
2568 cupsFilePrintf(fp, "*InputSlot %s%s%s: \"\"\n",
2569 ppdname,
2570 (human_readable ? "/" : ""),
2571 (human_readable ? human_readable : ""));
2572 }
2573 cupsFilePuts(fp, "*CloseUI: *InputSlot\n");
2574 }
2575
2576 /*
2577 * MediaType...
2578 */
2579
2580 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type",
2581 IPP_TAG_ZERO)) != NULL)
2582 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
2583 else
2584 strlcpy(ppdname, "Unknown", sizeof(ppdname));
2585
2586 if ((attr = ippFindAttribute(response, "media-type-supported",
2587 IPP_TAG_ZERO)) != NULL &&
2588 (count = ippGetCount(attr)) > 1) {
2589 static const char * const media_types[][2] =
2590 { /* "media-type" strings */
2591 { "aluminum", _("Aluminum") },
2592 { "auto", _("Automatic") },
2593 { "back-print-film", _("Back Print Film") },
2594 { "cardboard", _("Cardboard") },
2595 { "cardstock", _("Cardstock") },
2596 { "cd", _("CD") },
2597 { "com.hp.advanced-photo", _("Advanced Photo Paper") }, /* HP */
2598 { "com.hp.brochure-glossy", _("Glossy Brochure Paper") }, /* HP */
2599 { "com.hp.brochure-matte", _("Matte Brochure Paper") }, /* HP */
2600 { "com.hp.cover-matte", _("Matte Cover Paper") }, /* HP */
2601 { "com.hp.ecosmart-lite", _("Office Recycled Paper") }, /* HP */
2602 { "com.hp.everyday-glossy", _("Everyday Glossy Photo Paper") }, /* HP */
2603 { "com.hp.everyday-matte", _("Everyday Matte Paper") }, /* HP */
2604 { "com.hp.extra-heavy", _("Extra Heavyweight Paper") }, /* HP */
2605 { "com.hp.intermediate", _("Multipurpose Paper") }, /* HP */
2606 { "com.hp.mid-weight", _("Mid-Weight Paper") }, /* HP */
2607 { "com.hp.premium-inkjet", _("Premium Inkjet Paper") }, /* HP */
2608 { "com.hp.premium-photo", _("Premium Photo Glossy Paper") }, /* HP */
2609 { "com.hp.premium-presentation-matte", _("Premium Presentation Matte Paper") }, /* HP */
2610 { "continuous", _("Continuous") },
2611 { "continuous-long", _("Continuous Long") },
2612 { "continuous-short", _("Continuous Short") },
2613 { "disc", _("Optical Disc") },
2614 { "disc-glossy", _("Glossy Optical Disc") },
2615 { "disc-high-gloss", _("High Gloss Optical Disc") },
2616 { "disc-matte", _("Matte Optical Disc") },
2617 { "disc-satin", _("Satin Optical Disc") },
2618 { "disc-semi-gloss", _("Semi-Gloss Optical Disc") },
2619 { "double-wall", _("Double Wall Cardboard") },
2620 { "dry-film", _("Dry Film") },
2621 { "dvd", _("DVD") },
2622 { "embossing-foil", _("Embossing Foil") },
2623 { "end-board", _("End Board") },
2624 { "envelope", _("Envelope") },
2625 { "envelope-archival", _("Archival Envelope") },
2626 { "envelope-bond", _("Bond Envelope") },
2627 { "envelope-coated", _("Coated Envelope") },
2628 { "envelope-cotton", _("Cotton Envelope") },
2629 { "envelope-fine", _("Fine Envelope") },
2630 { "envelope-heavyweight", _("Heavyweight Envelope") },
2631 { "envelope-inkjet", _("Inkjet Envelope") },
2632 { "envelope-lightweight", _("Lightweight Envelope") },
2633 { "envelope-plain", _("Plain Envelope") },
2634 { "envelope-preprinted", _("Preprinted Envelope") },
2635 { "envelope-window", _("Windowed Envelope") },
2636 { "fabric", _("Fabric") },
2637 { "fabric-archival", _("Archival Fabric") },
2638 { "fabric-glossy", _("Glossy Fabric") },
2639 { "fabric-high-gloss", _("High Gloss Fabric") },
2640 { "fabric-matte", _("Matte Fabric") },
2641 { "fabric-semi-gloss", _("Semi-Gloss Fabric") },
2642 { "fabric-waterproof", _("Waterproof Fabric") },
2643 { "film", _("Film") },
2644 { "flexo-base", _("Flexo Base") },
2645 { "flexo-photo-polymer", _("Flexo Photo Polymer") },
2646 { "flute", _("Flute") },
2647 { "foil", _("Foil") },
2648 { "full-cut-tabs", _("Full Cut Tabs") },
2649 { "glass", _("Glass") },
2650 { "glass-colored", _("Glass Colored") },
2651 { "glass-opaque", _("Glass Opaque") },
2652 { "glass-surfaced", _("Glass Surfaced") },
2653 { "glass-textured", _("Glass Textured") },
2654 { "gravure-cylinder", _("Gravure Cylinder") },
2655 { "image-setter-paper", _("Image Setter Paper") },
2656 { "imaging-cylinder", _("Imaging Cylinder") },
2657 { "jp.co.canon_photo-paper-plus-glossy-ii", _("Photo Paper Plus Glossy II") }, /* Canon */
2658 { "jp.co.canon_photo-paper-pro-platinum", _("Photo Paper Pro Platinum") }, /* Canon */
2659 { "jp.co.canon-photo-paper-plus-glossy-ii", _("Photo Paper Plus Glossy II") }, /* Canon */
2660 { "jp.co.canon-photo-paper-pro-platinum", _("Photo Paper Pro Platinum") }, /* Canon */
2661 { "labels", _("Labels") },
2662 { "labels-colored", _("Colored Labels") },
2663 { "labels-glossy", _("Glossy Labels") },
2664 { "labels-high-gloss", _("High Gloss Labels") },
2665 { "labels-inkjet", _("Inkjet Labels") },
2666 { "labels-matte", _("Matte Labels") },
2667 { "labels-permanent", _("Permanent Labels") },
2668 { "labels-satin", _("Satin Labels") },
2669 { "labels-security", _("Security Labels") },
2670 { "labels-semi-gloss", _("Semi-Gloss Labels") },
2671 { "laminating-foil", _("Laminating Foil") },
2672 { "letterhead", _("Letterhead") },
2673 { "metal", _("Metal") },
2674 { "metal-glossy", _("Metal Glossy") },
2675 { "metal-high-gloss", _("Metal High Gloss") },
2676 { "metal-matte", _("Metal Matte") },
2677 { "metal-satin", _("Metal Satin") },
2678 { "metal-semi-gloss", _("Metal Semi Gloss") },
2679 { "mounting-tape", _("Mounting Tape") },
2680 { "multi-layer", _("Multi Layer") },
2681 { "multi-part-form", _("Multi Part Form") },
2682 { "other", _("Other") },
2683 { "paper", _("Paper") },
2684 { "photo", _("Photo Paper") }, /* HP mis-spelling */
2685 { "photographic", _("Photo Paper") },
2686 { "photographic-archival", _("Archival Photo Paper") },
2687 { "photographic-film", _("Photo Film") },
2688 { "photographic-glossy", _("Glossy Photo Paper") },
2689 { "photographic-high-gloss", _("High Gloss Photo Paper") },
2690 { "photographic-matte", _("Matte Photo Paper") },
2691 { "photographic-satin", _("Satin Photo Paper") },
2692 { "photographic-semi-gloss", _("Semi-Gloss Photo Paper") },
2693 { "plastic", _("Plastic") },
2694 { "plastic-archival", _("Plastic Archival") },
2695 { "plastic-colored", _("Plastic Colored") },
2696 { "plastic-glossy", _("Plastic Glossy") },
2697 { "plastic-high-gloss", _("Plastic High Gloss") },
2698 { "plastic-matte", _("Plastic Matte") },
2699 { "plastic-satin", _("Plastic Satin") },
2700 { "plastic-semi-gloss", _("Plastic Semi Gloss") },
2701 { "plate", _("Plate") },
2702 { "polyester", _("Polyester") },
2703 { "pre-cut-tabs", _("Pre Cut Tabs") },
2704 { "roll", _("Roll") },
2705 { "screen", _("Screen") },
2706 { "screen-paged", _("Screen Paged") },
2707 { "self-adhesive", _("Self Adhesive") },
2708 { "self-adhesive-film", _("Self Adhesive Film") },
2709 { "shrink-foil", _("Shrink Foil") },
2710 { "single-face", _("Single Face") },
2711 { "single-wall", _("Single Wall Cardboard") },
2712 { "sleeve", _("Sleeve") },
2713 { "stationery", _("Plain Paper") },
2714 { "stationery-archival", _("Archival Paper") },
2715 { "stationery-coated", _("Coated Paper") },
2716 { "stationery-cotton", _("Cotton Paper") },
2717 { "stationery-fine", _("Vellum Paper") },
2718 { "stationery-heavyweight", _("Heavyweight Paper") },
2719 { "stationery-heavyweight-coated", _("Heavyweight Coated Paper") },
2720 { "stationery-inkjet", _("Inkjet Paper") },
2721 { "stationery-letterhead", _("Letterhead") },
2722 { "stationery-lightweight", _("Lightweight Paper") },
2723 { "stationery-preprinted", _("Preprinted Paper") },
2724 { "stationery-prepunched", _("Punched Paper") },
2725 { "tab-stock", _("Tab Stock") },
2726 { "tractor", _("Tractor") },
2727 { "transfer", _("Transfer") },
2728 { "transparency", _("Transparency") },
2729 { "triple-wall", _("Triple Wall Cardboard") },
2730 { "wet-film", _("Wet Film") }
2731 };
2732
2733 human_readable = lookup_option("media-type", opt_strings_catalog,
2734 printer_opt_strings_catalog);
2735 cupsFilePrintf(fp, "*OpenUI *MediaType/%s: PickOne\n"
2736 "*OrderDependency: 10 AnySetup *MediaType\n"
2737 "*DefaultMediaType: %s\n",
2738 (human_readable ? human_readable : "Media Type"),
2739 ppdname);
2740 for (i = 0; i < count; i ++) {
2741 keyword = ippGetString(attr, i, NULL);
2742
2743 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
2744
2745 human_readable = lookup_choice((char *)keyword, "media-type",
2746 opt_strings_catalog,
2747 printer_opt_strings_catalog);
2748 if (human_readable == NULL)
2749 for (j = 0; j < (int)(sizeof(media_types) / sizeof(media_types[0]));
2750 j ++)
2751 if (!strcmp(media_types[j][0], keyword)) {
2752 human_readable = (char *)_cupsLangString(lang, media_types[j][1]);
2753 break;
2754 }
2755 cupsFilePrintf(fp, "*MediaType %s%s%s: \"<</MediaType(%s)>>setpagedevice\"\n",
2756 ppdname,
2757 (human_readable ? "/" : ""),
2758 (human_readable ? human_readable : ""),
2759 ppdname);
2760 }
2761 cupsFilePuts(fp, "*CloseUI: *MediaType\n");
2762 }
2763
2764 /*
2765 * ColorModel...
2766 */
2767
2768 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) ==
2769 NULL)
2770 if ((attr = ippFindAttribute(response, "print-color-mode-supported",
2771 IPP_TAG_KEYWORD)) == NULL)
2772 if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported",
2773 IPP_TAG_KEYWORD)) == NULL)
2774 attr = ippFindAttribute(response, "output-mode-supported",
2775 IPP_TAG_KEYWORD);
2776
2777 human_readable = lookup_option("print-color-mode", opt_strings_catalog,
2778 printer_opt_strings_catalog);
2779 if (attr && ippGetCount(attr) > 0) {
2780 const char *default_color = NULL; /* Default */
2781 int first_choice = 1,
2782 have_bi_level = 0,
2783 have_mono = 0;
2784
2785 cupsFilePrintf(fp, "*%% ColorModel from %s\n", ippGetName(attr));
2786
2787 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2788 keyword = ippGetString(attr, i, NULL); /* Keyword for color/bit depth */
2789
2790 if (!have_bi_level &&
2791 (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") ||
2792 !strcmp(keyword, "process-bi-level"))) {
2793 have_bi_level = 1;
2794 if (first_choice) {
2795 first_choice = 0;
2796 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2797 "*OrderDependency: 10 AnySetup *ColorModel\n",
2798 (human_readable ? human_readable :
2799 _cupsLangString(lang, _("Color Mode"))));
2800 }
2801
2802 human_readable2 = lookup_choice("bi-level", "print-color-mode",
2803 opt_strings_catalog,
2804 printer_opt_strings_catalog);
2805 cupsFilePrintf(fp, "*ColorModel FastGray/%s: \"<</cupsColorSpace 3/cupsBitsPerColor 1/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2806 (human_readable2 ? human_readable2 :
2807 _cupsLangString(lang, _("Fast Grayscale"))));
2808
2809 if (!default_color)
2810 default_color = "FastGray";
2811 } else if (!have_mono &&
2812 (!strcasecmp(keyword, "sgray_8") ||
2813 !strncmp(keyword, "W8", 2) ||
2814 !strcmp(keyword, "monochrome") ||
2815 !strcmp(keyword, "process-monochrome"))) {
2816 have_mono = 1;
2817 if (first_choice) {
2818 first_choice = 0;
2819 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2820 "*OrderDependency: 10 AnySetup *ColorModel\n",
2821 (human_readable ? human_readable :
2822 _cupsLangString(lang, _("Color Mode"))));
2823 }
2824
2825 human_readable2 = lookup_choice("monochrome", "print-color-mode",
2826 opt_strings_catalog,
2827 printer_opt_strings_catalog);
2828 cupsFilePrintf(fp, "*ColorModel Gray/%s: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2829 (human_readable2 ? human_readable2 :
2830 _cupsLangString(lang, _("Grayscale"))));
2831
2832 if (!default_color || !strcmp(default_color, "FastGray"))
2833 default_color = "Gray";
2834 } else if (!strcasecmp(keyword, "sgray_16") ||
2835 !strncmp(keyword, "W8-16", 5) ||
2836 !strncmp(keyword, "W16", 3)) {
2837 if (first_choice) {
2838 first_choice = 0;
2839 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2840 "*OrderDependency: 10 AnySetup *ColorModel\n",
2841 (human_readable ? human_readable :
2842 _cupsLangString(lang, _("Color Mode"))));
2843 }
2844
2845 cupsFilePrintf(fp, "*ColorModel Gray16/%s: \"<</cupsColorSpace 18/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2846 _cupsLangString(lang, _("Deep Gray (High Definition Grayscale)")));
2847
2848 if (!default_color || !strcmp(default_color, "FastGray"))
2849 default_color = "Gray16";
2850 } else if (!strcasecmp(keyword, "srgb_8") ||
2851 !strncmp(keyword, "SRGB24", 6) ||
2852 !strcmp(keyword, "color")) {
2853 if (first_choice) {
2854 first_choice = 0;
2855 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2856 "*OrderDependency: 10 AnySetup *ColorModel\n",
2857 (human_readable ? human_readable :
2858 _cupsLangString(lang, _("Color Mode"))));
2859 }
2860
2861 human_readable2 = lookup_choice("color", "print-color-mode",
2862 opt_strings_catalog,
2863 printer_opt_strings_catalog);
2864 cupsFilePrintf(fp, "*ColorModel RGB/%s: \"<</cupsColorSpace 19/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2865 (human_readable2 ? human_readable2 :
2866 _cupsLangString(lang, _("Color"))));
2867
2868 default_color = "RGB";
2869 } else if ((!strcasecmp(keyword, "srgb_16") ||
2870 !strncmp(keyword, "SRGB48", 6)) &&
2871 !ippContainsString(attr, "srgb_8")) {
2872 if (first_choice) {
2873 first_choice = 0;
2874 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2875 "*OrderDependency: 10 AnySetup *ColorModel\n",
2876 (human_readable ? human_readable :
2877 _cupsLangString(lang, _("Color Mode"))));
2878 }
2879
2880 human_readable2 = lookup_choice("color", "print-color-mode",
2881 opt_strings_catalog,
2882 printer_opt_strings_catalog);
2883 cupsFilePrintf(fp, "*ColorModel RGB/%s: \"<</cupsColorSpace 19/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2884 (human_readable2 ? human_readable2 :
2885 _cupsLangString(lang, _("Color"))));
2886
2887 default_color = "RGB";
2888
2889 /* Apparently some printers only advertise color support, so make sure
2890 we also do grayscale for these printers... */
2891 if (!ippContainsString(attr, "sgray_8") &&
2892 !ippContainsString(attr, "black_1") &&
2893 !ippContainsString(attr, "black_8") &&
2894 !ippContainsString(attr, "W8") &&
2895 !ippContainsString(attr, "W8-16")) {
2896 human_readable2 = lookup_choice("monochrome", "print-color-mode",
2897 opt_strings_catalog,
2898 printer_opt_strings_catalog);
2899 cupsFilePrintf(fp, "*ColorModel Gray/%s: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2900 (human_readable2 ? human_readable2 :
2901 _cupsLangString(lang, _("Grayscale"))));
2902 }
2903 } else if (!strcasecmp(keyword, "adobe-rgb_16") ||
2904 !strncmp(keyword, "ADOBERGB48", 10) ||
2905 !strncmp(keyword, "ADOBERGB24-48", 13)) {
2906 if (first_choice) {
2907 first_choice = 0;
2908 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2909 "*OrderDependency: 10 AnySetup *ColorModel\n",
2910 (human_readable ? human_readable :
2911 _cupsLangString(lang, _("Color Mode"))));
2912 }
2913
2914 cupsFilePrintf(fp, "*ColorModel AdobeRGB/%s: \"<</cupsColorSpace 20/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2915 _cupsLangString(lang, _("Deep Color (Wide Color Gamut, AdobeRGB)")));
2916
2917 if (!default_color)
2918 default_color = "AdobeRGB";
2919 } else if ((!strcasecmp(keyword, "adobe-rgb_8") ||
2920 !strcmp(keyword, "ADOBERGB24")) &&
2921 !ippContainsString(attr, "adobe-rgb_16")) {
2922 if (first_choice) {
2923 first_choice = 0;
2924 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2925 "*OrderDependency: 10 AnySetup *ColorModel\n",
2926 (human_readable ? human_readable :
2927 _cupsLangString(lang, _("Color Mode"))));
2928 }
2929
2930 cupsFilePrintf(fp, "*ColorModel AdobeRGB/%s: \"<</cupsColorSpace 20/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2931 _cupsLangString(lang, _("Deep Color (Wide Color Gamut, AdobeRGB)")));
2932
2933 if (!default_color)
2934 default_color = "AdobeRGB";
2935 } else if ((!strcasecmp(keyword, "black_8") &&
2936 !ippContainsString(attr, "black_16")) ||
2937 !strcmp(keyword, "DEVW8")) {
2938 if (first_choice) {
2939 first_choice = 0;
2940 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2941 "*OrderDependency: 10 AnySetup *ColorModel\n",
2942 (human_readable ? human_readable :
2943 _cupsLangString(lang, _("Color Mode"))));
2944 }
2945
2946 cupsFilePrintf(fp, "*ColorModel DeviceGray/%s: \"<</cupsColorSpace 0/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2947 _cupsLangString(lang, _("Device Gray")));
2948 } else if (!strcasecmp(keyword, "black_16") ||
2949 !strcmp(keyword, "DEVW16") ||
2950 !strcmp(keyword, "DEVW8-16")) {
2951 if (first_choice) {
2952 first_choice = 0;
2953 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2954 "*OrderDependency: 10 AnySetup *ColorModel\n",
2955 (human_readable ? human_readable :
2956 _cupsLangString(lang, _("Color Mode"))));
2957 }
2958
2959 cupsFilePrintf(fp, "*ColorModel DeviceGray/%s: \"<</cupsColorSpace 0/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2960 _cupsLangString(lang, _("Device Gray")));
2961 } else if ((!strcasecmp(keyword, "cmyk_8") &&
2962 !ippContainsString(attr, "cmyk_16")) ||
2963 !strcmp(keyword, "DEVCMYK32")) {
2964 if (first_choice) {
2965 first_choice = 0;
2966 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2967 "*OrderDependency: 10 AnySetup *ColorModel\n",
2968 (human_readable ? human_readable :
2969 _cupsLangString(lang, _("Color Mode"))));
2970 }
2971
2972 cupsFilePrintf(fp, "*ColorModel CMYK/%s: \"<</cupsColorSpace 6/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2973 _cupsLangString(lang, _("Device CMYK")));
2974 } else if (!strcasecmp(keyword, "cmyk_16") ||
2975 !strcmp(keyword, "DEVCMYK32-64") ||
2976 !strcmp(keyword, "DEVCMYK64")) {
2977 if (first_choice) {
2978 first_choice = 0;
2979 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2980 "*OrderDependency: 10 AnySetup *ColorModel\n",
2981 (human_readable ? human_readable :
2982 _cupsLangString(lang, _("Color Mode"))));
2983 }
2984
2985 cupsFilePrintf(fp, "*ColorModel CMYK/%s: \"<</cupsColorSpace 6/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2986 _cupsLangString(lang, _("Device CMYK")));
2987 } else if ((!strcasecmp(keyword, "rgb_8") &&
2988 !ippContainsString(attr, "rgb_16")) ||
2989 !strcmp(keyword, "DEVRGB24")) {
2990 if (first_choice) {
2991 first_choice = 0;
2992 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2993 "*OrderDependency: 10 AnySetup *ColorModel\n",
2994 (human_readable ? human_readable :
2995 _cupsLangString(lang, _("Color Mode"))));
2996 }
2997
2998 cupsFilePrintf(fp, "*ColorModel DeviceRGB/%s: \"<</cupsColorSpace 1/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2999 _cupsLangString(lang, _("Device RGB")));
3000 } else if (!strcasecmp(keyword, "rgb_16") ||
3001 !strcmp(keyword, "DEVRGB24-48") ||
3002 !strcmp(keyword, "DEVRGB48")) {
3003 if (first_choice) {
3004 first_choice = 0;
3005 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3006 "*OrderDependency: 10 AnySetup *ColorModel\n",
3007 (human_readable ? human_readable :
3008 _cupsLangString(lang, _("Color Mode"))));
3009 }
3010
3011 cupsFilePrintf(fp, "*ColorModel DeviceRGB/%s: \"<</cupsColorSpace 1/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
3012 _cupsLangString(lang, _("Device RGB")));
3013 }
3014 }
3015
3016 if (default_pagesize != NULL) {
3017 /* Here we are dealing with a cluster, if the default cluster color
3018 is not supplied we set it Gray*/
3019 if (default_cluster_color != NULL) {
3020 default_color = default_cluster_color;
3021 } else
3022 default_color = "Gray";
3023 }
3024
3025 if (default_color)
3026 cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color);
3027 if (!first_choice)
3028 cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
3029 } else {
3030 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3031 "*OrderDependency: 10 AnySetup *ColorModel\n",
3032 (human_readable ? human_readable :
3033 _cupsLangString(lang, _("Color Mode"))));
3034 cupsFilePrintf(fp, "*DefaultColorModel: Gray\n");
3035 cupsFilePuts(fp, "*ColorModel FastGray/Fast Grayscale: \"<</cupsColorSpace 3/cupsBitsPerColor 1/cupsColorOrder 0/cupsCompression 0/ProcessColorModel /DeviceGray>>setpagedevice\"\n");
3036 cupsFilePuts(fp, "*ColorModel Gray/Grayscale: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0/ProcessColorModel /DeviceGray>>setpagedevice\"\n");
3037 if (color) {
3038 /* Color printer according to DNS-SD (or unknown) */
3039 cupsFilePuts(fp, "*ColorModel RGB/Color: \"<</cupsColorSpace 19/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0/ProcessColorModel /DeviceRGB>>setpagedevice\"\n");
3040 }
3041 cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
3042 }
3043
3044 /*
3045 * Duplex...
3046 */
3047
3048 if (((attr = ippFindAttribute(response, "sides-supported",
3049 IPP_TAG_KEYWORD)) != NULL &&
3050 ippContainsString(attr, "two-sided-long-edge")) ||
3051 (attr == NULL && duplex)) {
3052 human_readable = lookup_option("sides", opt_strings_catalog,
3053 printer_opt_strings_catalog);
3054 cupsFilePrintf(fp, "*OpenUI *Duplex/%s: PickOne\n"
3055 "*OrderDependency: 10 AnySetup *Duplex\n"
3056 "*DefaultDuplex: None\n",
3057 (human_readable ? human_readable :
3058 _cupsLangString(lang, _("2-Sided Printing"))));
3059 human_readable = lookup_choice("one-sided", "sides", opt_strings_catalog,
3060 printer_opt_strings_catalog);
3061 cupsFilePrintf(fp, "*Duplex None/%s: \"<</Duplex false>>setpagedevice\"\n",
3062 (human_readable ? human_readable :
3063 _cupsLangString(lang, _("Off (1-Sided)"))));
3064 human_readable = lookup_choice("two-sided-long-edge", "sides",
3065 opt_strings_catalog,
3066 printer_opt_strings_catalog);
3067 cupsFilePrintf(fp, "*Duplex DuplexNoTumble/%s: \"<</Duplex true/Tumble false>>setpagedevice\"\n",
3068 (human_readable ? human_readable :
3069 _cupsLangString(lang, _("Long-Edge (Portrait)"))));
3070 human_readable = lookup_choice("two-sided-short-edge", "sides",
3071 opt_strings_catalog,
3072 printer_opt_strings_catalog);
3073 cupsFilePrintf(fp, "*Duplex DuplexTumble/%s: \"<</Duplex true/Tumble true>>setpagedevice\"\n",
3074 (human_readable ? human_readable :
3075 _cupsLangString(lang, _("Short-Edge (Landscape)"))));
3076 cupsFilePrintf(fp, "*CloseUI: *Duplex\n");
3077
3078 if ((attr = ippFindAttribute(response, "urf-supported",
3079 IPP_TAG_KEYWORD)) != NULL) {
3080 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
3081 const char *dm = ippGetString(attr, i, NULL); /* DM value */
3082
3083 if (!_cups_strcasecmp(dm, "DM1")) {
3084 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3085 break;
3086 } else if (!_cups_strcasecmp(dm, "DM2")) {
3087 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3088 break;
3089 } else if (!_cups_strcasecmp(dm, "DM3")) {
3090 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3091 break;
3092 } else if (!_cups_strcasecmp(dm, "DM4")) {
3093 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3094 break;
3095 }
3096 }
3097 } else if ((attr = ippFindAttribute(response,
3098 "pwg-raster-document-sheet-back",
3099 IPP_TAG_KEYWORD)) != NULL) {
3100 keyword = ippGetString(attr, 0, NULL); /* Keyword value */
3101
3102 if (!strcmp(keyword, "flipped"))
3103 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3104 else if (!strcmp(keyword, "manual-tumble"))
3105 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3106 else if (!strcmp(keyword, "normal"))
3107 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3108 else
3109 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3110 }
3111 }
3112
3113 /*
3114 * Output bin...
3115 */
3116
3117 if ((attr = ippFindAttribute(response, "output-bin-default",
3118 IPP_TAG_ZERO)) != NULL)
3119 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3120 else
3121 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3122
3123 if ((attr = ippFindAttribute(response, "output-bin-supported",
3124 IPP_TAG_ZERO)) != NULL &&
3125 (count = ippGetCount(attr)) > 0) {
3126 static const char * const output_bins[][2] =
3127 { /* "output-bin" strings */
3128 { "auto", _("Automatic") },
3129 { "bottom", _("Bottom Tray") },
3130 { "center", _("Center Tray") },
3131 { "face-down", _("Face Down") },
3132 { "face-up", _("Face Up") },
3133 { "large-capacity", _("Large Capacity Tray") },
3134 { "left", _("Left Tray") },
3135 { "mailbox-1", _("Mailbox 1") },
3136 { "mailbox-2", _("Mailbox 2") },
3137 { "mailbox-3", _("Mailbox 3") },
3138 { "mailbox-4", _("Mailbox 4") },
3139 { "mailbox-5", _("Mailbox 5") },
3140 { "mailbox-6", _("Mailbox 6") },
3141 { "mailbox-7", _("Mailbox 7") },
3142 { "mailbox-8", _("Mailbox 8") },
3143 { "mailbox-9", _("Mailbox 9") },
3144 { "mailbox-10", _("Mailbox 10") },
3145 { "middle", _("Middle") },
3146 { "my-mailbox", _("My Mailbox") },
3147 { "rear", _("Rear Tray") },
3148 { "right", _("Right Tray") },
3149 { "side", _("Side Tray") },
3150 { "stacker-1", _("Stacker 1") },
3151 { "stacker-2", _("Stacker 2") },
3152 { "stacker-3", _("Stacker 3") },
3153 { "stacker-4", _("Stacker 4") },
3154 { "stacker-5", _("Stacker 5") },
3155 { "stacker-6", _("Stacker 6") },
3156 { "stacker-7", _("Stacker 7") },
3157 { "stacker-8", _("Stacker 8") },
3158 { "stacker-9", _("Stacker 9") },
3159 { "stacker-10", _("Stacker 10") },
3160 { "top", _("Top Tray") },
3161 { "tray-1", _("Tray 1") },
3162 { "tray-2", _("Tray 2") },
3163 { "tray-3", _("Tray 3") },
3164 { "tray-4", _("Tray 4") },
3165 { "tray-5", _("Tray 5") },
3166 { "tray-6", _("Tray 6") },
3167 { "tray-7", _("Tray 7") },
3168 { "tray-8", _("Tray 8") },
3169 { "tray-9", _("Tray 9") },
3170 { "tray-10", _("Tray 10") }
3171 };
3172
3173 human_readable = lookup_option("output-bin", opt_strings_catalog,
3174 printer_opt_strings_catalog);
3175 cupsFilePrintf(fp, "*OpenUI *OutputBin/%s: PickOne\n"
3176 "*OrderDependency: 10 AnySetup *OutputBin\n"
3177 "*DefaultOutputBin: %s\n",
3178 (human_readable ? human_readable : "Output Bin"),
3179 ppdname);
3180 attr2 = ippFindAttribute(response, "printer-output-tray", IPP_TAG_STRING);
3181 for (i = 0; i < count; i ++) {
3182 keyword = ippGetString(attr, i, NULL);
3183
3184 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3185
3186 human_readable = lookup_choice((char *)keyword, "output-bin",
3187 opt_strings_catalog,
3188 printer_opt_strings_catalog);
3189 if (human_readable == NULL)
3190 for (j = 0; j < (int)(sizeof(output_bins) / sizeof(output_bins[0]));
3191 j ++)
3192 if (!strcmp(output_bins[j][0], keyword)) {
3193 human_readable = (char *)_cupsLangString(lang, output_bins[j][1]);
3194 break;
3195 }
3196 cupsFilePrintf(fp, "*OutputBin %s%s%s: \"\"\n",
3197 ppdname,
3198 (human_readable ? "/" : ""),
3199 (human_readable ? human_readable : ""));
3200 outputorderinfofound = 0;
3201 faceupdown = 1;
3202 firsttolast = 1;
3203 if (attr2 && i < ippGetCount(attr2)) {
3204 outbin_properties_octet = ippGetOctetString(attr2, i, &octet_str_len);
3205 memset(outbin_properties, 0, sizeof(outbin_properties));
3206 memcpy(outbin_properties, outbin_properties_octet,
3207 ((size_t)octet_str_len < sizeof(outbin_properties) - 1 ?
3208 (size_t)octet_str_len : sizeof(outbin_properties) - 1));
3209 if (strcasestr(outbin_properties, "pagedelivery=faceUp")) {
3210 outputorderinfofound = 1;
3211 faceupdown = -1;
3212 } else if (strcasestr(outbin_properties, "pagedelivery=faceDown")) {
3213 outputorderinfofound = 1;
3214 faceupdown = 1;
3215 }
3216 if (strcasestr(outbin_properties, "stackingorder=lastToFirst")) {
3217 outputorderinfofound = 1;
3218 firsttolast = -1;
3219 } else if (strcasestr(outbin_properties, "stackingorder=firstToLast")) {
3220 outputorderinfofound = 1;
3221 firsttolast = 1;
3222 }
3223 }
3224 if (outputorderinfofound == 0) {
3225 if (strcasestr(keyword, "face-up")) {
3226 outputorderinfofound = 1;
3227 faceupdown = -1;
3228 }
3229 if (strcasestr(keyword, "face-down")) {
3230 outputorderinfofound = 1;
3231 faceupdown = 1;
3232 }
3233 }
3234 if (outputorderinfofound)
3235 cupsFilePrintf(fp, "*PageStackOrder %s: %s\n",
3236 ppdname,
3237 (firsttolast * faceupdown < 0 ? "Reverse" : "Normal"));
3238 }
3239 cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
3240 }
3241
3242 /*
3243 * Finishing options...
3244 */
3245
3246 if ((attr = ippFindAttribute(response, "finishings-supported",
3247 IPP_TAG_ENUM)) != NULL) {
3248 int value; /* Enum value */
3249 const char *ppd_keyword; /* PPD keyword for enum */
3250 cups_array_t *names; /* Names we've added */
3251 static const char * const base_keywords[] =
3252 { /* Base STD 92 keywords */
3253 NULL, /* none */
3254 "SingleAuto", /* staple */
3255 "SingleAuto", /* punch */
3256 NULL, /* cover */
3257 "BindAuto", /* bind */
3258 "SaddleStitch", /* saddle-stitch */
3259 "EdgeStitchAuto", /* edge-stitch */
3260 "Auto", /* fold */
3261 NULL, /* trim */
3262 NULL, /* bale */
3263 NULL, /* booklet-maker */
3264 NULL, /* jog-offset */
3265 NULL, /* coat */
3266 NULL /* laminate */
3267 };
3268 static const char * const finishings[][2] =
3269 { /* Finishings strings */
3270 { "bale", _("Bale") },
3271 { "bind", _("Bind") },
3272 { "bind-bottom", _("Bind (Reverse Landscape)") },
3273 { "bind-left", _("Bind (Portrait)") },
3274 { "bind-right", _("Bind (Reverse Portrait)") },
3275 { "bind-top", _("Bind (Landscape)") },
3276 { "booklet-maker", _("Booklet Maker") },
3277 { "coat", _("Coat") },
3278 { "cover", _("Cover") },
3279 { "edge-stitch", _("Staple Edge") },
3280 { "edge-stitch-bottom", _("Staple Edge (Reverse Landscape)") },
3281 { "edge-stitch-left", _("Staple Edge (Portrait)") },
3282 { "edge-stitch-right", _("Staple Edge (Reverse Portrait)") },
3283 { "edge-stitch-top", _("Staple Edge (Landscape)") },
3284 { "fold", _("Fold") },
3285 { "fold-accordian", _("Accordian Fold") },
3286 { "fold-double-gate", _("Double Gate Fold") },
3287 { "fold-engineering-z", _("Engineering Z Fold") },
3288 { "fold-gate", _("Gate Fold") },
3289 { "fold-half", _("Half Fold") },
3290 { "fold-half-z", _("Half Z Fold") },
3291 { "fold-left-gate", _("Left Gate Fold") },
3292 { "fold-letter", _("Letter Fold") },
3293 { "fold-parallel", _("Parallel Fold") },
3294 { "fold-poster", _("Poster Fold") },
3295 { "fold-right-gate", _("Right Gate Fold") },
3296 { "fold-z", _("Z Fold") },
3297 { "jog-offset", _("Jog") },
3298 { "laminate", _("Laminate") },
3299 { "punch", _("Punch") },
3300 { "punch-bottom-left", _("Single Punch (Reverse Landscape)") },
3301 { "punch-bottom-right", _("Single Punch (Reverse Portrait)") },
3302 { "punch-double-bottom", _("2-Hole Punch (Reverse Portrait)") },
3303 { "punch-double-left", _("2-Hole Punch (Reverse Landscape)") },
3304 { "punch-double-right", _("2-Hole Punch (Landscape)") },
3305 { "punch-double-top", _("2-Hole Punch (Portrait)") },
3306 { "punch-quad-bottom", _("4-Hole Punch (Reverse Landscape)") },
3307 { "punch-quad-left", _("4-Hole Punch (Portrait)") },
3308 { "punch-quad-right", _("4-Hole Punch (Reverse Portrait)") },
3309 { "punch-quad-top", _("4-Hole Punch (Landscape)") },
3310 { "punch-top-left", _("Single Punch (Portrait)") },
3311 { "punch-top-right", _("Single Punch (Landscape)") },
3312 { "punch-triple-bottom", _("3-Hole Punch (Reverse Landscape)") },
3313 { "punch-triple-left", _("3-Hole Punch (Portrait)") },
3314 { "punch-triple-right", _("3-Hole Punch (Reverse Portrait)") },
3315 { "punch-triple-top", _("3-Hole Punch (Landscape)") },
3316 { "punch-multiple-bottom", _("Multi-Hole Punch (Reverse Landscape)") },
3317 { "punch-multiple-left", _("Multi-Hole Punch (Portrait)") },
3318 { "punch-multiple-right", _("Multi-Hole Punch (Reverse Portrait)") },
3319 { "punch-multiple-top", _("Multi-Hole Punch (Landscape)") },
3320 { "saddle-stitch", _("Saddle Stitch") },
3321 { "staple", _("Staple") },
3322 { "staple-bottom-left", _("Single Staple (Reverse Landscape)") },
3323 { "staple-bottom-right", _("Single Staple (Reverse Portrait)") },
3324 { "staple-dual-bottom", _("Double Staple (Reverse Landscape)") },
3325 { "staple-dual-left", _("Double Staple (Portrait)") },
3326 { "staple-dual-right", _("Double Staple (Reverse Portrait)") },
3327 { "staple-dual-top", _("Double Staple (Landscape)") },
3328 { "staple-top-left", _("Single Staple (Portrait)") },
3329 { "staple-top-right", _("Single Staple (Landscape)") },
3330 { "staple-triple-bottom", _("Triple Staple (Reverse Landscape)") },
3331 { "staple-triple-left", _("Triple Staple (Portrait)") },
3332 { "staple-triple-right", _("Triple Staple (Reverse Portrait)") },
3333 { "staple-triple-top", _("Triple Staple (Landscape)") },
3334 { "trim", _("Cut Media") }
3335 };
3336
3337 count = ippGetCount(attr);
3338 names = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0,
3339 (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
3340 fin_options = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3341
3342 /*
3343 * Staple/Bind/Stitch
3344 */
3345
3346 for (i = 0; i < count; i ++) {
3347 value = ippGetInteger(attr, i);
3348 keyword = ippEnumString("finishings", value);
3349
3350 if (!strncmp(keyword, "staple-", 7) ||
3351 !strncmp(keyword, "bind-", 5) ||
3352 !strncmp(keyword, "edge-stitch-", 12) ||
3353 !strcmp(keyword, "saddle-stitch"))
3354 break;
3355 }
3356
3357 if (i < count) {
3358 static const char * const staple_keywords[] =
3359 { /* StapleLocation keywords */
3360 "SinglePortrait",
3361 "SingleRevLandscape",
3362 "SingleLandscape",
3363 "SingleRevPortrait",
3364 "EdgeStitchPortrait",
3365 "EdgeStitchLandscape",
3366 "EdgeStitchRevPortrait",
3367 "EdgeStitchRevLandscape",
3368 "DualPortrait",
3369 "DualLandscape",
3370 "DualRevPortrait",
3371 "DualRevLandscape",
3372 "TriplePortrait",
3373 "TripleLandscape",
3374 "TripleRevPortrait",
3375 "TripleRevLandscape"
3376 };
3377 static const char * const bind_keywords[] =
3378 { /* StapleLocation binding keywords */
3379 "BindPortrait",
3380 "BindLandscape",
3381 "BindRevPortrait",
3382 "BindRevLandscape"
3383 };
3384
3385 cupsArrayAdd(fin_options, "*StapleLocation");
3386
3387 human_readable = lookup_choice("staple", "finishing-template",
3388 opt_strings_catalog,
3389 printer_opt_strings_catalog);
3390 cupsFilePrintf(fp, "*OpenUI *StapleLocation/%s: PickOne\n",
3391 (human_readable ? human_readable :
3392 _cupsLangString(lang, _("Staple"))));
3393 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
3394 cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
3395 cupsFilePrintf(fp, "*StapleLocation None/%s: \"\"\n",
3396 _cupsLangString(lang, _("None")));
3397
3398 for (; i < count; i ++) {
3399 value = ippGetInteger(attr, i);
3400 keyword = ippEnumString("finishings", value);
3401
3402 if (strncmp(keyword, "staple-", 7) &&
3403 strncmp(keyword, "bind-", 5) &&
3404 strncmp(keyword, "edge-stitch-", 12) &&
3405 strcmp(keyword, "saddle-stitch"))
3406 continue;
3407
3408 if (cupsArrayFind(names, (char *)keyword))
3409 continue; /* Already did this finishing template */
3410
3411 cupsArrayAdd(names, (char *)keyword);
3412
3413 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
3414 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
3415 else if (value >= IPP_FINISHINGS_STAPLE_TOP_LEFT &&
3416 value <= IPP_FINISHINGS_STAPLE_TRIPLE_BOTTOM)
3417 ppd_keyword = staple_keywords[value - IPP_FINISHINGS_STAPLE_TOP_LEFT];
3418 else if (value >= IPP_FINISHINGS_BIND_LEFT &&
3419 value <= IPP_FINISHINGS_BIND_BOTTOM)
3420 ppd_keyword = bind_keywords[value - IPP_FINISHINGS_BIND_LEFT];
3421 else
3422 ppd_keyword = NULL;
3423
3424 if (!ppd_keyword)
3425 continue;
3426
3427 snprintf(buf, sizeof(buf), "%d", value);
3428 human_readable = lookup_choice(buf, "finishings", opt_strings_catalog,
3429 printer_opt_strings_catalog);
3430 if (human_readable == NULL)
3431 for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0]));
3432 j ++)
3433 if (!strcmp(finishings[j][0], keyword)) {
3434 human_readable = (char *)_cupsLangString(lang, finishings[j][1]);
3435 break;
3436 }
3437 cupsFilePrintf(fp, "*StapleLocation %s%s%s: \"\"\n", ppd_keyword,
3438 (human_readable ? "/" : ""),
3439 (human_readable ? human_readable : ""));
3440 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n",
3441 value, keyword, ppd_keyword);
3442 }
3443
3444 cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
3445 }
3446
3447 /*
3448 * Fold
3449 */
3450
3451 for (i = 0; i < count; i ++) {
3452 value = ippGetInteger(attr, i);
3453 keyword = ippEnumString("finishings", value);
3454
3455 if (!strncmp(keyword, "cups-fold-", 10) ||
3456 !strcmp(keyword, "fold") ||
3457 !strncmp(keyword, "fold-", 5))
3458 break;
3459 }
3460
3461 if (i < count) {
3462 static const char * const fold_keywords[] =
3463 { /* FoldType keywords */
3464 "Accordion",
3465 "DoubleGate",
3466 "Gate",
3467 "Half",
3468 "HalfZ",
3469 "LeftGate",
3470 "Letter",
3471 "Parallel",
3472 "XFold",
3473 "RightGate",
3474 "ZFold",
3475 "EngineeringZ"
3476 };
3477
3478 cupsArrayAdd(fin_options, "*FoldType");
3479
3480 human_readable = lookup_choice("fold", "finishing-template",
3481 opt_strings_catalog,
3482 printer_opt_strings_catalog);
3483 cupsFilePrintf(fp, "*OpenUI *FoldType/%s: PickOne\n",
3484 (human_readable ? human_readable :
3485 _cupsLangString(lang, _("Fold"))));
3486 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
3487 cupsFilePuts(fp, "*DefaultFoldType: None\n");
3488 cupsFilePrintf(fp, "*FoldType None/%s: \"\"\n",
3489 _cupsLangString(lang, _("None")));
3490
3491 for (; i < count; i ++) {
3492 value = ippGetInteger(attr, i);
3493 keyword = ippEnumString("finishings", value);
3494
3495 if (!strncmp(keyword, "cups-fold-", 10))
3496 keyword += 5;
3497 else if (strcmp(keyword, "fold") && strncmp(keyword, "fold-", 5))
3498 continue;
3499
3500 if (cupsArrayFind(names, (char *)keyword))
3501 continue; /* Already did this finishing template */
3502
3503 cupsArrayAdd(names, (char *)keyword);
3504
3505 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
3506 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
3507 else if (value >= IPP_FINISHINGS_FOLD_ACCORDION &&
3508 value <= IPP_FINISHINGS_FOLD_ENGINEERING_Z)
3509 ppd_keyword = fold_keywords[value - IPP_FINISHINGS_FOLD_ACCORDION];
3510 else if (value >= IPP_FINISHINGS_CUPS_FOLD_ACCORDION &&
3511 value <= IPP_FINISHINGS_CUPS_FOLD_Z)
3512 ppd_keyword = fold_keywords[value -
3513 IPP_FINISHINGS_CUPS_FOLD_ACCORDION];
3514 else
3515 ppd_keyword = NULL;
3516
3517 if (!ppd_keyword)
3518 continue;
3519
3520 snprintf(buf, sizeof(buf), "%d", value);
3521 human_readable = lookup_choice(buf, "finishings", opt_strings_catalog,
3522 printer_opt_strings_catalog);
3523 if (human_readable == NULL)
3524 for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0]));
3525 j ++)
3526 if (!strcmp(finishings[j][0], keyword)) {
3527 human_readable = (char *)_cupsLangString(lang, finishings[j][1]);
3528 break;
3529 }
3530 cupsFilePrintf(fp, "*FoldType %s%s%s: \"\"\n", ppd_keyword,
3531 (human_readable ? "/" : ""),
3532 (human_readable ? human_readable : ""));
3533 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n",
3534 value, keyword, ppd_keyword);
3535 }
3536
3537 cupsFilePuts(fp, "*CloseUI: *FoldType\n");
3538 }
3539
3540 /*
3541 * Punch
3542 */
3543
3544 for (i = 0; i < count; i ++) {
3545 value = ippGetInteger(attr, i);
3546 keyword = ippEnumString("finishings", value);
3547
3548 if (!strncmp(keyword, "cups-punch-", 11) ||
3549 !strncmp(keyword, "punch-", 6))
3550 break;
3551 }
3552
3553 if (i < count) {
3554 static const char * const punch_keywords[] =
3555 { /* PunchMedia keywords */
3556 "SinglePortrait",
3557 "SingleRevLandscape",
3558 "SingleLandscape",
3559 "SingleRevPortrait",
3560 "DualPortrait",
3561 "DualLandscape",
3562 "DualRevPortrait",
3563 "DualRevLandscape",
3564 "TriplePortrait",
3565 "TripleLandscape",
3566 "TripleRevPortrait",
3567 "TripleRevLandscape",
3568 "QuadPortrait",
3569 "QuadLandscape",
3570 "QuadRevPortrait",
3571 "QuadRevLandscape",
3572 "MultiplePortrait",
3573 "MultipleLandscape",
3574 "MultipleRevPortrait",
3575 "MultipleRevLandscape"
3576 };
3577
3578 cupsArrayAdd(fin_options, "*PunchMedia");
3579
3580 human_readable = lookup_choice("punch", "finishing-template",
3581 opt_strings_catalog,
3582 printer_opt_strings_catalog);
3583 cupsFilePrintf(fp, "*OpenUI *PunchMedia/%s: PickOne\n",
3584 (human_readable ? human_readable :
3585 _cupsLangString(lang, _("Punch"))));
3586 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
3587 cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
3588 cupsFilePrintf(fp, "*PunchMedia None/%s: \"\"\n",
3589 _cupsLangString(lang, _("None")));
3590
3591 for (i = 0; i < count; i ++) {
3592 value = ippGetInteger(attr, i);
3593 keyword = ippEnumString("finishings", value);
3594
3595 if (!strncmp(keyword, "cups-punch-", 11))
3596 keyword += 5;
3597 else if (strncmp(keyword, "punch-", 6))
3598 continue;
3599
3600 if (cupsArrayFind(names, (char *)keyword))
3601 continue; /* Already did this finishing template */
3602
3603 cupsArrayAdd(names, (char *)keyword);
3604
3605 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
3606 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
3607 else if (value >= IPP_FINISHINGS_PUNCH_TOP_LEFT &&
3608 value <= IPP_FINISHINGS_PUNCH_MULTIPLE_BOTTOM)
3609 ppd_keyword = punch_keywords[value - IPP_FINISHINGS_PUNCH_TOP_LEFT];
3610 else if (value >= IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT &&
3611 value <= IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM)
3612 ppd_keyword = punch_keywords[value -
3613 IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT];
3614 else
3615 ppd_keyword = NULL;
3616
3617 if (!ppd_keyword)
3618 continue;
3619
3620 snprintf(buf, sizeof(buf), "%d", value);
3621 human_readable = lookup_choice(buf, "finishings", opt_strings_catalog,
3622 printer_opt_strings_catalog);
3623 if (human_readable == NULL)
3624 for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0]));
3625 j ++)
3626 if (!strcmp(finishings[j][0], keyword)) {
3627 human_readable = (char *)_cupsLangString(lang, finishings[j][1]);
3628 break;
3629 }
3630 cupsFilePrintf(fp, "*PunchMedia %s%s%s: \"\"\n", ppd_keyword,
3631 (human_readable ? "/" : ""),
3632 (human_readable ? human_readable : ""));
3633 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n",
3634 value, keyword, ppd_keyword);
3635 }
3636
3637 cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
3638 }
3639
3640 /*
3641 * Booklet
3642 */
3643
3644 if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER)) {
3645 cupsArrayAdd(fin_options, "*Booklet");
3646
3647 human_readable = lookup_choice("booklet-maker", "finishing-template",
3648 opt_strings_catalog,
3649 printer_opt_strings_catalog);
3650 cupsFilePrintf(fp, "*OpenUI *Booklet/%s: Boolean\n",
3651 (human_readable ? human_readable :
3652 _cupsLangString(lang, _("Booklet"))));
3653 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
3654 cupsFilePuts(fp, "*DefaultBooklet: False\n");
3655 cupsFilePuts(fp, "*Booklet False: \"\"\n");
3656 cupsFilePuts(fp, "*Booklet True: \"\"\n");
3657 cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n",
3658 IPP_FINISHINGS_BOOKLET_MAKER);
3659 cupsFilePuts(fp, "*CloseUI: *Booklet\n");
3660 }
3661
3662 /*
3663 * CutMedia
3664 */
3665
3666 for (i = 0; i < count; i ++) {
3667 value = ippGetInteger(attr, i);
3668 keyword = ippEnumString("finishings", value);
3669
3670 if (!strcmp(keyword, "trim") || !strncmp(keyword, "trim-", 5))
3671 break;
3672 }
3673
3674 if (i < count) {
3675 static const char * const trim_keywords[] =
3676 { /* CutMedia keywords */
3677 "EndOfPage",
3678 "EndOfDoc",
3679 "EndOfSet",
3680 "EndOfJob"
3681 };
3682
3683 cupsArrayAdd(fin_options, "*CutMedia");
3684
3685 human_readable = lookup_choice("trim", "finishing-template",
3686 opt_strings_catalog,
3687 printer_opt_strings_catalog);
3688 cupsFilePrintf(fp, "*OpenUI *CutMedia/%s: PickOne\n",
3689 (human_readable ? human_readable :
3690 _cupsLangString(lang, _("Cut"))));
3691 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *CutMedia\n");
3692 cupsFilePuts(fp, "*DefaultCutMedia: None\n");
3693 cupsFilePrintf(fp, "*CutMedia None/%s: \"\"\n",
3694 _cupsLangString(lang, _("None")));
3695
3696 for (i = 0; i < count; i ++) {
3697 value = ippGetInteger(attr, i);
3698 keyword = ippEnumString("finishings", value);
3699
3700 if (strcmp(keyword, "trim") && strncmp(keyword, "trim-", 5))
3701 continue;
3702
3703 if (cupsArrayFind(names, (char *)keyword))
3704 continue; /* Already did this finishing template */
3705
3706 cupsArrayAdd(names, (char *)keyword);
3707
3708 if (value == IPP_FINISHINGS_TRIM)
3709 ppd_keyword = "Auto";
3710 else
3711 ppd_keyword = trim_keywords[value - IPP_FINISHINGS_TRIM_AFTER_PAGES];
3712
3713 snprintf(buf, sizeof(buf), "%d", value);
3714 human_readable = lookup_choice(buf, "finishings", opt_strings_catalog,
3715 printer_opt_strings_catalog);
3716 if (human_readable == NULL)
3717 for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0]));
3718 j ++)
3719 if (!strcmp(finishings[j][0], keyword)) {
3720 human_readable = (char *)_cupsLangString(lang, finishings[j][1]);
3721 break;
3722 }
3723 cupsFilePrintf(fp, "*CutMedia %s%s%s: \"\"\n", ppd_keyword,
3724 (human_readable ? "/" : ""),
3725 (human_readable ? human_readable : ""));
3726 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*CutMedia %s\"\n",
3727 value, keyword, ppd_keyword);
3728 }
3729
3730 cupsFilePuts(fp, "*CloseUI: *CutMedia\n");
3731 }
3732
3733 cupsArrayDelete(names);
3734 }
3735
3736 if ((attr = ippFindAttribute(response, "finishings-col-database",
3737 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
3738 ipp_t *finishing_col; /* Current finishing collection */
3739 ipp_attribute_t *finishing_attr; /* Current finishing member attribute */
3740 cups_array_t *templates; /* Finishing templates */
3741
3742 cupsFilePrintf(fp, "*OpenUI *cupsFinishingTemplate/%s: PickOne\n",
3743 _cupsLangString(lang, _("Finishing Preset")));
3744 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n");
3745 cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n");
3746 cupsFilePrintf(fp, "*cupsFinishingTemplate none/%s: \"\"\n",
3747 _cupsLangString(lang, _("None")));
3748
3749 templates = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3750 count = ippGetCount(attr);
3751
3752 for (i = 0; i < count; i ++) {
3753 finishing_col = ippGetCollection(attr, i);
3754 keyword = ippGetString(ippFindAttribute(finishing_col,
3755 "finishing-template",
3756 IPP_TAG_ZERO), 0, NULL);
3757
3758 if (!keyword || cupsArrayFind(templates, (void *)keyword))
3759 continue;
3760
3761 if (!strcmp(keyword, "none"))
3762 continue;
3763
3764 cupsArrayAdd(templates, (void *)keyword);
3765
3766 human_readable = lookup_choice((char *)keyword, "finishing-template",
3767 opt_strings_catalog,
3768 printer_opt_strings_catalog);
3769 if (human_readable == NULL)
3770 human_readable = (char *)keyword;
3771 cupsFilePrintf(fp, "*cupsFinishingTemplate %s/%s: \"\n", keyword,
3772 human_readable);
3773 for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr;
3774 finishing_attr = ippNextAttribute(finishing_col)) {
3775 if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION) {
3776 const char *name = ippGetName(finishing_attr);
3777 /* Member attribute name */
3778
3779 if (strcmp(name, "media-size"))
3780 cupsFilePrintf(fp, "%% %s\n", name);
3781 }
3782 }
3783 cupsFilePuts(fp, "\"\n");
3784 cupsFilePuts(fp, "*End\n");
3785 }
3786
3787 cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n");
3788
3789 if (cupsArrayCount(fin_options)) {
3790 const char *fin_option; /* Current finishing option */
3791
3792 cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate");
3793 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option;
3794 fin_option = (const char *)cupsArrayNext(fin_options))
3795 cupsFilePrintf(fp, " %s", fin_option);
3796 cupsFilePuts(fp, "\"\n");
3797
3798 cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate None");
3799 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option;
3800 fin_option = (const char *)cupsArrayNext(fin_options))
3801 cupsFilePrintf(fp, " %s None", fin_option);
3802 cupsFilePuts(fp, "\"\n");
3803 }
3804
3805 cupsArrayDelete(templates);
3806 }
3807
3808 cupsArrayDelete(fin_options);
3809
3810 /*
3811 * DefaultResolution...
3812 */
3813
3814 xres = common_def->x;
3815 yres = common_def->y;
3816 if (xres == yres)
3817 cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", xres);
3818 else
3819 cupsFilePrintf(fp, "*DefaultResolution: %dx%ddpi\n", xres, yres);
3820
3821 /*
3822 * cupsPrintQuality...
3823 */
3824
3825 if ((quality =
3826 ippFindAttribute(response, "print-quality-supported",
3827 IPP_TAG_ENUM)) != NULL) {
3828 human_readable = lookup_option("print-quality", opt_strings_catalog,
3829 printer_opt_strings_catalog);
3830 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
3831 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
3832 "*DefaultcupsPrintQuality: Normal\n",
3833 (human_readable ? human_readable :
3834 _cupsLangString(lang, _("Print Quality"))));
3835 if (ippContainsInteger(quality, IPP_QUALITY_DRAFT)) {
3836 human_readable = lookup_choice("3", "print-quality", opt_strings_catalog,
3837 printer_opt_strings_catalog);
3838 cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n",
3839 (human_readable ? human_readable :
3840 _cupsLangString(lang, _("Draft"))),
3841 min_res->x, min_res->y);
3842 }
3843 human_readable = lookup_choice("4", "print-quality", opt_strings_catalog,
3844 printer_opt_strings_catalog);
3845 cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n",
3846 (human_readable ? human_readable :
3847 _cupsLangString(lang, _("Normal"))),
3848 common_def->x, common_def->y);
3849 if (ippContainsInteger(quality, IPP_QUALITY_HIGH)) {
3850 human_readable = lookup_choice("5", "print-quality", opt_strings_catalog,
3851 printer_opt_strings_catalog);
3852 cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n",
3853 (human_readable ? human_readable :
3854 _cupsLangString(lang, _("High"))),
3855 max_res->x, max_res->y);
3856 }
3857 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
3858 }
3859
3860 /* Only add these options if jobs get sent to the printer as PDF,
3861 PWG Raster, or Apple Raster, as only then arbitrary IPP
3862 attributes get passed through from the filter command line
3863 to the printer by the "ipp" CUPS backend. */
3864 if (is_pdf || is_pwg || is_apple) {
3865 /*
3866 * Print Optimization ...
3867 */
3868
3869 if ((attr = ippFindAttribute(response, "print-content-optimize-default",
3870 IPP_TAG_ZERO)) != NULL)
3871 strlcpy(ppdname, ippGetString(attr, 0, NULL), sizeof(ppdname));
3872 else
3873 strlcpy(ppdname, "auto", sizeof(ppdname));
3874
3875 if ((attr = ippFindAttribute(response, "print-content-optimize-supported",
3876 IPP_TAG_ZERO)) != NULL &&
3877 (count = ippGetCount(attr)) > 1) {
3878 static const char * const content_optimize_types[][2] =
3879 { /* "print-content-optimize" strings */
3880 { "auto", _("Automatic") },
3881 { "graphic", _("Graphics") },
3882 { "graphics", _("Graphics") },
3883 { "photo", _("Photo") },
3884 { "text", _("Text") },
3885 { "text-and-graphic", _("Text And Graphics") },
3886 { "text-and-graphics", _("Text And Graphics") }
3887 };
3888
3889 human_readable = lookup_option("print-content-optimize",
3890 opt_strings_catalog,
3891 printer_opt_strings_catalog);
3892 cupsFilePrintf(fp, "*OpenUI *print-content-optimize/%s: PickOne\n"
3893 "*OrderDependency: 10 AnySetup *print-content-optimize\n"
3894 "*Defaultprint-content-optimize: %s\n",
3895 (human_readable ? human_readable : "Print Optimization"),
3896 ppdname);
3897 for (i = 0; i < count; i ++) {
3898 keyword = ippGetString(attr, i, NULL);
3899
3900 human_readable = lookup_choice((char *)keyword,
3901 "print-content-optimize",
3902 opt_strings_catalog,
3903 printer_opt_strings_catalog);
3904 if (human_readable == NULL)
3905 for (j = 0;
3906 j < (int)(sizeof(content_optimize_types) /
3907 sizeof(content_optimize_types[0]));
3908 j ++)
3909 if (!strcmp(content_optimize_types[j][0], keyword)) {
3910 human_readable =
3911 (char *)_cupsLangString(lang,
3912 content_optimize_types[j][1]);
3913 break;
3914 }
3915 cupsFilePrintf(fp, "*print-content-optimize %s%s%s: \"\"\n",
3916 keyword,
3917 (human_readable ? "/" : ""),
3918 (human_readable ? human_readable : ""));
3919 }
3920 cupsFilePuts(fp, "*CloseUI: *print-content-optimize\n");
3921 }
3922
3923 /*
3924 * Print Rendering Intent ...
3925 */
3926
3927 if ((attr = ippFindAttribute(response, "print-rendering-intent-default",
3928 IPP_TAG_ZERO)) != NULL)
3929 strlcpy(ppdname, ippGetString(attr, 0, NULL), sizeof(ppdname));
3930 else
3931 strlcpy(ppdname, "auto", sizeof(ppdname));
3932
3933 if ((attr = ippFindAttribute(response, "print-rendering-intent-supported",
3934 IPP_TAG_ZERO)) != NULL &&
3935 (count = ippGetCount(attr)) > 1) {
3936 static const char * const rendering_intents[][2] =
3937 { /* "print-rendering-intent" strings */
3938 { "auto", _("Automatic") },
3939 { "absolute", _("Absolute") },
3940 { "perceptual", _("Perceptual") },
3941 { "relative", _("Relative") },
3942 { "relative-bpc", _("Relative w/Black Point Compensation") },
3943 { "saturation", _("Saturation") }
3944 };
3945
3946 human_readable = lookup_option("print-rendering-intent",
3947 opt_strings_catalog,
3948 printer_opt_strings_catalog);
3949 cupsFilePrintf(fp, "*OpenUI *print-rendering-intent/%s: PickOne\n"
3950 "*OrderDependency: 10 AnySetup *print-rendering-intent\n"
3951 "*Defaultprint-rendering-intent: %s\n",
3952 (human_readable ? human_readable :
3953 "Print Rendering Intent"),
3954 ppdname);
3955 for (i = 0; i < count; i ++) {
3956 keyword = ippGetString(attr, i, NULL);
3957
3958 human_readable = lookup_choice((char *)keyword,
3959 "print-rendering-intent",
3960 opt_strings_catalog,
3961 printer_opt_strings_catalog);
3962 if (human_readable == NULL)
3963 for (j = 0;
3964 j < (int)(sizeof(rendering_intents) /
3965 sizeof(rendering_intents[0]));
3966 j ++)
3967 if (!strcmp(rendering_intents[j][0], keyword)) {
3968 human_readable =
3969 (char *)_cupsLangString(lang,
3970 rendering_intents[j][1]);
3971 break;
3972 }
3973 cupsFilePrintf(fp, "*print-rendering-intent %s%s%s: \"\"\n",
3974 keyword,
3975 (human_readable ? "/" : ""),
3976 (human_readable ? human_readable : ""));
3977 }
3978 cupsFilePuts(fp, "*CloseUI: *print-rendering-intent\n");
3979 }
3980
3981 /*
3982 * Print Scaling ...
3983 */
3984
3985 if ((attr = ippFindAttribute(response, "print-scaling-default",
3986 IPP_TAG_ZERO)) != NULL)
3987 strlcpy(ppdname, ippGetString(attr, 0, NULL), sizeof(ppdname));
3988 else
3989 strlcpy(ppdname, "auto", sizeof(ppdname));
3990
3991 if ((attr = ippFindAttribute(response, "print-scaling-supported",
3992 IPP_TAG_ZERO)) != NULL &&
3993 (count = ippGetCount(attr)) > 1) {
3994 static const char * const scaling_types[][2] =
3995 { /* "print-scaling" strings */
3996 { "auto", _("Automatic") },
3997 { "auto-fit", _("Auto Fit") },
3998 { "fill", _("Fill") },
3999 { "fit", _("Fit") },
4000 { "none", _("None") }
4001 };
4002
4003 human_readable = lookup_option("print-scaling", opt_strings_catalog,
4004 printer_opt_strings_catalog);
4005 cupsFilePrintf(fp, "*OpenUI *print-scaling/%s: PickOne\n"
4006 "*OrderDependency: 10 AnySetup *print-scaling\n"
4007 "*Defaultprint-scaling: %s\n",
4008 (human_readable ? human_readable : "Print Scaling"),
4009 ppdname);
4010 for (i = 0; i < count; i ++) {
4011 keyword = ippGetString(attr, i, NULL);
4012
4013 human_readable = lookup_choice((char *)keyword, "print-scaling",
4014 opt_strings_catalog,
4015 printer_opt_strings_catalog);
4016 if (human_readable == NULL)
4017 for (j = 0;
4018 j < (int)(sizeof(scaling_types) /
4019 sizeof(scaling_types[0]));
4020 j ++)
4021 if (!strcmp(scaling_types[j][0], keyword)) {
4022 human_readable =
4023 (char *)_cupsLangString(lang, scaling_types[j][1]);
4024 break;
4025 }
4026 cupsFilePrintf(fp, "*print-scaling %s%s%s: \"\"\n",
4027 keyword,
4028 (human_readable ? "/" : ""),
4029 (human_readable ? human_readable : ""));
4030 }
4031 cupsFilePuts(fp, "*CloseUI: *print-scaling\n");
4032 }
4033 }
4034
4035 /*
4036 * Phone Options for Fax..
4037 */
4038
4039 if (is_fax) {
4040 human_readable = lookup_option("Phone", opt_strings_catalog,
4041 printer_opt_strings_catalog);
4042
4043 cupsFilePrintf(fp, "*OpenUI *phone/%s: PickOne\n"
4044 "*OrderDependency: 10 AnySetup *phone\n"
4045 "*Defaultphone: None\n"
4046 "*phone None: \"\"\n"
4047 "*CloseUI: *phone\n",
4048 (human_readable ? human_readable : "Phone Number"));
4049 cupsFilePrintf(fp,"*Customphone True: \"\"\n"
4050 "*ParamCustomphone Text: 1 string 0 64\n");
4051
4052 human_readable = lookup_option("faxPrefix", opt_strings_catalog,
4053 printer_opt_strings_catalog);
4054
4055 cupsFilePrintf(fp, "*OpenUI *faxPrefix/%s: PickOne\n"
4056 "*OrderDependency: 10 AnySetup *faxPrefix\n"
4057 "*DefaultfaxPrefix: None\n"
4058 "*faxPrefix None: \"\"\n"
4059 "*CloseUI: *faxPrefix\n",
4060 (human_readable ? human_readable : "Pre-Dial Number"));
4061 cupsFilePrintf(fp,"*CustomfaxPrefix True: \"\"\n"
4062 "*ParamCustomfaxPrefix Text: 1 string 0 64\n");
4063 }
4064
4065 /*
4066 * Presets...
4067 */
4068
4069 if ((attr = ippFindAttribute(response, "job-presets-supported",
4070 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
4071 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
4072 ipp_t *preset = ippGetCollection(attr, i); /* Preset collection */
4073 const char *preset_name = /* Preset name */
4074 ippGetString(ippFindAttribute(preset,
4075 "preset-name", IPP_TAG_ZERO), 0, NULL),
4076 *localized_name; /* Localized preset name */
4077 ipp_attribute_t *member; /* Member attribute in preset */
4078 const char *member_name; /* Member attribute name */
4079 char member_value[256]; /* Member attribute value */
4080
4081 if (!preset || !preset_name)
4082 continue;
4083
4084 if ((localized_name = lookup_option((char *)preset_name,
4085 opt_strings_catalog,
4086 printer_opt_strings_catalog)) == NULL)
4087 cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", preset_name);
4088 else
4089 cupsFilePrintf(fp, "*APPrinterPreset %s/%s: \"\n", preset_name,
4090 localized_name);
4091
4092 for (member = ippFirstAttribute(preset); member;
4093 member = ippNextAttribute(preset)) {
4094 member_name = ippGetName(member);
4095
4096 if (!member_name || !strcmp(member_name, "preset-name"))
4097 continue;
4098
4099 if (!strcmp(member_name, "finishings")) {
4100 for (i = 0, count = ippGetCount(member); i < count; i ++) {
4101 const char *option = NULL; /* PPD option name */
4102
4103 keyword = ippEnumString("finishings", ippGetInteger(member, i));
4104
4105 if (!strcmp(keyword, "booklet-maker")) {
4106 option = "Booklet";
4107 keyword = "True";
4108 } else if (!strncmp(keyword, "fold-", 5))
4109 option = "FoldType";
4110 else if (!strncmp(keyword, "punch-", 6))
4111 option = "PunchMedia";
4112 else if (!strncmp(keyword, "bind-", 5) ||
4113 !strncmp(keyword, "edge-stitch-", 12) ||
4114 !strcmp(keyword, "saddle-stitch") ||
4115 !strncmp(keyword, "staple-", 7))
4116 option = "StapleLocation";
4117
4118 if (option && keyword)
4119 cupsFilePrintf(fp, "*%s %s\n", option, keyword);
4120 }
4121 } else if (!strcmp(member_name, "finishings-col")) {
4122 ipp_t *fin_col; /* finishings-col value */
4123
4124 for (i = 0, count = ippGetCount(member); i < count; i ++) {
4125 fin_col = ippGetCollection(member, i);
4126
4127 if ((keyword =
4128 ippGetString(ippFindAttribute(fin_col,
4129 "finishing-template",
4130 IPP_TAG_ZERO), 0, NULL)) != NULL)
4131 cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", keyword);
4132 }
4133 } else if (!strcmp(member_name, "media")) {
4134 /*
4135 * Map media to PageSize...
4136 */
4137
4138 if ((pwg = pwgMediaForPWG(ippGetString(member, 0, NULL))) != NULL &&
4139 pwg->ppd)
4140 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4141 } else if (!strcmp(member_name, "media-col")) {
4142 media_col = ippGetCollection(member, 0);
4143
4144 if ((media_size =
4145 ippGetCollection(ippFindAttribute(media_col,
4146 "media-size",
4147 IPP_TAG_BEGIN_COLLECTION),
4148 0)) != NULL) {
4149 x_dim = ippFindAttribute(media_size, "x-dimension",
4150 IPP_TAG_INTEGER);
4151 y_dim = ippFindAttribute(media_size, "y-dimension",
4152 IPP_TAG_INTEGER);
4153 if ((pwg = pwgMediaForSize(ippGetInteger(x_dim, 0),
4154 ippGetInteger(y_dim, 0))) != NULL &&
4155 pwg->ppd)
4156 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4157 }
4158
4159 if ((keyword = ippGetString(ippFindAttribute(media_col,
4160 "media-source",
4161 IPP_TAG_ZERO), 0,
4162 NULL)) != NULL) {
4163 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4164 cupsFilePrintf(fp, "*InputSlot %s\n", keyword);
4165 }
4166
4167 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-type",
4168 IPP_TAG_ZERO), 0,
4169 NULL)) != NULL) {
4170 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4171 cupsFilePrintf(fp, "*MediaType %s\n", keyword);
4172 }
4173 } else if (!strcmp(member_name, "print-quality")) {
4174 /*
4175 * Map print-quality to cupsPrintQuality...
4176 */
4177
4178 int qval = ippGetInteger(member, 0);
4179 /* print-quality value */
4180 static const char * const qualities[] = { "Draft", "Normal", "High" };
4181 /* cupsPrintQuality values */
4182
4183 if (qval >= IPP_QUALITY_DRAFT && qval <= IPP_QUALITY_HIGH)
4184 cupsFilePrintf(fp, "*cupsPrintQuality %s\n",
4185 qualities[qval - IPP_QUALITY_DRAFT]);
4186 } else if (!strcmp(member_name, "output-bin")) {
4187 pwg_ppdize_name(ippGetString(member, 0, NULL), ppdname,
4188 sizeof(ppdname));
4189 cupsFilePrintf(fp, "*OutputBin %s\n", ppdname);
4190 } else if (!strcmp(member_name, "sides")) {
4191 keyword = ippGetString(member, 0, NULL);
4192 if (keyword && !strcmp(keyword, "one-sided"))
4193 cupsFilePuts(fp, "*Duplex None\n");
4194 else if (keyword && !strcmp(keyword, "two-sided-long-edge"))
4195 cupsFilePuts(fp, "*Duplex DuplexNoTumble\n");
4196 else if (keyword && !strcmp(keyword, "two-sided-short-edge"))
4197 cupsFilePuts(fp, "*Duplex DuplexTumble\n");
4198 } else {
4199 /*
4200 * Add attribute name and value as-is...
4201 */
4202
4203 ippAttributeString(member, member_value, sizeof(member_value));
4204 cupsFilePrintf(fp, "*%s %s\n", member_name, member_value);
4205 }
4206 }
4207
4208 cupsFilePuts(fp, "\"\n*End\n");
4209 }
4210 }
4211
4212 /*
4213 * constraints
4214 */
4215 if (conflicts != NULL) {
4216 char* constraint;
4217 for (constraint = (char *)cupsArrayFirst(conflicts); constraint;
4218 constraint = (char *)cupsArrayNext(conflicts)) {
4219 cupsFilePrintf(fp, "%s", constraint);
4220 }
4221 }
4222
4223 /*
4224 * Close up and return...
4225 */
4226
4227 free(common_def);
4228 free(min_res);
4229 free(max_res);
4230
4231 snprintf(ppdgenerator_msg, sizeof(ppdgenerator_msg),
4232 "%s %sPPD generated.",
4233 (is_apple ? "Apple Raster" :
4234 (is_pwg ? "PWG Raster" :
4235 (is_pdf ? "PDF" :
4236 (is_pclm ? "PCLm" :
4237 "Legacy IPP printer")))),
4238 (is_fax ? "Fax " : ""));
4239
4240 cupsFileClose(fp);
4241 if (printer_opt_strings_catalog)
4242 cupsArrayDelete(printer_opt_strings_catalog);
4243
4244 return (buffer);
4245
4246 /*
4247 * If we get here then there was a problem creating the PPD...
4248 */
4249
4250 bad_ppd:
4251
4252 if (common_res) cupsArrayDelete(common_res);
4253 if (common_def) free(common_def);
4254 if (min_res) free(min_res);
4255 if (max_res) free(max_res);
4256
4257 cupsFileClose(fp);
4258 if (printer_opt_strings_catalog)
4259 cupsArrayDelete(printer_opt_strings_catalog);
4260 unlink(buffer);
4261 *buffer = '\0';
4262
4263 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
4264 _("Printer does not support required IPP attributes or document formats."),
4265 1);
4266
4267 return (NULL);
4268 }
4269
4270
4271 /*
4272 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
4273 * media-source.
4274 */
4275
4276 const char * /* O - InputSlot name */
_pwgInputSlotForSource(const char * media_source,char * name,size_t namesize)4277 _pwgInputSlotForSource(
4278 const char *media_source, /* I - PWG media-source */
4279 char *name, /* I - Name buffer */
4280 size_t namesize) /* I - Size of name buffer */
4281 {
4282 /*
4283 * Range check input...
4284 */
4285
4286 if (!media_source || !name || namesize < PPD_MAX_NAME)
4287 return (NULL);
4288
4289 if (_cups_strcasecmp(media_source, "main"))
4290 strlcpy(name, "Cassette", namesize);
4291 else if (_cups_strcasecmp(media_source, "alternate"))
4292 strlcpy(name, "Multipurpose", namesize);
4293 else if (_cups_strcasecmp(media_source, "large-capacity"))
4294 strlcpy(name, "LargeCapacity", namesize);
4295 else if (_cups_strcasecmp(media_source, "bottom"))
4296 strlcpy(name, "Lower", namesize);
4297 else if (_cups_strcasecmp(media_source, "middle"))
4298 strlcpy(name, "Middle", namesize);
4299 else if (_cups_strcasecmp(media_source, "top"))
4300 strlcpy(name, "Upper", namesize);
4301 else if (_cups_strcasecmp(media_source, "rear"))
4302 strlcpy(name, "Rear", namesize);
4303 else if (_cups_strcasecmp(media_source, "side"))
4304 strlcpy(name, "Side", namesize);
4305 else if (_cups_strcasecmp(media_source, "envelope"))
4306 strlcpy(name, "Envelope", namesize);
4307 else if (_cups_strcasecmp(media_source, "main-roll"))
4308 strlcpy(name, "Roll", namesize);
4309 else if (_cups_strcasecmp(media_source, "alternate-roll"))
4310 strlcpy(name, "Roll2", namesize);
4311 else
4312 pwg_ppdize_name(media_source, name, namesize);
4313
4314 return (name);
4315 }
4316
4317
4318 /*
4319 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
4320 * media-type.
4321 */
4322
4323 const char * /* O - MediaType name */
_pwgMediaTypeForType(const char * media_type,char * name,size_t namesize)4324 _pwgMediaTypeForType(
4325 const char *media_type, /* I - PWG media-type */
4326 char *name, /* I - Name buffer */
4327 size_t namesize) /* I - Size of name buffer */
4328 {
4329 /*
4330 * Range check input...
4331 */
4332
4333 if (!media_type || !name || namesize < PPD_MAX_NAME)
4334 return (NULL);
4335
4336 if (_cups_strcasecmp(media_type, "auto"))
4337 strlcpy(name, "Auto", namesize);
4338 else if (_cups_strcasecmp(media_type, "cardstock"))
4339 strlcpy(name, "Cardstock", namesize);
4340 else if (_cups_strcasecmp(media_type, "envelope"))
4341 strlcpy(name, "Envelope", namesize);
4342 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
4343 strlcpy(name, "Glossy", namesize);
4344 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
4345 strlcpy(name, "HighGloss", namesize);
4346 else if (_cups_strcasecmp(media_type, "photographic-matte"))
4347 strlcpy(name, "Matte", namesize);
4348 else if (_cups_strcasecmp(media_type, "stationery"))
4349 strlcpy(name, "Plain", namesize);
4350 else if (_cups_strcasecmp(media_type, "stationery-coated"))
4351 strlcpy(name, "Coated", namesize);
4352 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
4353 strlcpy(name, "Inkjet", namesize);
4354 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
4355 strlcpy(name, "Letterhead", namesize);
4356 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
4357 strlcpy(name, "Preprinted", namesize);
4358 else if (_cups_strcasecmp(media_type, "transparency"))
4359 strlcpy(name, "Transparency", namesize);
4360 else
4361 pwg_ppdize_name(media_type, name, namesize);
4362
4363 return (name);
4364 }
4365
4366
4367 /*
4368 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
4369 */
4370
4371 const char * /* O - PageSize name */
_pwgPageSizeForMedia(pwg_media_t * media,char * name,size_t namesize)4372 _pwgPageSizeForMedia(
4373 pwg_media_t *media, /* I - Media */
4374 char *name, /* I - PageSize name buffer */
4375 size_t namesize) /* I - Size of name buffer */
4376 {
4377 const char *sizeptr, /* Pointer to size in PWG name */
4378 *dimptr; /* Pointer to dimensions in PWG name */
4379
4380
4381 /*
4382 * Range check input...
4383 */
4384
4385 if (!media || !name || namesize < PPD_MAX_NAME)
4386 return (NULL);
4387
4388 /*
4389 * Copy or generate a PageSize name...
4390 */
4391
4392 if (media->ppd) {
4393 /*
4394 * Use a standard Adobe name...
4395 */
4396
4397 strlcpy(name, media->ppd, namesize);
4398 }
4399 else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
4400 (sizeptr = strchr(media->pwg, '_')) == NULL ||
4401 (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
4402 (size_t)(dimptr - sizeptr) > namesize) {
4403 /*
4404 * Use a name of the form "wNNNhNNN"...
4405 */
4406
4407 snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
4408 (int)PWG_TO_POINTS(media->length));
4409 } else {
4410 /*
4411 * Copy the size name from class_sizename_dimensions...
4412 */
4413
4414 memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
4415 name[dimptr - sizeptr - 1] = '\0';
4416 }
4417
4418 return (name);
4419 }
4420
4421
4422 /*
4423 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
4424 */
4425
4426 static void
pwg_ppdize_name(const char * ipp,char * name,size_t namesize)4427 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
4428 char *name, /* I - Name buffer */
4429 size_t namesize) /* I - Size of name buffer */
4430 {
4431 char *ptr, /* Pointer into name buffer */
4432 *end; /* End of name buffer */
4433
4434
4435 *name = (char)toupper(*ipp++);
4436
4437 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;) {
4438 if (*ipp == '-') {
4439 ipp ++;
4440 if (_cups_isalpha(*ipp))
4441 *ptr++ = (char)toupper(*ipp++ & 255);
4442 } else
4443 *ptr++ = *ipp++;
4444 }
4445
4446 *ptr = '\0';
4447 }
4448
4449
4450
4451 /*
4452 * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values.
4453 */
4454
4455 static void
pwg_ppdize_resolution(ipp_attribute_t * attr,int element,int * xres,int * yres,char * name,size_t namesize)4456 pwg_ppdize_resolution(
4457 ipp_attribute_t *attr, /* I - Attribute to convert */
4458 int element, /* I - Element to convert */
4459 int *xres, /* O - X resolution in DPI */
4460 int *yres, /* O - Y resolution in DPI */
4461 char *name, /* I - Name buffer */
4462 size_t namesize) /* I - Size of name buffer */
4463 {
4464 ipp_res_t units; /* Units for resolution */
4465
4466 *xres = ippGetResolution(attr, element, yres, &units);
4467
4468 if (units == IPP_RES_PER_CM) {
4469 *xres = (int)(*xres * 2.54);
4470 *yres = (int)(*yres * 2.54);
4471 }
4472
4473 if (name && namesize > 4) {
4474 if (*xres == *yres)
4475 snprintf(name, namesize, "%ddpi", *xres);
4476 else
4477 snprintf(name, namesize, "%dx%ddpi", *xres, *yres);
4478 }
4479 }
4480 #endif /* HAVE_CUPS_1_6 */
4481