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, j, 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 // 2 attributes which hold a list of media-col structures. Paesing is the
1189 // same for them, so we use the same code. "media-col-database" is parsed
1190 // first as it is more complete an accurate, "media-col-ready" is more
1191 // a fallback if there is no "media-col-database".
1192 const char * const col_attrs[] =
1193 {
1194 "media-col-database",
1195 "media-col-ready",
1196 };
1197
1198
1199 if ((attr = ippFindAttribute(response, "media-bottom-margin-supported",
1200 IPP_TAG_INTEGER)) != NULL) {
1201 for (i = 1, *bottom = ippGetInteger(attr, 0), count = ippGetCount(attr);
1202 i < count; i ++)
1203 if (ippGetInteger(attr, i) < *bottom)
1204 *bottom = ippGetInteger(attr, i);
1205 } else
1206 *bottom = 1270;
1207
1208 if ((attr = ippFindAttribute(response, "media-left-margin-supported",
1209 IPP_TAG_INTEGER)) != NULL) {
1210 for (i = 1, *left = ippGetInteger(attr, 0), count = ippGetCount(attr);
1211 i < count; i ++)
1212 if (ippGetInteger(attr, i) < *left)
1213 *left = ippGetInteger(attr, i);
1214 } else
1215 *left = 635;
1216
1217 if ((attr = ippFindAttribute(response, "media-right-margin-supported",
1218 IPP_TAG_INTEGER)) != NULL) {
1219 for (i = 1, *right = ippGetInteger(attr, 0), count = ippGetCount(attr);
1220 i < count; i ++)
1221 if (ippGetInteger(attr, i) < *right)
1222 *right = ippGetInteger(attr, i);
1223 } else
1224 *right = 635;
1225
1226 if ((attr = ippFindAttribute(response, "media-top-margin-supported",
1227 IPP_TAG_INTEGER)) != NULL) {
1228 for (i = 1, *top = ippGetInteger(attr, 0), count = ippGetCount(attr);
1229 i < count; i ++)
1230 if (ippGetInteger(attr, i) < *top)
1231 *top = ippGetInteger(attr, i);
1232 } else
1233 *top = 1270;
1234
1235 if ((*defattr = ippFindAttribute(response, "media-col-default",
1236 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
1237 if ((attr = ippFindAttribute(ippGetCollection(*defattr, 0), "media-size",
1238 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
1239 media_size = ippGetCollection(attr, 0);
1240 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
1241 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
1242
1243 if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0),
1244 "media-bottom-margin", IPP_TAG_INTEGER))
1245 != NULL)
1246 bottom_def = ippGetInteger(margin, 0);
1247 else
1248 bottom_def = *bottom;
1249
1250 if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0),
1251 "media-left-margin", IPP_TAG_INTEGER))
1252 != NULL)
1253 left_def = ippGetInteger(margin, 0);
1254 else
1255 left_def = *left;
1256
1257 if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0),
1258 "media-right-margin", IPP_TAG_INTEGER))
1259 != NULL)
1260 right_def = ippGetInteger(margin, 0);
1261 else
1262 right_def = *right;
1263
1264 if ((margin = ippFindAttribute(ippGetCollection(*defattr, 0),
1265 "media-top-margin", IPP_TAG_INTEGER))
1266 != NULL)
1267 top_def = ippGetInteger(margin, 0);
1268 else
1269 top_def = *top;
1270
1271 if (x_dim && y_dim &&
1272 (pwg = pwgMediaForSize(ippGetInteger(x_dim, 0),
1273 ippGetInteger(y_dim, 0))) != NULL) {
1274 psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg);
1275 if (bottom_def == 0 && left_def == 0 && right_def == 0 && top_def == 0)
1276 snprintf(ppdname, PPD_MAX_NAME, "%s.Borderless", psname);
1277 else
1278 strlcpy(ppdname, psname, PPD_MAX_NAME);
1279 } else
1280 strlcpy(ppdname, "Unknown", PPD_MAX_NAME);
1281 } else
1282 strlcpy(ppdname, "Unknown", PPD_MAX_NAME);
1283 } else if ((pwg =
1284 pwgMediaForPWG(ippGetString(ippFindAttribute(response,
1285 "media-default",
1286 IPP_TAG_ZERO), 0,
1287 NULL))) != NULL) {
1288 psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg);
1289 strlcpy(ppdname, psname, PPD_MAX_NAME);
1290 } else
1291 strlcpy(ppdname, "Unknown", PPD_MAX_NAME);
1292
1293 sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes, NULL, NULL, 0,
1294 (cups_acopy_func_t)pwg_copy_size,
1295 (cups_afree_func_t)free);
1296
1297 // Go through all attributes which are lists of media-col structures
1298 for (j = 0; j < sizeof(col_attrs) / sizeof(col_attrs[0]); j ++)
1299 if ((attr = ippFindAttribute(response, col_attrs[j],
1300 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
1301 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1302 cups_size_t temp; /* Current size */
1303
1304 media_col = ippGetCollection(attr, i);
1305 media_size =
1306 ippGetCollection(ippFindAttribute(media_col, "media-size",
1307 IPP_TAG_BEGIN_COLLECTION), 0);
1308 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
1309 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
1310 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0),
1311 ippGetInteger(y_dim, 0));
1312
1313 if (pwg) {
1314 temp.width = pwg->width;
1315 temp.length = pwg->length;
1316
1317 if ((margin = ippFindAttribute(media_col, "media-bottom-margin",
1318 IPP_TAG_INTEGER)) != NULL)
1319 temp.bottom = ippGetInteger(margin, 0);
1320 else
1321 temp.bottom = *bottom;
1322
1323 if ((margin = ippFindAttribute(media_col, "media-left-margin",
1324 IPP_TAG_INTEGER)) != NULL)
1325 temp.left = ippGetInteger(margin, 0);
1326 else
1327 temp.left = *left;
1328
1329 if ((margin = ippFindAttribute(media_col, "media-right-margin",
1330 IPP_TAG_INTEGER)) != NULL)
1331 temp.right = ippGetInteger(margin, 0);
1332 else
1333 temp.right = *right;
1334
1335 if ((margin = ippFindAttribute(media_col, "media-top-margin",
1336 IPP_TAG_INTEGER)) != NULL)
1337 temp.top = ippGetInteger(margin, 0);
1338 else
1339 temp.top = *top;
1340
1341 psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg);
1342 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 &&
1343 temp.top == 0)
1344 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname);
1345 else
1346 strlcpy(temp.media, psname, sizeof(temp.media));
1347
1348 if (!cupsArrayFind(sizes, &temp))
1349 cupsArrayAdd(sizes, &temp);
1350 } else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE ||
1351 ippGetValueTag(y_dim) == IPP_TAG_RANGE) {
1352 /*
1353 * Custom size - record the min/max values...
1354 */
1355
1356 int lower, upper; /* Range values */
1357
1358 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
1359 lower = ippGetRange(x_dim, 0, &upper);
1360 else
1361 lower = upper = ippGetInteger(x_dim, 0);
1362
1363 if (lower < *min_width)
1364 *min_width = lower;
1365 if (upper > *max_width)
1366 *max_width = upper;
1367
1368 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
1369 lower = ippGetRange(y_dim, 0, &upper);
1370 else
1371 lower = upper = ippGetInteger(y_dim, 0);
1372
1373 if (lower < *min_length)
1374 *min_length = lower;
1375 if (upper > *max_length)
1376 *max_length = upper;
1377 }
1378 }
1379 }
1380 if ((attr = ippFindAttribute(response, "media-size-supported",
1381 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
1382 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1383 cups_size_t temp; /* Current size */
1384
1385 media_size = ippGetCollection(attr, i);
1386 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
1387 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
1388 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0),
1389 ippGetInteger(y_dim, 0));
1390
1391 if (pwg) {
1392 temp.width = pwg->width;
1393 temp.length = pwg->length;
1394 temp.bottom = *bottom;
1395 temp.left = *left;
1396 temp.right = *right;
1397 temp.top = *top;
1398
1399 psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg);
1400 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 &&
1401 temp.top == 0)
1402 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname);
1403 else
1404 strlcpy(temp.media, psname, sizeof(temp.media));
1405
1406 if (!cupsArrayFind(sizes, &temp))
1407 cupsArrayAdd(sizes, &temp);
1408 } else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE ||
1409 ippGetValueTag(y_dim) == IPP_TAG_RANGE) {
1410 /*
1411 * Custom size - record the min/max values...
1412 */
1413
1414 int lower, upper; /* Range values */
1415
1416 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
1417 lower = ippGetRange(x_dim, 0, &upper);
1418 else
1419 lower = upper = ippGetInteger(x_dim, 0);
1420
1421 if (lower < *min_width)
1422 *min_width = lower;
1423 if (upper > *max_width)
1424 *max_width = upper;
1425
1426 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
1427 lower = ippGetRange(y_dim, 0, &upper);
1428 else
1429 lower = upper = ippGetInteger(y_dim, 0);
1430
1431 if (lower < *min_length)
1432 *min_length = lower;
1433 if (upper > *max_length)
1434 *max_length = upper;
1435 }
1436 }
1437 }
1438 if ((attr = ippFindAttribute(response, "media-supported", IPP_TAG_ZERO))
1439 != NULL) {
1440 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1441 const char *pwg_size = ippGetString(attr, i, NULL);
1442 /* PWG size name */
1443 cups_size_t temp, *temp2; /* Current size, found size */
1444
1445 if ((pwg = pwgMediaForPWG(pwg_size)) != NULL) {
1446 if (strstr(pwg_size, "_max_") || strstr(pwg_size, "_max.")) {
1447 if (pwg->width > *max_width)
1448 *max_width = pwg->width;
1449 if (pwg->length > *max_length)
1450 *max_length = pwg->length;
1451 } else if (strstr(pwg_size, "_min_") || strstr(pwg_size, "_min.")) {
1452 if (pwg->width < *min_width)
1453 *min_width = pwg->width;
1454 if (pwg->length < *min_length)
1455 *min_length = pwg->length;
1456 } else {
1457 temp.width = pwg->width;
1458 temp.length = pwg->length;
1459 temp.bottom = *bottom;
1460 temp.left = *left;
1461 temp.right = *right;
1462 temp.top = *top;
1463
1464 psname = (pwg->ppd != NULL ? pwg->ppd : pwg->pwg);
1465 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 &&
1466 temp.top == 0)
1467 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname);
1468 else
1469 strlcpy(temp.media, psname, sizeof(temp.media));
1470
1471 /* Add the printer's original IPP name to an already found size */
1472 if ((temp2 = cupsArrayFind(sizes, &temp)) != NULL) {
1473 snprintf(temp2->media + strlen(temp2->media),
1474 sizeof(temp2->media) - strlen(temp2->media),
1475 " %s", pwg_size);
1476 /* Check if we have also a borderless version of the size and add
1477 the original IPP name also there */
1478 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", psname);
1479 if ((temp2 = cupsArrayFind(sizes, &temp)) != NULL)
1480 snprintf(temp2->media + strlen(temp2->media),
1481 sizeof(temp2->media) - strlen(temp2->media),
1482 " %s", pwg_size);
1483 } else
1484 cupsArrayAdd(sizes, &temp);
1485 }
1486 }
1487 }
1488 }
1489 return sizes;
1490 }
1491
is_colordevice(const char * keyword,ipp_attribute_t * attr)1492 int is_colordevice(const char *keyword,ipp_attribute_t *attr)
1493 {
1494 if (!strcasecmp(keyword, "sgray_16") || !strncmp(keyword, "W8-16", 5) ||
1495 !strncmp(keyword, "W16", 3))
1496 return 1;
1497 else if (!strcasecmp(keyword, "srgb_8") || !strncmp(keyword, "SRGB24", 6) ||
1498 !strcmp(keyword, "color"))
1499 return 1;
1500 else if ((!strcasecmp(keyword, "srgb_16") ||
1501 !strncmp(keyword, "SRGB48", 6)) &&
1502 !ippContainsString(attr, "srgb_8"))
1503 return 1;
1504 else if (!strcasecmp(keyword, "adobe-rgb_16") ||
1505 !strncmp(keyword, "ADOBERGB48", 10) ||
1506 !strncmp(keyword, "ADOBERGB24-48", 13))
1507 return 1;
1508 else if ((!strcasecmp(keyword, "adobe-rgb_8") ||
1509 !strcmp(keyword, "ADOBERGB24")) &&
1510 !ippContainsString(attr, "adobe-rgb_16"))
1511 return 1;
1512 else if ((!strcasecmp(keyword, "cmyk_8") &&
1513 !ippContainsString(attr, "cmyk_16")) ||
1514 !strcmp(keyword, "DEVCMYK32"))
1515 return 1;
1516 else if (!strcasecmp(keyword, "cmyk_16") ||
1517 !strcmp(keyword, "DEVCMYK32-64") ||
1518 !strcmp(keyword, "DEVCMYK64"))
1519 return 1;
1520 else if ((!strcasecmp(keyword, "rgb_8") &&
1521 !ippContainsString(attr, "rgb_16"))
1522 || !strcmp(keyword, "DEVRGB24"))
1523 return 1;
1524 else if (!strcasecmp(keyword, "rgb_16") ||
1525 !strcmp(keyword, "DEVRGB24-48") ||
1526 !strcmp(keyword, "DEVRGB48"))
1527 return 1;
1528 return 0;
1529 }
1530
1531 /*
1532 * 'ppdCreateFromIPP()' - Create a PPD file describing the capabilities
1533 * of an IPP printer (legacy interface).
1534 */
1535
1536 char * /* O - PPD filename or NULL on
1537 error */
ppdCreateFromIPP(char * buffer,size_t bufsize,ipp_t * response,const char * make_model,const char * pdl,int color,int duplex)1538 ppdCreateFromIPP (char *buffer, /* I - Filename buffer */
1539 size_t bufsize, /* I - Size of filename
1540 buffer */
1541 ipp_t *response, /* I - Get-Printer-Attributes
1542 response */
1543 const char *make_model, /* I - Make and model from
1544 DNS-SD */
1545 const char *pdl, /* I - List of PDLs from
1546 DNS-SD */
1547 int color, /* I - Color printer? (from
1548 DNS-SD) */
1549 int duplex) /* I - Duplex printer? (from
1550 DNS-SD) */
1551 {
1552 return ppdCreateFromIPP2(buffer, bufsize, response, make_model, pdl,
1553 color, duplex, NULL, NULL, NULL, NULL);
1554 }
1555
1556 /*
1557 * 'ppdCreateFromIPP2()' - Create a PPD file describing the capabilities
1558 * of an IPP printer.
1559 */
1560
1561 char * /* O - PPD filename or NULL on
1562 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)1563 ppdCreateFromIPP2(char *buffer, /* I - Filename buffer */
1564 size_t bufsize, /* I - Size of filename
1565 buffer */
1566 ipp_t *response, /* I - Get-Printer-Attributes
1567 response */
1568 const char *make_model, /* I - Make and model from
1569 DNS-SD */
1570 const char *pdl, /* I - List of PDLs from
1571 DNS-SD */
1572 int color, /* I - Color printer? (from
1573 DNS-SD) */
1574 int duplex, /* I - Duplex printer? (from
1575 DNS-SD) */
1576 cups_array_t *conflicts, /* I - Array of constraints */
1577 cups_array_t *sizes, /* I - Media sizes we've
1578 added */
1579 char* default_pagesize, /* I - Default page size*/
1580 const char *default_cluster_color) /* I - cluster def
1581 color (if cluster's
1582 attributes are
1583 returned) */
1584 {
1585 cups_file_t *fp; /* PPD file */
1586 cups_array_t *printer_sizes; /* Media sizes we've added */
1587 cups_size_t *size; /* Current media size */
1588 ipp_attribute_t *attr, /* xxx-supported */
1589 *attr2,
1590 *defattr, /* xxx-default */
1591 *quality, /* print-quality-supported */
1592 *x_dim, *y_dim; /* Media dimensions */
1593 ipp_t *media_col, /* Media collection */
1594 *media_size; /* Media size collection */
1595 char make[256], /* Make and model */
1596 *model, /* Model name */
1597 ppdname[PPD_MAX_NAME];
1598 /* PPD keyword */
1599 int i, j, /* Looping vars */
1600 count = 0, /* Number of values */
1601 bottom, /* Largest bottom margin */
1602 left, /* Largest left margin */
1603 right, /* Largest right margin */
1604 top, /* Largest top margin */
1605 max_length = 0, /* Maximum custom size */
1606 max_width = 0,
1607 min_length = INT_MAX,
1608 /* Minimum custom size */
1609 min_width = INT_MAX,
1610 is_apple = 0, /* Does the printer support Apple
1611 Raster? */
1612 is_pwg = 0, /* Does the printer support PWG
1613 Raster? */
1614 is_pclm = 0, /* Does the printer support PCLm? */
1615 is_pdf = 0; /* Does the printer support PDF? */
1616 pwg_media_t *pwg; /* PWG media size */
1617 int xres, yres; /* Resolution values */
1618 cups_array_t *common_res, /* Common resolutions of all PDLs */
1619 *current_res, /* Resolutions of current PDL */
1620 *pdl_list; /* List of PDLs */
1621 res_t *common_def, /* Common default resolution */
1622 *current_def, /* Default resolution of current PDL */
1623 *min_res, /* Minimum common resolution */
1624 *max_res; /* Maximum common resolution */
1625 cups_lang_t *lang = cupsLangDefault();
1626 /* Localization info */
1627 struct lconv *loc = localeconv();
1628 /* Locale data */
1629 cups_array_t *printer_opt_strings_catalog = NULL;
1630 /* Printer-specific option UI strings */
1631 char *human_readable,
1632 *human_readable2;
1633 const char *keyword; /* Keyword value */
1634 cups_array_t *fin_options = NULL;
1635 /* Finishing options */
1636 char buf[256],
1637 filter_path[1024];
1638 /* Path to filter executable */
1639 const char *cups_serverbin;/* CUPS_SERVERBIN environment
1640 variable */
1641 char *defaultoutbin = NULL;
1642 const char *outbin;
1643 char outbin_properties[1024];
1644 int octet_str_len;
1645 void *outbin_properties_octet;
1646 int outputorderinfofound = 0,
1647 faceupdown = 1,
1648 firsttolast = 1;
1649 int manual_copies = -1,
1650 is_fax = 0;
1651
1652 /*
1653 * Range check input...
1654 */
1655
1656 if (buffer)
1657 *buffer = '\0';
1658
1659 if (!buffer || bufsize < 1) {
1660 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1661 return (NULL);
1662 }
1663
1664 if (!response) {
1665 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No IPP attributes."), 1);
1666 return (NULL);
1667 }
1668
1669 /*
1670 * Open a temporary file for the PPD...
1671 */
1672
1673 if ((fp = cupsTempFile2(buffer, (int)bufsize)) == NULL) {
1674 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
1675 return (NULL);
1676 }
1677
1678 /*
1679 * Standard stuff for PPD file...
1680 */
1681
1682 cupsFilePuts(fp, "*PPD-Adobe: \"4.3\"\n");
1683 cupsFilePuts(fp, "*FormatVersion: \"4.3\"\n");
1684 cupsFilePrintf(fp, "*FileVersion: \"%s\"\n", VERSION);
1685 cupsFilePuts(fp, "*LanguageVersion: English\n");
1686 cupsFilePuts(fp, "*LanguageEncoding: ISOLatin1\n");
1687 cupsFilePuts(fp, "*PSVersion: \"(3010.000) 0\"\n");
1688 cupsFilePuts(fp, "*LanguageLevel: \"3\"\n");
1689 cupsFilePuts(fp, "*FileSystem: False\n");
1690 cupsFilePuts(fp, "*PCFileName: \"drvless.ppd\"\n");
1691
1692 if ((attr = ippFindAttribute(response, "ipp-features-supported",
1693 IPP_TAG_KEYWORD)) != NULL &&
1694 ippContainsString(attr, "faxout"))
1695 {
1696 attr = ippFindAttribute(response, "printer-uri-supported",
1697 IPP_TAG_URI);
1698 if (attr)
1699 {
1700 ippAttributeString(attr, buf, sizeof(buf));
1701 if (strcasestr(buf, "faxout"))
1702 is_fax = 1;
1703 }
1704 }
1705
1706 if ((attr = ippFindAttribute(response, "printer-make-and-model",
1707 IPP_TAG_TEXT)) != NULL)
1708 strlcpy(make, ippGetString(attr, 0, NULL), sizeof(make));
1709 else if (make_model && make_model[0] != '\0')
1710 strlcpy(make, make_model, sizeof(make));
1711 else
1712 strlcpy(make, "Unknown Printer", sizeof(make));
1713
1714 if (!_cups_strncasecmp(make, "Hewlett Packard ", 16) ||
1715 !_cups_strncasecmp(make, "Hewlett-Packard ", 16)) {
1716 model = make + 16;
1717 strlcpy(make, "HP", sizeof(make));
1718 }
1719 else if ((model = strchr(make, ' ')) != NULL)
1720 *model++ = '\0';
1721 else
1722 model = make;
1723
1724 cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make);
1725 cupsFilePrintf(fp, "*ModelName: \"%s %s\"\n", make, model);
1726 cupsFilePrintf(fp, "*Product: \"(%s %s)\"\n", make, model);
1727 cupsFilePrintf(fp, "*NickName: \"%s %s, %sdriverless, cups-filters %s\"\n",
1728 make, model, (is_fax ? "Fax, " : ""), VERSION);
1729 cupsFilePrintf(fp, "*ShortNickName: \"%s %s\"\n", make, model);
1730
1731 /* Which is the default output bin? */
1732 if ((attr = ippFindAttribute(response, "output-bin-default", IPP_TAG_ZERO))
1733 != NULL)
1734 defaultoutbin = strdup(ippGetString(attr, 0, NULL));
1735 /* Find out on which position of the list of output bins the default one is,
1736 if there is no default bin, take the first of this list */
1737 i = 0;
1738 if ((attr = ippFindAttribute(response, "output-bin-supported",
1739 IPP_TAG_ZERO)) != NULL) {
1740 count = ippGetCount(attr);
1741 for (i = 0; i < count; i ++) {
1742 outbin = ippGetString(attr, i, NULL);
1743 if (outbin == NULL)
1744 continue;
1745 if (defaultoutbin == NULL) {
1746 defaultoutbin = strdup(outbin);
1747 break;
1748 } else if (strcasecmp(outbin, defaultoutbin) == 0)
1749 break;
1750 }
1751 }
1752 if ((attr = ippFindAttribute(response, "printer-output-tray",
1753 IPP_TAG_STRING)) != NULL &&
1754 i < ippGetCount(attr)) {
1755 outbin_properties_octet = ippGetOctetString(attr, i, &octet_str_len);
1756 memset(outbin_properties, 0, sizeof(outbin_properties));
1757 memcpy(outbin_properties, outbin_properties_octet,
1758 ((size_t)octet_str_len < sizeof(outbin_properties) - 1 ?
1759 (size_t)octet_str_len : sizeof(outbin_properties) - 1));
1760 if (strcasestr(outbin_properties, "pagedelivery=faceUp")) {
1761 outputorderinfofound = 1;
1762 faceupdown = -1;
1763 }
1764 if (strcasestr(outbin_properties, "stackingorder=lastToFirst"))
1765 firsttolast = -1;
1766 }
1767 if (outputorderinfofound == 0 && defaultoutbin &&
1768 strcasestr(defaultoutbin, "face-up"))
1769 faceupdown = -1;
1770 if (defaultoutbin)
1771 free (defaultoutbin);
1772 if (firsttolast * faceupdown < 0)
1773 cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n");
1774 else
1775 cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n");
1776
1777 /* To decide whether the printer is coloured or not we see the various
1778 colormodel supported by the printer*/
1779 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD))
1780 == NULL)
1781 if ((attr = ippFindAttribute(response,
1782 "pwg-raster-document-type-supported",
1783 IPP_TAG_KEYWORD)) == NULL)
1784 if ((attr = ippFindAttribute(response, "print-color-mode-supported",
1785 IPP_TAG_KEYWORD)) == NULL)
1786 attr = ippFindAttribute(response, "output-mode-supported",
1787 IPP_TAG_KEYWORD);
1788 if (attr==NULL || !ippGetCount(attr)) {
1789 if ((attr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN))
1790 != NULL) {
1791 if (ippGetBoolean(attr, 0))
1792 cupsFilePuts(fp, "*ColorDevice: True\n");
1793 else
1794 cupsFilePuts(fp, "*ColorDevice: False\n");
1795 } else {
1796 if(color)
1797 cupsFilePuts(fp, "*ColorDevice: True\n");
1798 else
1799 cupsFilePuts(fp, "*ColorDevice: False\n");
1800 }
1801 } else {
1802 int colordevice = 0;
1803 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1804 keyword = ippGetString(attr, i, NULL);
1805 colordevice = is_colordevice(keyword,attr);
1806 if (colordevice) {
1807 cupsFilePuts(fp, "*ColorDevice: True\n");
1808 break;
1809 }
1810 }
1811 if (colordevice == 0)
1812 cupsFilePuts(fp, "*ColorDevice: False\n");
1813 }
1814
1815 cupsFilePrintf(fp, "*cupsVersion: %d.%d\n", CUPS_VERSION_MAJOR,
1816 CUPS_VERSION_MINOR);
1817 cupsFilePuts(fp, "*cupsSNMPSupplies: False\n");
1818 cupsFilePuts(fp, "*cupsLanguages: \"en\"\n");
1819
1820 if ((attr = ippFindAttribute(response, "printer-more-info", IPP_TAG_URI)) !=
1821 NULL)
1822 cupsFilePrintf(fp, "*APSupplies: \"%s\"\n", ippGetString(attr, 0, NULL));
1823
1824 if ((attr = ippFindAttribute(response, "printer-charge-info-uri",
1825 IPP_TAG_URI)) != NULL)
1826 cupsFilePrintf(fp, "*cupsChargeInfoURI: \"%s\"\n", ippGetString(attr, 0,
1827 NULL));
1828
1829 /* Message catalogs for UI strings */
1830 if (opt_strings_catalog == NULL) {
1831 opt_strings_catalog = optArrayNew();
1832 load_opt_strings_catalog(NULL, opt_strings_catalog);
1833 }
1834 if ((attr = ippFindAttribute(response, "printer-strings-uri",
1835 IPP_TAG_URI)) != NULL) {
1836 printer_opt_strings_catalog = optArrayNew();
1837 load_opt_strings_catalog(ippGetString(attr, 0, NULL),
1838 printer_opt_strings_catalog);
1839 if (printer_opt_strings_catalog)
1840 cupsFilePrintf(fp, "*cupsStringsURI: \"%s\"\n", ippGetString(attr, 0,
1841 NULL));
1842 }
1843
1844 /*
1845 * Accounting...
1846 */
1847
1848 if (ippGetBoolean(ippFindAttribute(response, "job-account-id-supported",
1849 IPP_TAG_BOOLEAN), 0))
1850 cupsFilePuts(fp, "*cupsJobAccountId: True\n");
1851
1852 if (ippGetBoolean(ippFindAttribute(response,
1853 "job-accounting-user-id-supported",
1854 IPP_TAG_BOOLEAN), 0))
1855 cupsFilePuts(fp, "*cupsJobAccountingUserId: True\n");
1856
1857 /*
1858 * Password/PIN printing...
1859 */
1860
1861 if ((attr = ippFindAttribute(response, "job-password-supported",
1862 IPP_TAG_INTEGER)) != NULL) {
1863 char pattern[33]; /* Password pattern */
1864 int maxlen = ippGetInteger(attr, 0);
1865 /* Maximum length */
1866 const char *repertoire =
1867 ippGetString(ippFindAttribute(response,
1868 "job-password-repertoire-configured",
1869 IPP_TAG_KEYWORD), 0, NULL);
1870 /* Type of password */
1871
1872 if (maxlen > (int)(sizeof(pattern) - 1))
1873 maxlen = (int)sizeof(pattern) - 1;
1874
1875 if (!repertoire || !strcmp(repertoire, "iana_us-ascii_digits"))
1876 memset(pattern, '1', (size_t)maxlen);
1877 else if (!strcmp(repertoire, "iana_us-ascii_letters"))
1878 memset(pattern, 'A', (size_t)maxlen);
1879 else if (!strcmp(repertoire, "iana_us-ascii_complex"))
1880 memset(pattern, 'C', (size_t)maxlen);
1881 else if (!strcmp(repertoire, "iana_us-ascii_any"))
1882 memset(pattern, '.', (size_t)maxlen);
1883 else if (!strcmp(repertoire, "iana_utf-8_digits"))
1884 memset(pattern, 'N', (size_t)maxlen);
1885 else if (!strcmp(repertoire, "iana_utf-8_letters"))
1886 memset(pattern, 'U', (size_t)maxlen);
1887 else
1888 memset(pattern, '*', (size_t)maxlen);
1889
1890 pattern[maxlen] = '\0';
1891
1892 cupsFilePrintf(fp, "*cupsJobPassword: \"%s\"\n", pattern);
1893 }
1894
1895
1896
1897 /*
1898 * PDLs and common resolutions ...
1899 */
1900
1901 common_res = NULL;
1902 current_res = NULL;
1903 common_def = NULL;
1904 current_def = NULL;
1905 min_res = NULL;
1906 max_res = NULL;
1907 /* Put all available PDls into a simple case-insensitevely searchable
1908 sorted string list */
1909 if ((pdl_list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1910 (cups_acopy_func_t)strdup,
1911 (cups_afree_func_t)free)) == NULL)
1912 goto bad_ppd;
1913 int formatfound = 0;
1914
1915 if (((attr = ippFindAttribute(response, "document-format-supported",
1916 IPP_TAG_MIMETYPE)) != NULL) ||
1917 (pdl && pdl[0] != '\0')) {
1918 const char *format = pdl;
1919 i = 0;
1920 count = ippGetCount(attr);
1921 while ((attr && i < count) || /* Go through formats in attribute */
1922 (!attr && pdl && pdl[0] != '\0' && format[0] != '\0')) {
1923 /* Go through formats in pdl string (from DNS-SD record) */
1924
1925 /* Pick next format from attribute */
1926 if (attr) format = ippGetString(attr, i, NULL);
1927 /* Add format to list of supported PDLs, skip duplicates */
1928 if (!cupsArrayFind(pdl_list, (void *)format))
1929 cupsArrayAdd(pdl_list, (void *)format);
1930 if (attr)
1931 /* Next format in attribute */
1932 i ++;
1933 else {
1934 /* Find the next format in the string pdl, if there is none left,
1935 go to the terminating zero */
1936 while (!isspace(*format) && *format != ',' && *format != '\0')
1937 format ++;
1938 while ((isspace(*format) || *format == ',') && *format != '\0')
1939 format ++;
1940 }
1941 }
1942 }
1943
1944 /*
1945 * Fax
1946 */
1947
1948 if (is_fax)
1949 cupsFilePuts(fp, "*cupsIPPFaxOut: True\n");
1950
1951 /* Check for each CUPS/cups-filters-supported PDL, starting with the
1952 most desirable going to the least desirable. If a PDL requires a
1953 certain set of resolutions (the raster-based PDLs), find the
1954 resolutions and find out which are the common resolutions of all
1955 supported PDLs. Choose the default resolution from the most
1956 desirable of all resolution-requiring PDLs if it is common in all
1957 of them. Skip a resolution-requiring PDL if its resolution list
1958 attribute is missing or contains only broken entries. Use the
1959 general resolution list and default resolution of the printer
1960 only if it does not support any resolution-requiring PDL. Use 300
1961 dpi if there is no resolution info at all in the attributes.
1962 In case of PDF as PDL check whether also the
1963 "application/vnd.cups-pdf" MIME type is accepted. In this case
1964 our printer is a remote CUPS queue which already runs the
1965 pdftopdf filter on the server, so let the PPD take
1966 "application/pdf" as input format so that pdftopdf does not also
1967 get executed on the client, applying option settings twice. See
1968 https://github.com/apple/cups/issues/5361 */
1969 if (cupsArrayFind(pdl_list, "application/vnd.cups-pdf")) {
1970 cupsFilePuts(fp, "*cupsFilter2: \"application/pdf application/pdf 0 -\"\n");
1971 manual_copies = 0;
1972 formatfound = 1;
1973 is_pdf = 1;
1974 } else if (cupsArrayFind(pdl_list, "application/pdf")) {
1975 cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 200 -\"\n");
1976 manual_copies = 0;
1977 formatfound = 1;
1978 is_pdf = 1;
1979 }
1980 #ifdef CUPS_RASTER_HAVE_APPLERASTER
1981 if (cupsArrayFind(pdl_list, "image/urf")) {
1982 if ((attr = ippFindAttribute(response, "urf-supported",
1983 IPP_TAG_KEYWORD)) != NULL) {
1984 int lowdpi = 0, hidpi = 0; /* Lower and higher resolution */
1985 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1986 const char *rs = ippGetString(attr, i, NULL); /* RS value */
1987 if (_cups_strncasecmp(rs, "RS", 2))
1988 continue;
1989 lowdpi = atoi(rs + 2);
1990 if ((rs = strrchr(rs, '-')) != NULL)
1991 hidpi = atoi(rs + 1);
1992 else
1993 hidpi = lowdpi;
1994 break;
1995 }
1996 if (lowdpi == 0) {
1997 /* Invalid "urf-supported" value... */
1998 goto bad_ppd;
1999 } else {
2000 if ((current_res = resolutionArrayNew()) != NULL) {
2001 if ((current_def = resolutionNew(lowdpi, lowdpi)) != NULL)
2002 {
2003 cupsArrayAdd(current_res, current_def);
2004 free_resolution(current_def, NULL);
2005 }
2006 if (hidpi != lowdpi &&
2007 (current_def = resolutionNew(hidpi, hidpi)) != NULL)
2008 {
2009 cupsArrayAdd(current_res, current_def);
2010 free_resolution(current_def, NULL);
2011 }
2012 current_def = NULL;
2013 if (cupsArrayCount(current_res) > 0 &&
2014 joinResolutionArrays(&common_res, ¤t_res, &common_def,
2015 ¤t_def)) {
2016 cupsFilePuts(fp, "*cupsFilter2: \"image/urf image/urf 0 -\"\n");
2017 if (formatfound == 0) manual_copies = 1;
2018 formatfound = 1;
2019 is_apple = 1;
2020 }
2021 }
2022 }
2023 }
2024 }
2025 #endif
2026 if (!is_apple && cupsArrayFind(pdl_list, "image/pwg-raster")) {
2027 if ((attr = ippFindAttribute(response,
2028 "pwg-raster-document-resolution-supported",
2029 IPP_TAG_RESOLUTION)) != NULL) {
2030 current_def = NULL;
2031 if ((current_res = ippResolutionListToArray(attr)) != NULL &&
2032 joinResolutionArrays(&common_res, ¤t_res, &common_def,
2033 ¤t_def)) {
2034 cupsFilePuts(fp, "*cupsFilter2: \"image/pwg-raster image/pwg-raster 10 -\"\n");
2035 if (formatfound == 0) manual_copies = 1;
2036 formatfound = 1;
2037 is_pwg = 1;
2038 }
2039 }
2040 }
2041 #ifdef QPDF_HAVE_PCLM
2042 if (!is_apple && !is_pwg && cupsArrayFind(pdl_list, "application/PCLm")) {
2043 if ((attr = ippFindAttribute(response, "pclm-source-resolution-supported",
2044 IPP_TAG_RESOLUTION)) != NULL) {
2045 if ((defattr = ippFindAttribute(response,
2046 "pclm-source-resolution-default",
2047 IPP_TAG_RESOLUTION)) != NULL)
2048 current_def = ippResolutionToRes(defattr, 0);
2049 else
2050 current_def = NULL;
2051 if ((current_res = ippResolutionListToArray(attr)) != NULL &&
2052 joinResolutionArrays(&common_res, ¤t_res, &common_def,
2053 ¤t_def)) {
2054 cupsFilePuts(fp, "*cupsFilter2: \"application/PCLm application/PCLm 300 -\"\n");
2055 if (formatfound == 0) manual_copies = 1;
2056 formatfound = 1;
2057 is_pclm = 1;
2058 }
2059 }
2060 }
2061 #endif
2062 /* Legacy formats only if we have no driverless format */
2063 if (!is_pdf && !is_apple && !is_pwg && !is_pclm) {
2064 if (cupsArrayFind(pdl_list, "application/vnd.hp-pclxl")) {
2065 /* Check whether the gstopxl filter is installed,
2066 otherwise ignore the PCL-XL support of the printer */
2067 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
2068 cups_serverbin = CUPS_SERVERBIN;
2069 snprintf(filter_path, sizeof(filter_path), "%s/filter/gstopxl",
2070 cups_serverbin);
2071 if (access(filter_path, X_OK) == 0) {
2072 /* We put a high cost factor here as if a printer supports also
2073 another format, like PWG or Apple Raster, we prefer it, as some
2074 PCL-XL printers have bugs in their PCL-XL interpreters */
2075 cupsFilePrintf(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/vnd.hp-pclxl 400 gstopxl\"\n");
2076 if (formatfound == 0) manual_copies = 1;
2077 formatfound = 1;
2078 }
2079 }
2080 if (cupsArrayFind(pdl_list, "application/postscript")) {
2081 /* We put a high cost factor here as if a printer supports also
2082 another format, like PWG or Apple Raster, we prefer it, as many
2083 PostScript printers have bugs in their PostScript interpreters */
2084 cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-postscript application/postscript 600 -\"\n");
2085 if (formatfound == 0) manual_copies = 0;
2086 formatfound = 1;
2087 }
2088 if (cupsArrayFind(pdl_list, "application/vnd.hp-pcl")) {
2089 /* We put a high cost factor here as if a printer supports also
2090 another format, like PWG or Apple Raster, we prefer it, as there
2091 are some printers, like HP inkjets which report to accept PCL
2092 but do not support PCL 5c/e or PCL-XL */
2093 cupsFilePrintf(fp, "*cupsFilter2: \"application/vnd.cups-raster application/vnd.hp-pcl 800 rastertopclx\"\n");
2094 if (formatfound == 0) manual_copies = 1;
2095 formatfound = 1;
2096 }
2097 }
2098 if (cupsArrayFind(pdl_list, "image/jpeg"))
2099 cupsFilePuts(fp, "*cupsFilter2: \"image/jpeg image/jpeg 0 -\"\n");
2100 if (cupsArrayFind(pdl_list, "image/png"))
2101 cupsFilePuts(fp, "*cupsFilter2: \"image/png image/png 0 -\"\n");
2102 cupsArrayDelete(pdl_list);
2103 if (manual_copies < 0) manual_copies = 1;
2104 if (formatfound == 0)
2105 goto bad_ppd;
2106
2107 /* For the case that we will print in a raster format and not in a high-level
2108 format, we need to create multiple copies on the client. We add a line to
2109 the PPD which tells the pdftopdf filter to generate the copies */
2110 if (manual_copies == 1)
2111 cupsFilePuts(fp, "*cupsManualCopies: True\n");
2112
2113 /* No resolution requirements by any of the supported PDLs?
2114 Use "printer-resolution-supported" attribute */
2115 if (common_res == NULL) {
2116 if ((attr = ippFindAttribute(response, "printer-resolution-supported",
2117 IPP_TAG_RESOLUTION)) != NULL) {
2118 if ((defattr = ippFindAttribute(response, "printer-resolution-default",
2119 IPP_TAG_RESOLUTION)) != NULL)
2120 current_def = ippResolutionToRes(defattr, 0);
2121 else
2122 current_def = NULL;
2123 if ((current_res = ippResolutionListToArray(attr)) != NULL)
2124 joinResolutionArrays(&common_res, ¤t_res, &common_def,
2125 ¤t_def);
2126 }
2127 }
2128 /* Still no resolution found? Default to 300 dpi */
2129 if (common_res == NULL) {
2130 if ((common_res = resolutionArrayNew()) != NULL) {
2131 if ((current_def = resolutionNew(300, 300)) != NULL)
2132 {
2133 cupsArrayAdd(common_res, current_def);
2134 free_resolution(current_def, NULL);
2135 }
2136 current_def = NULL;
2137 } else
2138 goto bad_ppd;
2139 }
2140 /* No default resolution determined yet */
2141 if (common_def == NULL) {
2142 if ((defattr = ippFindAttribute(response, "printer-resolution-default",
2143 IPP_TAG_RESOLUTION)) != NULL) {
2144 common_def = ippResolutionToRes(defattr, 0);
2145 if (!cupsArrayFind(common_res, common_def)) {
2146 free(common_def);
2147 common_def = NULL;
2148 }
2149 }
2150 if (common_def == NULL) {
2151 count = cupsArrayCount(common_res);
2152 common_def = copy_resolution(cupsArrayIndex(common_res, count / 2), NULL);
2153 }
2154 }
2155 /* Get minimum and maximum resolution */
2156 min_res = copy_resolution(cupsArrayFirst(common_res), NULL);
2157 max_res = copy_resolution(cupsArrayLast(common_res), NULL);
2158 cupsArrayDelete(common_res);
2159
2160 #ifdef QPDF_HAVE_PCLM
2161 /*
2162 * Generically check for PCLm attributes in IPP response
2163 * and ppdize them one by one
2164 */
2165
2166 if (is_pclm) {
2167 attr = ippFirstAttribute(response); /* first attribute */
2168 while (attr) { /* loop through all the attributes */
2169 if (_cups_strncasecmp(ippGetName(attr), "pclm", 4) == 0) {
2170 pwg_ppdize_name(ippGetName(attr), ppdname, sizeof(ppdname));
2171 cupsFilePrintf(fp, "*cups%s: ", ppdname);
2172 ipp_tag_t tag = ippGetValueTag(attr);
2173 count = ippGetCount(attr);
2174
2175 if (tag == IPP_TAG_RESOLUTION) { /* ppdize values of type resolution */
2176 if ((current_res = ippResolutionListToArray(attr)) != NULL) {
2177 count = cupsArrayCount(current_res);
2178 if (count > 1)
2179 cupsFilePuts(fp, "\"");
2180 for (i = 0, current_def = cupsArrayFirst(current_res);
2181 current_def;
2182 i ++, current_def = cupsArrayNext(current_res)) {
2183 int x = current_def->x;
2184 int y = current_def->y;
2185 if (x == y)
2186 cupsFilePrintf(fp, "%ddpi", x);
2187 else
2188 cupsFilePrintf(fp, "%dx%ddpi", x, y);
2189 if (i < count - 1)
2190 cupsFilePuts(fp, ",");
2191 }
2192 if (count > 1)
2193 cupsFilePuts(fp, "\"");
2194 cupsFilePuts(fp, "\n");
2195 } else
2196 cupsFilePuts(fp, "\"\"\n");
2197 cupsArrayDelete(current_res);
2198 } else {
2199 ippAttributeString(attr, ppdname, sizeof(ppdname));
2200 if (count > 1 || /* quotes around multi-valued and string
2201 attributes */
2202 tag == IPP_TAG_STRING ||
2203 tag == IPP_TAG_TEXT ||
2204 tag == IPP_TAG_TEXTLANG)
2205 cupsFilePrintf(fp, "\"%s\"\n", ppdname);
2206 else
2207 cupsFilePrintf(fp, "%s\n", ppdname);
2208 }
2209 }
2210 attr = ippNextAttribute(response);
2211 }
2212 }
2213 #endif
2214
2215 /*
2216 * PageSize/PageRegion/ImageableArea/PaperDimension
2217 */
2218 printer_sizes = generate_sizes(response, &defattr, &min_length, &min_width,
2219 &max_length, &max_width,
2220 &bottom, &left, &right, &top, ppdname);
2221 if (sizes==NULL) {
2222 sizes = printer_sizes;
2223 } else
2224 strcpy(ppdname, default_pagesize);
2225
2226 if (cupsArrayCount(sizes) > 0) {
2227 /*
2228 * List all of the standard sizes...
2229 */
2230
2231 char tleft[256], /* Left string */
2232 tbottom[256], /* Bottom string */
2233 tright[256], /* Right string */
2234 ttop[256], /* Top string */
2235 twidth[256], /* Width string */
2236 tlength[256], /* Length string */
2237 ppdsizename[128];
2238 char *ippsizename,
2239 *suffix;
2240 int all_borderless = 1;
2241
2242 /* Find a page size without ".Borderless" suffix */
2243 /* (if all are ".Borderless" we drop the suffix in the PPD) */
2244 for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2245 size = (cups_size_t *)cupsArrayNext(sizes))
2246 if (strcasestr(size->media, ".Borderless") == NULL)
2247 break;
2248 if (size)
2249 all_borderless = 0;
2250
2251 if (all_borderless) {
2252 suffix = strcasestr(ppdname, ".Borderless");
2253 if (suffix)
2254 *suffix = '\0';
2255 }
2256
2257 cupsFilePrintf(fp, "*OpenUI *PageSize/%s: PickOne\n"
2258 "*OrderDependency: 10 AnySetup *PageSize\n"
2259 "*DefaultPageSize: %s\n", "Media Size", ppdname);
2260 for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2261 size = (cups_size_t *)cupsArrayNext(sizes)) {
2262 _cupsStrFormatd(twidth, twidth + sizeof(twidth),
2263 size->width * 72.0 / 2540.0, loc);
2264 _cupsStrFormatd(tlength, tlength + sizeof(tlength),
2265 size->length * 72.0 / 2540.0, loc);
2266 strlcpy(ppdsizename, size->media, sizeof(ppdsizename));
2267 if ((ippsizename = strchr(ppdsizename, ' ')) != NULL) {
2268 *ippsizename = '\0';
2269 ippsizename ++;
2270 }
2271
2272 if (ippsizename)
2273 human_readable = lookup_choice(ippsizename, "media",
2274 opt_strings_catalog,
2275 printer_opt_strings_catalog);
2276 else
2277 human_readable = NULL;
2278 if (!human_readable) {
2279 pwg = pwgMediaForSize(size->width, size->length);
2280 if (pwg)
2281 human_readable = lookup_choice((char *)pwg->pwg, "media",
2282 opt_strings_catalog,
2283 printer_opt_strings_catalog);
2284 }
2285
2286 if (all_borderless) {
2287 suffix = strcasestr(ppdsizename, ".Borderless");
2288 if (suffix)
2289 *suffix = '\0';
2290 }
2291
2292 cupsFilePrintf(fp, "*PageSize %s%s%s%s: \"<</PageSize[%s %s]>>setpagedevice\"\n",
2293 ppdsizename,
2294 (human_readable ? "/" : ""),
2295 (human_readable ? human_readable : ""),
2296 (human_readable && strstr(ppdsizename, ".Borderless") ?
2297 " (Borderless)" : ""),
2298 twidth, tlength);
2299 }
2300 cupsFilePuts(fp, "*CloseUI: *PageSize\n");
2301
2302 cupsFilePrintf(fp, "*OpenUI *PageRegion/%s: PickOne\n"
2303 "*OrderDependency: 10 AnySetup *PageRegion\n"
2304 "*DefaultPageRegion: %s\n", "Media Size", ppdname);
2305 for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2306 size = (cups_size_t *)cupsArrayNext(sizes)) {
2307 _cupsStrFormatd(twidth, twidth + sizeof(twidth),
2308 size->width * 72.0 / 2540.0, loc);
2309 _cupsStrFormatd(tlength, tlength + sizeof(tlength),
2310 size->length * 72.0 / 2540.0, loc);
2311 strlcpy(ppdsizename, size->media, sizeof(ppdsizename));
2312 if ((ippsizename = strchr(ppdsizename, ' ')) != NULL) {
2313 *ippsizename = '\0';
2314 ippsizename ++;
2315 }
2316
2317 if (ippsizename)
2318 human_readable = lookup_choice(ippsizename, "media",
2319 opt_strings_catalog,
2320 printer_opt_strings_catalog);
2321 else
2322 human_readable = NULL;
2323 if (!human_readable) {
2324 pwg = pwgMediaForSize(size->width, size->length);
2325 if (pwg)
2326 human_readable = lookup_choice((char *)pwg->pwg, "media",
2327 opt_strings_catalog,
2328 printer_opt_strings_catalog);
2329 }
2330
2331 if (all_borderless) {
2332 suffix = strcasestr(ppdsizename, ".Borderless");
2333 if (suffix)
2334 *suffix = '\0';
2335 }
2336
2337 cupsFilePrintf(fp, "*PageRegion %s%s%s%s: \"<</PageSize[%s %s]>>setpagedevice\"\n",
2338 ppdsizename,
2339 (human_readable ? "/" : ""),
2340 (human_readable ? human_readable : ""),
2341 (human_readable && strstr(ppdsizename, ".Borderless") ?
2342 " (Borderless)" : ""),
2343 twidth, tlength);
2344 }
2345 cupsFilePuts(fp, "*CloseUI: *PageRegion\n");
2346
2347 cupsFilePrintf(fp, "*DefaultImageableArea: %s\n"
2348 "*DefaultPaperDimension: %s\n", ppdname, ppdname);
2349
2350 for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2351 size = (cups_size_t *)cupsArrayNext(sizes)) {
2352 _cupsStrFormatd(tleft, tleft + sizeof(tleft),
2353 size->left * 72.0 / 2540.0, loc);
2354 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom),
2355 size->bottom * 72.0 / 2540.0, loc);
2356 _cupsStrFormatd(tright, tright + sizeof(tright),
2357 (size->width - size->right) * 72.0 / 2540.0, loc);
2358 _cupsStrFormatd(ttop, ttop + sizeof(ttop),
2359 (size->length - size->top) * 72.0 / 2540.0, loc);
2360 _cupsStrFormatd(twidth, twidth + sizeof(twidth),
2361 size->width * 72.0 / 2540.0, loc);
2362 _cupsStrFormatd(tlength, tlength + sizeof(tlength),
2363 size->length * 72.0 / 2540.0, loc);
2364 strlcpy(ppdsizename, size->media, sizeof(ppdsizename));
2365 if ((ippsizename = strchr(ppdsizename, ' ')) != NULL)
2366 *ippsizename = '\0';
2367
2368 if (all_borderless) {
2369 suffix = strcasestr(ppdsizename, ".Borderless");
2370 if (suffix)
2371 *suffix = '\0';
2372 }
2373
2374 cupsFilePrintf(fp, "*ImageableArea %s: \"%s %s %s %s\"\n", ppdsizename,
2375 tleft, tbottom, tright, ttop);
2376 cupsFilePrintf(fp, "*PaperDimension %s: \"%s %s\"\n", ppdsizename,
2377 twidth, tlength);
2378 }
2379
2380 /*
2381 * Custom size support...
2382 */
2383
2384 if (max_width > 0 && min_width < INT_MAX && max_length > 0 &&
2385 min_length < INT_MAX) {
2386 char tmax[256], tmin[256]; /* Min/max values */
2387
2388 _cupsStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc);
2389 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom),
2390 bottom * 72.0 / 2540.0, loc);
2391 _cupsStrFormatd(tright, tright + sizeof(tright), right * 72.0 / 2540.0,
2392 loc);
2393 _cupsStrFormatd(ttop, ttop + sizeof(ttop), top * 72.0 / 2540.0, loc);
2394
2395 cupsFilePrintf(fp, "*HWMargins: \"%s %s %s %s\"\n", tleft, tbottom,
2396 tright, ttop);
2397
2398 _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_width * 72.0 / 2540.0,
2399 loc);
2400 _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_width * 72.0 / 2540.0,
2401 loc);
2402 cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s\n", tmin,
2403 tmax);
2404
2405 _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_length * 72.0 / 2540.0,
2406 loc);
2407 _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_length * 72.0 / 2540.0,
2408 loc);
2409 cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s\n", tmin,
2410 tmax);
2411
2412 cupsFilePuts(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0\n");
2413 cupsFilePuts(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0\n");
2414 cupsFilePuts(fp, "*ParamCustomPageSize Orientation: 5 int 0 3\n");
2415 cupsFilePuts(fp, "*CustomPageSize True: \"pop pop pop <</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\"\n");
2416 }
2417 } else {
2418 cupsFilePrintf(fp,
2419 "*%% Printer did not supply page size info via IPP, using defaults\n"
2420 "*OpenUI *PageSize/Media Size: PickOne\n"
2421 "*OrderDependency: 10 AnySetup *PageSize\n"
2422 "*DefaultPageSize: Letter\n"
2423 "*PageSize Letter/US Letter: \"<</PageSize[612 792]>>setpagedevice\"\n"
2424 "*PageSize Legal/US Legal: \"<</PageSize[612 1008]>>setpagedevice\"\n"
2425 "*PageSize Executive/Executive: \"<</PageSize[522 756]>>setpagedevice\"\n"
2426 "*PageSize Tabloid/Tabloid: \"<</PageSize[792 1224]>>setpagedevice\"\n"
2427 "*PageSize A3/A3: \"<</PageSize[842 1191]>>setpagedevice\"\n"
2428 "*PageSize A4/A4: \"<</PageSize[595 842]>>setpagedevice\"\n"
2429 "*PageSize A5/A5: \"<</PageSize[420 595]>>setpagedevice\"\n"
2430 "*PageSize B5/JIS B5: \"<</PageSize[516 729]>>setpagedevice\"\n"
2431 "*PageSize EnvISOB5/Envelope B5: \"<</PageSize[499 709]>>setpagedevice\"\n"
2432 "*PageSize Env10/Envelope #10 : \"<</PageSize[297 684]>>setpagedevice\"\n"
2433 "*PageSize EnvC5/Envelope C5: \"<</PageSize[459 649]>>setpagedevice\"\n"
2434 "*PageSize EnvDL/Envelope DL: \"<</PageSize[312 624]>>setpagedevice\"\n"
2435 "*PageSize EnvMonarch/Envelope Monarch: \"<</PageSize[279 540]>>setpagedevice\"\n"
2436 "*CloseUI: *PageSize\n"
2437 "*OpenUI *PageRegion/Media Size: PickOne\n"
2438 "*OrderDependency: 10 AnySetup *PageRegion\n"
2439 "*DefaultPageRegion: Letter\n"
2440 "*PageRegion Letter/US Letter: \"<</PageSize[612 792]>>setpagedevice\"\n"
2441 "*PageRegion Legal/US Legal: \"<</PageSize[612 1008]>>setpagedevice\"\n"
2442 "*PageRegion Executive/Executive: \"<</PageSize[522 756]>>setpagedevice\"\n"
2443 "*PageRegion Tabloid/Tabloid: \"<</PageSize[792 1224]>>setpagedevice\"\n"
2444 "*PageRegion A3/A3: \"<</PageSize[842 1191]>>setpagedevice\"\n"
2445 "*PageRegion A4/A4: \"<</PageSize[595 842]>>setpagedevice\"\n"
2446 "*PageRegion A5/A5: \"<</PageSize[420 595]>>setpagedevice\"\n"
2447 "*PageRegion B5/JIS B5: \"<</PageSize[516 729]>>setpagedevice\"\n"
2448 "*PageRegion EnvISOB5/Envelope B5: \"<</PageSize[499 709]>>setpagedevice\"\n"
2449 "*PageRegion Env10/Envelope #10 : \"<</PageSize[297 684]>>setpagedevice\"\n"
2450 "*PageRegion EnvC5/Envelope C5: \"<</PageSize[459 649]>>setpagedevice\"\n"
2451 "*PageRegion EnvDL/Envelope DL: \"<</PageSize[312 624]>>setpagedevice\"\n"
2452 "*PageRegion EnvMonarch/Envelope Monarch: \"<</PageSize[279 540]>>setpagedevice\"\n"
2453 "*CloseUI: *PageSize\n"
2454 "*DefaultImageableArea: Letter\n"
2455 "*ImageableArea Letter/US Letter: \"18 12 594 780\"\n"
2456 "*ImageableArea Legal/US Legal: \"18 12 594 996\"\n"
2457 "*ImageableArea Executive/Executive: \"18 12 504 744\"\n"
2458 "*ImageableArea Tabloid/Tabloid: \"18 12 774 1212\"\n"
2459 "*ImageableArea A3/A3: \"18 12 824 1179\"\n"
2460 "*ImageableArea A4/A4: \"18 12 577 830\"\n"
2461 "*ImageableArea A5/A5: \"18 12 402 583\"\n"
2462 "*ImageableArea B5/JIS B5: \"18 12 498 717\"\n"
2463 "*ImageableArea EnvISOB5/Envelope B5: \"18 12 481 697\"\n"
2464 "*ImageableArea Env10/Envelope #10 : \"18 12 279 672\"\n"
2465 "*ImageableArea EnvC5/Envelope C5: \"18 12 441 637\"\n"
2466 "*ImageableArea EnvDL/Envelope DL: \"18 12 294 612\"\n"
2467 "*ImageableArea EnvMonarch/Envelope Monarch: \"18 12 261 528\"\n"
2468 "*DefaultPaperDimension: Letter\n"
2469 "*PaperDimension Letter/US Letter: \"612 792\"\n"
2470 "*PaperDimension Legal/US Legal: \"612 1008\"\n"
2471 "*PaperDimension Executive/Executive: \"522 756\"\n"
2472 "*PaperDimension Tabloid/Tabloid: \"792 1224\"\n"
2473 "*PaperDimension A3/A3: \"842 1191\"\n"
2474 "*PaperDimension A4/A4: \"595 842\"\n"
2475 "*PaperDimension A5/A5: \"420 595\"\n"
2476 "*PaperDimension B5/JIS B5: \"516 729\"\n"
2477 "*PaperDimension EnvISOB5/Envelope B5: \"499 709\"\n"
2478 "*PaperDimension Env10/Envelope #10 : \"297 684\"\n"
2479 "*PaperDimension EnvC5/Envelope C5: \"459 649\"\n"
2480 "*PaperDimension EnvDL/Envelope DL: \"312 624\"\n"
2481 "*PaperDimension EnvMonarch/Envelope Monarch: \"279 540\"\n");
2482 }
2483
2484 cupsArrayDelete(printer_sizes);
2485
2486 /*
2487 * InputSlot...
2488 */
2489
2490 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source",
2491 IPP_TAG_KEYWORD)) != NULL)
2492 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
2493 else
2494 ppdname[0] = '\0';
2495
2496 if ((attr = ippFindAttribute(response, "media-source-supported",
2497 IPP_TAG_KEYWORD)) != NULL &&
2498 (count = ippGetCount(attr)) > 1) {
2499 int have_default = ppdname[0] != '\0';
2500 /* Do we have a default InputSlot? */
2501 static const char * const sources[][2] =
2502 { /* "media-source" strings */
2503 { "Auto", _("Automatic") },
2504 { "Main", _("Main") },
2505 { "Alternate", _("Alternate") },
2506 { "LargeCapacity", _("Large Capacity") },
2507 { "Manual", _("Manual") },
2508 { "Envelope", _("Envelope") },
2509 { "Disc", _("Disc") },
2510 { "Photo", _("Photo") },
2511 { "Hagaki", _("Hagaki") },
2512 { "MainRoll", _("Main Roll") },
2513 { "AlternateRoll", _("Alternate Roll") },
2514 { "Top", _("Top") },
2515 { "Middle", _("Middle") },
2516 { "Bottom", _("Bottom") },
2517 { "Side", _("Side") },
2518 { "Left", _("Left") },
2519 { "Right", _("Right") },
2520 { "Center", _("Center") },
2521 { "Rear", _("Rear") },
2522 { "ByPassTray", _("Multipurpose") },
2523 { "Tray1", _("Tray 1") },
2524 { "Tray2", _("Tray 2") },
2525 { "Tray3", _("Tray 3") },
2526 { "Tray4", _("Tray 4") },
2527 { "Tray5", _("Tray 5") },
2528 { "Tray6", _("Tray 6") },
2529 { "Tray7", _("Tray 7") },
2530 { "Tray8", _("Tray 8") },
2531 { "Tray9", _("Tray 9") },
2532 { "Tray10", _("Tray 10") },
2533 { "Tray11", _("Tray 11") },
2534 { "Tray12", _("Tray 12") },
2535 { "Tray13", _("Tray 13") },
2536 { "Tray14", _("Tray 14") },
2537 { "Tray15", _("Tray 15") },
2538 { "Tray16", _("Tray 16") },
2539 { "Tray17", _("Tray 17") },
2540 { "Tray18", _("Tray 18") },
2541 { "Tray19", _("Tray 19") },
2542 { "Tray20", _("Tray 20") },
2543 { "Roll1", _("Roll 1") },
2544 { "Roll2", _("Roll 2") },
2545 { "Roll3", _("Roll 3") },
2546 { "Roll4", _("Roll 4") },
2547 { "Roll5", _("Roll 5") },
2548 { "Roll6", _("Roll 6") },
2549 { "Roll7", _("Roll 7") },
2550 { "Roll8", _("Roll 8") },
2551 { "Roll9", _("Roll 9") },
2552 { "Roll10", _("Roll 10") }
2553 };
2554
2555 human_readable = lookup_option("media-source", opt_strings_catalog,
2556 printer_opt_strings_catalog);
2557 cupsFilePrintf(fp, "*OpenUI *InputSlot/%s: PickOne\n"
2558 "*OrderDependency: 10 AnySetup *InputSlot\n",
2559 (human_readable ? human_readable : "Media Source"));
2560 if (have_default)
2561 cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
2562 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2563 keyword = ippGetString(attr, i, NULL);
2564
2565 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
2566
2567 if (i == 0 && !have_default)
2568 cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
2569
2570 human_readable = lookup_choice((char *)keyword, "media-source",
2571 opt_strings_catalog,
2572 printer_opt_strings_catalog);
2573 for (j = (int)(sizeof(sources) / sizeof(sources[0])) - 1; j >= 0; j --)
2574 if (!strcmp(sources[j][0], ppdname)) {
2575 if (human_readable == NULL)
2576 human_readable = (char *)_cupsLangString(lang, sources[j][1]);
2577 break;
2578 }
2579 if (j >= 0)
2580 cupsFilePrintf(fp, "*InputSlot %s/%s: \"<</MediaPosition %d>>setpagedevice\"\n",
2581 ppdname, human_readable, j);
2582 else
2583 cupsFilePrintf(fp, "*InputSlot %s%s%s: \"\"\n",
2584 ppdname,
2585 (human_readable ? "/" : ""),
2586 (human_readable ? human_readable : ""));
2587 }
2588 cupsFilePuts(fp, "*CloseUI: *InputSlot\n");
2589 }
2590
2591 /*
2592 * MediaType...
2593 */
2594
2595 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type",
2596 IPP_TAG_ZERO)) != NULL)
2597 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
2598 else
2599 strlcpy(ppdname, "Unknown", sizeof(ppdname));
2600
2601 if ((attr = ippFindAttribute(response, "media-type-supported",
2602 IPP_TAG_ZERO)) != NULL &&
2603 (count = ippGetCount(attr)) > 1) {
2604 static const char * const media_types[][2] =
2605 { /* "media-type" strings */
2606 { "aluminum", _("Aluminum") },
2607 { "auto", _("Automatic") },
2608 { "back-print-film", _("Back Print Film") },
2609 { "cardboard", _("Cardboard") },
2610 { "cardstock", _("Cardstock") },
2611 { "cd", _("CD") },
2612 { "com.hp.advanced-photo", _("Advanced Photo Paper") }, /* HP */
2613 { "com.hp.brochure-glossy", _("Glossy Brochure Paper") }, /* HP */
2614 { "com.hp.brochure-matte", _("Matte Brochure Paper") }, /* HP */
2615 { "com.hp.cover-matte", _("Matte Cover Paper") }, /* HP */
2616 { "com.hp.ecosmart-lite", _("Office Recycled Paper") }, /* HP */
2617 { "com.hp.everyday-glossy", _("Everyday Glossy Photo Paper") }, /* HP */
2618 { "com.hp.everyday-matte", _("Everyday Matte Paper") }, /* HP */
2619 { "com.hp.extra-heavy", _("Extra Heavyweight Paper") }, /* HP */
2620 { "com.hp.intermediate", _("Multipurpose Paper") }, /* HP */
2621 { "com.hp.mid-weight", _("Mid-Weight Paper") }, /* HP */
2622 { "com.hp.premium-inkjet", _("Premium Inkjet Paper") }, /* HP */
2623 { "com.hp.premium-photo", _("Premium Photo Glossy Paper") }, /* HP */
2624 { "com.hp.premium-presentation-matte", _("Premium Presentation Matte Paper") }, /* HP */
2625 { "continuous", _("Continuous") },
2626 { "continuous-long", _("Continuous Long") },
2627 { "continuous-short", _("Continuous Short") },
2628 { "disc", _("Optical Disc") },
2629 { "disc-glossy", _("Glossy Optical Disc") },
2630 { "disc-high-gloss", _("High Gloss Optical Disc") },
2631 { "disc-matte", _("Matte Optical Disc") },
2632 { "disc-satin", _("Satin Optical Disc") },
2633 { "disc-semi-gloss", _("Semi-Gloss Optical Disc") },
2634 { "double-wall", _("Double Wall Cardboard") },
2635 { "dry-film", _("Dry Film") },
2636 { "dvd", _("DVD") },
2637 { "embossing-foil", _("Embossing Foil") },
2638 { "end-board", _("End Board") },
2639 { "envelope", _("Envelope") },
2640 { "envelope-archival", _("Archival Envelope") },
2641 { "envelope-bond", _("Bond Envelope") },
2642 { "envelope-coated", _("Coated Envelope") },
2643 { "envelope-cotton", _("Cotton Envelope") },
2644 { "envelope-fine", _("Fine Envelope") },
2645 { "envelope-heavyweight", _("Heavyweight Envelope") },
2646 { "envelope-inkjet", _("Inkjet Envelope") },
2647 { "envelope-lightweight", _("Lightweight Envelope") },
2648 { "envelope-plain", _("Plain Envelope") },
2649 { "envelope-preprinted", _("Preprinted Envelope") },
2650 { "envelope-window", _("Windowed Envelope") },
2651 { "fabric", _("Fabric") },
2652 { "fabric-archival", _("Archival Fabric") },
2653 { "fabric-glossy", _("Glossy Fabric") },
2654 { "fabric-high-gloss", _("High Gloss Fabric") },
2655 { "fabric-matte", _("Matte Fabric") },
2656 { "fabric-semi-gloss", _("Semi-Gloss Fabric") },
2657 { "fabric-waterproof", _("Waterproof Fabric") },
2658 { "film", _("Film") },
2659 { "flexo-base", _("Flexo Base") },
2660 { "flexo-photo-polymer", _("Flexo Photo Polymer") },
2661 { "flute", _("Flute") },
2662 { "foil", _("Foil") },
2663 { "full-cut-tabs", _("Full Cut Tabs") },
2664 { "glass", _("Glass") },
2665 { "glass-colored", _("Glass Colored") },
2666 { "glass-opaque", _("Glass Opaque") },
2667 { "glass-surfaced", _("Glass Surfaced") },
2668 { "glass-textured", _("Glass Textured") },
2669 { "gravure-cylinder", _("Gravure Cylinder") },
2670 { "image-setter-paper", _("Image Setter Paper") },
2671 { "imaging-cylinder", _("Imaging Cylinder") },
2672 { "jp.co.canon_photo-paper-plus-glossy-ii", _("Photo Paper Plus Glossy II") }, /* Canon */
2673 { "jp.co.canon_photo-paper-pro-platinum", _("Photo Paper Pro Platinum") }, /* Canon */
2674 { "jp.co.canon-photo-paper-plus-glossy-ii", _("Photo Paper Plus Glossy II") }, /* Canon */
2675 { "jp.co.canon-photo-paper-pro-platinum", _("Photo Paper Pro Platinum") }, /* Canon */
2676 { "labels", _("Labels") },
2677 { "labels-colored", _("Colored Labels") },
2678 { "labels-glossy", _("Glossy Labels") },
2679 { "labels-high-gloss", _("High Gloss Labels") },
2680 { "labels-inkjet", _("Inkjet Labels") },
2681 { "labels-matte", _("Matte Labels") },
2682 { "labels-permanent", _("Permanent Labels") },
2683 { "labels-satin", _("Satin Labels") },
2684 { "labels-security", _("Security Labels") },
2685 { "labels-semi-gloss", _("Semi-Gloss Labels") },
2686 { "laminating-foil", _("Laminating Foil") },
2687 { "letterhead", _("Letterhead") },
2688 { "metal", _("Metal") },
2689 { "metal-glossy", _("Metal Glossy") },
2690 { "metal-high-gloss", _("Metal High Gloss") },
2691 { "metal-matte", _("Metal Matte") },
2692 { "metal-satin", _("Metal Satin") },
2693 { "metal-semi-gloss", _("Metal Semi Gloss") },
2694 { "mounting-tape", _("Mounting Tape") },
2695 { "multi-layer", _("Multi Layer") },
2696 { "multi-part-form", _("Multi Part Form") },
2697 { "other", _("Other") },
2698 { "paper", _("Paper") },
2699 { "photo", _("Photo Paper") }, /* HP mis-spelling */
2700 { "photographic", _("Photo Paper") },
2701 { "photographic-archival", _("Archival Photo Paper") },
2702 { "photographic-film", _("Photo Film") },
2703 { "photographic-glossy", _("Glossy Photo Paper") },
2704 { "photographic-high-gloss", _("High Gloss Photo Paper") },
2705 { "photographic-matte", _("Matte Photo Paper") },
2706 { "photographic-satin", _("Satin Photo Paper") },
2707 { "photographic-semi-gloss", _("Semi-Gloss Photo Paper") },
2708 { "plastic", _("Plastic") },
2709 { "plastic-archival", _("Plastic Archival") },
2710 { "plastic-colored", _("Plastic Colored") },
2711 { "plastic-glossy", _("Plastic Glossy") },
2712 { "plastic-high-gloss", _("Plastic High Gloss") },
2713 { "plastic-matte", _("Plastic Matte") },
2714 { "plastic-satin", _("Plastic Satin") },
2715 { "plastic-semi-gloss", _("Plastic Semi Gloss") },
2716 { "plate", _("Plate") },
2717 { "polyester", _("Polyester") },
2718 { "pre-cut-tabs", _("Pre Cut Tabs") },
2719 { "roll", _("Roll") },
2720 { "screen", _("Screen") },
2721 { "screen-paged", _("Screen Paged") },
2722 { "self-adhesive", _("Self Adhesive") },
2723 { "self-adhesive-film", _("Self Adhesive Film") },
2724 { "shrink-foil", _("Shrink Foil") },
2725 { "single-face", _("Single Face") },
2726 { "single-wall", _("Single Wall Cardboard") },
2727 { "sleeve", _("Sleeve") },
2728 { "stationery", _("Plain Paper") },
2729 { "stationery-archival", _("Archival Paper") },
2730 { "stationery-coated", _("Coated Paper") },
2731 { "stationery-cotton", _("Cotton Paper") },
2732 { "stationery-fine", _("Vellum Paper") },
2733 { "stationery-heavyweight", _("Heavyweight Paper") },
2734 { "stationery-heavyweight-coated", _("Heavyweight Coated Paper") },
2735 { "stationery-inkjet", _("Inkjet Paper") },
2736 { "stationery-letterhead", _("Letterhead") },
2737 { "stationery-lightweight", _("Lightweight Paper") },
2738 { "stationery-preprinted", _("Preprinted Paper") },
2739 { "stationery-prepunched", _("Punched Paper") },
2740 { "tab-stock", _("Tab Stock") },
2741 { "tractor", _("Tractor") },
2742 { "transfer", _("Transfer") },
2743 { "transparency", _("Transparency") },
2744 { "triple-wall", _("Triple Wall Cardboard") },
2745 { "wet-film", _("Wet Film") }
2746 };
2747
2748 human_readable = lookup_option("media-type", opt_strings_catalog,
2749 printer_opt_strings_catalog);
2750 cupsFilePrintf(fp, "*OpenUI *MediaType/%s: PickOne\n"
2751 "*OrderDependency: 10 AnySetup *MediaType\n"
2752 "*DefaultMediaType: %s\n",
2753 (human_readable ? human_readable : "Media Type"),
2754 ppdname);
2755 for (i = 0; i < count; i ++) {
2756 keyword = ippGetString(attr, i, NULL);
2757
2758 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
2759
2760 human_readable = lookup_choice((char *)keyword, "media-type",
2761 opt_strings_catalog,
2762 printer_opt_strings_catalog);
2763 if (human_readable == NULL)
2764 for (j = 0; j < (int)(sizeof(media_types) / sizeof(media_types[0]));
2765 j ++)
2766 if (!strcmp(media_types[j][0], keyword)) {
2767 human_readable = (char *)_cupsLangString(lang, media_types[j][1]);
2768 break;
2769 }
2770 cupsFilePrintf(fp, "*MediaType %s%s%s: \"<</MediaType(%s)>>setpagedevice\"\n",
2771 ppdname,
2772 (human_readable ? "/" : ""),
2773 (human_readable ? human_readable : ""),
2774 ppdname);
2775 }
2776 cupsFilePuts(fp, "*CloseUI: *MediaType\n");
2777 }
2778
2779 /*
2780 * ColorModel...
2781 */
2782 if ((defattr = ippFindAttribute(response, "print-color-mode-default",
2783 IPP_TAG_KEYWORD)) == NULL)
2784 defattr = ippFindAttribute(response, "output-mode-default",
2785 IPP_TAG_KEYWORD);
2786
2787 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) ==
2788 NULL)
2789 if ((attr = ippFindAttribute(response, "print-color-mode-supported",
2790 IPP_TAG_KEYWORD)) == NULL)
2791 if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported",
2792 IPP_TAG_KEYWORD)) == NULL)
2793 attr = ippFindAttribute(response, "output-mode-supported",
2794 IPP_TAG_KEYWORD);
2795
2796 human_readable = lookup_option("print-color-mode", opt_strings_catalog,
2797 printer_opt_strings_catalog);
2798 if (attr && ippGetCount(attr) > 0) {
2799 const char *default_color = NULL; /* Default */
2800 int first_choice = 1,
2801 have_bi_level = 0,
2802 have_mono = 0;
2803
2804 if ((keyword = ippGetString(defattr, 0, NULL)) != NULL)
2805 {
2806 if (!strcmp(keyword, "bi-level"))
2807 default_color = "FastGray";
2808 else if (!strcmp(keyword, "monochrome") ||
2809 !strcmp(keyword, "auto-monochrome"))
2810 default_color = "Gray";
2811 else
2812 default_color = "RGB";
2813 }
2814
2815 cupsFilePrintf(fp, "*%% ColorModel from %s\n", ippGetName(attr));
2816
2817 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2818 keyword = ippGetString(attr, i, NULL); /* Keyword for color/bit depth */
2819
2820 if (!have_bi_level &&
2821 (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") ||
2822 !strcmp(keyword, "process-bi-level"))) {
2823 have_bi_level = 1;
2824 if (first_choice) {
2825 first_choice = 0;
2826 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2827 "*OrderDependency: 10 AnySetup *ColorModel\n",
2828 (human_readable ? human_readable :
2829 _cupsLangString(lang, _("Color Mode"))));
2830 }
2831
2832 human_readable2 = lookup_choice("bi-level", "print-color-mode",
2833 opt_strings_catalog,
2834 printer_opt_strings_catalog);
2835 cupsFilePrintf(fp, "*ColorModel FastGray/%s: \"<</cupsColorSpace 3/cupsBitsPerColor 1/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2836 (human_readable2 ? human_readable2 :
2837 _cupsLangString(lang, _("Fast Grayscale"))));
2838
2839 if (!default_color)
2840 default_color = "FastGray";
2841 } else if (!have_mono &&
2842 (!strcasecmp(keyword, "sgray_8") ||
2843 !strncmp(keyword, "W8", 2) ||
2844 !strcmp(keyword, "monochrome") ||
2845 !strcmp(keyword, "process-monochrome"))) {
2846 have_mono = 1;
2847 if (first_choice) {
2848 first_choice = 0;
2849 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2850 "*OrderDependency: 10 AnySetup *ColorModel\n",
2851 (human_readable ? human_readable :
2852 _cupsLangString(lang, _("Color Mode"))));
2853 }
2854
2855 human_readable2 = lookup_choice("monochrome", "print-color-mode",
2856 opt_strings_catalog,
2857 printer_opt_strings_catalog);
2858 cupsFilePrintf(fp, "*ColorModel Gray/%s: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2859 (human_readable2 ? human_readable2 :
2860 _cupsLangString(lang, _("Grayscale"))));
2861
2862 if (!default_color || (!defattr && !strcmp(default_color, "FastGray")))
2863 default_color = "Gray";
2864 } else if (!strcasecmp(keyword, "sgray_16") ||
2865 !strncmp(keyword, "W8-16", 5) ||
2866 !strncmp(keyword, "W16", 3)) {
2867 if (first_choice) {
2868 first_choice = 0;
2869 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2870 "*OrderDependency: 10 AnySetup *ColorModel\n",
2871 (human_readable ? human_readable :
2872 _cupsLangString(lang, _("Color Mode"))));
2873 }
2874
2875 cupsFilePrintf(fp, "*ColorModel Gray16/%s: \"<</cupsColorSpace 18/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2876 _cupsLangString(lang, _("Deep Gray (High Definition Grayscale)")));
2877
2878 if (!default_color || (!defattr && !strcmp(default_color, "FastGray")))
2879 default_color = "Gray16";
2880 } else if (!strcasecmp(keyword, "srgb_8") ||
2881 !strncmp(keyword, "SRGB24", 6) ||
2882 !strcmp(keyword, "color")) {
2883 if (first_choice) {
2884 first_choice = 0;
2885 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2886 "*OrderDependency: 10 AnySetup *ColorModel\n",
2887 (human_readable ? human_readable :
2888 _cupsLangString(lang, _("Color Mode"))));
2889 }
2890
2891 human_readable2 = lookup_choice("color", "print-color-mode",
2892 opt_strings_catalog,
2893 printer_opt_strings_catalog);
2894 cupsFilePrintf(fp, "*ColorModel RGB/%s: \"<</cupsColorSpace 19/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2895 (human_readable2 ? human_readable2 :
2896 _cupsLangString(lang, _("Color"))));
2897
2898 if (!defattr)
2899 default_color = "RGB";
2900 } else if ((!strcasecmp(keyword, "srgb_16") ||
2901 !strncmp(keyword, "SRGB48", 6)) &&
2902 !ippContainsString(attr, "srgb_8")) {
2903 if (first_choice) {
2904 first_choice = 0;
2905 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2906 "*OrderDependency: 10 AnySetup *ColorModel\n",
2907 (human_readable ? human_readable :
2908 _cupsLangString(lang, _("Color Mode"))));
2909 }
2910
2911 human_readable2 = lookup_choice("color", "print-color-mode",
2912 opt_strings_catalog,
2913 printer_opt_strings_catalog);
2914 cupsFilePrintf(fp, "*ColorModel RGB/%s: \"<</cupsColorSpace 19/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2915 (human_readable2 ? human_readable2 :
2916 _cupsLangString(lang, _("Color"))));
2917
2918 if (!defattr)
2919 default_color = "RGB";
2920
2921 /* Apparently some printers only advertise color support, so make sure
2922 we also do grayscale for these printers... */
2923 if (!ippContainsString(attr, "sgray_8") &&
2924 !ippContainsString(attr, "black_1") &&
2925 !ippContainsString(attr, "black_8") &&
2926 !ippContainsString(attr, "W8") &&
2927 !ippContainsString(attr, "W8-16")) {
2928 human_readable2 = lookup_choice("monochrome", "print-color-mode",
2929 opt_strings_catalog,
2930 printer_opt_strings_catalog);
2931 cupsFilePrintf(fp, "*ColorModel Gray/%s: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2932 (human_readable2 ? human_readable2 :
2933 _cupsLangString(lang, _("Grayscale"))));
2934 }
2935 } else if (!strcasecmp(keyword, "adobe-rgb_16") ||
2936 !strncmp(keyword, "ADOBERGB48", 10) ||
2937 !strncmp(keyword, "ADOBERGB24-48", 13)) {
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 AdobeRGB/%s: \"<</cupsColorSpace 20/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2947 _cupsLangString(lang, _("Deep Color (Wide Color Gamut, AdobeRGB)")));
2948
2949 if (!default_color)
2950 default_color = "AdobeRGB";
2951 } else if ((!strcasecmp(keyword, "adobe-rgb_8") ||
2952 !strcmp(keyword, "ADOBERGB24")) &&
2953 !ippContainsString(attr, "adobe-rgb_16")) {
2954 if (first_choice) {
2955 first_choice = 0;
2956 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2957 "*OrderDependency: 10 AnySetup *ColorModel\n",
2958 (human_readable ? human_readable :
2959 _cupsLangString(lang, _("Color Mode"))));
2960 }
2961
2962 cupsFilePrintf(fp, "*ColorModel AdobeRGB/%s: \"<</cupsColorSpace 20/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2963 _cupsLangString(lang, _("Deep Color (Wide Color Gamut, AdobeRGB)")));
2964
2965 if (!default_color)
2966 default_color = "AdobeRGB";
2967 } else if ((!strcasecmp(keyword, "black_8") &&
2968 !ippContainsString(attr, "black_16")) ||
2969 !strcmp(keyword, "DEVW8")) {
2970 if (first_choice) {
2971 first_choice = 0;
2972 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2973 "*OrderDependency: 10 AnySetup *ColorModel\n",
2974 (human_readable ? human_readable :
2975 _cupsLangString(lang, _("Color Mode"))));
2976 }
2977
2978 cupsFilePrintf(fp, "*ColorModel DeviceGray/%s: \"<</cupsColorSpace 0/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2979 _cupsLangString(lang, _("Device Gray")));
2980 } else if (!strcasecmp(keyword, "black_16") ||
2981 !strcmp(keyword, "DEVW16") ||
2982 !strcmp(keyword, "DEVW8-16")) {
2983 if (first_choice) {
2984 first_choice = 0;
2985 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2986 "*OrderDependency: 10 AnySetup *ColorModel\n",
2987 (human_readable ? human_readable :
2988 _cupsLangString(lang, _("Color Mode"))));
2989 }
2990
2991 cupsFilePrintf(fp, "*ColorModel DeviceGray/%s: \"<</cupsColorSpace 0/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
2992 _cupsLangString(lang, _("Device Gray")));
2993 } else if ((!strcasecmp(keyword, "cmyk_8") &&
2994 !ippContainsString(attr, "cmyk_16")) ||
2995 !strcmp(keyword, "DEVCMYK32")) {
2996 if (first_choice) {
2997 first_choice = 0;
2998 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
2999 "*OrderDependency: 10 AnySetup *ColorModel\n",
3000 (human_readable ? human_readable :
3001 _cupsLangString(lang, _("Color Mode"))));
3002 }
3003
3004 cupsFilePrintf(fp, "*ColorModel CMYK/%s: \"<</cupsColorSpace 6/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
3005 _cupsLangString(lang, _("Device CMYK")));
3006 } else if (!strcasecmp(keyword, "cmyk_16") ||
3007 !strcmp(keyword, "DEVCMYK32-64") ||
3008 !strcmp(keyword, "DEVCMYK64")) {
3009 if (first_choice) {
3010 first_choice = 0;
3011 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3012 "*OrderDependency: 10 AnySetup *ColorModel\n",
3013 (human_readable ? human_readable :
3014 _cupsLangString(lang, _("Color Mode"))));
3015 }
3016
3017 cupsFilePrintf(fp, "*ColorModel CMYK/%s: \"<</cupsColorSpace 6/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
3018 _cupsLangString(lang, _("Device CMYK")));
3019 } else if ((!strcasecmp(keyword, "rgb_8") &&
3020 !ippContainsString(attr, "rgb_16")) ||
3021 !strcmp(keyword, "DEVRGB24")) {
3022 if (first_choice) {
3023 first_choice = 0;
3024 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3025 "*OrderDependency: 10 AnySetup *ColorModel\n",
3026 (human_readable ? human_readable :
3027 _cupsLangString(lang, _("Color Mode"))));
3028 }
3029
3030 cupsFilePrintf(fp, "*ColorModel DeviceRGB/%s: \"<</cupsColorSpace 1/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
3031 _cupsLangString(lang, _("Device RGB")));
3032 } else if (!strcasecmp(keyword, "rgb_16") ||
3033 !strcmp(keyword, "DEVRGB24-48") ||
3034 !strcmp(keyword, "DEVRGB48")) {
3035 if (first_choice) {
3036 first_choice = 0;
3037 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3038 "*OrderDependency: 10 AnySetup *ColorModel\n",
3039 (human_readable ? human_readable :
3040 _cupsLangString(lang, _("Color Mode"))));
3041 }
3042
3043 cupsFilePrintf(fp, "*ColorModel DeviceRGB/%s: \"<</cupsColorSpace 1/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n",
3044 _cupsLangString(lang, _("Device RGB")));
3045 }
3046 }
3047
3048 if (default_pagesize != NULL) {
3049 /* Here we are dealing with a cluster, if the default cluster color
3050 is not supplied we set it Gray*/
3051 if (default_cluster_color != NULL && (!default_color || !defattr)) {
3052 default_color = default_cluster_color;
3053 } else
3054 default_color = "Gray";
3055 }
3056
3057 if (default_color)
3058 cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color);
3059 if (!first_choice)
3060 cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
3061 } else {
3062 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3063 "*OrderDependency: 10 AnySetup *ColorModel\n",
3064 (human_readable ? human_readable :
3065 _cupsLangString(lang, _("Color Mode"))));
3066 cupsFilePrintf(fp, "*DefaultColorModel: Gray\n");
3067 cupsFilePuts(fp, "*ColorModel FastGray/Fast Grayscale: \"<</cupsColorSpace 3/cupsBitsPerColor 1/cupsColorOrder 0/cupsCompression 0/ProcessColorModel /DeviceGray>>setpagedevice\"\n");
3068 cupsFilePuts(fp, "*ColorModel Gray/Grayscale: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0/ProcessColorModel /DeviceGray>>setpagedevice\"\n");
3069 if (color) {
3070 /* Color printer according to DNS-SD (or unknown) */
3071 cupsFilePuts(fp, "*ColorModel RGB/Color: \"<</cupsColorSpace 19/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0/ProcessColorModel /DeviceRGB>>setpagedevice\"\n");
3072 }
3073 cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
3074 }
3075
3076 /*
3077 * Duplex...
3078 */
3079
3080 if (((attr = ippFindAttribute(response, "sides-supported",
3081 IPP_TAG_KEYWORD)) != NULL &&
3082 ippContainsString(attr, "two-sided-long-edge")) ||
3083 (attr == NULL && duplex)) {
3084 human_readable = lookup_option("sides", opt_strings_catalog,
3085 printer_opt_strings_catalog);
3086 cupsFilePrintf(fp, "*OpenUI *Duplex/%s: PickOne\n"
3087 "*OrderDependency: 10 AnySetup *Duplex\n"
3088 "*DefaultDuplex: None\n",
3089 (human_readable ? human_readable :
3090 _cupsLangString(lang, _("2-Sided Printing"))));
3091 human_readable = lookup_choice("one-sided", "sides", opt_strings_catalog,
3092 printer_opt_strings_catalog);
3093 cupsFilePrintf(fp, "*Duplex None/%s: \"<</Duplex false>>setpagedevice\"\n",
3094 (human_readable ? human_readable :
3095 _cupsLangString(lang, _("Off (1-Sided)"))));
3096 human_readable = lookup_choice("two-sided-long-edge", "sides",
3097 opt_strings_catalog,
3098 printer_opt_strings_catalog);
3099 cupsFilePrintf(fp, "*Duplex DuplexNoTumble/%s: \"<</Duplex true/Tumble false>>setpagedevice\"\n",
3100 (human_readable ? human_readable :
3101 _cupsLangString(lang, _("Long-Edge (Portrait)"))));
3102 human_readable = lookup_choice("two-sided-short-edge", "sides",
3103 opt_strings_catalog,
3104 printer_opt_strings_catalog);
3105 cupsFilePrintf(fp, "*Duplex DuplexTumble/%s: \"<</Duplex true/Tumble true>>setpagedevice\"\n",
3106 (human_readable ? human_readable :
3107 _cupsLangString(lang, _("Short-Edge (Landscape)"))));
3108 cupsFilePrintf(fp, "*CloseUI: *Duplex\n");
3109
3110 if ((attr = ippFindAttribute(response, "urf-supported",
3111 IPP_TAG_KEYWORD)) != NULL) {
3112 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
3113 const char *dm = ippGetString(attr, i, NULL); /* DM value */
3114
3115 if (!_cups_strcasecmp(dm, "DM1")) {
3116 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3117 break;
3118 } else if (!_cups_strcasecmp(dm, "DM2")) {
3119 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3120 break;
3121 } else if (!_cups_strcasecmp(dm, "DM3")) {
3122 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3123 break;
3124 } else if (!_cups_strcasecmp(dm, "DM4")) {
3125 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3126 break;
3127 }
3128 }
3129 } else if ((attr = ippFindAttribute(response,
3130 "pwg-raster-document-sheet-back",
3131 IPP_TAG_KEYWORD)) != NULL) {
3132 keyword = ippGetString(attr, 0, NULL); /* Keyword value */
3133
3134 if (!strcmp(keyword, "flipped"))
3135 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3136 else if (!strcmp(keyword, "manual-tumble"))
3137 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3138 else if (!strcmp(keyword, "normal"))
3139 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3140 else
3141 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3142 }
3143 }
3144
3145 /*
3146 * Output bin...
3147 */
3148
3149 if ((attr = ippFindAttribute(response, "output-bin-default",
3150 IPP_TAG_ZERO)) != NULL)
3151 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3152 else
3153 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3154
3155 if ((attr = ippFindAttribute(response, "output-bin-supported",
3156 IPP_TAG_ZERO)) != NULL &&
3157 (count = ippGetCount(attr)) > 0) {
3158 static const char * const output_bins[][2] =
3159 { /* "output-bin" strings */
3160 { "auto", _("Automatic") },
3161 { "bottom", _("Bottom Tray") },
3162 { "center", _("Center Tray") },
3163 { "face-down", _("Face Down") },
3164 { "face-up", _("Face Up") },
3165 { "large-capacity", _("Large Capacity Tray") },
3166 { "left", _("Left Tray") },
3167 { "mailbox-1", _("Mailbox 1") },
3168 { "mailbox-2", _("Mailbox 2") },
3169 { "mailbox-3", _("Mailbox 3") },
3170 { "mailbox-4", _("Mailbox 4") },
3171 { "mailbox-5", _("Mailbox 5") },
3172 { "mailbox-6", _("Mailbox 6") },
3173 { "mailbox-7", _("Mailbox 7") },
3174 { "mailbox-8", _("Mailbox 8") },
3175 { "mailbox-9", _("Mailbox 9") },
3176 { "mailbox-10", _("Mailbox 10") },
3177 { "middle", _("Middle") },
3178 { "my-mailbox", _("My Mailbox") },
3179 { "rear", _("Rear Tray") },
3180 { "right", _("Right Tray") },
3181 { "side", _("Side Tray") },
3182 { "stacker-1", _("Stacker 1") },
3183 { "stacker-2", _("Stacker 2") },
3184 { "stacker-3", _("Stacker 3") },
3185 { "stacker-4", _("Stacker 4") },
3186 { "stacker-5", _("Stacker 5") },
3187 { "stacker-6", _("Stacker 6") },
3188 { "stacker-7", _("Stacker 7") },
3189 { "stacker-8", _("Stacker 8") },
3190 { "stacker-9", _("Stacker 9") },
3191 { "stacker-10", _("Stacker 10") },
3192 { "top", _("Top Tray") },
3193 { "tray-1", _("Tray 1") },
3194 { "tray-2", _("Tray 2") },
3195 { "tray-3", _("Tray 3") },
3196 { "tray-4", _("Tray 4") },
3197 { "tray-5", _("Tray 5") },
3198 { "tray-6", _("Tray 6") },
3199 { "tray-7", _("Tray 7") },
3200 { "tray-8", _("Tray 8") },
3201 { "tray-9", _("Tray 9") },
3202 { "tray-10", _("Tray 10") }
3203 };
3204
3205 human_readable = lookup_option("output-bin", opt_strings_catalog,
3206 printer_opt_strings_catalog);
3207 cupsFilePrintf(fp, "*OpenUI *OutputBin/%s: PickOne\n"
3208 "*OrderDependency: 10 AnySetup *OutputBin\n"
3209 "*DefaultOutputBin: %s\n",
3210 (human_readable ? human_readable : "Output Bin"),
3211 ppdname);
3212 attr2 = ippFindAttribute(response, "printer-output-tray", IPP_TAG_STRING);
3213 for (i = 0; i < count; i ++) {
3214 keyword = ippGetString(attr, i, NULL);
3215
3216 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3217
3218 human_readable = lookup_choice((char *)keyword, "output-bin",
3219 opt_strings_catalog,
3220 printer_opt_strings_catalog);
3221 if (human_readable == NULL)
3222 for (j = 0; j < (int)(sizeof(output_bins) / sizeof(output_bins[0]));
3223 j ++)
3224 if (!strcmp(output_bins[j][0], keyword)) {
3225 human_readable = (char *)_cupsLangString(lang, output_bins[j][1]);
3226 break;
3227 }
3228 cupsFilePrintf(fp, "*OutputBin %s%s%s: \"\"\n",
3229 ppdname,
3230 (human_readable ? "/" : ""),
3231 (human_readable ? human_readable : ""));
3232 outputorderinfofound = 0;
3233 faceupdown = 1;
3234 firsttolast = 1;
3235 if (attr2 && i < ippGetCount(attr2)) {
3236 outbin_properties_octet = ippGetOctetString(attr2, i, &octet_str_len);
3237 memset(outbin_properties, 0, sizeof(outbin_properties));
3238 memcpy(outbin_properties, outbin_properties_octet,
3239 ((size_t)octet_str_len < sizeof(outbin_properties) - 1 ?
3240 (size_t)octet_str_len : sizeof(outbin_properties) - 1));
3241 if (strcasestr(outbin_properties, "pagedelivery=faceUp")) {
3242 outputorderinfofound = 1;
3243 faceupdown = -1;
3244 } else if (strcasestr(outbin_properties, "pagedelivery=faceDown")) {
3245 outputorderinfofound = 1;
3246 faceupdown = 1;
3247 }
3248 if (strcasestr(outbin_properties, "stackingorder=lastToFirst")) {
3249 outputorderinfofound = 1;
3250 firsttolast = -1;
3251 } else if (strcasestr(outbin_properties, "stackingorder=firstToLast")) {
3252 outputorderinfofound = 1;
3253 firsttolast = 1;
3254 }
3255 }
3256 if (outputorderinfofound == 0) {
3257 if (strcasestr(keyword, "face-up")) {
3258 outputorderinfofound = 1;
3259 faceupdown = -1;
3260 }
3261 if (strcasestr(keyword, "face-down")) {
3262 outputorderinfofound = 1;
3263 faceupdown = 1;
3264 }
3265 }
3266 if (outputorderinfofound)
3267 cupsFilePrintf(fp, "*PageStackOrder %s: %s\n",
3268 ppdname,
3269 (firsttolast * faceupdown < 0 ? "Reverse" : "Normal"));
3270 }
3271 cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
3272 }
3273
3274 /*
3275 * Finishing options...
3276 */
3277
3278 if ((attr = ippFindAttribute(response, "finishings-supported",
3279 IPP_TAG_ENUM)) != NULL) {
3280 int value; /* Enum value */
3281 const char *ppd_keyword; /* PPD keyword for enum */
3282 cups_array_t *names; /* Names we've added */
3283 static const char * const base_keywords[] =
3284 { /* Base STD 92 keywords */
3285 NULL, /* none */
3286 "SingleAuto", /* staple */
3287 "SingleAuto", /* punch */
3288 NULL, /* cover */
3289 "BindAuto", /* bind */
3290 "SaddleStitch", /* saddle-stitch */
3291 "EdgeStitchAuto", /* edge-stitch */
3292 "Auto", /* fold */
3293 NULL, /* trim */
3294 NULL, /* bale */
3295 NULL, /* booklet-maker */
3296 NULL, /* jog-offset */
3297 NULL, /* coat */
3298 NULL /* laminate */
3299 };
3300 static const char * const finishings[][2] =
3301 { /* Finishings strings */
3302 { "bale", _("Bale") },
3303 { "bind", _("Bind") },
3304 { "bind-bottom", _("Bind (Reverse Landscape)") },
3305 { "bind-left", _("Bind (Portrait)") },
3306 { "bind-right", _("Bind (Reverse Portrait)") },
3307 { "bind-top", _("Bind (Landscape)") },
3308 { "booklet-maker", _("Booklet Maker") },
3309 { "coat", _("Coat") },
3310 { "cover", _("Cover") },
3311 { "edge-stitch", _("Staple Edge") },
3312 { "edge-stitch-bottom", _("Staple Edge (Reverse Landscape)") },
3313 { "edge-stitch-left", _("Staple Edge (Portrait)") },
3314 { "edge-stitch-right", _("Staple Edge (Reverse Portrait)") },
3315 { "edge-stitch-top", _("Staple Edge (Landscape)") },
3316 { "fold", _("Fold") },
3317 { "fold-accordian", _("Accordian Fold") },
3318 { "fold-double-gate", _("Double Gate Fold") },
3319 { "fold-engineering-z", _("Engineering Z Fold") },
3320 { "fold-gate", _("Gate Fold") },
3321 { "fold-half", _("Half Fold") },
3322 { "fold-half-z", _("Half Z Fold") },
3323 { "fold-left-gate", _("Left Gate Fold") },
3324 { "fold-letter", _("Letter Fold") },
3325 { "fold-parallel", _("Parallel Fold") },
3326 { "fold-poster", _("Poster Fold") },
3327 { "fold-right-gate", _("Right Gate Fold") },
3328 { "fold-z", _("Z Fold") },
3329 { "jog-offset", _("Jog") },
3330 { "laminate", _("Laminate") },
3331 { "punch", _("Punch") },
3332 { "punch-bottom-left", _("Single Punch (Reverse Landscape)") },
3333 { "punch-bottom-right", _("Single Punch (Reverse Portrait)") },
3334 { "punch-double-bottom", _("2-Hole Punch (Reverse Portrait)") },
3335 { "punch-double-left", _("2-Hole Punch (Reverse Landscape)") },
3336 { "punch-double-right", _("2-Hole Punch (Landscape)") },
3337 { "punch-double-top", _("2-Hole Punch (Portrait)") },
3338 { "punch-quad-bottom", _("4-Hole Punch (Reverse Landscape)") },
3339 { "punch-quad-left", _("4-Hole Punch (Portrait)") },
3340 { "punch-quad-right", _("4-Hole Punch (Reverse Portrait)") },
3341 { "punch-quad-top", _("4-Hole Punch (Landscape)") },
3342 { "punch-top-left", _("Single Punch (Portrait)") },
3343 { "punch-top-right", _("Single Punch (Landscape)") },
3344 { "punch-triple-bottom", _("3-Hole Punch (Reverse Landscape)") },
3345 { "punch-triple-left", _("3-Hole Punch (Portrait)") },
3346 { "punch-triple-right", _("3-Hole Punch (Reverse Portrait)") },
3347 { "punch-triple-top", _("3-Hole Punch (Landscape)") },
3348 { "punch-multiple-bottom", _("Multi-Hole Punch (Reverse Landscape)") },
3349 { "punch-multiple-left", _("Multi-Hole Punch (Portrait)") },
3350 { "punch-multiple-right", _("Multi-Hole Punch (Reverse Portrait)") },
3351 { "punch-multiple-top", _("Multi-Hole Punch (Landscape)") },
3352 { "saddle-stitch", _("Saddle Stitch") },
3353 { "staple", _("Staple") },
3354 { "staple-bottom-left", _("Single Staple (Reverse Landscape)") },
3355 { "staple-bottom-right", _("Single Staple (Reverse Portrait)") },
3356 { "staple-dual-bottom", _("Double Staple (Reverse Landscape)") },
3357 { "staple-dual-left", _("Double Staple (Portrait)") },
3358 { "staple-dual-right", _("Double Staple (Reverse Portrait)") },
3359 { "staple-dual-top", _("Double Staple (Landscape)") },
3360 { "staple-top-left", _("Single Staple (Portrait)") },
3361 { "staple-top-right", _("Single Staple (Landscape)") },
3362 { "staple-triple-bottom", _("Triple Staple (Reverse Landscape)") },
3363 { "staple-triple-left", _("Triple Staple (Portrait)") },
3364 { "staple-triple-right", _("Triple Staple (Reverse Portrait)") },
3365 { "staple-triple-top", _("Triple Staple (Landscape)") },
3366 { "trim", _("Cut Media") }
3367 };
3368
3369 count = ippGetCount(attr);
3370 names = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0,
3371 (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
3372 fin_options = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3373
3374 /*
3375 * Staple/Bind/Stitch
3376 */
3377
3378 for (i = 0; i < count; i ++) {
3379 value = ippGetInteger(attr, i);
3380 keyword = ippEnumString("finishings", value);
3381
3382 if (!strncmp(keyword, "staple-", 7) ||
3383 !strncmp(keyword, "bind-", 5) ||
3384 !strncmp(keyword, "edge-stitch-", 12) ||
3385 !strcmp(keyword, "saddle-stitch"))
3386 break;
3387 }
3388
3389 if (i < count) {
3390 static const char * const staple_keywords[] =
3391 { /* StapleLocation keywords */
3392 "SinglePortrait",
3393 "SingleRevLandscape",
3394 "SingleLandscape",
3395 "SingleRevPortrait",
3396 "EdgeStitchPortrait",
3397 "EdgeStitchLandscape",
3398 "EdgeStitchRevPortrait",
3399 "EdgeStitchRevLandscape",
3400 "DualPortrait",
3401 "DualLandscape",
3402 "DualRevPortrait",
3403 "DualRevLandscape",
3404 "TriplePortrait",
3405 "TripleLandscape",
3406 "TripleRevPortrait",
3407 "TripleRevLandscape"
3408 };
3409 static const char * const bind_keywords[] =
3410 { /* StapleLocation binding keywords */
3411 "BindPortrait",
3412 "BindLandscape",
3413 "BindRevPortrait",
3414 "BindRevLandscape"
3415 };
3416
3417 cupsArrayAdd(fin_options, "*StapleLocation");
3418
3419 human_readable = lookup_choice("staple", "finishing-template",
3420 opt_strings_catalog,
3421 printer_opt_strings_catalog);
3422 cupsFilePrintf(fp, "*OpenUI *StapleLocation/%s: PickOne\n",
3423 (human_readable ? human_readable :
3424 _cupsLangString(lang, _("Staple"))));
3425 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
3426 cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
3427 cupsFilePrintf(fp, "*StapleLocation None/%s: \"\"\n",
3428 _cupsLangString(lang, _("None")));
3429
3430 for (; i < count; i ++) {
3431 value = ippGetInteger(attr, i);
3432 keyword = ippEnumString("finishings", value);
3433
3434 if (strncmp(keyword, "staple-", 7) &&
3435 strncmp(keyword, "bind-", 5) &&
3436 strncmp(keyword, "edge-stitch-", 12) &&
3437 strcmp(keyword, "saddle-stitch"))
3438 continue;
3439
3440 if (cupsArrayFind(names, (char *)keyword))
3441 continue; /* Already did this finishing template */
3442
3443 cupsArrayAdd(names, (char *)keyword);
3444
3445 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
3446 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
3447 else if (value >= IPP_FINISHINGS_STAPLE_TOP_LEFT &&
3448 value <= IPP_FINISHINGS_STAPLE_TRIPLE_BOTTOM)
3449 ppd_keyword = staple_keywords[value - IPP_FINISHINGS_STAPLE_TOP_LEFT];
3450 else if (value >= IPP_FINISHINGS_BIND_LEFT &&
3451 value <= IPP_FINISHINGS_BIND_BOTTOM)
3452 ppd_keyword = bind_keywords[value - IPP_FINISHINGS_BIND_LEFT];
3453 else
3454 ppd_keyword = NULL;
3455
3456 if (!ppd_keyword)
3457 continue;
3458
3459 snprintf(buf, sizeof(buf), "%d", value);
3460 human_readable = lookup_choice(buf, "finishings", opt_strings_catalog,
3461 printer_opt_strings_catalog);
3462 if (human_readable == NULL)
3463 for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0]));
3464 j ++)
3465 if (!strcmp(finishings[j][0], keyword)) {
3466 human_readable = (char *)_cupsLangString(lang, finishings[j][1]);
3467 break;
3468 }
3469 cupsFilePrintf(fp, "*StapleLocation %s%s%s: \"\"\n", ppd_keyword,
3470 (human_readable ? "/" : ""),
3471 (human_readable ? human_readable : ""));
3472 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n",
3473 value, keyword, ppd_keyword);
3474 }
3475
3476 cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
3477 }
3478
3479 /*
3480 * Fold
3481 */
3482
3483 for (i = 0; i < count; i ++) {
3484 value = ippGetInteger(attr, i);
3485 keyword = ippEnumString("finishings", value);
3486
3487 if (!strncmp(keyword, "cups-fold-", 10) ||
3488 !strcmp(keyword, "fold") ||
3489 !strncmp(keyword, "fold-", 5))
3490 break;
3491 }
3492
3493 if (i < count) {
3494 static const char * const fold_keywords[] =
3495 { /* FoldType keywords */
3496 "Accordion",
3497 "DoubleGate",
3498 "Gate",
3499 "Half",
3500 "HalfZ",
3501 "LeftGate",
3502 "Letter",
3503 "Parallel",
3504 "XFold",
3505 "RightGate",
3506 "ZFold",
3507 "EngineeringZ"
3508 };
3509
3510 cupsArrayAdd(fin_options, "*FoldType");
3511
3512 human_readable = lookup_choice("fold", "finishing-template",
3513 opt_strings_catalog,
3514 printer_opt_strings_catalog);
3515 cupsFilePrintf(fp, "*OpenUI *FoldType/%s: PickOne\n",
3516 (human_readable ? human_readable :
3517 _cupsLangString(lang, _("Fold"))));
3518 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
3519 cupsFilePuts(fp, "*DefaultFoldType: None\n");
3520 cupsFilePrintf(fp, "*FoldType None/%s: \"\"\n",
3521 _cupsLangString(lang, _("None")));
3522
3523 for (; i < count; i ++) {
3524 value = ippGetInteger(attr, i);
3525 keyword = ippEnumString("finishings", value);
3526
3527 if (!strncmp(keyword, "cups-fold-", 10))
3528 keyword += 5;
3529 else if (strcmp(keyword, "fold") && strncmp(keyword, "fold-", 5))
3530 continue;
3531
3532 if (cupsArrayFind(names, (char *)keyword))
3533 continue; /* Already did this finishing template */
3534
3535 cupsArrayAdd(names, (char *)keyword);
3536
3537 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
3538 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
3539 else if (value >= IPP_FINISHINGS_FOLD_ACCORDION &&
3540 value <= IPP_FINISHINGS_FOLD_ENGINEERING_Z)
3541 ppd_keyword = fold_keywords[value - IPP_FINISHINGS_FOLD_ACCORDION];
3542 else if (value >= IPP_FINISHINGS_CUPS_FOLD_ACCORDION &&
3543 value <= IPP_FINISHINGS_CUPS_FOLD_Z)
3544 ppd_keyword = fold_keywords[value -
3545 IPP_FINISHINGS_CUPS_FOLD_ACCORDION];
3546 else
3547 ppd_keyword = NULL;
3548
3549 if (!ppd_keyword)
3550 continue;
3551
3552 snprintf(buf, sizeof(buf), "%d", value);
3553 human_readable = lookup_choice(buf, "finishings", opt_strings_catalog,
3554 printer_opt_strings_catalog);
3555 if (human_readable == NULL)
3556 for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0]));
3557 j ++)
3558 if (!strcmp(finishings[j][0], keyword)) {
3559 human_readable = (char *)_cupsLangString(lang, finishings[j][1]);
3560 break;
3561 }
3562 cupsFilePrintf(fp, "*FoldType %s%s%s: \"\"\n", ppd_keyword,
3563 (human_readable ? "/" : ""),
3564 (human_readable ? human_readable : ""));
3565 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n",
3566 value, keyword, ppd_keyword);
3567 }
3568
3569 cupsFilePuts(fp, "*CloseUI: *FoldType\n");
3570 }
3571
3572 /*
3573 * Punch
3574 */
3575
3576 for (i = 0; i < count; i ++) {
3577 value = ippGetInteger(attr, i);
3578 keyword = ippEnumString("finishings", value);
3579
3580 if (!strncmp(keyword, "cups-punch-", 11) ||
3581 !strncmp(keyword, "punch-", 6))
3582 break;
3583 }
3584
3585 if (i < count) {
3586 static const char * const punch_keywords[] =
3587 { /* PunchMedia keywords */
3588 "SinglePortrait",
3589 "SingleRevLandscape",
3590 "SingleLandscape",
3591 "SingleRevPortrait",
3592 "DualPortrait",
3593 "DualLandscape",
3594 "DualRevPortrait",
3595 "DualRevLandscape",
3596 "TriplePortrait",
3597 "TripleLandscape",
3598 "TripleRevPortrait",
3599 "TripleRevLandscape",
3600 "QuadPortrait",
3601 "QuadLandscape",
3602 "QuadRevPortrait",
3603 "QuadRevLandscape",
3604 "MultiplePortrait",
3605 "MultipleLandscape",
3606 "MultipleRevPortrait",
3607 "MultipleRevLandscape"
3608 };
3609
3610 cupsArrayAdd(fin_options, "*PunchMedia");
3611
3612 human_readable = lookup_choice("punch", "finishing-template",
3613 opt_strings_catalog,
3614 printer_opt_strings_catalog);
3615 cupsFilePrintf(fp, "*OpenUI *PunchMedia/%s: PickOne\n",
3616 (human_readable ? human_readable :
3617 _cupsLangString(lang, _("Punch"))));
3618 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
3619 cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
3620 cupsFilePrintf(fp, "*PunchMedia None/%s: \"\"\n",
3621 _cupsLangString(lang, _("None")));
3622
3623 for (i = 0; i < count; i ++) {
3624 value = ippGetInteger(attr, i);
3625 keyword = ippEnumString("finishings", value);
3626
3627 if (!strncmp(keyword, "cups-punch-", 11))
3628 keyword += 5;
3629 else if (strncmp(keyword, "punch-", 6))
3630 continue;
3631
3632 if (cupsArrayFind(names, (char *)keyword))
3633 continue; /* Already did this finishing template */
3634
3635 cupsArrayAdd(names, (char *)keyword);
3636
3637 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
3638 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
3639 else if (value >= IPP_FINISHINGS_PUNCH_TOP_LEFT &&
3640 value <= IPP_FINISHINGS_PUNCH_MULTIPLE_BOTTOM)
3641 ppd_keyword = punch_keywords[value - IPP_FINISHINGS_PUNCH_TOP_LEFT];
3642 else if (value >= IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT &&
3643 value <= IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM)
3644 ppd_keyword = punch_keywords[value -
3645 IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT];
3646 else
3647 ppd_keyword = NULL;
3648
3649 if (!ppd_keyword)
3650 continue;
3651
3652 snprintf(buf, sizeof(buf), "%d", value);
3653 human_readable = lookup_choice(buf, "finishings", opt_strings_catalog,
3654 printer_opt_strings_catalog);
3655 if (human_readable == NULL)
3656 for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0]));
3657 j ++)
3658 if (!strcmp(finishings[j][0], keyword)) {
3659 human_readable = (char *)_cupsLangString(lang, finishings[j][1]);
3660 break;
3661 }
3662 cupsFilePrintf(fp, "*PunchMedia %s%s%s: \"\"\n", ppd_keyword,
3663 (human_readable ? "/" : ""),
3664 (human_readable ? human_readable : ""));
3665 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n",
3666 value, keyword, ppd_keyword);
3667 }
3668
3669 cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
3670 }
3671
3672 /*
3673 * Booklet
3674 */
3675
3676 if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER)) {
3677 cupsArrayAdd(fin_options, "*Booklet");
3678
3679 human_readable = lookup_choice("booklet-maker", "finishing-template",
3680 opt_strings_catalog,
3681 printer_opt_strings_catalog);
3682 cupsFilePrintf(fp, "*OpenUI *Booklet/%s: Boolean\n",
3683 (human_readable ? human_readable :
3684 _cupsLangString(lang, _("Booklet"))));
3685 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
3686 cupsFilePuts(fp, "*DefaultBooklet: False\n");
3687 cupsFilePuts(fp, "*Booklet False: \"\"\n");
3688 cupsFilePuts(fp, "*Booklet True: \"\"\n");
3689 cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n",
3690 IPP_FINISHINGS_BOOKLET_MAKER);
3691 cupsFilePuts(fp, "*CloseUI: *Booklet\n");
3692 }
3693
3694 /*
3695 * CutMedia
3696 */
3697
3698 for (i = 0; i < count; i ++) {
3699 value = ippGetInteger(attr, i);
3700 keyword = ippEnumString("finishings", value);
3701
3702 if (!strcmp(keyword, "trim") || !strncmp(keyword, "trim-", 5))
3703 break;
3704 }
3705
3706 if (i < count) {
3707 static const char * const trim_keywords[] =
3708 { /* CutMedia keywords */
3709 "EndOfPage",
3710 "EndOfDoc",
3711 "EndOfSet",
3712 "EndOfJob"
3713 };
3714
3715 cupsArrayAdd(fin_options, "*CutMedia");
3716
3717 human_readable = lookup_choice("trim", "finishing-template",
3718 opt_strings_catalog,
3719 printer_opt_strings_catalog);
3720 cupsFilePrintf(fp, "*OpenUI *CutMedia/%s: PickOne\n",
3721 (human_readable ? human_readable :
3722 _cupsLangString(lang, _("Cut"))));
3723 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *CutMedia\n");
3724 cupsFilePuts(fp, "*DefaultCutMedia: None\n");
3725 cupsFilePrintf(fp, "*CutMedia None/%s: \"\"\n",
3726 _cupsLangString(lang, _("None")));
3727
3728 for (i = 0; i < count; i ++) {
3729 value = ippGetInteger(attr, i);
3730 keyword = ippEnumString("finishings", value);
3731
3732 if (strcmp(keyword, "trim") && strncmp(keyword, "trim-", 5))
3733 continue;
3734
3735 if (cupsArrayFind(names, (char *)keyword))
3736 continue; /* Already did this finishing template */
3737
3738 cupsArrayAdd(names, (char *)keyword);
3739
3740 if (value == IPP_FINISHINGS_TRIM)
3741 ppd_keyword = "Auto";
3742 else
3743 ppd_keyword = trim_keywords[value - IPP_FINISHINGS_TRIM_AFTER_PAGES];
3744
3745 snprintf(buf, sizeof(buf), "%d", value);
3746 human_readable = lookup_choice(buf, "finishings", opt_strings_catalog,
3747 printer_opt_strings_catalog);
3748 if (human_readable == NULL)
3749 for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0]));
3750 j ++)
3751 if (!strcmp(finishings[j][0], keyword)) {
3752 human_readable = (char *)_cupsLangString(lang, finishings[j][1]);
3753 break;
3754 }
3755 cupsFilePrintf(fp, "*CutMedia %s%s%s: \"\"\n", ppd_keyword,
3756 (human_readable ? "/" : ""),
3757 (human_readable ? human_readable : ""));
3758 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*CutMedia %s\"\n",
3759 value, keyword, ppd_keyword);
3760 }
3761
3762 cupsFilePuts(fp, "*CloseUI: *CutMedia\n");
3763 }
3764
3765 cupsArrayDelete(names);
3766 }
3767
3768 if ((attr = ippFindAttribute(response, "finishings-col-database",
3769 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
3770 ipp_t *finishing_col; /* Current finishing collection */
3771 ipp_attribute_t *finishing_attr; /* Current finishing member attribute */
3772 cups_array_t *templates; /* Finishing templates */
3773
3774 cupsFilePrintf(fp, "*OpenUI *cupsFinishingTemplate/%s: PickOne\n",
3775 _cupsLangString(lang, _("Finishing Preset")));
3776 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n");
3777 cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n");
3778 cupsFilePrintf(fp, "*cupsFinishingTemplate none/%s: \"\"\n",
3779 _cupsLangString(lang, _("None")));
3780
3781 templates = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3782 count = ippGetCount(attr);
3783
3784 for (i = 0; i < count; i ++) {
3785 finishing_col = ippGetCollection(attr, i);
3786 keyword = ippGetString(ippFindAttribute(finishing_col,
3787 "finishing-template",
3788 IPP_TAG_ZERO), 0, NULL);
3789
3790 if (!keyword || cupsArrayFind(templates, (void *)keyword))
3791 continue;
3792
3793 if (!strcmp(keyword, "none"))
3794 continue;
3795
3796 cupsArrayAdd(templates, (void *)keyword);
3797
3798 human_readable = lookup_choice((char *)keyword, "finishing-template",
3799 opt_strings_catalog,
3800 printer_opt_strings_catalog);
3801 if (human_readable == NULL)
3802 human_readable = (char *)keyword;
3803 cupsFilePrintf(fp, "*cupsFinishingTemplate %s/%s: \"\n", keyword,
3804 human_readable);
3805 for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr;
3806 finishing_attr = ippNextAttribute(finishing_col)) {
3807 if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION) {
3808 const char *name = ippGetName(finishing_attr);
3809 /* Member attribute name */
3810
3811 if (strcmp(name, "media-size"))
3812 cupsFilePrintf(fp, "%% %s\n", name);
3813 }
3814 }
3815 cupsFilePuts(fp, "\"\n");
3816 cupsFilePuts(fp, "*End\n");
3817 }
3818
3819 cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n");
3820
3821 if (cupsArrayCount(fin_options)) {
3822 const char *fin_option; /* Current finishing option */
3823
3824 cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate");
3825 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option;
3826 fin_option = (const char *)cupsArrayNext(fin_options))
3827 cupsFilePrintf(fp, " %s", fin_option);
3828 cupsFilePuts(fp, "\"\n");
3829
3830 cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate None");
3831 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option;
3832 fin_option = (const char *)cupsArrayNext(fin_options))
3833 cupsFilePrintf(fp, " %s None", fin_option);
3834 cupsFilePuts(fp, "\"\n");
3835 }
3836
3837 cupsArrayDelete(templates);
3838 }
3839
3840 cupsArrayDelete(fin_options);
3841
3842 /*
3843 * DefaultResolution...
3844 */
3845
3846 xres = common_def->x;
3847 yres = common_def->y;
3848 if (xres == yres)
3849 cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", xres);
3850 else
3851 cupsFilePrintf(fp, "*DefaultResolution: %dx%ddpi\n", xres, yres);
3852
3853 /*
3854 * cupsPrintQuality...
3855 */
3856
3857 if ((quality =
3858 ippFindAttribute(response, "print-quality-supported",
3859 IPP_TAG_ENUM)) != NULL) {
3860 human_readable = lookup_option("print-quality", opt_strings_catalog,
3861 printer_opt_strings_catalog);
3862 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
3863 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
3864 "*DefaultcupsPrintQuality: Normal\n",
3865 (human_readable ? human_readable :
3866 _cupsLangString(lang, _("Print Quality"))));
3867 if (ippContainsInteger(quality, IPP_QUALITY_DRAFT)) {
3868 human_readable = lookup_choice("3", "print-quality", opt_strings_catalog,
3869 printer_opt_strings_catalog);
3870 cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n",
3871 (human_readable ? human_readable :
3872 _cupsLangString(lang, _("Draft"))),
3873 min_res->x, min_res->y);
3874 }
3875 human_readable = lookup_choice("4", "print-quality", opt_strings_catalog,
3876 printer_opt_strings_catalog);
3877 cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n",
3878 (human_readable ? human_readable :
3879 _cupsLangString(lang, _("Normal"))),
3880 common_def->x, common_def->y);
3881 if (ippContainsInteger(quality, IPP_QUALITY_HIGH)) {
3882 human_readable = lookup_choice("5", "print-quality", opt_strings_catalog,
3883 printer_opt_strings_catalog);
3884 cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n",
3885 (human_readable ? human_readable :
3886 _cupsLangString(lang, _("High"))),
3887 max_res->x, max_res->y);
3888 }
3889 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
3890 }
3891
3892 /* Only add these options if jobs get sent to the printer as PDF,
3893 PWG Raster, or Apple Raster, as only then arbitrary IPP
3894 attributes get passed through from the filter command line
3895 to the printer by the "ipp" CUPS backend. */
3896 if (is_pdf || is_pwg || is_apple) {
3897 /*
3898 * Print Optimization ...
3899 */
3900
3901 if ((attr = ippFindAttribute(response, "print-content-optimize-default",
3902 IPP_TAG_ZERO)) != NULL)
3903 strlcpy(ppdname, ippGetString(attr, 0, NULL), sizeof(ppdname));
3904 else
3905 strlcpy(ppdname, "auto", sizeof(ppdname));
3906
3907 if ((attr = ippFindAttribute(response, "print-content-optimize-supported",
3908 IPP_TAG_ZERO)) != NULL &&
3909 (count = ippGetCount(attr)) > 1) {
3910 static const char * const content_optimize_types[][2] =
3911 { /* "print-content-optimize" strings */
3912 { "auto", _("Automatic") },
3913 { "graphic", _("Graphics") },
3914 { "graphics", _("Graphics") },
3915 { "photo", _("Photo") },
3916 { "text", _("Text") },
3917 { "text-and-graphic", _("Text And Graphics") },
3918 { "text-and-graphics", _("Text And Graphics") }
3919 };
3920
3921 human_readable = lookup_option("print-content-optimize",
3922 opt_strings_catalog,
3923 printer_opt_strings_catalog);
3924 cupsFilePrintf(fp, "*OpenUI *print-content-optimize/%s: PickOne\n"
3925 "*OrderDependency: 10 AnySetup *print-content-optimize\n"
3926 "*Defaultprint-content-optimize: %s\n",
3927 (human_readable ? human_readable : "Print Optimization"),
3928 ppdname);
3929 for (i = 0; i < count; i ++) {
3930 keyword = ippGetString(attr, i, NULL);
3931
3932 human_readable = lookup_choice((char *)keyword,
3933 "print-content-optimize",
3934 opt_strings_catalog,
3935 printer_opt_strings_catalog);
3936 if (human_readable == NULL)
3937 for (j = 0;
3938 j < (int)(sizeof(content_optimize_types) /
3939 sizeof(content_optimize_types[0]));
3940 j ++)
3941 if (!strcmp(content_optimize_types[j][0], keyword)) {
3942 human_readable =
3943 (char *)_cupsLangString(lang,
3944 content_optimize_types[j][1]);
3945 break;
3946 }
3947 cupsFilePrintf(fp, "*print-content-optimize %s%s%s: \"\"\n",
3948 keyword,
3949 (human_readable ? "/" : ""),
3950 (human_readable ? human_readable : ""));
3951 }
3952 cupsFilePuts(fp, "*CloseUI: *print-content-optimize\n");
3953 }
3954
3955 /*
3956 * Print Rendering Intent ...
3957 */
3958
3959 if ((attr = ippFindAttribute(response, "print-rendering-intent-default",
3960 IPP_TAG_ZERO)) != NULL)
3961 strlcpy(ppdname, ippGetString(attr, 0, NULL), sizeof(ppdname));
3962 else
3963 strlcpy(ppdname, "auto", sizeof(ppdname));
3964
3965 if ((attr = ippFindAttribute(response, "print-rendering-intent-supported",
3966 IPP_TAG_ZERO)) != NULL &&
3967 (count = ippGetCount(attr)) > 1) {
3968 static const char * const rendering_intents[][2] =
3969 { /* "print-rendering-intent" strings */
3970 { "auto", _("Automatic") },
3971 { "absolute", _("Absolute") },
3972 { "perceptual", _("Perceptual") },
3973 { "relative", _("Relative") },
3974 { "relative-bpc", _("Relative w/Black Point Compensation") },
3975 { "saturation", _("Saturation") }
3976 };
3977
3978 human_readable = lookup_option("print-rendering-intent",
3979 opt_strings_catalog,
3980 printer_opt_strings_catalog);
3981 cupsFilePrintf(fp, "*OpenUI *print-rendering-intent/%s: PickOne\n"
3982 "*OrderDependency: 10 AnySetup *print-rendering-intent\n"
3983 "*Defaultprint-rendering-intent: %s\n",
3984 (human_readable ? human_readable :
3985 "Print Rendering Intent"),
3986 ppdname);
3987 for (i = 0; i < count; i ++) {
3988 keyword = ippGetString(attr, i, NULL);
3989
3990 human_readable = lookup_choice((char *)keyword,
3991 "print-rendering-intent",
3992 opt_strings_catalog,
3993 printer_opt_strings_catalog);
3994 if (human_readable == NULL)
3995 for (j = 0;
3996 j < (int)(sizeof(rendering_intents) /
3997 sizeof(rendering_intents[0]));
3998 j ++)
3999 if (!strcmp(rendering_intents[j][0], keyword)) {
4000 human_readable =
4001 (char *)_cupsLangString(lang,
4002 rendering_intents[j][1]);
4003 break;
4004 }
4005 cupsFilePrintf(fp, "*print-rendering-intent %s%s%s: \"\"\n",
4006 keyword,
4007 (human_readable ? "/" : ""),
4008 (human_readable ? human_readable : ""));
4009 }
4010 cupsFilePuts(fp, "*CloseUI: *print-rendering-intent\n");
4011 }
4012
4013 /*
4014 * Print Scaling ...
4015 */
4016
4017 if ((attr = ippFindAttribute(response, "print-scaling-default",
4018 IPP_TAG_ZERO)) != NULL)
4019 strlcpy(ppdname, ippGetString(attr, 0, NULL), sizeof(ppdname));
4020 else
4021 strlcpy(ppdname, "auto", sizeof(ppdname));
4022
4023 if ((attr = ippFindAttribute(response, "print-scaling-supported",
4024 IPP_TAG_ZERO)) != NULL &&
4025 (count = ippGetCount(attr)) > 1) {
4026 static const char * const scaling_types[][2] =
4027 { /* "print-scaling" strings */
4028 { "auto", _("Automatic") },
4029 { "auto-fit", _("Auto Fit") },
4030 { "fill", _("Fill") },
4031 { "fit", _("Fit") },
4032 { "none", _("None") }
4033 };
4034
4035 human_readable = lookup_option("print-scaling", opt_strings_catalog,
4036 printer_opt_strings_catalog);
4037 cupsFilePrintf(fp, "*OpenUI *print-scaling/%s: PickOne\n"
4038 "*OrderDependency: 10 AnySetup *print-scaling\n"
4039 "*Defaultprint-scaling: %s\n",
4040 (human_readable ? human_readable : "Print Scaling"),
4041 ppdname);
4042 for (i = 0; i < count; i ++) {
4043 keyword = ippGetString(attr, i, NULL);
4044
4045 human_readable = lookup_choice((char *)keyword, "print-scaling",
4046 opt_strings_catalog,
4047 printer_opt_strings_catalog);
4048 if (human_readable == NULL)
4049 for (j = 0;
4050 j < (int)(sizeof(scaling_types) /
4051 sizeof(scaling_types[0]));
4052 j ++)
4053 if (!strcmp(scaling_types[j][0], keyword)) {
4054 human_readable =
4055 (char *)_cupsLangString(lang, scaling_types[j][1]);
4056 break;
4057 }
4058 cupsFilePrintf(fp, "*print-scaling %s%s%s: \"\"\n",
4059 keyword,
4060 (human_readable ? "/" : ""),
4061 (human_readable ? human_readable : ""));
4062 }
4063 cupsFilePuts(fp, "*CloseUI: *print-scaling\n");
4064 }
4065 }
4066
4067 /*
4068 * Phone Options for Fax..
4069 */
4070
4071 if (is_fax) {
4072 human_readable = lookup_option("Phone", opt_strings_catalog,
4073 printer_opt_strings_catalog);
4074
4075 cupsFilePrintf(fp, "*OpenUI *phone/%s: PickOne\n"
4076 "*OrderDependency: 10 AnySetup *phone\n"
4077 "*Defaultphone: None\n"
4078 "*phone None: \"\"\n"
4079 "*CloseUI: *phone\n",
4080 (human_readable ? human_readable : "Phone Number"));
4081 cupsFilePrintf(fp,"*Customphone True: \"\"\n"
4082 "*ParamCustomphone Text: 1 string 0 64\n");
4083
4084 human_readable = lookup_option("faxPrefix", opt_strings_catalog,
4085 printer_opt_strings_catalog);
4086
4087 cupsFilePrintf(fp, "*OpenUI *faxPrefix/%s: PickOne\n"
4088 "*OrderDependency: 10 AnySetup *faxPrefix\n"
4089 "*DefaultfaxPrefix: None\n"
4090 "*faxPrefix None: \"\"\n"
4091 "*CloseUI: *faxPrefix\n",
4092 (human_readable ? human_readable : "Pre-Dial Number"));
4093 cupsFilePrintf(fp,"*CustomfaxPrefix True: \"\"\n"
4094 "*ParamCustomfaxPrefix Text: 1 string 0 64\n");
4095 }
4096
4097 /*
4098 * Presets...
4099 */
4100
4101 if ((attr = ippFindAttribute(response, "job-presets-supported",
4102 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
4103 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
4104 ipp_t *preset = ippGetCollection(attr, i); /* Preset collection */
4105 const char *preset_name = /* Preset name */
4106 ippGetString(ippFindAttribute(preset,
4107 "preset-name", IPP_TAG_ZERO), 0, NULL),
4108 *localized_name; /* Localized preset name */
4109 ipp_attribute_t *member; /* Member attribute in preset */
4110 const char *member_name; /* Member attribute name */
4111 char member_value[256]; /* Member attribute value */
4112
4113 if (!preset || !preset_name)
4114 continue;
4115
4116 if ((localized_name = lookup_option((char *)preset_name,
4117 opt_strings_catalog,
4118 printer_opt_strings_catalog)) == NULL)
4119 cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", preset_name);
4120 else
4121 cupsFilePrintf(fp, "*APPrinterPreset %s/%s: \"\n", preset_name,
4122 localized_name);
4123
4124 for (member = ippFirstAttribute(preset); member;
4125 member = ippNextAttribute(preset)) {
4126 member_name = ippGetName(member);
4127
4128 if (!member_name || !strcmp(member_name, "preset-name"))
4129 continue;
4130
4131 if (!strcmp(member_name, "finishings")) {
4132 for (i = 0, count = ippGetCount(member); i < count; i ++) {
4133 const char *option = NULL; /* PPD option name */
4134
4135 keyword = ippEnumString("finishings", ippGetInteger(member, i));
4136
4137 if (!strcmp(keyword, "booklet-maker")) {
4138 option = "Booklet";
4139 keyword = "True";
4140 } else if (!strncmp(keyword, "fold-", 5))
4141 option = "FoldType";
4142 else if (!strncmp(keyword, "punch-", 6))
4143 option = "PunchMedia";
4144 else if (!strncmp(keyword, "bind-", 5) ||
4145 !strncmp(keyword, "edge-stitch-", 12) ||
4146 !strcmp(keyword, "saddle-stitch") ||
4147 !strncmp(keyword, "staple-", 7))
4148 option = "StapleLocation";
4149
4150 if (option && keyword)
4151 cupsFilePrintf(fp, "*%s %s\n", option, keyword);
4152 }
4153 } else if (!strcmp(member_name, "finishings-col")) {
4154 ipp_t *fin_col; /* finishings-col value */
4155
4156 for (i = 0, count = ippGetCount(member); i < count; i ++) {
4157 fin_col = ippGetCollection(member, i);
4158
4159 if ((keyword =
4160 ippGetString(ippFindAttribute(fin_col,
4161 "finishing-template",
4162 IPP_TAG_ZERO), 0, NULL)) != NULL)
4163 cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", keyword);
4164 }
4165 } else if (!strcmp(member_name, "media")) {
4166 /*
4167 * Map media to PageSize...
4168 */
4169
4170 if ((pwg = pwgMediaForPWG(ippGetString(member, 0, NULL))) != NULL &&
4171 pwg->ppd)
4172 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4173 } else if (!strcmp(member_name, "media-col")) {
4174 media_col = ippGetCollection(member, 0);
4175
4176 if ((media_size =
4177 ippGetCollection(ippFindAttribute(media_col,
4178 "media-size",
4179 IPP_TAG_BEGIN_COLLECTION),
4180 0)) != NULL) {
4181 x_dim = ippFindAttribute(media_size, "x-dimension",
4182 IPP_TAG_INTEGER);
4183 y_dim = ippFindAttribute(media_size, "y-dimension",
4184 IPP_TAG_INTEGER);
4185 if ((pwg = pwgMediaForSize(ippGetInteger(x_dim, 0),
4186 ippGetInteger(y_dim, 0))) != NULL &&
4187 pwg->ppd)
4188 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4189 }
4190
4191 if ((keyword = ippGetString(ippFindAttribute(media_col,
4192 "media-source",
4193 IPP_TAG_ZERO), 0,
4194 NULL)) != NULL) {
4195 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4196 cupsFilePrintf(fp, "*InputSlot %s\n", keyword);
4197 }
4198
4199 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-type",
4200 IPP_TAG_ZERO), 0,
4201 NULL)) != NULL) {
4202 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4203 cupsFilePrintf(fp, "*MediaType %s\n", keyword);
4204 }
4205 } else if (!strcmp(member_name, "print-quality")) {
4206 /*
4207 * Map print-quality to cupsPrintQuality...
4208 */
4209
4210 int qval = ippGetInteger(member, 0);
4211 /* print-quality value */
4212 static const char * const qualities[] = { "Draft", "Normal", "High" };
4213 /* cupsPrintQuality values */
4214
4215 if (qval >= IPP_QUALITY_DRAFT && qval <= IPP_QUALITY_HIGH)
4216 cupsFilePrintf(fp, "*cupsPrintQuality %s\n",
4217 qualities[qval - IPP_QUALITY_DRAFT]);
4218 } else if (!strcmp(member_name, "output-bin")) {
4219 pwg_ppdize_name(ippGetString(member, 0, NULL), ppdname,
4220 sizeof(ppdname));
4221 cupsFilePrintf(fp, "*OutputBin %s\n", ppdname);
4222 } else if (!strcmp(member_name, "sides")) {
4223 keyword = ippGetString(member, 0, NULL);
4224 if (keyword && !strcmp(keyword, "one-sided"))
4225 cupsFilePuts(fp, "*Duplex None\n");
4226 else if (keyword && !strcmp(keyword, "two-sided-long-edge"))
4227 cupsFilePuts(fp, "*Duplex DuplexNoTumble\n");
4228 else if (keyword && !strcmp(keyword, "two-sided-short-edge"))
4229 cupsFilePuts(fp, "*Duplex DuplexTumble\n");
4230 } else {
4231 /*
4232 * Add attribute name and value as-is...
4233 */
4234
4235 ippAttributeString(member, member_value, sizeof(member_value));
4236 cupsFilePrintf(fp, "*%s %s\n", member_name, member_value);
4237 }
4238 }
4239
4240 cupsFilePuts(fp, "\"\n*End\n");
4241 }
4242 }
4243
4244 /*
4245 * constraints
4246 */
4247 if (conflicts != NULL) {
4248 char* constraint;
4249 for (constraint = (char *)cupsArrayFirst(conflicts); constraint;
4250 constraint = (char *)cupsArrayNext(conflicts)) {
4251 cupsFilePrintf(fp, "%s", constraint);
4252 }
4253 }
4254
4255 /*
4256 * Close up and return...
4257 */
4258
4259 free(common_def);
4260 free(min_res);
4261 free(max_res);
4262
4263 snprintf(ppdgenerator_msg, sizeof(ppdgenerator_msg),
4264 "%s %sPPD generated.",
4265 (is_apple ? "Apple Raster" :
4266 (is_pwg ? "PWG Raster" :
4267 (is_pdf ? "PDF" :
4268 (is_pclm ? "PCLm" :
4269 "Legacy IPP printer")))),
4270 (is_fax ? "Fax " : ""));
4271
4272 cupsFileClose(fp);
4273 if (printer_opt_strings_catalog)
4274 cupsArrayDelete(printer_opt_strings_catalog);
4275
4276 return (buffer);
4277
4278 /*
4279 * If we get here then there was a problem creating the PPD...
4280 */
4281
4282 bad_ppd:
4283
4284 if (common_res) cupsArrayDelete(common_res);
4285 if (common_def) free(common_def);
4286 if (min_res) free(min_res);
4287 if (max_res) free(max_res);
4288
4289 cupsFileClose(fp);
4290 if (printer_opt_strings_catalog)
4291 cupsArrayDelete(printer_opt_strings_catalog);
4292 unlink(buffer);
4293 *buffer = '\0';
4294
4295 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
4296 _("Printer does not support required IPP attributes or document formats."),
4297 1);
4298
4299 return (NULL);
4300 }
4301
4302
4303 /*
4304 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
4305 * media-source.
4306 */
4307
4308 const char * /* O - InputSlot name */
_pwgInputSlotForSource(const char * media_source,char * name,size_t namesize)4309 _pwgInputSlotForSource(
4310 const char *media_source, /* I - PWG media-source */
4311 char *name, /* I - Name buffer */
4312 size_t namesize) /* I - Size of name buffer */
4313 {
4314 /*
4315 * Range check input...
4316 */
4317
4318 if (!media_source || !name || namesize < PPD_MAX_NAME)
4319 return (NULL);
4320
4321 if (_cups_strcasecmp(media_source, "main"))
4322 strlcpy(name, "Cassette", namesize);
4323 else if (_cups_strcasecmp(media_source, "alternate"))
4324 strlcpy(name, "Multipurpose", namesize);
4325 else if (_cups_strcasecmp(media_source, "large-capacity"))
4326 strlcpy(name, "LargeCapacity", namesize);
4327 else if (_cups_strcasecmp(media_source, "bottom"))
4328 strlcpy(name, "Lower", namesize);
4329 else if (_cups_strcasecmp(media_source, "middle"))
4330 strlcpy(name, "Middle", namesize);
4331 else if (_cups_strcasecmp(media_source, "top"))
4332 strlcpy(name, "Upper", namesize);
4333 else if (_cups_strcasecmp(media_source, "rear"))
4334 strlcpy(name, "Rear", namesize);
4335 else if (_cups_strcasecmp(media_source, "side"))
4336 strlcpy(name, "Side", namesize);
4337 else if (_cups_strcasecmp(media_source, "envelope"))
4338 strlcpy(name, "Envelope", namesize);
4339 else if (_cups_strcasecmp(media_source, "main-roll"))
4340 strlcpy(name, "Roll", namesize);
4341 else if (_cups_strcasecmp(media_source, "alternate-roll"))
4342 strlcpy(name, "Roll2", namesize);
4343 else
4344 pwg_ppdize_name(media_source, name, namesize);
4345
4346 return (name);
4347 }
4348
4349
4350 /*
4351 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
4352 * media-type.
4353 */
4354
4355 const char * /* O - MediaType name */
_pwgMediaTypeForType(const char * media_type,char * name,size_t namesize)4356 _pwgMediaTypeForType(
4357 const char *media_type, /* I - PWG media-type */
4358 char *name, /* I - Name buffer */
4359 size_t namesize) /* I - Size of name buffer */
4360 {
4361 /*
4362 * Range check input...
4363 */
4364
4365 if (!media_type || !name || namesize < PPD_MAX_NAME)
4366 return (NULL);
4367
4368 if (_cups_strcasecmp(media_type, "auto"))
4369 strlcpy(name, "Auto", namesize);
4370 else if (_cups_strcasecmp(media_type, "cardstock"))
4371 strlcpy(name, "Cardstock", namesize);
4372 else if (_cups_strcasecmp(media_type, "envelope"))
4373 strlcpy(name, "Envelope", namesize);
4374 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
4375 strlcpy(name, "Glossy", namesize);
4376 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
4377 strlcpy(name, "HighGloss", namesize);
4378 else if (_cups_strcasecmp(media_type, "photographic-matte"))
4379 strlcpy(name, "Matte", namesize);
4380 else if (_cups_strcasecmp(media_type, "stationery"))
4381 strlcpy(name, "Plain", namesize);
4382 else if (_cups_strcasecmp(media_type, "stationery-coated"))
4383 strlcpy(name, "Coated", namesize);
4384 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
4385 strlcpy(name, "Inkjet", namesize);
4386 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
4387 strlcpy(name, "Letterhead", namesize);
4388 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
4389 strlcpy(name, "Preprinted", namesize);
4390 else if (_cups_strcasecmp(media_type, "transparency"))
4391 strlcpy(name, "Transparency", namesize);
4392 else
4393 pwg_ppdize_name(media_type, name, namesize);
4394
4395 return (name);
4396 }
4397
4398
4399 /*
4400 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
4401 */
4402
4403 const char * /* O - PageSize name */
_pwgPageSizeForMedia(pwg_media_t * media,char * name,size_t namesize)4404 _pwgPageSizeForMedia(
4405 pwg_media_t *media, /* I - Media */
4406 char *name, /* I - PageSize name buffer */
4407 size_t namesize) /* I - Size of name buffer */
4408 {
4409 const char *sizeptr, /* Pointer to size in PWG name */
4410 *dimptr; /* Pointer to dimensions in PWG name */
4411
4412
4413 /*
4414 * Range check input...
4415 */
4416
4417 if (!media || !name || namesize < PPD_MAX_NAME)
4418 return (NULL);
4419
4420 /*
4421 * Copy or generate a PageSize name...
4422 */
4423
4424 if (media->ppd) {
4425 /*
4426 * Use a standard Adobe name...
4427 */
4428
4429 strlcpy(name, media->ppd, namesize);
4430 }
4431 else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
4432 (sizeptr = strchr(media->pwg, '_')) == NULL ||
4433 (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
4434 (size_t)(dimptr - sizeptr) > namesize) {
4435 /*
4436 * Use a name of the form "wNNNhNNN"...
4437 */
4438
4439 snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
4440 (int)PWG_TO_POINTS(media->length));
4441 } else {
4442 /*
4443 * Copy the size name from class_sizename_dimensions...
4444 */
4445
4446 memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
4447 name[dimptr - sizeptr - 1] = '\0';
4448 }
4449
4450 return (name);
4451 }
4452
4453
4454 /*
4455 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
4456 */
4457
4458 static void
pwg_ppdize_name(const char * ipp,char * name,size_t namesize)4459 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
4460 char *name, /* I - Name buffer */
4461 size_t namesize) /* I - Size of name buffer */
4462 {
4463 char *ptr, /* Pointer into name buffer */
4464 *end; /* End of name buffer */
4465
4466
4467 *name = (char)toupper(*ipp++);
4468
4469 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;) {
4470 if (*ipp == '-') {
4471 ipp ++;
4472 if (_cups_isalpha(*ipp))
4473 *ptr++ = (char)toupper(*ipp++ & 255);
4474 } else
4475 *ptr++ = *ipp++;
4476 }
4477
4478 *ptr = '\0';
4479 }
4480
4481
4482
4483 /*
4484 * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values.
4485 */
4486
4487 static void
pwg_ppdize_resolution(ipp_attribute_t * attr,int element,int * xres,int * yres,char * name,size_t namesize)4488 pwg_ppdize_resolution(
4489 ipp_attribute_t *attr, /* I - Attribute to convert */
4490 int element, /* I - Element to convert */
4491 int *xres, /* O - X resolution in DPI */
4492 int *yres, /* O - Y resolution in DPI */
4493 char *name, /* I - Name buffer */
4494 size_t namesize) /* I - Size of name buffer */
4495 {
4496 ipp_res_t units; /* Units for resolution */
4497
4498 *xres = ippGetResolution(attr, element, yres, &units);
4499
4500 if (units == IPP_RES_PER_CM) {
4501 *xres = (int)(*xres * 2.54);
4502 *yres = (int)(*yres * 2.54);
4503 }
4504
4505 if (name && namesize > 4) {
4506 if (*xres == *yres)
4507 snprintf(name, namesize, "%ddpi", *xres);
4508 else
4509 snprintf(name, namesize, "%dx%ddpi", *xres, *yres);
4510 }
4511 }
4512 #endif /* HAVE_CUPS_1_6 */
4513