]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd-cache.c
4f88892e64a0b9d372937c453e1c29bfa019cd22
[thirdparty/cups.git] / cups / ppd-cache.c
1 /*
2 * PPD cache implementation for CUPS.
3 *
4 * Copyright © 2022-2025 by OpenPrinting.
5 * Copyright © 2010-2021 by Apple Inc.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
9 */
10
11 #include "cups-private.h"
12 #include "ppd-private.h"
13 #include "debug-internal.h"
14 #include <math.h>
15 #include <ctype.h>
16
17
18 /*
19 * Macro to test for two almost-equal PWG measurements.
20 */
21
22 #define _PWG_EQUIVALENT(x, y) (abs((x)-(y)) < 2)
23
24
25 /*
26 * Local functions...
27 */
28
29 static int cups_connect(http_t **http, const char *url, char *resource, size_t ressize);
30 static cups_lang_t *cups_get_strings(http_t **http, const char *printer_uri, const char *language);
31 static int cups_get_url(http_t **http, const char *url, const char *suffix, char *name, size_t namesize);
32 static const char *ppd_get_string(cups_lang_t *base, cups_lang_t *printer, const char *msgid, char *buffer, size_t bufsize);
33 static void ppd_get_strings(ppd_file_t *ppd, cups_lang_t *langs, const char *option, ppd_choice_t *choice, const char *pwg_msgid);
34 static const char *ppd_inputslot_for_keyword(_ppd_cache_t *pc, const char *keyword);
35 static void ppd_put_strings(cups_file_t *fp, cups_lang_t *langs, const char *ppd_option, const char *ppd_choice, const char *pwg_msgid);
36 static void pwg_add_finishing(cups_array_t *finishings, ipp_finishings_t template, const char *name, const char *value);
37 static int pwg_compare_finishings(_pwg_finishings_t *a, _pwg_finishings_t *b, void *data);
38 static int pwg_compare_sizes(cups_size_t *a, cups_size_t *b, void *data);
39 static cups_size_t *pwg_copy_size(cups_size_t *size, void *data);
40 static void pwg_free_finishings(_pwg_finishings_t *f, void *data);
41 static void pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
42 static void pwg_ppdize_resolution(ipp_attribute_t *attr, int element, int *xres, int *yres, char *name, size_t namesize);
43 static void pwg_unppdize_name(const char *ppd, char *name, size_t namesize,
44 const char *dashchars);
45
46
47 /*
48 * '_cupsConvertOptions()' - Convert printer options to standard IPP attributes.
49 *
50 * This functions converts PPD and CUPS-specific options to their standard IPP
51 * attributes and values and adds them to the specified IPP request.
52 */
53
54 int /* O - New number of copies */
55 _cupsConvertOptions(
56 ipp_t *request, /* I - IPP request */
57 ppd_file_t *ppd, /* I - PPD file */
58 _ppd_cache_t *pc, /* I - PPD cache info */
59 ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
60 ipp_attribute_t *doc_handling_sup, /* I - multiple-document-handling-supported values */
61 ipp_attribute_t *print_color_mode_sup,
62 /* I - Printer supports print-color-mode */
63 const char *user, /* I - User info */
64 const char *format, /* I - document-format value */
65 int copies, /* I - Number of copies */
66 int num_options, /* I - Number of options */
67 cups_option_t *options) /* I - Options */
68 {
69 int i; /* Looping var */
70 const char *keyword, /* PWG keyword */
71 *password; /* Password string */
72 pwg_size_t *size; /* PWG media size */
73 ipp_t *media_col, /* media-col value */
74 *media_size; /* media-size value */
75 const char *media_source, /* media-source value */
76 *media_type, /* media-type value */
77 *collate_str, /* multiple-document-handling value */
78 *color_attr_name, /* Supported color attribute */
79 *mandatory, /* Mandatory attributes */
80 *finishing_template; /* Finishing template */
81 int num_finishings = 0, /* Number of finishing values */
82 finishings[10]; /* Finishing enum values */
83 ppd_choice_t *choice; /* Marked choice */
84 int finishings_copies = copies,
85 /* Number of copies for finishings */
86 job_pages = 0, /* job-pages value */
87 number_up = 1; /* number-up value */
88 const char *value; /* Option value */
89
90
91 /*
92 * Send standard IPP attributes...
93 */
94
95 if (pc->password && (password = cupsGetOption("job-password", num_options, options)) != NULL && ippGetOperation(request) != IPP_OP_VALIDATE_JOB)
96 {
97 ipp_attribute_t *attr = NULL; /* job-password attribute */
98
99 if ((keyword = cupsGetOption("job-password-encryption", num_options, options)) == NULL)
100 keyword = "none";
101
102 if (!strcmp(keyword, "none"))
103 {
104 /*
105 * Add plain-text job-password...
106 */
107
108 attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", password, (int)strlen(password));
109 }
110 else
111 {
112 /*
113 * Add hashed job-password...
114 */
115
116 unsigned char hash[64]; /* Hash of password */
117 ssize_t hashlen; /* Length of hash */
118
119 if ((hashlen = cupsHashData(keyword, password, strlen(password), hash, sizeof(hash))) > 0)
120 attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", hash, (int)hashlen);
121 }
122
123 if (attr)
124 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "job-password-encryption", NULL, keyword);
125 }
126
127 if (pc->account_id)
128 {
129 if ((keyword = cupsGetOption("job-account-id", num_options, options)) == NULL)
130 keyword = cupsGetOption("job-billing", num_options, options);
131
132 if (keyword)
133 ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id", NULL, keyword);
134 }
135
136 if (pc->accounting_user_id)
137 {
138 if ((keyword = cupsGetOption("job-accounting-user-id", num_options, options)) == NULL)
139 keyword = user;
140
141 if (keyword)
142 ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-accounting-user-id", NULL, keyword);
143 }
144
145 for (mandatory = (const char *)cupsArrayFirst(pc->mandatory); mandatory; mandatory = (const char *)cupsArrayNext(pc->mandatory))
146 {
147 if (strcmp(mandatory, "copies") &&
148 strcmp(mandatory, "destination-uris") &&
149 strcmp(mandatory, "finishings") &&
150 strcmp(mandatory, "finishings-col") &&
151 strcmp(mandatory, "finishing-template") &&
152 strcmp(mandatory, "job-account-id") &&
153 strcmp(mandatory, "job-accounting-user-id") &&
154 strcmp(mandatory, "job-password") &&
155 strcmp(mandatory, "job-password-encryption") &&
156 strcmp(mandatory, "media") &&
157 strncmp(mandatory, "media-col", 9) &&
158 strcmp(mandatory, "multiple-document-handling") &&
159 strcmp(mandatory, "output-bin") &&
160 strcmp(mandatory, "print-color-mode") &&
161 strcmp(mandatory, "print-quality") &&
162 strcmp(mandatory, "sides") &&
163 (keyword = cupsGetOption(mandatory, num_options, options)) != NULL)
164 {
165 _ipp_option_t *opt = _ippFindOption(mandatory);
166 /* Option type */
167 ipp_tag_t value_tag = opt ? opt->value_tag : IPP_TAG_NAME;
168 /* Value type */
169
170 switch (value_tag)
171 {
172 case IPP_TAG_INTEGER :
173 case IPP_TAG_ENUM :
174 ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory, atoi(keyword));
175 break;
176 case IPP_TAG_BOOLEAN :
177 ippAddBoolean(request, IPP_TAG_JOB, mandatory, !_cups_strcasecmp(keyword, "true"));
178 break;
179 case IPP_TAG_RANGE :
180 {
181 int lower, upper; /* Range */
182
183 if (sscanf(keyword, "%d-%d", &lower, &upper) != 2)
184 lower = upper = atoi(keyword);
185
186 ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper);
187 }
188 break;
189 case IPP_TAG_STRING :
190 ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword, (int)strlen(keyword));
191 break;
192 default :
193 if (!strcmp(mandatory, "print-color-mode") && !strcmp(keyword, "monochrome"))
194 {
195 if (ippContainsString(print_color_mode_sup, "auto-monochrome"))
196 keyword = "auto-monochrome";
197 else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome"))
198 keyword = "process-monochrome";
199 }
200
201 ippAddString(request, IPP_TAG_JOB, value_tag, mandatory, NULL, keyword);
202 break;
203 }
204 }
205 }
206
207 if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
208 keyword = cupsGetOption("media", num_options, options);
209
210 media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot", num_options, options));
211 media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType", num_options, options));
212 size = _ppdCacheGetSize(pc, keyword, /*ppd_size*/NULL);
213
214 if (media_col_sup && (size || media_source || media_type))
215 {
216 /*
217 * Add a media-col value...
218 */
219
220 media_col = ippNew();
221
222 if (size)
223 {
224 media_size = ippNew();
225 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
226 "x-dimension", size->width);
227 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
228 "y-dimension", size->length);
229
230 ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
231 ippDelete(media_size);
232 }
233
234 for (i = 0; i < media_col_sup->num_values; i ++)
235 {
236 if (size && !strcmp(media_col_sup->values[i].string.text, "media-left-margin"))
237 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-left-margin", size->left);
238 else if (size && !strcmp(media_col_sup->values[i].string.text, "media-bottom-margin"))
239 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-bottom-margin", size->bottom);
240 else if (size && !strcmp(media_col_sup->values[i].string.text, "media-right-margin"))
241 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-right-margin", size->right);
242 else if (size && !strcmp(media_col_sup->values[i].string.text, "media-top-margin"))
243 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-top-margin", size->top);
244 else if (media_source && !strcmp(media_col_sup->values[i].string.text, "media-source"))
245 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-source", NULL, media_source);
246 else if (media_type && !strcmp(media_col_sup->values[i].string.text, "media-type"))
247 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-type", NULL, media_type);
248 }
249
250 ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
251 ippDelete(media_col);
252 }
253
254 if ((keyword = cupsGetOption("output-bin", num_options, options)) == NULL)
255 {
256 if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL)
257 keyword = _ppdCacheGetBin(pc, choice->choice);
258 }
259
260 if (keyword)
261 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin", NULL, keyword);
262
263 color_attr_name = print_color_mode_sup ? "print-color-mode" : "output-mode";
264
265 /*
266 * If we use PPD with standardized PPD option for color support - ColorModel,
267 * prefer it to don't break color/grayscale support for PPDs, either classic
268 * or the ones generated from IPP Get-Printer-Attributes response.
269 */
270
271 if ((keyword = cupsGetOption("ColorModel", num_options, options)) == NULL)
272 {
273 /*
274 * No ColorModel in options...
275 */
276
277 if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL)
278 {
279 /*
280 * ColorModel is taken from PPD as its default option.
281 */
282
283 if (!strcmp(choice->choice, "Gray") || !strcmp(choice->choice, "FastGray") || !strcmp(choice->choice, "DeviceGray"))
284 keyword = "monochrome";
285 else
286 keyword = "color";
287 }
288 else
289 /*
290 * print-color-mode is a default option since 2.4.1, use it as a fallback if there is no
291 * ColorModel in options or PPD...
292 */
293 keyword = cupsGetOption("print-color-mode", num_options, options);
294 }
295 else
296 {
297 /*
298 * ColorModel found in options...
299 */
300
301 if (!strcmp(keyword, "Gray") || !strcmp(keyword, "FastGray") || !strcmp(keyword, "DeviceGray"))
302 keyword = "monochrome";
303 else
304 keyword = "color";
305 }
306
307 if (keyword && !strcmp(keyword, "monochrome"))
308 {
309 if (ippContainsString(print_color_mode_sup, "auto-monochrome"))
310 keyword = "auto-monochrome";
311 else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome"))
312 keyword = "process-monochrome";
313 }
314
315 if (keyword)
316 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, color_attr_name, NULL, keyword);
317
318 if ((keyword = cupsGetOption("print-quality", num_options, options)) != NULL)
319 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", atoi(keyword));
320 else if ((choice = ppdFindMarkedChoice(ppd, "cupsPrintQuality")) != NULL)
321 {
322 if (!_cups_strcasecmp(choice->choice, "draft"))
323 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_DRAFT);
324 else if (!_cups_strcasecmp(choice->choice, "normal"))
325 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_NORMAL);
326 else if (!_cups_strcasecmp(choice->choice, "high"))
327 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_HIGH);
328 }
329
330 if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
331 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, keyword);
332 else if (pc->sides_option && (choice = ppdFindMarkedChoice(ppd, pc->sides_option)) != NULL)
333 {
334 if (pc->sides_1sided && !_cups_strcasecmp(choice->choice, pc->sides_1sided))
335 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "one-sided");
336 else if (pc->sides_2sided_long && !_cups_strcasecmp(choice->choice, pc->sides_2sided_long))
337 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-long-edge");
338 else if (pc->sides_2sided_short && !_cups_strcasecmp(choice->choice, pc->sides_2sided_short))
339 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-short-edge");
340 }
341
342 /*
343 * Copies...
344 */
345
346 if ((keyword = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
347 {
348 if (strstr(keyword, "uncollated"))
349 keyword = "false";
350 else
351 keyword = "true";
352 }
353 else if ((keyword = cupsGetOption("collate", num_options, options)) == NULL)
354 keyword = "true";
355
356 if (format)
357 {
358 if (!_cups_strcasecmp(format, "image/gif") ||
359 !_cups_strcasecmp(format, "image/jp2") ||
360 !_cups_strcasecmp(format, "image/jpeg") ||
361 !_cups_strcasecmp(format, "image/png") ||
362 !_cups_strcasecmp(format, "image/tiff") ||
363 !_cups_strncasecmp(format, "image/x-", 8))
364 {
365 /*
366 * Collation makes no sense for single page image formats...
367 */
368
369 keyword = "false";
370 }
371 else if (!_cups_strncasecmp(format, "image/", 6) ||
372 !_cups_strcasecmp(format, "application/vnd.cups-raster"))
373 {
374 /*
375 * Multi-page image formats will have copies applied by the upstream
376 * filters...
377 */
378
379 copies = 1;
380 }
381 }
382
383 if (doc_handling_sup)
384 {
385 if (!_cups_strcasecmp(keyword, "true"))
386 collate_str = "separate-documents-collated-copies";
387 else
388 collate_str = "separate-documents-uncollated-copies";
389
390 for (i = 0; i < doc_handling_sup->num_values; i ++)
391 {
392 if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
393 {
394 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "multiple-document-handling", NULL, collate_str);
395 break;
396 }
397 }
398
399 if (i >= doc_handling_sup->num_values)
400 copies = 1;
401 }
402
403 /*
404 * Map finishing options...
405 */
406
407 if (copies != finishings_copies)
408 {
409 // Figure out the proper job-pages-per-set value...
410 if ((value = cupsGetOption("job-pages", num_options, options)) == NULL)
411 value = cupsGetOption("com.apple.print.PrintSettings.PMTotalBeginPages..n.", num_options, options);
412
413 if (value)
414 {
415 if ((job_pages = atoi(value)) < 1)
416 job_pages = 1;
417 }
418
419 // Adjust for number-up
420 if ((value = cupsGetOption("number-up", num_options, options)) != NULL)
421 {
422 if ((number_up = atoi(value)) < 1)
423 number_up = 1;
424 }
425
426 job_pages = (job_pages + number_up - 1) / number_up;
427
428 // When duplex printing, raster data will include an extra (blank) page to
429 // make the total number of pages even. Make sure this is reflected in the
430 // page count...
431 if ((job_pages & 1) && (keyword = cupsGetOption("sides", num_options, options)) != NULL && strcmp(keyword, "one-sided"))
432 job_pages ++;
433 }
434
435 if ((finishing_template = cupsGetOption("cupsFinishingTemplate", num_options, options)) == NULL)
436 finishing_template = cupsGetOption("finishing-template", num_options, options);
437
438 if (finishing_template && strcmp(finishing_template, "none"))
439 {
440 ipp_t *fin_col = ippNew(); /* finishings-col value */
441
442 ippAddString(fin_col, IPP_TAG_JOB, IPP_TAG_KEYWORD, "finishing-template", NULL, finishing_template);
443 ippAddCollection(request, IPP_TAG_JOB, "finishings-col", fin_col);
444 ippDelete(fin_col);
445
446 if (copies != finishings_copies && job_pages > 0)
447 {
448 /*
449 * Send job-pages-per-set attribute to apply finishings correctly...
450 */
451
452 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", job_pages);
453 }
454 }
455 else
456 {
457 num_finishings = _ppdCacheGetFinishingValues(ppd, pc, (int)(sizeof(finishings) / sizeof(finishings[0])), finishings);
458 if (num_finishings > 0)
459 {
460 ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", num_finishings, finishings);
461
462 if (copies != finishings_copies && job_pages > 0)
463 {
464 /*
465 * Send job-pages-per-set attribute to apply finishings correctly...
466 */
467
468 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", job_pages);
469 }
470 }
471 }
472
473 return (copies);
474 }
475
476
477 /*
478 * '_ppdCacheCreateWithFile()' - Create PPD cache and mapping data from a
479 * written file.
480 *
481 * Use the @link _ppdCacheWriteFile@ function to write PWG mapping data to a
482 * file.
483 */
484
485 _ppd_cache_t * /* O - PPD cache and mapping data */
486 _ppdCacheCreateWithFile(
487 const char *filename, /* I - File to read */
488 ipp_t **attrs) /* IO - IPP attributes, if any */
489 {
490 cups_file_t *fp; /* File */
491 _ppd_cache_t *pc; /* PWG mapping data */
492 pwg_size_t *size; /* Current size */
493 pwg_map_t *map; /* Current map */
494 _pwg_finishings_t *finishings; /* Current finishings option */
495 int linenum, /* Current line number */
496 num_bins, /* Number of bins in file */
497 num_sizes, /* Number of sizes in file */
498 num_sources, /* Number of sources in file */
499 num_types; /* Number of types in file */
500 char line[2048], /* Current line */
501 *value, /* Pointer to value in line */
502 *valueptr, /* Pointer into value */
503 pwg_keyword[128], /* PWG keyword */
504 ppd_keyword[PPD_MAX_NAME];
505 /* PPD keyword */
506 _pwg_print_color_mode_t print_color_mode;
507 /* Print color mode for preset */
508 _pwg_print_quality_t print_quality; /* Print quality for preset */
509
510
511 DEBUG_printf("_ppdCacheCreateWithFile(filename=\"%s\")", filename);
512
513 /*
514 * Range check input...
515 */
516
517 if (attrs)
518 *attrs = NULL;
519
520 if (!filename)
521 {
522 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
523 return (NULL);
524 }
525
526 /*
527 * Open the file...
528 */
529
530 if ((fp = cupsFileOpen(filename, "r")) == NULL)
531 {
532 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
533 return (NULL);
534 }
535
536 /*
537 * Read the first line and make sure it has "#CUPS-PPD-CACHE-version" in it...
538 */
539
540 if (!cupsFileGets(fp, line, sizeof(line)))
541 {
542 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
543 DEBUG_puts("_ppdCacheCreateWithFile: Unable to read first line.");
544 cupsFileClose(fp);
545 return (NULL);
546 }
547
548 if (strncmp(line, "#CUPS-PPD-CACHE-", 16))
549 {
550 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
551 DEBUG_printf("_ppdCacheCreateWithFile: Wrong first line \"%s\".", line);
552 cupsFileClose(fp);
553 return (NULL);
554 }
555
556 if (atoi(line + 16) != _PPD_CACHE_VERSION)
557 {
558 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of date PPD cache file."), 1);
559 DEBUG_printf("_ppdCacheCreateWithFile: Cache file has version %s, expected %d.", line + 16, _PPD_CACHE_VERSION);
560 cupsFileClose(fp);
561 return (NULL);
562 }
563
564 /*
565 * Allocate the mapping data structure...
566 */
567
568 if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
569 {
570 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
571 DEBUG_puts("_ppdCacheCreateWithFile: Unable to allocate _ppd_cache_t.");
572 goto create_error;
573 }
574
575 pc->max_copies = 9999;
576
577 /*
578 * Read the file...
579 */
580
581 linenum = 0;
582 num_bins = 0;
583 num_sizes = 0;
584 num_sources = 0;
585 num_types = 0;
586
587 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
588 {
589 DEBUG_printf("_ppdCacheCreateWithFile: line=\"%s\", value=\"%s\", linenum=%d", line, value, linenum);
590
591 if (!value)
592 {
593 DEBUG_printf("_ppdCacheCreateWithFile: Missing value on line %d.", linenum);
594 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
595 goto create_error;
596 }
597 else if (!_cups_strcasecmp(line, "Filter"))
598 {
599 if (!pc->filters)
600 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
601
602 cupsArrayAdd(pc->filters, value);
603 }
604 else if (!_cups_strcasecmp(line, "PreFilter"))
605 {
606 if (!pc->prefilters)
607 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
608
609 cupsArrayAdd(pc->prefilters, value);
610 }
611 else if (!_cups_strcasecmp(line, "Product"))
612 {
613 pc->product = strdup(value);
614 }
615 else if (!_cups_strcasecmp(line, "SingleFile"))
616 {
617 pc->single_file = !_cups_strcasecmp(value, "true");
618 }
619 else if (!_cups_strcasecmp(line, "IPP"))
620 {
621 off_t pos = cupsFileTell(fp), /* Position in file */
622 length = strtol(value, NULL, 10);
623 /* Length of IPP attributes */
624
625 if (attrs && *attrs)
626 {
627 DEBUG_puts("_ppdCacheCreateWithFile: IPP listed multiple times.");
628 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
629 goto create_error;
630 }
631 else if (length <= 0)
632 {
633 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP length.");
634 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
635 goto create_error;
636 }
637
638 if (attrs)
639 {
640 /*
641 * Read IPP attributes into the provided variable...
642 */
643
644 *attrs = ippNew();
645
646 if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
647 *attrs) != IPP_STATE_DATA)
648 {
649 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
650 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
651 goto create_error;
652 }
653 }
654 else
655 {
656 /*
657 * Skip the IPP data entirely...
658 */
659
660 cupsFileSeek(fp, pos + length);
661 }
662
663 if (cupsFileTell(fp) != (pos + length))
664 {
665 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
666 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
667 goto create_error;
668 }
669 }
670 else if (!_cups_strcasecmp(line, "NumBins"))
671 {
672 if (num_bins > 0)
673 {
674 DEBUG_puts("_ppdCacheCreateWithFile: NumBins listed multiple times.");
675 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
676 goto create_error;
677 }
678
679 if ((num_bins = atoi(value)) <= 0 || num_bins > 65536)
680 {
681 DEBUG_printf("_ppdCacheCreateWithFile: Bad NumBins value %d on line %d.", num_sizes, linenum);
682 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
683 goto create_error;
684 }
685
686 if ((pc->bins = calloc((size_t)num_bins, sizeof(pwg_map_t))) == NULL)
687 {
688 DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d bins.", num_sizes);
689 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
690 goto create_error;
691 }
692 }
693 else if (!_cups_strcasecmp(line, "Bin"))
694 {
695 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
696 {
697 DEBUG_printf("_ppdCacheCreateWithFile: Bad Bin on line %d.", linenum);
698 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
699 goto create_error;
700 }
701
702 if (pc->num_bins >= num_bins)
703 {
704 DEBUG_printf("_ppdCacheCreateWithFile: Too many Bin's on line %d.", linenum);
705 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
706 goto create_error;
707 }
708
709 map = pc->bins + pc->num_bins;
710 map->pwg = strdup(pwg_keyword);
711 map->ppd = strdup(ppd_keyword);
712
713 pc->num_bins ++;
714 }
715 else if (!_cups_strcasecmp(line, "NumSizes"))
716 {
717 if (num_sizes > 0)
718 {
719 DEBUG_puts("_ppdCacheCreateWithFile: NumSizes listed multiple times.");
720 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
721 goto create_error;
722 }
723
724 if ((num_sizes = atoi(value)) < 0 || num_sizes > 65536)
725 {
726 DEBUG_printf("_ppdCacheCreateWithFile: Bad NumSizes value %d on line %d.", num_sizes, linenum);
727 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
728 goto create_error;
729 }
730
731 if (num_sizes > 0)
732 {
733 if ((pc->sizes = calloc((size_t)num_sizes, sizeof(pwg_size_t))) == NULL)
734 {
735 DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d sizes.", num_sizes);
736 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
737 goto create_error;
738 }
739 }
740 }
741 else if (!_cups_strcasecmp(line, "Size"))
742 {
743 if (pc->num_sizes >= num_sizes)
744 {
745 DEBUG_printf("_ppdCacheCreateWithFile: Too many Size's on line %d.", linenum);
746 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
747 goto create_error;
748 }
749
750 size = pc->sizes + pc->num_sizes;
751
752 if (sscanf(value, "%127s%40s%d%d%d%d%d%d", pwg_keyword, ppd_keyword,
753 &(size->width), &(size->length), &(size->left),
754 &(size->bottom), &(size->right), &(size->top)) != 8)
755 {
756 DEBUG_printf("_ppdCacheCreateWithFile: Bad Size on line %d.", linenum);
757 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
758 goto create_error;
759 }
760
761 size->map.pwg = strdup(pwg_keyword);
762 size->map.ppd = strdup(ppd_keyword);
763
764 pc->num_sizes ++;
765 }
766 else if (!_cups_strcasecmp(line, "CustomSize"))
767 {
768 if (pc->custom_max_width > 0)
769 {
770 DEBUG_printf("_ppdCacheCreateWithFile: Too many CustomSize's on line %d.", linenum);
771 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
772 goto create_error;
773 }
774
775 if (sscanf(value, "%d%d%d%d%d%d%d%d", &(pc->custom_max_width),
776 &(pc->custom_max_length), &(pc->custom_min_width),
777 &(pc->custom_min_length), &(pc->custom_size.left),
778 &(pc->custom_size.bottom), &(pc->custom_size.right),
779 &(pc->custom_size.top)) != 8)
780 {
781 DEBUG_printf("_ppdCacheCreateWithFile: Bad CustomSize on line %d.", linenum);
782 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
783 goto create_error;
784 }
785
786 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
787 pc->custom_max_width, pc->custom_max_length, NULL);
788 pc->custom_max_keyword = strdup(pwg_keyword);
789
790 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
791 pc->custom_min_width, pc->custom_min_length, NULL);
792 pc->custom_min_keyword = strdup(pwg_keyword);
793 }
794 else if (!_cups_strcasecmp(line, "SourceOption"))
795 {
796 pc->source_option = strdup(value);
797 }
798 else if (!_cups_strcasecmp(line, "NumSources"))
799 {
800 if (num_sources > 0)
801 {
802 DEBUG_puts("_ppdCacheCreateWithFile: NumSources listed multiple "
803 "times.");
804 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
805 goto create_error;
806 }
807
808 if ((num_sources = atoi(value)) <= 0 || num_sources > 65536)
809 {
810 DEBUG_printf("_ppdCacheCreateWithFile: Bad NumSources value %d on line %d.", num_sources, linenum);
811 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
812 goto create_error;
813 }
814
815 if ((pc->sources = calloc((size_t)num_sources, sizeof(pwg_map_t))) == NULL)
816 {
817 DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d sources.", num_sources);
818 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
819 goto create_error;
820 }
821 }
822 else if (!_cups_strcasecmp(line, "Source"))
823 {
824 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
825 {
826 DEBUG_printf("_ppdCacheCreateWithFile: Bad Source on line %d.", linenum);
827 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
828 goto create_error;
829 }
830
831 if (pc->num_sources >= num_sources)
832 {
833 DEBUG_printf("_ppdCacheCreateWithFile: Too many Source's on line %d.", linenum);
834 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
835 goto create_error;
836 }
837
838 map = pc->sources + pc->num_sources;
839 map->pwg = strdup(pwg_keyword);
840 map->ppd = strdup(ppd_keyword);
841
842 pc->num_sources ++;
843 }
844 else if (!_cups_strcasecmp(line, "NumTypes"))
845 {
846 if (num_types > 0)
847 {
848 DEBUG_puts("_ppdCacheCreateWithFile: NumTypes listed multiple times.");
849 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
850 goto create_error;
851 }
852
853 if ((num_types = atoi(value)) <= 0 || num_types > 65536)
854 {
855 DEBUG_printf("_ppdCacheCreateWithFile: Bad NumTypes value %d on line %d.", num_types, linenum);
856 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
857 goto create_error;
858 }
859
860 if ((pc->types = calloc((size_t)num_types, sizeof(pwg_map_t))) == NULL)
861 {
862 DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d types.", num_types);
863 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
864 goto create_error;
865 }
866 }
867 else if (!_cups_strcasecmp(line, "Type"))
868 {
869 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
870 {
871 DEBUG_printf("_ppdCacheCreateWithFile: Bad Type on line %d.", linenum);
872 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
873 goto create_error;
874 }
875
876 if (pc->num_types >= num_types)
877 {
878 DEBUG_printf("_ppdCacheCreateWithFile: Too many Type's on line %d.", linenum);
879 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
880 goto create_error;
881 }
882
883 map = pc->types + pc->num_types;
884 map->pwg = strdup(pwg_keyword);
885 map->ppd = strdup(ppd_keyword);
886
887 pc->num_types ++;
888 }
889 else if (!_cups_strcasecmp(line, "Preset"))
890 {
891 /*
892 * Preset output-mode print-quality name=value ...
893 */
894
895 print_color_mode = (_pwg_print_color_mode_t)strtol(value, &valueptr, 10);
896 print_quality = (_pwg_print_quality_t)strtol(valueptr, &valueptr, 10);
897
898 if (print_color_mode < _PWG_PRINT_COLOR_MODE_MONOCHROME ||
899 print_color_mode >= _PWG_PRINT_COLOR_MODE_MAX ||
900 print_quality < _PWG_PRINT_QUALITY_DRAFT ||
901 print_quality >= _PWG_PRINT_QUALITY_MAX ||
902 valueptr == value || !*valueptr)
903 {
904 DEBUG_printf("_ppdCacheCreateWithFile: Bad Preset on line %d.", linenum);
905 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
906 goto create_error;
907 }
908
909 pc->num_presets[print_color_mode][print_quality] =
910 cupsParseOptions(valueptr, 0,
911 pc->presets[print_color_mode] + print_quality);
912 }
913 else if (!_cups_strcasecmp(line, "SidesOption"))
914 pc->sides_option = strdup(value);
915 else if (!_cups_strcasecmp(line, "Sides1Sided"))
916 pc->sides_1sided = strdup(value);
917 else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
918 pc->sides_2sided_long = strdup(value);
919 else if (!_cups_strcasecmp(line, "Sides2SidedShort"))
920 pc->sides_2sided_short = strdup(value);
921 else if (!_cups_strcasecmp(line, "Finishings"))
922 {
923 if (!pc->finishings)
924 pc->finishings =
925 cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
926 NULL, NULL, 0, NULL,
927 (cups_afree_func_t)pwg_free_finishings);
928
929 if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
930 goto create_error;
931
932 finishings->value = (ipp_finishings_t)strtol(value, &valueptr, 10);
933 finishings->num_options = cupsParseOptions(valueptr, 0,
934 &(finishings->options));
935
936 cupsArrayAdd(pc->finishings, finishings);
937 }
938 else if (!_cups_strcasecmp(line, "FinishingTemplate"))
939 {
940 if (!pc->templates)
941 pc->templates = cupsArrayNew3((cups_array_func_t)_cupsArrayStrcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
942
943 cupsArrayAdd(pc->templates, value);
944 }
945 else if (!_cups_strcasecmp(line, "MaxCopies"))
946 pc->max_copies = atoi(value);
947 else if (!_cups_strcasecmp(line, "ChargeInfoURI"))
948 pc->charge_info_uri = strdup(value);
949 else if (!_cups_strcasecmp(line, "JobAccountId"))
950 pc->account_id = !_cups_strcasecmp(value, "true");
951 else if (!_cups_strcasecmp(line, "JobAccountingUserId"))
952 pc->accounting_user_id = !_cups_strcasecmp(value, "true");
953 else if (!_cups_strcasecmp(line, "JobPassword"))
954 pc->password = strdup(value);
955 else if (!_cups_strcasecmp(line, "Mandatory"))
956 {
957 if (pc->mandatory)
958 cupsArrayAddStrings(pc->mandatory, value, ' ');
959 else
960 pc->mandatory = cupsArrayNewStrings(value, ' ');
961 }
962 else if (!_cups_strcasecmp(line, "SupportFile"))
963 {
964 if (!pc->support_files)
965 pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
966
967 cupsArrayAdd(pc->support_files, value);
968 }
969 else
970 {
971 DEBUG_printf("_ppdCacheCreateWithFile: Unknown %s on line %d.", line, linenum);
972 }
973 }
974
975 if (pc->num_sizes < num_sizes)
976 {
977 DEBUG_printf("_ppdCacheCreateWithFile: Not enough sizes (%d < %d).", pc->num_sizes, num_sizes);
978 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
979 goto create_error;
980 }
981
982 if (pc->num_sources < num_sources)
983 {
984 DEBUG_printf("_ppdCacheCreateWithFile: Not enough sources (%d < %d).", pc->num_sources, num_sources);
985 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
986 goto create_error;
987 }
988
989 if (pc->num_types < num_types)
990 {
991 DEBUG_printf("_ppdCacheCreateWithFile: Not enough types (%d < %d).", pc->num_types, num_types);
992 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
993 goto create_error;
994 }
995
996 cupsFileClose(fp);
997
998 return (pc);
999
1000 /*
1001 * If we get here the file was bad - free any data and return...
1002 */
1003
1004 create_error:
1005
1006 cupsFileClose(fp);
1007 _ppdCacheDestroy(pc);
1008
1009 if (attrs)
1010 {
1011 ippDelete(*attrs);
1012 *attrs = NULL;
1013 }
1014
1015 return (NULL);
1016 }
1017
1018
1019 /*
1020 * '_ppdCacheCreateWithPPD()' - Create PWG mapping data from a PPD file.
1021 */
1022
1023 _ppd_cache_t * /* O - PPD cache and mapping data */
1024 _ppdCacheCreateWithPPD(
1025 cups_lang_t *langs, /* I - Languages to load */
1026 ppd_file_t *ppd) /* I - PPD file */
1027 {
1028 int i, j, k; /* Looping vars */
1029 _ppd_cache_t *pc; /* PWG mapping data */
1030 ppd_option_t *input_slot, /* InputSlot option */
1031 *media_type, /* MediaType option */
1032 *output_bin, /* OutputBin option */
1033 *color_model, /* ColorModel option */
1034 *duplex, /* Duplex option */
1035 *ppd_option; /* Other PPD option */
1036 ppd_choice_t *choice; /* Current InputSlot/MediaType */
1037 pwg_map_t *map; /* Current source/type map */
1038 ppd_attr_t *ppd_attr; /* Current PPD preset attribute */
1039 int num_options; /* Number of preset options and props */
1040 cups_option_t *options; /* Preset options and properties */
1041 ppd_size_t *ppd_size; /* Current PPD size */
1042 pwg_size_t *pwg_size; /* Current PWG size */
1043 char pwg_keyword[3 + PPD_MAX_NAME + 1 + 12 + 1 + 12 + 3],
1044 /* PWG keyword string */
1045 ppd_name[PPD_MAX_NAME];
1046 /* Normalized PPD name */
1047 const char *pwg_name; /* Standard PWG media name */
1048 pwg_media_t *pwg_media, /* PWG media data */
1049 pwg_mediatemp; /* PWG media data buffer */
1050 _pwg_print_color_mode_t pwg_print_color_mode;
1051 /* print-color-mode index */
1052 _pwg_print_quality_t pwg_print_quality;
1053 /* print-quality index */
1054 int similar; /* Are the old and new size similar? */
1055 pwg_size_t *old_size; /* Current old size */
1056 int old_imageable, /* Old imageable length in 2540ths */
1057 old_borderless, /* Old borderless state */
1058 old_known_pwg; /* Old PWG name is well-known */
1059 int new_width, /* New width in 2540ths */
1060 new_length, /* New length in 2540ths */
1061 new_left, /* New left margin in 2540ths */
1062 new_bottom, /* New bottom margin in 2540ths */
1063 new_right, /* New right margin in 2540ths */
1064 new_top, /* New top margin in 2540ths */
1065 new_imageable, /* New imageable length in 2540ths */
1066 new_borderless, /* New borderless state */
1067 new_known_pwg; /* New PWG name is well-known */
1068 pwg_size_t *new_size; /* New size to add, if any */
1069 const char *filter; /* Current filter */
1070 _pwg_finishings_t *finishings; /* Current finishings value */
1071 char msg_id[256]; /* Message identifier */
1072 cups_lang_t *lang; /* Current language */
1073 _cups_message_t msg; /* Message */
1074
1075
1076 DEBUG_printf("_ppdCacheCreateWithPPD(ppd=%p)", (void *)ppd);
1077
1078 /*
1079 * Range check input...
1080 */
1081
1082 if (!ppd)
1083 return (NULL);
1084
1085 /*
1086 * Allocate memory...
1087 */
1088
1089 if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
1090 {
1091 DEBUG_puts("_ppdCacheCreateWithPPD: Unable to allocate _ppd_cache_t.");
1092 goto create_error;
1093 }
1094
1095 /*
1096 * Copy and convert size data...
1097 */
1098
1099 if (ppd->num_sizes > 0)
1100 {
1101 if ((pc->sizes = calloc((size_t)ppd->num_sizes, sizeof(pwg_size_t))) == NULL)
1102 {
1103 DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_size_t's.", ppd->num_sizes);
1104 goto create_error;
1105 }
1106
1107 for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes;
1108 i > 0;
1109 i --, ppd_size ++)
1110 {
1111 /*
1112 * Don't copy over custom size...
1113 */
1114
1115 if (!_cups_strcasecmp(ppd_size->name, "Custom"))
1116 continue;
1117
1118 /*
1119 * Convert the PPD size name to the corresponding PWG keyword name.
1120 */
1121
1122 if ((pwg_media = pwgMediaForSize(PWG_FROM_POINTS(ppd_size->width), PWG_FROM_POINTS(ppd_size->length))) != NULL)
1123 {
1124 /*
1125 * Standard name, do we have conflicts?
1126 */
1127
1128 for (j = 0; j < pc->num_sizes; j ++)
1129 if (!strcmp(pc->sizes[j].map.pwg, pwg_media->pwg))
1130 {
1131 pwg_media = NULL;
1132 break;
1133 }
1134 }
1135
1136 if (pwg_media)
1137 {
1138 /*
1139 * Standard name and no conflicts, use it!
1140 */
1141
1142 pwg_name = pwg_media->pwg;
1143 new_known_pwg = 1;
1144 }
1145 else
1146 {
1147 /*
1148 * Not a standard name; convert it to a PWG vendor name of the form:
1149 *
1150 * pp_lowerppd_WIDTHxHEIGHTuu
1151 */
1152
1153 pwg_name = pwg_keyword;
1154 new_known_pwg = 0;
1155
1156 pwg_unppdize_name(ppd_size->name, ppd_name, sizeof(ppd_name), "_.");
1157 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), NULL, ppd_name,
1158 PWG_FROM_POINTS(ppd_size->width),
1159 PWG_FROM_POINTS(ppd_size->length), NULL);
1160 }
1161
1162 /*
1163 * If we have a similar paper with non-zero margins then we only want to
1164 * keep it if it has a larger imageable area length. The NULL check is for
1165 * dimensions that are <= 0...
1166 */
1167
1168 if ((pwg_media = _pwgMediaNearSize(&pwg_mediatemp, /*keyword*/NULL, /*keysize*/0, /*ppdname*/NULL, /*ppdsize*/0, PWG_FROM_POINTS(ppd_size->width), PWG_FROM_POINTS(ppd_size->length), /*epsilon*/0)) == NULL)
1169 continue;
1170
1171 new_width = pwg_media->width;
1172 new_length = pwg_media->length;
1173 new_left = PWG_FROM_POINTS(ppd_size->left);
1174 new_bottom = PWG_FROM_POINTS(ppd_size->bottom);
1175 new_right = PWG_FROM_POINTS(ppd_size->width - ppd_size->right);
1176 new_top = PWG_FROM_POINTS(ppd_size->length - ppd_size->top);
1177 new_imageable = new_length - new_top - new_bottom;
1178 new_borderless = new_bottom == 0 && new_top == 0 &&
1179 new_left == 0 && new_right == 0;
1180
1181 for (k = pc->num_sizes, similar = 0, old_size = pc->sizes, new_size = NULL;
1182 k > 0 && !similar;
1183 k --, old_size ++)
1184 {
1185 old_imageable = old_size->length - old_size->top - old_size->bottom;
1186 old_borderless = old_size->left == 0 && old_size->bottom == 0 &&
1187 old_size->right == 0 && old_size->top == 0;
1188 old_known_pwg = strncmp(old_size->map.pwg, "oe_", 3) &&
1189 strncmp(old_size->map.pwg, "om_", 3);
1190
1191 similar = old_borderless == new_borderless &&
1192 _PWG_EQUIVALENT(old_size->width, new_width) &&
1193 _PWG_EQUIVALENT(old_size->length, new_length);
1194
1195 if (similar &&
1196 (new_known_pwg || (!old_known_pwg && new_imageable > old_imageable)))
1197 {
1198 /*
1199 * The new paper has a larger imageable area so it could replace
1200 * the older paper. Regardless of the imageable area, we always
1201 * prefer the size with a well-known PWG name.
1202 */
1203
1204 new_size = old_size;
1205 free(old_size->map.ppd);
1206 free(old_size->map.pwg);
1207 }
1208 }
1209
1210 if (!similar)
1211 {
1212 /*
1213 * The paper was unique enough to deserve its own entry so add it to the
1214 * end.
1215 */
1216
1217 new_size = pwg_size ++;
1218 pc->num_sizes ++;
1219 }
1220
1221 if (new_size)
1222 {
1223 /*
1224 * Save this size...
1225 */
1226
1227 new_size->map.ppd = strdup(ppd_size->name);
1228 new_size->map.pwg = strdup(pwg_name);
1229 new_size->width = new_width;
1230 new_size->length = new_length;
1231 new_size->left = new_left;
1232 new_size->bottom = new_bottom;
1233 new_size->right = new_right;
1234 new_size->top = new_top;
1235 }
1236 }
1237 }
1238
1239 if (ppd->variable_sizes)
1240 {
1241 /*
1242 * Generate custom size data...
1243 */
1244
1245 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
1246 PWG_FROM_POINTS(ppd->custom_max[0]),
1247 PWG_FROM_POINTS(ppd->custom_max[1]), NULL);
1248 pc->custom_max_keyword = strdup(pwg_keyword);
1249 pc->custom_max_width = PWG_FROM_POINTS(ppd->custom_max[0]);
1250 pc->custom_max_length = PWG_FROM_POINTS(ppd->custom_max[1]);
1251
1252 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
1253 PWG_FROM_POINTS(ppd->custom_min[0]),
1254 PWG_FROM_POINTS(ppd->custom_min[1]), NULL);
1255 pc->custom_min_keyword = strdup(pwg_keyword);
1256 pc->custom_min_width = PWG_FROM_POINTS(ppd->custom_min[0]);
1257 pc->custom_min_length = PWG_FROM_POINTS(ppd->custom_min[1]);
1258
1259 pc->custom_size.left = PWG_FROM_POINTS(ppd->custom_margins[0]);
1260 pc->custom_size.bottom = PWG_FROM_POINTS(ppd->custom_margins[1]);
1261 pc->custom_size.right = PWG_FROM_POINTS(ppd->custom_margins[2]);
1262 pc->custom_size.top = PWG_FROM_POINTS(ppd->custom_margins[3]);
1263 }
1264
1265 /*
1266 * Copy and convert InputSlot data...
1267 */
1268
1269 if ((input_slot = ppdFindOption(ppd, "InputSlot")) == NULL)
1270 input_slot = ppdFindOption(ppd, "HPPaperSource");
1271
1272 if (input_slot)
1273 {
1274 pc->source_option = strdup(input_slot->keyword);
1275
1276 if ((pc->sources = calloc((size_t)input_slot->num_choices, sizeof(pwg_map_t))) == NULL)
1277 {
1278 DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_map_t's for InputSlot.", input_slot->num_choices);
1279 goto create_error;
1280 }
1281
1282 pc->num_sources = input_slot->num_choices;
1283
1284 for (i = input_slot->num_choices, choice = input_slot->choices,
1285 map = pc->sources;
1286 i > 0;
1287 i --, choice ++, map ++)
1288 {
1289 if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
1290 !_cups_strncasecmp(choice->text, "Auto", 4) ||
1291 !_cups_strcasecmp(choice->choice, "Default") ||
1292 !_cups_strcasecmp(choice->text, "Default"))
1293 pwg_name = "auto";
1294 else if (!_cups_strcasecmp(choice->choice, "Cassette"))
1295 pwg_name = "main";
1296 else if (!_cups_strcasecmp(choice->choice, "PhotoTray"))
1297 pwg_name = "photo";
1298 else if (!_cups_strcasecmp(choice->choice, "CDTray"))
1299 pwg_name = "disc";
1300 else if (!_cups_strncasecmp(choice->choice, "Multipurpose", 12) ||
1301 !_cups_strcasecmp(choice->choice, "MP") ||
1302 !_cups_strcasecmp(choice->choice, "MPTray"))
1303 pwg_name = "by-pass-tray";
1304 else if (!_cups_strcasecmp(choice->choice, "LargeCapacity"))
1305 pwg_name = "large-capacity";
1306 else if (!_cups_strncasecmp(choice->choice, "Lower", 5))
1307 pwg_name = "bottom";
1308 else if (!_cups_strncasecmp(choice->choice, "Middle", 6))
1309 pwg_name = "middle";
1310 else if (!_cups_strncasecmp(choice->choice, "Upper", 5))
1311 pwg_name = "top";
1312 else if (!_cups_strncasecmp(choice->choice, "Side", 4))
1313 pwg_name = "side";
1314 else if (!_cups_strcasecmp(choice->choice, "Roll"))
1315 pwg_name = "main-roll";
1316 else if (!_cups_strcasecmp(choice->choice, "0"))
1317 pwg_name = "tray-1";
1318 else if (!_cups_strcasecmp(choice->choice, "1"))
1319 pwg_name = "tray-2";
1320 else if (!_cups_strcasecmp(choice->choice, "2"))
1321 pwg_name = "tray-3";
1322 else if (!_cups_strcasecmp(choice->choice, "3"))
1323 pwg_name = "tray-4";
1324 else if (!_cups_strcasecmp(choice->choice, "4"))
1325 pwg_name = "tray-5";
1326 else if (!_cups_strcasecmp(choice->choice, "5"))
1327 pwg_name = "tray-6";
1328 else if (!_cups_strcasecmp(choice->choice, "6"))
1329 pwg_name = "tray-7";
1330 else if (!_cups_strcasecmp(choice->choice, "7"))
1331 pwg_name = "tray-8";
1332 else if (!_cups_strcasecmp(choice->choice, "8"))
1333 pwg_name = "tray-9";
1334 else if (!_cups_strcasecmp(choice->choice, "9"))
1335 pwg_name = "tray-10";
1336 else
1337 {
1338 /*
1339 * Convert PPD name to lowercase...
1340 */
1341
1342 pwg_name = pwg_keyword;
1343 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1344 "_");
1345 }
1346
1347 map->pwg = strdup(pwg_name);
1348 map->ppd = strdup(choice->choice);
1349
1350 /*
1351 * Add localized text for PWG keyword to message catalog...
1352 */
1353
1354 snprintf(msg_id, sizeof(msg_id), "media-source.%s", pwg_name);
1355 ppd_get_strings(ppd, langs, "InputSlot", choice, msg_id);
1356 }
1357 }
1358
1359 /*
1360 * Copy and convert MediaType data...
1361 */
1362
1363 if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
1364 {
1365 static const struct
1366 {
1367 const char *ppd_name; /* PPD MediaType name or prefix to match */
1368 int match_length; /* Length of prefix, or -1 to match entire string */
1369 const char *pwg_name; /* Registered PWG media-type name to use */
1370 } standard_types[] = {
1371 {"Auto", 4, "auto"},
1372 {"Any", -1, "auto"},
1373 {"Default", -1, "auto"},
1374 {"Card", 4, "cardstock"},
1375 {"Env", 3, "envelope"},
1376 {"Gloss", 5, "photographic-glossy"},
1377 {"HighGloss", -1, "photographic-high-gloss"},
1378 {"Matte", -1, "photographic-matte"},
1379 {"Plain", 5, "stationery"},
1380 {"Coated", 6, "stationery-coated"},
1381 {"Inkjet", -1, "stationery-inkjet"},
1382 {"Letterhead", -1, "stationery-letterhead"},
1383 {"Preprint", 8, "stationery-preprinted"},
1384 {"Recycled", -1, "stationery-recycled"},
1385 {"Transparen", 10, "transparency"},
1386 };
1387 const int num_standard_types = (int)(sizeof(standard_types) / sizeof(standard_types[0]));
1388 /* Length of the standard_types array */
1389 int match_counts[sizeof(standard_types) / sizeof(standard_types[0])] = {0};
1390 /* Number of matches for each standard type */
1391
1392 if ((pc->types = calloc((size_t)media_type->num_choices, sizeof(pwg_map_t))) == NULL)
1393 {
1394 DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_map_t's for MediaType.", media_type->num_choices);
1395 goto create_error;
1396 }
1397
1398 pc->num_types = media_type->num_choices;
1399
1400 for (i = media_type->num_choices, choice = media_type->choices,
1401 map = pc->types;
1402 i > 0;
1403 i --, choice ++, map ++)
1404 {
1405 pwg_name = NULL;
1406
1407 for (j = 0; j < num_standard_types; j ++)
1408 {
1409 if (standard_types[j].match_length <= 0)
1410 {
1411 if (!_cups_strcasecmp(choice->choice, standard_types[j].ppd_name))
1412 {
1413 pwg_name = standard_types[j].pwg_name;
1414 match_counts[j] ++;
1415 }
1416 }
1417 else if (!_cups_strncasecmp(choice->choice, standard_types[j].ppd_name, (size_t)standard_types[j].match_length))
1418 {
1419 pwg_name = standard_types[j].pwg_name;
1420 match_counts[j] ++;
1421 }
1422 }
1423
1424 if (!pwg_name)
1425 {
1426 /*
1427 * Convert PPD name to lowercase...
1428 */
1429
1430 pwg_name = pwg_keyword;
1431 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1432 "_");
1433 }
1434
1435 map->pwg = strdup(pwg_name);
1436 map->ppd = strdup(choice->choice);
1437 }
1438
1439 /*
1440 * Since three PPD name patterns can map to "auto", their match counts
1441 * should each be the count of all three combined.
1442 */
1443
1444 i = match_counts[0] + match_counts[1] + match_counts[2];
1445 match_counts[0] = match_counts[1] = match_counts[2] = i;
1446
1447 for (i = 0, choice = media_type->choices, map = pc->types;
1448 i < media_type->num_choices;
1449 i ++, choice ++, map ++)
1450 {
1451 /*
1452 * If there are two matches for any standard PWG media type, don't give
1453 * the PWG name to either one.
1454 */
1455
1456 for (j = 0; j < num_standard_types; j ++)
1457 {
1458 if (match_counts[j] > 1 && !strcmp(map->pwg, standard_types[j].pwg_name))
1459 {
1460 free(map->pwg);
1461 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
1462 map->pwg = strdup(pwg_keyword);
1463 }
1464 }
1465
1466 /*
1467 * Add localized text for PWG keyword to message catalog...
1468 */
1469
1470 snprintf(msg_id, sizeof(msg_id), "media-type.%s", map->pwg);
1471 ppd_get_strings(ppd, langs, "MediaType", choice, msg_id);
1472 }
1473 }
1474
1475 /*
1476 * Copy and convert OutputBin data...
1477 */
1478
1479 if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
1480 {
1481 if ((pc->bins = calloc((size_t)output_bin->num_choices, sizeof(pwg_map_t))) == NULL)
1482 {
1483 DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_map_t's for OutputBin.", output_bin->num_choices);
1484 goto create_error;
1485 }
1486
1487 pc->num_bins = output_bin->num_choices;
1488
1489 for (i = output_bin->num_choices, choice = output_bin->choices,
1490 map = pc->bins;
1491 i > 0;
1492 i --, choice ++, map ++)
1493 {
1494 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
1495
1496 map->pwg = strdup(pwg_keyword);
1497 map->ppd = strdup(choice->choice);
1498
1499 /*
1500 * Add localized text for PWG keyword to message catalog...
1501 */
1502
1503 snprintf(msg_id, sizeof(msg_id), "output-bin.%s", pwg_keyword);
1504 ppd_get_strings(ppd, langs, "OutputBin", choice, msg_id);
1505 }
1506 }
1507
1508 if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
1509 {
1510 /*
1511 * Copy and convert APPrinterPreset (output-mode + print-quality) data...
1512 */
1513
1514 const char *quality, /* com.apple.print.preset.quality value */
1515 *output_mode, /* com.apple.print.preset.output-mode value */
1516 *color_model_val, /* ColorModel choice */
1517 *graphicsType, /* com.apple.print.preset.graphicsType value */
1518 *media_front_coating; /* com.apple.print.preset.media-front-coating value */
1519
1520 do
1521 {
1522 #if 0
1523 /*
1524 * Add localized text for PWG keyword to message catalog...
1525 */
1526
1527 snprintf(msg_id, sizeof(msg_id), "preset-name.%s", ppd_attr->spec);
1528 pwg_add_message(pc->strings, msg_id, ppd_attr->text);
1529 #endif // 0
1530
1531 /*
1532 * Get the options for this preset...
1533 */
1534
1535 num_options = _ppdParseOptions(ppd_attr->value, 0, &options,
1536 _PPD_PARSE_ALL);
1537
1538 if ((quality = cupsGetOption("com.apple.print.preset.quality",
1539 num_options, options)) != NULL)
1540 {
1541 /*
1542 * Get the print-quality for this preset...
1543 */
1544
1545 if (!strcmp(quality, "low"))
1546 pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1547 else if (!strcmp(quality, "high"))
1548 pwg_print_quality = _PWG_PRINT_QUALITY_HIGH;
1549 else
1550 pwg_print_quality = _PWG_PRINT_QUALITY_NORMAL;
1551
1552 /*
1553 * Ignore graphicsType "Photo" presets that are not high quality.
1554 */
1555
1556 graphicsType = cupsGetOption("com.apple.print.preset.graphicsType",
1557 num_options, options);
1558
1559 if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH && graphicsType &&
1560 !strcmp(graphicsType, "Photo"))
1561 continue;
1562
1563 /*
1564 * Ignore presets for normal and draft quality where the coating
1565 * isn't "none" or "autodetect".
1566 */
1567
1568 media_front_coating = cupsGetOption(
1569 "com.apple.print.preset.media-front-coating",
1570 num_options, options);
1571
1572 if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH &&
1573 media_front_coating &&
1574 strcmp(media_front_coating, "none") &&
1575 strcmp(media_front_coating, "autodetect"))
1576 continue;
1577
1578 /*
1579 * Get the output mode for this preset...
1580 */
1581
1582 output_mode = cupsGetOption("com.apple.print.preset.output-mode",
1583 num_options, options);
1584 color_model_val = cupsGetOption("ColorModel", num_options, options);
1585
1586 if (output_mode)
1587 {
1588 if (!strcmp(output_mode, "monochrome"))
1589 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1590 else
1591 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1592 }
1593 else if (color_model_val)
1594 {
1595 if (!_cups_strcasecmp(color_model_val, "Gray"))
1596 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1597 else
1598 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1599 }
1600 else
1601 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1602
1603 /*
1604 * Save the options for this combination as needed...
1605 */
1606
1607 if (!pc->num_presets[pwg_print_color_mode][pwg_print_quality])
1608 pc->num_presets[pwg_print_color_mode][pwg_print_quality] =
1609 _ppdParseOptions(ppd_attr->value, 0,
1610 pc->presets[pwg_print_color_mode] +
1611 pwg_print_quality, _PPD_PARSE_OPTIONS);
1612 }
1613
1614 cupsFreeOptions(num_options, options);
1615 }
1616 while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) != NULL);
1617 }
1618
1619 if (!pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_DRAFT] &&
1620 !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_NORMAL] &&
1621 !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_HIGH])
1622 {
1623 /*
1624 * Try adding some common color options to create grayscale presets. These
1625 * are listed in order of popularity...
1626 */
1627
1628 const char *color_option = NULL, /* Color control option */
1629 *gray_choice = NULL; /* Choice to select grayscale */
1630
1631 if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL &&
1632 ppdFindChoice(color_model, "Gray"))
1633 {
1634 color_option = "ColorModel";
1635 gray_choice = "Gray";
1636 }
1637 else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL &&
1638 ppdFindChoice(color_model, "grayscale"))
1639 {
1640 color_option = "HPColorMode";
1641 gray_choice = "grayscale";
1642 }
1643 else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL &&
1644 ppdFindChoice(color_model, "Mono"))
1645 {
1646 color_option = "BRMonoColor";
1647 gray_choice = "Mono";
1648 }
1649 else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL &&
1650 ppdFindChoice(color_model, "1"))
1651 {
1652 color_option = "CNIJSGrayScale";
1653 gray_choice = "1";
1654 }
1655 else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL &&
1656 ppdFindChoice(color_model, "True"))
1657 {
1658 color_option = "HPColorAsGray";
1659 gray_choice = "True";
1660 }
1661
1662 if (color_option && gray_choice)
1663 {
1664 /*
1665 * Copy and convert ColorModel (output-mode) data...
1666 */
1667
1668 cups_option_t *coption, /* Color option */
1669 *moption; /* Monochrome option */
1670
1671 for (pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1672 pwg_print_quality < _PWG_PRINT_QUALITY_MAX;
1673 pwg_print_quality ++)
1674 {
1675 if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality])
1676 {
1677 /*
1678 * Copy the color options...
1679 */
1680
1681 num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
1682 [pwg_print_quality];
1683 options = calloc((size_t)num_options, sizeof(cups_option_t));
1684
1685 if (options)
1686 {
1687 for (i = num_options, moption = options,
1688 coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
1689 [pwg_print_quality];
1690 i > 0;
1691 i --, moption ++, coption ++)
1692 {
1693 moption->name = _cupsStrRetain(coption->name);
1694 moption->value = _cupsStrRetain(coption->value);
1695 }
1696
1697 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1698 num_options;
1699 pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1700 options;
1701 }
1702 }
1703 else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL)
1704 continue;
1705
1706 /*
1707 * Add the grayscale option to the preset...
1708 */
1709
1710 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1711 cupsAddOption(color_option, gray_choice,
1712 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
1713 [pwg_print_quality],
1714 pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME] +
1715 pwg_print_quality);
1716 }
1717 }
1718 }
1719
1720 /*
1721 * Copy and convert Duplex (sides) data...
1722 */
1723
1724 if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
1725 if ((duplex = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1726 if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
1727 if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
1728 duplex = ppdFindOption(ppd, "KD03Duplex");
1729
1730 if (duplex)
1731 {
1732 pc->sides_option = strdup(duplex->keyword);
1733
1734 for (i = duplex->num_choices, choice = duplex->choices;
1735 i > 0;
1736 i --, choice ++)
1737 {
1738 if ((!_cups_strcasecmp(choice->choice, "None") ||
1739 !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
1740 pc->sides_1sided = strdup(choice->choice);
1741 else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
1742 !_cups_strcasecmp(choice->choice, "LongEdge") ||
1743 !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
1744 pc->sides_2sided_long = strdup(choice->choice);
1745 else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
1746 !_cups_strcasecmp(choice->choice, "ShortEdge") ||
1747 !_cups_strcasecmp(choice->choice, "Bottom")) &&
1748 !pc->sides_2sided_short)
1749 pc->sides_2sided_short = strdup(choice->choice);
1750 }
1751 }
1752
1753 /*
1754 * Copy filters and pre-filters...
1755 */
1756
1757 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
1758
1759 cupsArrayAdd(pc->filters,
1760 "application/vnd.cups-raw application/octet-stream 0 -");
1761
1762 if ((ppd_attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL)
1763 {
1764 do
1765 {
1766 cupsArrayAdd(pc->filters, ppd_attr->value);
1767 }
1768 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
1769 }
1770 else if (ppd->num_filters > 0)
1771 {
1772 for (i = 0; i < ppd->num_filters; i ++)
1773 cupsArrayAdd(pc->filters, ppd->filters[i]);
1774 }
1775 else
1776 cupsArrayAdd(pc->filters, "application/vnd.cups-postscript 0 -");
1777
1778 /*
1779 * See if we have a command filter...
1780 */
1781
1782 for (filter = (const char *)cupsArrayFirst(pc->filters);
1783 filter;
1784 filter = (const char *)cupsArrayNext(pc->filters))
1785 if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
1786 _cups_isspace(filter[28]))
1787 break;
1788
1789 if (!filter &&
1790 ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL ||
1791 _cups_strcasecmp(ppd_attr->value, "none")))
1792 {
1793 /*
1794 * No command filter and no cupsCommands keyword telling us not to use one.
1795 * See if this is a PostScript printer, and if so add a PostScript command
1796 * filter...
1797 */
1798
1799 for (filter = (const char *)cupsArrayFirst(pc->filters);
1800 filter;
1801 filter = (const char *)cupsArrayNext(pc->filters))
1802 if (!_cups_strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
1803 _cups_isspace(filter[31]))
1804 break;
1805
1806 if (filter)
1807 cupsArrayAdd(pc->filters,
1808 "application/vnd.cups-command application/postscript 100 "
1809 "commandtops");
1810 }
1811
1812 if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
1813 {
1814 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
1815
1816 do
1817 {
1818 cupsArrayAdd(pc->prefilters, ppd_attr->value);
1819 }
1820 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) != NULL);
1821 }
1822
1823 if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL)
1824 pc->single_file = !_cups_strcasecmp(ppd_attr->value, "true");
1825
1826 /*
1827 * Copy the product string, if any...
1828 */
1829
1830 if (ppd->product)
1831 pc->product = strdup(ppd->product);
1832
1833 /*
1834 * Copy finishings mapping data...
1835 */
1836
1837 if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL)
1838 {
1839 /*
1840 * Have proper vendor mapping of IPP finishings values to PPD options...
1841 */
1842
1843 pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
1844 NULL, NULL, 0, NULL,
1845 (cups_afree_func_t)pwg_free_finishings);
1846
1847 do
1848 {
1849 if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
1850 goto create_error;
1851
1852 finishings->value = (ipp_finishings_t)atoi(ppd_attr->spec);
1853 finishings->num_options = _ppdParseOptions(ppd_attr->value, 0,
1854 &(finishings->options),
1855 _PPD_PARSE_OPTIONS);
1856
1857 cupsArrayAdd(pc->finishings, finishings);
1858 }
1859 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsIPPFinishings",
1860 NULL)) != NULL);
1861 }
1862 else
1863 {
1864 /*
1865 * No IPP mapping data, try to map common/standard PPD keywords...
1866 */
1867
1868 pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings, NULL, NULL, 0, NULL, (cups_afree_func_t)pwg_free_finishings);
1869
1870 if ((ppd_option = ppdFindOption(ppd, "StapleLocation")) != NULL)
1871 {
1872 /*
1873 * Add staple finishings...
1874 */
1875
1876 if (ppdFindChoice(ppd_option, "SinglePortrait"))
1877 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, "StapleLocation", "SinglePortrait");
1878 if (ppdFindChoice(ppd_option, "UpperLeft")) /* Ricoh extension */
1879 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, "StapleLocation", "UpperLeft");
1880 if (ppdFindChoice(ppd_option, "UpperRight")) /* Ricoh extension */
1881 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_RIGHT, "StapleLocation", "UpperRight");
1882 if (ppdFindChoice(ppd_option, "SingleLandscape"))
1883 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_BOTTOM_LEFT, "StapleLocation", "SingleLandscape");
1884 if (ppdFindChoice(ppd_option, "DualLandscape"))
1885 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_DUAL_LEFT, "StapleLocation", "DualLandscape");
1886 }
1887
1888 if ((ppd_option = ppdFindOption(ppd, "RIPunch")) != NULL)
1889 {
1890 /*
1891 * Add (Ricoh) punch finishings...
1892 */
1893
1894 if (ppdFindChoice(ppd_option, "Left2"))
1895 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_LEFT, "RIPunch", "Left2");
1896 if (ppdFindChoice(ppd_option, "Left3"))
1897 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_LEFT, "RIPunch", "Left3");
1898 if (ppdFindChoice(ppd_option, "Left4"))
1899 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_LEFT, "RIPunch", "Left4");
1900 if (ppdFindChoice(ppd_option, "Right2"))
1901 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_RIGHT, "RIPunch", "Right2");
1902 if (ppdFindChoice(ppd_option, "Right3"))
1903 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_RIGHT, "RIPunch", "Right3");
1904 if (ppdFindChoice(ppd_option, "Right4"))
1905 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_RIGHT, "RIPunch", "Right4");
1906 if (ppdFindChoice(ppd_option, "Upper2"))
1907 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_TOP, "RIPunch", "Upper2");
1908 if (ppdFindChoice(ppd_option, "Upper3"))
1909 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_TOP, "RIPunch", "Upper3");
1910 if (ppdFindChoice(ppd_option, "Upper4"))
1911 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_TOP, "RIPunch", "Upper4");
1912 }
1913
1914 if ((ppd_option = ppdFindOption(ppd, "BindEdge")) != NULL)
1915 {
1916 /*
1917 * Add bind finishings...
1918 */
1919
1920 if (ppdFindChoice(ppd_option, "Left"))
1921 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_LEFT, "BindEdge", "Left");
1922 if (ppdFindChoice(ppd_option, "Right"))
1923 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_RIGHT, "BindEdge", "Right");
1924 if (ppdFindChoice(ppd_option, "Top"))
1925 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_TOP, "BindEdge", "Top");
1926 if (ppdFindChoice(ppd_option, "Bottom"))
1927 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_BOTTOM, "BindEdge", "Bottom");
1928 }
1929
1930 if ((ppd_option = ppdFindOption(ppd, "FoldType")) != NULL)
1931 {
1932 /*
1933 * Add (Adobe) fold finishings...
1934 */
1935
1936 if (ppdFindChoice(ppd_option, "ZFold"))
1937 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_Z, "FoldType", "ZFold");
1938 if (ppdFindChoice(ppd_option, "Saddle"))
1939 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_HALF, "FoldType", "Saddle");
1940 if (ppdFindChoice(ppd_option, "DoubleGate"))
1941 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_DOUBLE_GATE, "FoldType", "DoubleGate");
1942 if (ppdFindChoice(ppd_option, "LeftGate"))
1943 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LEFT_GATE, "FoldType", "LeftGate");
1944 if (ppdFindChoice(ppd_option, "RightGate"))
1945 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_RIGHT_GATE, "FoldType", "RightGate");
1946 if (ppdFindChoice(ppd_option, "Letter"))
1947 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, "FoldType", "Letter");
1948 if (ppdFindChoice(ppd_option, "XFold"))
1949 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_POSTER, "FoldType", "XFold");
1950 }
1951
1952 if ((ppd_option = ppdFindOption(ppd, "RIFoldType")) != NULL)
1953 {
1954 /*
1955 * Add (Ricoh) fold finishings...
1956 */
1957
1958 if (ppdFindChoice(ppd_option, "OutsideTwoFold"))
1959 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, "RIFoldType", "OutsideTwoFold");
1960 }
1961
1962 if (cupsArrayCount(pc->finishings) == 0)
1963 {
1964 cupsArrayDelete(pc->finishings);
1965 pc->finishings = NULL;
1966 }
1967 }
1968
1969 if ((ppd_option = ppdFindOption(ppd, "cupsFinishingTemplate")) != NULL)
1970 {
1971 pc->templates = cupsArrayNew3((cups_array_func_t)_cupsArrayStrcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
1972
1973 for (choice = ppd_option->choices, i = ppd_option->num_choices; i > 0; choice ++, i --)
1974 {
1975 cupsArrayAdd(pc->templates, (void *)choice->choice);
1976
1977 /*
1978 * Add localized text for PWG keyword to message catalog...
1979 */
1980
1981 snprintf(msg_id, sizeof(msg_id), "finishing-template.%s", choice->choice);
1982 msg.msg = msg_id;
1983
1984 for (lang = langs; lang; lang = lang->next)
1985 {
1986 // See if the string is already localized...
1987 if (cupsArrayFind(lang->strings, &msg))
1988 continue; // Yes
1989
1990 // Otherwise add the text...
1991 if (!strcmp(lang->language, "en"))
1992 {
1993 // English
1994 msg.str = choice->text;
1995 }
1996 else
1997 {
1998 // Other languauge...
1999 snprintf(ppd_name, sizeof(ppd_name), "%s.cupsFinishingTemplate", lang->language);
2000 if ((ppd_attr = ppdFindAttr(ppd, ppd_name, choice->choice)) != NULL)
2001 msg.str = ppd_attr->text;
2002 else
2003 continue;
2004 }
2005
2006 cupsArrayAdd(lang->strings, &msg);
2007 }
2008 }
2009 }
2010
2011 /*
2012 * Max copies...
2013 */
2014
2015 if ((ppd_attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
2016 pc->max_copies = atoi(ppd_attr->value);
2017 else if (ppd->manual_copies)
2018 pc->max_copies = 1;
2019 else
2020 pc->max_copies = 9999;
2021
2022 /*
2023 * cupsChargeInfoURI, cupsJobAccountId, cupsJobAccountingUserId,
2024 * cupsJobPassword, and cupsMandatory.
2025 */
2026
2027 if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL)
2028 pc->charge_info_uri = strdup(ppd_attr->value);
2029
2030 if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountId", NULL)) != NULL)
2031 pc->account_id = !_cups_strcasecmp(ppd_attr->value, "true");
2032
2033 if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountingUserId", NULL)) != NULL)
2034 pc->accounting_user_id = !_cups_strcasecmp(ppd_attr->value, "true");
2035
2036 if ((ppd_attr = ppdFindAttr(ppd, "cupsJobPassword", NULL)) != NULL)
2037 pc->password = strdup(ppd_attr->value);
2038
2039 if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
2040 pc->mandatory = cupsArrayNewStrings(ppd_attr->value, ' ');
2041
2042 /*
2043 * Support files...
2044 */
2045
2046 pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
2047
2048 for (ppd_attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
2049 ppd_attr;
2050 ppd_attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
2051 cupsArrayAdd(pc->support_files, ppd_attr->value);
2052
2053 #ifdef HAVE_APPLICATIONSERVICES_H
2054 if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
2055 cupsArrayAdd(pc->support_files, ppd_attr->value);
2056 #endif
2057
2058 /*
2059 * Return the cache data...
2060 */
2061
2062 return (pc);
2063
2064 /*
2065 * If we get here we need to destroy the PWG mapping data and return NULL...
2066 */
2067
2068 create_error:
2069
2070 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of memory."), 1);
2071 _ppdCacheDestroy(pc);
2072
2073 return (NULL);
2074 }
2075
2076
2077 /*
2078 * '_ppdCacheDestroy()' - Free all memory used for PWG mapping data.
2079 */
2080
2081 void
2082 _ppdCacheDestroy(_ppd_cache_t *pc) /* I - PPD cache and mapping data */
2083 {
2084 int i; /* Looping var */
2085 pwg_map_t *map; /* Current map */
2086 pwg_size_t *size; /* Current size */
2087
2088
2089 /*
2090 * Range check input...
2091 */
2092
2093 if (!pc)
2094 return;
2095
2096 /*
2097 * Free memory as needed...
2098 */
2099
2100 if (pc->bins)
2101 {
2102 for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
2103 {
2104 free(map->pwg);
2105 free(map->ppd);
2106 }
2107
2108 free(pc->bins);
2109 }
2110
2111 if (pc->sizes)
2112 {
2113 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2114 {
2115 free(size->map.pwg);
2116 free(size->map.ppd);
2117 }
2118
2119 free(pc->sizes);
2120 }
2121
2122 free(pc->source_option);
2123
2124 if (pc->sources)
2125 {
2126 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
2127 {
2128 free(map->pwg);
2129 free(map->ppd);
2130 }
2131
2132 free(pc->sources);
2133 }
2134
2135 if (pc->types)
2136 {
2137 for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
2138 {
2139 free(map->pwg);
2140 free(map->ppd);
2141 }
2142
2143 free(pc->types);
2144 }
2145
2146 free(pc->custom_max_keyword);
2147 free(pc->custom_min_keyword);
2148
2149 free(pc->product);
2150 cupsArrayDelete(pc->filters);
2151 cupsArrayDelete(pc->prefilters);
2152 cupsArrayDelete(pc->finishings);
2153
2154 free(pc->charge_info_uri);
2155 free(pc->password);
2156
2157 free(pc->sides_option);
2158 free(pc->sides_1sided);
2159 free(pc->sides_2sided_long);
2160 free(pc->sides_2sided_short);
2161
2162 cupsArrayDelete(pc->mandatory);
2163
2164 cupsArrayDelete(pc->support_files);
2165
2166 free(pc);
2167 }
2168
2169
2170 /*
2171 * '_ppdCacheGetBin()' - Get the PWG output-bin keyword associated with a PPD
2172 * OutputBin.
2173 */
2174
2175 const char * /* O - output-bin or NULL */
2176 _ppdCacheGetBin(
2177 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2178 const char *output_bin) /* I - PPD OutputBin string */
2179 {
2180 int i; /* Looping var */
2181
2182
2183 /*
2184 * Range check input...
2185 */
2186
2187 if (!pc || !output_bin)
2188 return (NULL);
2189
2190 /*
2191 * Look up the OutputBin string...
2192 */
2193
2194
2195 for (i = 0; i < pc->num_bins; i ++)
2196 if (!_cups_strcasecmp(output_bin, pc->bins[i].ppd) || !_cups_strcasecmp(output_bin, pc->bins[i].pwg))
2197 return (pc->bins[i].pwg);
2198
2199 return (NULL);
2200 }
2201
2202
2203 /*
2204 * '_ppdCacheGetFinishingOptions()' - Get PPD finishing options for the given
2205 * IPP finishings value(s).
2206 */
2207
2208 int /* O - New number of options */
2209 _ppdCacheGetFinishingOptions(
2210 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2211 ipp_t *job, /* I - Job attributes or NULL */
2212 ipp_finishings_t value, /* I - IPP finishings value of IPP_FINISHINGS_NONE */
2213 int num_options, /* I - Number of options */
2214 cups_option_t **options) /* IO - Options */
2215 {
2216 int i; /* Looping var */
2217 _pwg_finishings_t *f, /* PWG finishings options */
2218 key; /* Search key */
2219 ipp_attribute_t *attr; /* Finishings attribute */
2220 cups_option_t *option; /* Current finishings option */
2221
2222
2223 /*
2224 * Range check input...
2225 */
2226
2227 if (!pc || cupsArrayCount(pc->finishings) == 0 || !options ||
2228 (!job && value == IPP_FINISHINGS_NONE))
2229 return (num_options);
2230
2231 /*
2232 * Apply finishing options...
2233 */
2234
2235 if (job && (attr = ippFindAttribute(job, "finishings", IPP_TAG_ENUM)) != NULL)
2236 {
2237 int num_values = ippGetCount(attr); /* Number of values */
2238
2239 for (i = 0; i < num_values; i ++)
2240 {
2241 key.value = (ipp_finishings_t)ippGetInteger(attr, i);
2242
2243 if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
2244 {
2245 int j; /* Another looping var */
2246
2247 for (j = f->num_options, option = f->options; j > 0; j --, option ++)
2248 num_options = cupsAddOption(option->name, option->value,
2249 num_options, options);
2250 }
2251 }
2252 }
2253 else if (value != IPP_FINISHINGS_NONE)
2254 {
2255 key.value = value;
2256
2257 if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
2258 {
2259 int j; /* Another looping var */
2260
2261 for (j = f->num_options, option = f->options; j > 0; j --, option ++)
2262 num_options = cupsAddOption(option->name, option->value,
2263 num_options, options);
2264 }
2265 }
2266
2267 return (num_options);
2268 }
2269
2270
2271 /*
2272 * '_ppdCacheGetFinishingValues()' - Get IPP finishings value(s) from the given
2273 * PPD options.
2274 */
2275
2276 int /* O - Number of finishings values */
2277 _ppdCacheGetFinishingValues(
2278 ppd_file_t *ppd, /* I - Marked PPD file */
2279 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2280 int max_values, /* I - Maximum number of finishings values */
2281 int *values) /* O - Finishings values */
2282 {
2283 int i, /* Looping var */
2284 num_values = 0; /* Number of values */
2285 _pwg_finishings_t *f; /* Current finishings option */
2286 cups_option_t *option; /* Current option */
2287 ppd_choice_t *choice; /* Marked PPD choice */
2288
2289
2290 /*
2291 * Range check input...
2292 */
2293
2294 DEBUG_printf("_ppdCacheGetFinishingValues(ppd=%p, pc=%p, max_values=%d, values=%p)", (void *)ppd, (void *)pc, max_values, (void *)values);
2295
2296 if (!ppd || !pc || max_values < 1 || !values)
2297 {
2298 DEBUG_puts("_ppdCacheGetFinishingValues: Bad arguments, returning 0.");
2299 return (0);
2300 }
2301 else if (!pc->finishings)
2302 {
2303 DEBUG_puts("_ppdCacheGetFinishingValues: No finishings support, returning 0.");
2304 return (0);
2305 }
2306
2307 /*
2308 * Go through the finishings options and see what is set...
2309 */
2310
2311 for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
2312 f;
2313 f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
2314 {
2315 DEBUG_printf("_ppdCacheGetFinishingValues: Checking %d (%s)", (int)f->value, ippEnumString("finishings", (int)f->value));
2316
2317 for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2318 {
2319 DEBUG_printf("_ppdCacheGetFinishingValues: %s=%s?", option->name, option->value);
2320
2321 if ((choice = ppdFindMarkedChoice(ppd, option->name)) == NULL || _cups_strcasecmp(option->value, choice->choice))
2322 {
2323 DEBUG_puts("_ppdCacheGetFinishingValues: NO");
2324 break;
2325 }
2326 }
2327
2328 if (i == 0)
2329 {
2330 DEBUG_printf("_ppdCacheGetFinishingValues: Adding %d (%s)", (int)f->value, ippEnumString("finishings", (int)f->value));
2331
2332 values[num_values ++] = (int)f->value;
2333
2334 if (num_values >= max_values)
2335 break;
2336 }
2337 }
2338
2339 if (num_values == 0)
2340 {
2341 /*
2342 * Always have at least "finishings" = 'none'...
2343 */
2344
2345 DEBUG_puts("_ppdCacheGetFinishingValues: Adding 3 (none).");
2346 values[0] = IPP_FINISHINGS_NONE;
2347 num_values ++;
2348 }
2349
2350 DEBUG_printf("_ppdCacheGetFinishingValues: Returning %d.", num_values);
2351
2352 return (num_values);
2353 }
2354
2355
2356 /*
2357 * 'ppd_inputslot_for_keyword()' - Return the PPD InputSlot associated
2358 * a keyword string, or NULL if no mapping
2359 * exists.
2360 */
2361 static const char * /* O - PPD InputSlot or NULL */
2362 ppd_inputslot_for_keyword(
2363 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2364 const char *keyword) /* I - Keyword string */
2365 {
2366 int i; /* Looping var */
2367
2368 if (!pc || !keyword)
2369 return (NULL);
2370
2371 for (i = 0; i < pc->num_sources; i ++)
2372 if (!_cups_strcasecmp(keyword, pc->sources[i].pwg) || !_cups_strcasecmp(keyword, pc->sources[i].ppd))
2373 return (pc->sources[i].ppd);
2374
2375 return (NULL);
2376 }
2377
2378
2379 /*
2380 * '_ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job
2381 * attributes or a keyword string.
2382 */
2383
2384 const char * /* O - PPD InputSlot or NULL */
2385 _ppdCacheGetInputSlot(
2386 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2387 ipp_t *job, /* I - Job attributes or NULL */
2388 const char *keyword) /* I - Keyword string or NULL */
2389 {
2390 /*
2391 * Range check input...
2392 */
2393
2394 if (!pc || pc->num_sources == 0 || (!job && !keyword))
2395 return (NULL);
2396
2397 if (job && !keyword)
2398 {
2399 /*
2400 * Lookup the media-col attribute and any media-source found there...
2401 */
2402
2403 ipp_attribute_t *media_col, /* media-col attribute */
2404 *media_source; /* media-source attribute */
2405 pwg_size_t size; /* Dimensional size */
2406 int margins_set; /* Were the margins set? */
2407
2408 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2409 if (media_col &&
2410 (media_source = ippFindAttribute(ippGetCollection(media_col, 0),
2411 "media-source",
2412 IPP_TAG_KEYWORD)) != NULL)
2413 {
2414 /*
2415 * Use the media-source value from media-col...
2416 */
2417
2418 keyword = ippGetString(media_source, 0, NULL);
2419 }
2420 else if (pwgInitSize(&size, job, &margins_set))
2421 {
2422 /*
2423 * For media <= 5x7, try to ask for automatic selection so the printer can
2424 * pick the photo tray. If auto isn't available, fall back to explicitly
2425 * asking for the photo tray.
2426 */
2427
2428 if (size.width <= (5 * 2540) && size.length <= (7 * 2540)) {
2429 const char* match;
2430 if ((match = ppd_inputslot_for_keyword(pc, "auto")) != NULL)
2431 return (match);
2432 keyword = "photo";
2433 }
2434 }
2435 }
2436
2437 return (ppd_inputslot_for_keyword(pc, keyword));
2438 }
2439
2440
2441 /*
2442 * '_ppdCacheGetMediaType()' - Get the PPD MediaType associated with the job
2443 * attributes or a keyword string.
2444 */
2445
2446 const char * /* O - PPD MediaType or NULL */
2447 _ppdCacheGetMediaType(
2448 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2449 ipp_t *job, /* I - Job attributes or NULL */
2450 const char *keyword) /* I - Keyword string or NULL */
2451 {
2452 /*
2453 * Range check input...
2454 */
2455
2456 if (!pc || pc->num_types == 0 || (!job && !keyword))
2457 return (NULL);
2458
2459 if (job && !keyword)
2460 {
2461 /*
2462 * Lookup the media-col attribute and any media-source found there...
2463 */
2464
2465 ipp_attribute_t *media_col, /* media-col attribute */
2466 *media_type; /* media-type attribute */
2467
2468 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2469 if (media_col)
2470 {
2471 if ((media_type = ippFindAttribute(media_col->values[0].collection,
2472 "media-type",
2473 IPP_TAG_KEYWORD)) == NULL)
2474 media_type = ippFindAttribute(media_col->values[0].collection,
2475 "media-type", IPP_TAG_NAME);
2476
2477 if (media_type)
2478 keyword = media_type->values[0].string.text;
2479 }
2480 }
2481
2482 if (keyword)
2483 {
2484 int i; /* Looping var */
2485
2486 for (i = 0; i < pc->num_types; i ++)
2487 if (!_cups_strcasecmp(keyword, pc->types[i].pwg) || !_cups_strcasecmp(keyword, pc->types[i].ppd))
2488 return (pc->types[i].ppd);
2489 }
2490
2491 return (NULL);
2492 }
2493
2494
2495 /*
2496 * '_ppdCacheGetOutputBin()' - Get the PPD OutputBin associated with the keyword
2497 * string.
2498 */
2499
2500 const char * /* O - PPD OutputBin or NULL */
2501 _ppdCacheGetOutputBin(
2502 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2503 const char *output_bin) /* I - Keyword string */
2504 {
2505 int i; /* Looping var */
2506
2507
2508 /*
2509 * Range check input...
2510 */
2511
2512 if (!pc || !output_bin)
2513 return (NULL);
2514
2515 /*
2516 * Look up the OutputBin string...
2517 */
2518
2519
2520 for (i = 0; i < pc->num_bins; i ++)
2521 if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg) || !_cups_strcasecmp(output_bin, pc->bins[i].ppd))
2522 return (pc->bins[i].ppd);
2523
2524 return (NULL);
2525 }
2526
2527
2528 /*
2529 * '_ppdCacheGetPageSize()' - Get the PPD PageSize associated with the job
2530 * attributes or a keyword string.
2531 */
2532
2533 const char * /* O - PPD PageSize or NULL */
2534 _ppdCacheGetPageSize(
2535 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2536 ipp_t *job, /* I - Job attributes or NULL */
2537 const char *keyword, /* I - Keyword string or NULL */
2538 int *exact) /* O - 1 if exact match, 0 otherwise */
2539 {
2540 int i; /* Looping var */
2541 pwg_size_t *size, /* Current size */
2542 *closest, /* Closest size */
2543 jobsize; /* Size data from job */
2544 int margins_set, /* Were the margins set? */
2545 dwidth, /* Difference in width */
2546 dlength, /* Difference in length */
2547 dleft, /* Difference in left margins */
2548 dright, /* Difference in right margins */
2549 dbottom, /* Difference in bottom margins */
2550 dtop, /* Difference in top margins */
2551 dmin, /* Minimum difference */
2552 dclosest; /* Closest difference */
2553 const char *ppd_name; /* PPD media name */
2554
2555
2556 DEBUG_printf("_ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)", (void *)pc, (void *)job, keyword, (void *)exact);
2557
2558 /*
2559 * Range check input...
2560 */
2561
2562 if (!pc || (!job && !keyword))
2563 return (NULL);
2564
2565 if (exact)
2566 *exact = 0;
2567
2568 ppd_name = keyword;
2569
2570 if (job)
2571 {
2572 /*
2573 * Try getting the PPD media name from the job attributes...
2574 */
2575
2576 ipp_attribute_t *attr; /* Job attribute */
2577
2578 if ((attr = ippFindAttribute(job, "PageSize", IPP_TAG_ZERO)) == NULL)
2579 if ((attr = ippFindAttribute(job, "PageRegion", IPP_TAG_ZERO)) == NULL)
2580 attr = ippFindAttribute(job, "media", IPP_TAG_ZERO);
2581
2582 #ifdef DEBUG
2583 if (attr)
2584 DEBUG_printf("1_ppdCacheGetPageSize: Found attribute %s (%s)", attr->name, ippTagString(attr->value_tag));
2585 else
2586 DEBUG_puts("1_ppdCacheGetPageSize: Did not find media attribute.");
2587 #endif /* DEBUG */
2588
2589 if (attr && (attr->value_tag == IPP_TAG_NAME ||
2590 attr->value_tag == IPP_TAG_KEYWORD))
2591 ppd_name = attr->values[0].string.text;
2592 }
2593
2594 DEBUG_printf("1_ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name);
2595
2596 if (ppd_name)
2597 {
2598 /*
2599 * Try looking up the named PPD size first...
2600 */
2601
2602 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2603 {
2604 DEBUG_printf("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]", (int)(size - pc->sizes), size->map.pwg, size->map.ppd);
2605
2606 if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
2607 !_cups_strcasecmp(ppd_name, size->map.pwg))
2608 {
2609 if (exact)
2610 *exact = 1;
2611
2612 DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\"", ppd_name);
2613
2614 return (size->map.ppd);
2615 }
2616 }
2617 }
2618
2619 if (job && !keyword)
2620 {
2621 /*
2622 * Get the size using media-col or media, with the preference being
2623 * media-col.
2624 */
2625
2626 if (!pwgInitSize(&jobsize, job, &margins_set))
2627 return (NULL);
2628 }
2629 else
2630 {
2631 /*
2632 * Get the size using a media keyword...
2633 */
2634
2635 pwg_media_t *media; /* Media definition */
2636
2637
2638 if ((media = pwgMediaForPWG(keyword)) == NULL)
2639 if ((media = pwgMediaForLegacy(keyword)) == NULL)
2640 if ((media = pwgMediaForPPD(keyword)) == NULL)
2641 return (NULL);
2642
2643 jobsize.width = media->width;
2644 jobsize.length = media->length;
2645 margins_set = 0;
2646 }
2647
2648 /*
2649 * Now that we have the dimensions and possibly the margins, look at the
2650 * available sizes and find the match...
2651 */
2652
2653 closest = NULL;
2654 dclosest = 999999999;
2655
2656 if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
2657 _cups_strncasecmp(ppd_name, "custom_", 7))
2658 {
2659 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2660 {
2661 /*
2662 * Adobe uses a size matching algorithm with an epsilon of 5 points, which
2663 * is just about 176/2540ths...
2664 */
2665
2666 dwidth = size->width - jobsize.width;
2667 dlength = size->length - jobsize.length;
2668
2669 if (dwidth <= -176 || dwidth >= 176 || dlength <= -176 || dlength >= 176)
2670 continue;
2671
2672 if (margins_set)
2673 {
2674 /*
2675 * Use a tighter epsilon of 1 point (35/2540ths) for margins...
2676 */
2677
2678 dleft = size->left - jobsize.left;
2679 dright = size->right - jobsize.right;
2680 dtop = size->top - jobsize.top;
2681 dbottom = size->bottom - jobsize.bottom;
2682
2683 if (dleft <= -35 || dleft >= 35 || dright <= -35 || dright >= 35 ||
2684 dtop <= -35 || dtop >= 35 || dbottom <= -35 || dbottom >= 35)
2685 {
2686 dleft = dleft < 0 ? -dleft : dleft;
2687 dright = dright < 0 ? -dright : dright;
2688 dbottom = dbottom < 0 ? -dbottom : dbottom;
2689 dtop = dtop < 0 ? -dtop : dtop;
2690 dmin = dleft + dright + dbottom + dtop;
2691
2692 if (dmin < dclosest)
2693 {
2694 dclosest = dmin;
2695 closest = size;
2696 }
2697
2698 continue;
2699 }
2700 }
2701
2702 if (exact)
2703 *exact = 1;
2704
2705 DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd);
2706
2707 return (size->map.ppd);
2708 }
2709 }
2710
2711 if (closest)
2712 {
2713 DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\" (closest)", closest->map.ppd);
2714
2715 return (closest->map.ppd);
2716 }
2717
2718 /*
2719 * If we get here we need to check for custom page size support...
2720 */
2721
2722 if (jobsize.width >= pc->custom_min_width &&
2723 jobsize.width <= pc->custom_max_width &&
2724 jobsize.length >= pc->custom_min_length &&
2725 jobsize.length <= pc->custom_max_length)
2726 {
2727 /*
2728 * In range, format as Custom.WWWWxLLLL (points).
2729 */
2730
2731 snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d",
2732 (int)PWG_TO_POINTS(jobsize.width), (int)PWG_TO_POINTS(jobsize.length));
2733
2734 if (margins_set && exact)
2735 {
2736 dleft = pc->custom_size.left - jobsize.left;
2737 dright = pc->custom_size.right - jobsize.right;
2738 dtop = pc->custom_size.top - jobsize.top;
2739 dbottom = pc->custom_size.bottom - jobsize.bottom;
2740
2741 if (dleft > -35 && dleft < 35 && dright > -35 && dright < 35 &&
2742 dtop > -35 && dtop < 35 && dbottom > -35 && dbottom < 35)
2743 *exact = 1;
2744 }
2745 else if (exact)
2746 *exact = 1;
2747
2748 DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\" (custom)", pc->custom_ppd_size);
2749
2750 return (pc->custom_ppd_size);
2751 }
2752
2753 /*
2754 * No custom page size support or the size is out of range - return NULL.
2755 */
2756
2757 DEBUG_puts("1_ppdCacheGetPageSize: Returning NULL");
2758
2759 return (NULL);
2760 }
2761
2762
2763 /*
2764 * '_ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize.
2765 */
2766
2767 pwg_size_t * /* O - PWG size or NULL */
2768 _ppdCacheGetSize(
2769 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2770 const char *page_size, /* I - PPD PageSize */
2771 ppd_size_t *ppd_size) /* I - PPD page size information */
2772 {
2773 int i; /* Looping var */
2774 pwg_media_t *media; /* Media */
2775 pwg_size_t *size; /* Current size */
2776
2777
2778 /*
2779 * Range check input...
2780 */
2781
2782 if (!pc || !page_size)
2783 return (NULL);
2784
2785 if (!_cups_strcasecmp(page_size, "Custom") || !_cups_strncasecmp(page_size, "Custom.", 7))
2786 {
2787 /*
2788 * Custom size; size name can be one of the following:
2789 *
2790 * Custom.WIDTHxLENGTHin - Size in inches
2791 * Custom.WIDTHxLENGTHft - Size in feet
2792 * Custom.WIDTHxLENGTHcm - Size in centimeters
2793 * Custom.WIDTHxLENGTHmm - Size in millimeters
2794 * Custom.WIDTHxLENGTHm - Size in meters
2795 * Custom.WIDTHxLENGTH[pt] - Size in points
2796 */
2797
2798 double w, l; /* Width and length of page */
2799 char *ptr; /* Pointer into PageSize */
2800 struct lconv *loc; /* Locale data */
2801
2802 if (page_size[6])
2803 {
2804 loc = localeconv();
2805 w = (float)_cupsStrScand(page_size + 7, &ptr, loc);
2806 if (!ptr || *ptr != 'x')
2807 return (NULL);
2808
2809 l = (float)_cupsStrScand(ptr + 1, &ptr, loc);
2810 if (!ptr)
2811 return (NULL);
2812
2813 if (!_cups_strcasecmp(ptr, "in"))
2814 {
2815 w *= 2540.0;
2816 l *= 2540.0;
2817 }
2818 else if (!_cups_strcasecmp(ptr, "ft"))
2819 {
2820 w *= 12.0 * 2540.0;
2821 l *= 12.0 * 2540.0;
2822 }
2823 else if (!_cups_strcasecmp(ptr, "mm"))
2824 {
2825 w *= 100.0;
2826 l *= 100.0;
2827 }
2828 else if (!_cups_strcasecmp(ptr, "cm"))
2829 {
2830 w *= 1000.0;
2831 l *= 1000.0;
2832 }
2833 else if (!_cups_strcasecmp(ptr, "m"))
2834 {
2835 w *= 100000.0;
2836 l *= 100000.0;
2837 }
2838 else
2839 {
2840 w *= 2540.0 / 72.0;
2841 l *= 2540.0 / 72.0;
2842 }
2843 }
2844 else if (ppd_size)
2845 {
2846 w = ppd_size->width * 2540.0 / 72.0;
2847 l = ppd_size->length * 2540.0 / 72.0;
2848 }
2849 else
2850 {
2851 // No custom size information...
2852 return (NULL);
2853 }
2854
2855 pc->custom_size.map.ppd = (char *)page_size;
2856 pc->custom_size.width = (int)w;
2857 pc->custom_size.length = (int)l;
2858
2859 if ((media = pwgMediaForSize((int)w, (int)l)) != NULL)
2860 pc->custom_size.map.pwg = (char *)media->pwg;
2861
2862 return (&(pc->custom_size));
2863 }
2864
2865 /*
2866 * Not a custom size - look it up...
2867 */
2868
2869 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2870 {
2871 if (!_cups_strcasecmp(page_size, size->map.ppd) ||
2872 !_cups_strcasecmp(page_size, size->map.pwg))
2873 return (size);
2874 }
2875
2876 /*
2877 * Look up standard sizes...
2878 */
2879
2880 if ((media = pwgMediaForPPD(page_size)) == NULL)
2881 {
2882 if ((media = pwgMediaForLegacy(page_size)) == NULL)
2883 media = pwgMediaForPWG(page_size);
2884 }
2885
2886 if (media)
2887 {
2888 pc->custom_size.map.ppd = (char *)page_size;
2889 pc->custom_size.map.pwg = (char *)media->pwg;
2890 pc->custom_size.width = media->width;
2891 pc->custom_size.length = media->length;
2892
2893 return (&(pc->custom_size));
2894 }
2895
2896 return (NULL);
2897 }
2898
2899
2900 /*
2901 * '_ppdCacheGetSource()' - Get the PWG media-source associated with a PPD
2902 * InputSlot.
2903 */
2904
2905 const char * /* O - PWG media-source keyword */
2906 _ppdCacheGetSource(
2907 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2908 const char *input_slot) /* I - PPD InputSlot */
2909 {
2910 int i; /* Looping var */
2911 pwg_map_t *source; /* Current source */
2912
2913
2914 /*
2915 * Range check input...
2916 */
2917
2918 if (!pc || !input_slot)
2919 return (NULL);
2920
2921 for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++)
2922 if (!_cups_strcasecmp(input_slot, source->ppd) || !_cups_strcasecmp(input_slot, source->pwg))
2923 return (source->pwg);
2924
2925 return (NULL);
2926 }
2927
2928
2929 /*
2930 * '_ppdCacheGetType()' - Get the PWG media-type associated with a PPD
2931 * MediaType.
2932 */
2933
2934 const char * /* O - PWG media-type keyword */
2935 _ppdCacheGetType(
2936 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2937 const char *media_type) /* I - PPD MediaType */
2938 {
2939 int i; /* Looping var */
2940 pwg_map_t *type; /* Current type */
2941
2942
2943 /*
2944 * Range check input...
2945 */
2946
2947 if (!pc || !media_type)
2948 return (NULL);
2949
2950 for (i = pc->num_types, type = pc->types; i > 0; i --, type ++)
2951 if (!_cups_strcasecmp(media_type, type->ppd) || !_cups_strcasecmp(media_type, type->pwg))
2952 return (type->pwg);
2953
2954 return (NULL);
2955 }
2956
2957
2958 /*
2959 * '_ppdCacheWriteFile()' - Write PWG mapping data to a file.
2960 */
2961
2962 int /* O - 1 on success, 0 on failure */
2963 _ppdCacheWriteFile(
2964 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2965 const char *filename, /* I - File to write */
2966 ipp_t *attrs) /* I - Attributes to write, if any */
2967 {
2968 int i, j, k; /* Looping vars */
2969 cups_file_t *fp; /* Output file */
2970 pwg_size_t *size; /* Current size */
2971 pwg_map_t *map; /* Current map */
2972 _pwg_finishings_t *f; /* Current finishing option */
2973 cups_option_t *option; /* Current option */
2974 const char *value; /* String value */
2975 char newfile[1024]; /* New filename */
2976
2977
2978 /*
2979 * Range check input...
2980 */
2981
2982 if (!pc || !filename)
2983 {
2984 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
2985 return (0);
2986 }
2987
2988 /*
2989 * Open the file and write with compression...
2990 */
2991
2992 snprintf(newfile, sizeof(newfile), "%s.N", filename);
2993 if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
2994 {
2995 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
2996 return (0);
2997 }
2998
2999 /*
3000 * Standard header...
3001 */
3002
3003 cupsFilePrintf(fp, "#CUPS-PPD-CACHE-%d\n", _PPD_CACHE_VERSION);
3004
3005 /*
3006 * Output bins...
3007 */
3008
3009 if (pc->num_bins > 0)
3010 {
3011 cupsFilePrintf(fp, "NumBins %d\n", pc->num_bins);
3012 for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
3013 cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd);
3014 }
3015
3016 /*
3017 * Media sizes...
3018 */
3019
3020 cupsFilePrintf(fp, "NumSizes %d\n", pc->num_sizes);
3021 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
3022 cupsFilePrintf(fp, "Size %s %s %d %d %d %d %d %d\n", size->map.pwg,
3023 size->map.ppd, size->width, size->length, size->left,
3024 size->bottom, size->right, size->top);
3025 if (pc->custom_max_width > 0)
3026 cupsFilePrintf(fp, "CustomSize %d %d %d %d %d %d %d %d\n",
3027 pc->custom_max_width, pc->custom_max_length,
3028 pc->custom_min_width, pc->custom_min_length,
3029 pc->custom_size.left, pc->custom_size.bottom,
3030 pc->custom_size.right, pc->custom_size.top);
3031
3032 /*
3033 * Media sources...
3034 */
3035
3036 if (pc->source_option)
3037 cupsFilePrintf(fp, "SourceOption %s\n", pc->source_option);
3038
3039 if (pc->num_sources > 0)
3040 {
3041 cupsFilePrintf(fp, "NumSources %d\n", pc->num_sources);
3042 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
3043 cupsFilePrintf(fp, "Source %s %s\n", map->pwg, map->ppd);
3044 }
3045
3046 /*
3047 * Media types...
3048 */
3049
3050 if (pc->num_types > 0)
3051 {
3052 cupsFilePrintf(fp, "NumTypes %d\n", pc->num_types);
3053 for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
3054 cupsFilePrintf(fp, "Type %s %s\n", map->pwg, map->ppd);
3055 }
3056
3057 /*
3058 * Presets...
3059 */
3060
3061 for (i = _PWG_PRINT_COLOR_MODE_MONOCHROME; i < _PWG_PRINT_COLOR_MODE_MAX; i ++)
3062 {
3063 for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
3064 {
3065 if (pc->num_presets[i][j])
3066 {
3067 cupsFilePrintf(fp, "Preset %d %d", i, j);
3068 for (k = pc->num_presets[i][j], option = pc->presets[i][j];
3069 k > 0;
3070 k --, option ++)
3071 cupsFilePrintf(fp, " %s=%s", option->name, option->value);
3072 cupsFilePutChar(fp, '\n');
3073 }
3074 }
3075 }
3076
3077 /*
3078 * Duplex/sides...
3079 */
3080
3081 if (pc->sides_option)
3082 cupsFilePrintf(fp, "SidesOption %s\n", pc->sides_option);
3083
3084 if (pc->sides_1sided)
3085 cupsFilePrintf(fp, "Sides1Sided %s\n", pc->sides_1sided);
3086
3087 if (pc->sides_2sided_long)
3088 cupsFilePrintf(fp, "Sides2SidedLong %s\n", pc->sides_2sided_long);
3089
3090 if (pc->sides_2sided_short)
3091 cupsFilePrintf(fp, "Sides2SidedShort %s\n", pc->sides_2sided_short);
3092
3093 /*
3094 * Product, cupsFilter, cupsFilter2, and cupsPreFilter...
3095 */
3096
3097 if (pc->product)
3098 cupsFilePutConf(fp, "Product", pc->product);
3099
3100 for (value = (const char *)cupsArrayFirst(pc->filters);
3101 value;
3102 value = (const char *)cupsArrayNext(pc->filters))
3103 cupsFilePutConf(fp, "Filter", value);
3104
3105 for (value = (const char *)cupsArrayFirst(pc->prefilters);
3106 value;
3107 value = (const char *)cupsArrayNext(pc->prefilters))
3108 cupsFilePutConf(fp, "PreFilter", value);
3109
3110 cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
3111
3112 /*
3113 * Finishing options...
3114 */
3115
3116 for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
3117 f;
3118 f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
3119 {
3120 cupsFilePrintf(fp, "Finishings %d", f->value);
3121 for (i = f->num_options, option = f->options; i > 0; i --, option ++)
3122 cupsFilePrintf(fp, " %s=%s", option->name, option->value);
3123 cupsFilePutChar(fp, '\n');
3124 }
3125
3126 for (value = (const char *)cupsArrayFirst(pc->templates); value; value = (const char *)cupsArrayNext(pc->templates))
3127 cupsFilePutConf(fp, "FinishingTemplate", value);
3128
3129 /*
3130 * Max copies...
3131 */
3132
3133 cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies);
3134
3135 /*
3136 * Accounting/quota/PIN/managed printing values...
3137 */
3138
3139 if (pc->charge_info_uri)
3140 cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri);
3141
3142 cupsFilePrintf(fp, "JobAccountId %s\n", pc->account_id ? "true" : "false");
3143 cupsFilePrintf(fp, "JobAccountingUserId %s\n",
3144 pc->accounting_user_id ? "true" : "false");
3145
3146 if (pc->password)
3147 cupsFilePutConf(fp, "JobPassword", pc->password);
3148
3149 for (value = (char *)cupsArrayFirst(pc->mandatory);
3150 value;
3151 value = (char *)cupsArrayNext(pc->mandatory))
3152 cupsFilePutConf(fp, "Mandatory", value);
3153
3154 /*
3155 * Support files...
3156 */
3157
3158 for (value = (char *)cupsArrayFirst(pc->support_files);
3159 value;
3160 value = (char *)cupsArrayNext(pc->support_files))
3161 cupsFilePutConf(fp, "SupportFile", value);
3162
3163 /*
3164 * IPP attributes, if any...
3165 */
3166
3167 if (attrs)
3168 {
3169 cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
3170
3171 attrs->state = IPP_STATE_IDLE;
3172 ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
3173 }
3174
3175 /*
3176 * Close and return...
3177 */
3178
3179 if (cupsFileClose(fp))
3180 {
3181 unlink(newfile);
3182 return (0);
3183 }
3184
3185 unlink(filename);
3186 return (!rename(newfile, filename));
3187 }
3188
3189
3190 /*
3191 * '_ppdCreateFromIPP()' - Create a PPD file describing the capabilities
3192 * of an IPP printer.
3193 */
3194
3195 char * /* O - PPD filename or `NULL` on error */
3196 _ppdCreateFromIPP(char *buffer, /* I - Filename buffer */
3197 size_t bufsize, /* I - Size of filename buffer */
3198 ipp_t *supported) /* I - Get-Printer-Attributes response */
3199 {
3200 const char *printer_uri; /* Printer URI */
3201 http_t *http = NULL; /* HTTP connection */
3202 cups_lang_t *base_lang, /* Base English language */
3203 *lang, /* Current language information */
3204 *langs = NULL; /* Language (strings) files */
3205 const char *prefix; /* Prefix string */
3206 cups_file_t *fp; /* PPD file */
3207 cups_array_t *sizes; /* Media sizes supported by printer */
3208 cups_size_t *size; /* Current media size */
3209 ipp_attribute_t *attr, /* xxx-supported */
3210 *lang_supp, /* printer-strings-languages-supported */
3211 *defattr, /* xxx-default */
3212 *quality, /* print-quality-supported */
3213 *x_dim, *y_dim; /* Media dimensions */
3214 ipp_t *media_col, /* Media collection */
3215 *media_size; /* Media size collection */
3216 char make[256], /* Make and model */
3217 *mptr, /* Pointer into make and model */
3218 ppdname[PPD_MAX_NAME],
3219 /* PPD keyword */
3220 ppdtext[PPD_MAX_TEXT];
3221 /* PPD English text */
3222 const char *model; /* Model name */
3223 int i, j, /* Looping vars */
3224 count, /* Number of values */
3225 bottom, /* Largest bottom margin */
3226 left, /* Largest left margin */
3227 right, /* Largest right margin */
3228 top, /* Largest top margin */
3229 max_length = 0, /* Maximum custom size */
3230 max_width = 0,
3231 min_length = INT_MAX,
3232 /* Minimum custom size */
3233 min_width = INT_MAX,
3234 is_apple = 0, /* Does the printer support Apple raster? */
3235 is_pdf = 0, /* Does the printer support PDF? */
3236 is_pwg = 0; /* Does the printer support PWG Raster? */
3237 pwg_media_t *pwg; /* PWG media size */
3238 int xres, yres; /* Resolution values */
3239 int resolutions[1000];
3240 /* Array of resolution indices */
3241 int have_qdraft = 0,/* Have draft quality? */
3242 have_qhigh = 0; /* Have high quality? */
3243 char msgid[256]; /* PWG message identifier (attr.value) */
3244 const char *keyword; /* Keyword value */
3245 struct lconv *loc = localeconv();
3246 /* Locale data */
3247 cups_array_t *fin_options = NULL;
3248 /* Finishing options */
3249
3250
3251 /*
3252 * Range check input...
3253 */
3254
3255 if (buffer)
3256 *buffer = '\0';
3257
3258 if (!buffer || bufsize < 1)
3259 {
3260 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
3261 return (NULL);
3262 }
3263
3264 if (!supported)
3265 {
3266 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No IPP attributes."), 1);
3267 return (NULL);
3268 }
3269
3270 if ((printer_uri = ippGetString(ippFindAttribute(supported, "printer-uri-supported", IPP_TAG_URI), 0, NULL)) == NULL)
3271 {
3272 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer-uri-supported attribute."), 1);
3273 return (NULL);
3274 }
3275
3276 /*
3277 * Open a temporary file for the PPD...
3278 */
3279
3280 if ((fp = cupsCreateTempFile("ippeve", ".ppd", buffer, bufsize)) == NULL)
3281 {
3282 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
3283 return (NULL);
3284 }
3285
3286 /*
3287 * Get a sanitized make and model...
3288 */
3289
3290 if ((attr = ippFindAttribute(supported, "printer-make-and-model", IPP_TAG_TEXT)) != NULL && ippValidateAttribute(attr))
3291 {
3292 /*
3293 * Sanitize the model name to only contain PPD-safe characters.
3294 */
3295
3296 cupsCopyString(make, ippGetString(attr, 0, NULL), sizeof(make));
3297
3298 for (mptr = make; *mptr; mptr ++)
3299 {
3300 if (*mptr < ' ' || *mptr >= 127 || *mptr == '\"')
3301 {
3302 /*
3303 * Truncate the make and model on the first bad character...
3304 */
3305
3306 *mptr = '\0';
3307 break;
3308 }
3309 }
3310
3311 while (mptr > make)
3312 {
3313 /*
3314 * Strip trailing whitespace...
3315 */
3316
3317 mptr --;
3318 if (*mptr == ' ')
3319 *mptr = '\0';
3320 else
3321 break;
3322 }
3323
3324 if (!make[0])
3325 {
3326 /*
3327 * Use a default make and model if nothing remains...
3328 */
3329
3330 cupsCopyString(make, "Unknown", sizeof(make));
3331 }
3332 }
3333 else
3334 {
3335 /*
3336 * Use a default make and model...
3337 */
3338
3339 cupsCopyString(make, "Unknown", sizeof(make));
3340 }
3341
3342 if (!_cups_strncasecmp(make, "Hewlett Packard ", 16) || !_cups_strncasecmp(make, "Hewlett-Packard ", 16))
3343 {
3344 /*
3345 * Normalize HP printer make and model...
3346 */
3347
3348 model = make + 16;
3349 cupsCopyString(make, "HP", sizeof(make));
3350
3351 if (!_cups_strncasecmp(model, "HP ", 3))
3352 model += 3;
3353 }
3354 else if ((mptr = strchr(make, ' ')) != NULL)
3355 {
3356 /*
3357 * Separate "MAKE MODEL"...
3358 */
3359
3360 while (*mptr && *mptr == ' ')
3361 *mptr++ = '\0';
3362
3363 model = mptr;
3364 }
3365 else
3366 {
3367 /*
3368 * No separate model name...
3369 */
3370
3371 model = "Printer";
3372 }
3373
3374 /*
3375 * Standard stuff for PPD file...
3376 */
3377
3378 cupsFilePuts(fp, "*PPD-Adobe: \"4.3\"\n");
3379 cupsFilePuts(fp, "*FormatVersion: \"4.3\"\n");
3380 cupsFilePrintf(fp, "*FileVersion: \"%d.%d\"\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
3381 cupsFilePuts(fp, "*LanguageVersion: English\n");
3382 cupsFilePuts(fp, "*LanguageEncoding: ISOLatin1\n");
3383 cupsFilePuts(fp, "*PSVersion: \"(3010.000) 0\"\n");
3384 cupsFilePuts(fp, "*LanguageLevel: \"3\"\n");
3385 cupsFilePuts(fp, "*FileSystem: False\n");
3386 cupsFilePuts(fp, "*PCFileName: \"ippeve.ppd\"\n");
3387 cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make);
3388 cupsFilePrintf(fp, "*ModelName: \"%s\"\n", model);
3389 cupsFilePrintf(fp, "*Product: \"(%s)\"\n", model);
3390 cupsFilePrintf(fp, "*NickName: \"%s - IPP Everywhere\"\n", model);
3391 cupsFilePrintf(fp, "*ShortNickName: \"%s - IPP Everywhere\"\n", model);
3392
3393 if (ippGetBoolean(ippFindAttribute(supported, "color-supported", IPP_TAG_BOOLEAN), 0))
3394 cupsFilePuts(fp, "*ColorDevice: True\n");
3395 else
3396 cupsFilePuts(fp, "*ColorDevice: False\n");
3397
3398 cupsFilePrintf(fp, "*cupsVersion: %d.%d\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
3399 #ifdef __APPLE__
3400 cupsFilePrintf(fp, "*APAirPrint: True\n");
3401 #endif // __APPLE__
3402 cupsFilePuts(fp, "*cupsSNMPSupplies: False\n");
3403
3404 if ((lang_supp = ippFindAttribute(supported, "printer-strings-languages-supported", IPP_TAG_LANGUAGE)) != NULL)
3405 {
3406 /*
3407 * List supported languages...
3408 */
3409
3410 for (i = 0, count = ippGetCount(lang_supp), prefix = "*cupsLanguages: \""; i < count; i ++)
3411 {
3412 keyword = ippGetString(lang_supp, i, NULL);
3413
3414 if ((lang = cups_get_strings(&http, printer_uri, keyword)) != NULL)
3415 {
3416 /*
3417 * Add language...
3418 */
3419
3420 lang->next = langs;
3421 langs = lang;
3422
3423 cupsFilePrintf(fp, "%s%s", prefix, keyword);
3424 prefix = " ";
3425 }
3426 }
3427
3428 if (!strcmp(prefix, " "))
3429 cupsFilePuts(fp, "\"\n");
3430 }
3431
3432 if (http)
3433 {
3434 httpClose(http);
3435 http = NULL;
3436 }
3437
3438 // Find the English localization...
3439 for (lang = langs; lang; lang = lang->next)
3440 {
3441 if (!strcmp(lang->language, "en"))
3442 break;
3443 }
3444
3445 base_lang = cupsLangGet("en");
3446
3447 if ((attr = ippFindAttribute(supported, "printer-more-info", IPP_TAG_URI)) != NULL && ippValidateAttribute(attr))
3448 cupsFilePrintf(fp, "*APSupplies: \"%s\"\n", ippGetString(attr, 0, NULL));
3449
3450 if ((attr = ippFindAttribute(supported, "printer-charge-info-uri", IPP_TAG_URI)) != NULL && ippValidateAttribute(attr))
3451 cupsFilePrintf(fp, "*cupsChargeInfoURI: \"%s\"\n", ippGetString(attr, 0, NULL));
3452
3453 /*
3454 * Accounting...
3455 */
3456
3457 if (ippGetBoolean(ippFindAttribute(supported, "job-account-id-supported", IPP_TAG_BOOLEAN), 0))
3458 cupsFilePuts(fp, "*cupsJobAccountId: True\n");
3459
3460 if (ippGetBoolean(ippFindAttribute(supported, "job-accounting-user-id-supported", IPP_TAG_BOOLEAN), 0))
3461 cupsFilePuts(fp, "*cupsJobAccountingUserId: True\n");
3462
3463 if ((attr = ippFindAttribute(supported, "printer-privacy-policy-uri", IPP_TAG_URI)) != NULL && ippValidateAttribute(attr))
3464 cupsFilePrintf(fp, "*cupsPrivacyURI: \"%s\"\n", ippGetString(attr, 0, NULL));
3465
3466 if ((attr = ippFindAttribute(supported, "printer-mandatory-job-attributes", IPP_TAG_KEYWORD)) != NULL && ippValidateAttribute(attr))
3467 {
3468 for (i = 0, count = ippGetCount(attr), prefix = "*cupsMandatory: \""; i < count; i ++)
3469 {
3470 keyword = ippGetString(attr, i, NULL);
3471
3472 if (strcmp(keyword, "attributes-charset") && strcmp(keyword, "attributes-natural-language") && strcmp(keyword, "printer-uri"))
3473 {
3474 cupsFilePrintf(fp, "%s%s", prefix, keyword);
3475 prefix = " ";
3476 }
3477 }
3478
3479 if (!strcmp(prefix, " "))
3480 cupsFilePuts(fp, "\"\n");
3481 }
3482
3483 if ((attr = ippFindAttribute(supported, "printer-requested-job-attributes", IPP_TAG_KEYWORD)) != NULL && ippValidateAttribute(attr))
3484 {
3485 for (i = 0, count = ippGetCount(attr), prefix = "*cupsRequested: \""; i < count; i ++)
3486 {
3487 keyword = ippGetString(attr, i, NULL);
3488
3489 if (strcmp(keyword, "attributes-charset") && strcmp(keyword, "attributes-natural-language") && strcmp(keyword, "printer-uri"))
3490 {
3491 cupsFilePrintf(fp, "%s%s", prefix, keyword);
3492 prefix = " ";
3493 }
3494 }
3495
3496 if (!strcmp(prefix, " "))
3497 cupsFilePuts(fp, "\"\n");
3498 }
3499
3500 /*
3501 * Password/PIN printing...
3502 */
3503
3504 if ((attr = ippFindAttribute(supported, "job-password-supported", IPP_TAG_INTEGER)) != NULL)
3505 {
3506 char pattern[33]; /* Password pattern */
3507 int maxlen = ippGetInteger(attr, 0);
3508 /* Maximum length */
3509 const char *repertoire = ippGetString(ippFindAttribute(supported, "job-password-repertoire-configured", IPP_TAG_KEYWORD), 0, NULL);
3510 /* Type of password */
3511
3512 if (maxlen > (int)(sizeof(pattern) - 1))
3513 maxlen = (int)sizeof(pattern) - 1;
3514
3515 if (!repertoire || !strcmp(repertoire, "iana_us-ascii_digits"))
3516 memset(pattern, '1', (size_t)maxlen);
3517 else if (!strcmp(repertoire, "iana_us-ascii_letters"))
3518 memset(pattern, 'A', (size_t)maxlen);
3519 else if (!strcmp(repertoire, "iana_us-ascii_complex"))
3520 memset(pattern, 'C', (size_t)maxlen);
3521 else if (!strcmp(repertoire, "iana_us-ascii_any"))
3522 memset(pattern, '.', (size_t)maxlen);
3523 else if (!strcmp(repertoire, "iana_utf-8_digits"))
3524 memset(pattern, 'N', (size_t)maxlen);
3525 else if (!strcmp(repertoire, "iana_utf-8_letters"))
3526 memset(pattern, 'U', (size_t)maxlen);
3527 else
3528 memset(pattern, '*', (size_t)maxlen);
3529
3530 pattern[maxlen] = '\0';
3531
3532 cupsFilePrintf(fp, "*cupsJobPassword: \"%s\"\n", pattern);
3533 }
3534
3535 /*
3536 * Filters...
3537 */
3538
3539 if ((attr = ippFindAttribute(supported, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL)
3540 {
3541 is_apple = ippContainsString(attr, "image/urf") && (ippFindAttribute(supported, "urf-supported", IPP_TAG_KEYWORD) != NULL);
3542 is_pdf = ippContainsString(attr, "application/pdf");
3543 is_pwg = ippContainsString(attr, "image/pwg-raster") && !is_apple &&
3544 (ippFindAttribute(supported, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION) != NULL) &&
3545 (ippFindAttribute(supported, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD) != NULL);
3546
3547 if (ippContainsString(attr, "image/jpeg"))
3548 cupsFilePuts(fp, "*cupsFilter2: \"image/jpeg image/jpeg 0 -\"\n");
3549 if (ippContainsString(attr, "image/png"))
3550 cupsFilePuts(fp, "*cupsFilter2: \"image/png image/png 0 -\"\n");
3551 if (is_pdf)
3552 {
3553 /*
3554 * Don't locally filter PDF content when printing to a CUPS shared
3555 * printer, otherwise the options will be applied twice...
3556 */
3557
3558 if (ippContainsString(attr, "application/vnd.cups-pdf"))
3559 cupsFilePuts(fp, "*cupsFilter2: \"application/pdf application/pdf 0 -\"\n");
3560 else
3561 cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 10 -\"\n");
3562 }
3563 else
3564 cupsFilePuts(fp, "*cupsManualCopies: True\n");
3565 if (is_apple)
3566 cupsFilePuts(fp, "*cupsFilter2: \"image/urf image/urf 100 -\"\n");
3567 if (is_pwg)
3568 cupsFilePuts(fp, "*cupsFilter2: \"image/pwg-raster image/pwg-raster 100 -\"\n");
3569 }
3570
3571 if (!is_apple && !is_pdf && !is_pwg)
3572 goto bad_ppd;
3573
3574 /*
3575 * PageSize/PageRegion/ImageableArea/PaperDimension
3576 */
3577
3578 if ((attr = ippFindAttribute(supported, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != NULL)
3579 {
3580 for (i = 1, bottom = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3581 if (ippGetInteger(attr, i) > bottom)
3582 bottom = ippGetInteger(attr, i);
3583 }
3584 else
3585 bottom = 1270;
3586
3587 if ((attr = ippFindAttribute(supported, "media-left-margin-supported", IPP_TAG_INTEGER)) != NULL)
3588 {
3589 for (i = 1, left = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3590 if (ippGetInteger(attr, i) > left)
3591 left = ippGetInteger(attr, i);
3592 }
3593 else
3594 left = 635;
3595
3596 if ((attr = ippFindAttribute(supported, "media-right-margin-supported", IPP_TAG_INTEGER)) != NULL)
3597 {
3598 for (i = 1, right = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3599 if (ippGetInteger(attr, i) > right)
3600 right = ippGetInteger(attr, i);
3601 }
3602 else
3603 right = 635;
3604
3605 if ((attr = ippFindAttribute(supported, "media-top-margin-supported", IPP_TAG_INTEGER)) != NULL)
3606 {
3607 for (i = 1, top = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3608 if (ippGetInteger(attr, i) > top)
3609 top = ippGetInteger(attr, i);
3610 }
3611 else
3612 top = 1270;
3613
3614 if ((defattr = ippFindAttribute(supported, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3615 {
3616 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3617 {
3618 media_size = ippGetCollection(attr, 0);
3619 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3620 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3621
3622 if (x_dim && y_dim && (pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL)
3623 cupsCopyString(ppdname, pwg->ppd, sizeof(ppdname));
3624 else
3625 cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
3626 }
3627 else
3628 cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
3629 }
3630 else if ((pwg = pwgMediaForPWG(ippGetString(ippFindAttribute(supported, "media-default", IPP_TAG_ZERO), 0, NULL))) != NULL)
3631 cupsCopyString(ppdname, pwg->ppd, sizeof(ppdname));
3632 else
3633 cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
3634
3635 sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes, NULL, NULL, 0, (cups_acopy_func_t)pwg_copy_size, _cupsArrayFree);
3636
3637 if ((attr = ippFindAttribute(supported, "media-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3638 {
3639 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3640 {
3641 cups_size_t temp; /* Current size */
3642 ipp_attribute_t *margin; /* media-xxx-margin attribute */
3643
3644 media_col = ippGetCollection(attr, i);
3645 media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0);
3646 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3647 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3648 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3649
3650 if (pwg)
3651 {
3652 temp.width = pwg->width;
3653 temp.length = pwg->length;
3654
3655 if ((margin = ippFindAttribute(media_col, "media-bottom-margin", IPP_TAG_INTEGER)) != NULL)
3656 temp.bottom = ippGetInteger(margin, 0);
3657 else
3658 temp.bottom = bottom;
3659
3660 if ((margin = ippFindAttribute(media_col, "media-left-margin", IPP_TAG_INTEGER)) != NULL)
3661 temp.left = ippGetInteger(margin, 0);
3662 else
3663 temp.left = left;
3664
3665 if ((margin = ippFindAttribute(media_col, "media-right-margin", IPP_TAG_INTEGER)) != NULL)
3666 temp.right = ippGetInteger(margin, 0);
3667 else
3668 temp.right = right;
3669
3670 if ((margin = ippFindAttribute(media_col, "media-top-margin", IPP_TAG_INTEGER)) != NULL)
3671 temp.top = ippGetInteger(margin, 0);
3672 else
3673 temp.top = top;
3674
3675 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3676 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3677 else
3678 cupsCopyString(temp.media, pwg->ppd, sizeof(temp.media));
3679
3680 if (!cupsArrayFind(sizes, &temp))
3681 cupsArrayAdd(sizes, &temp);
3682 }
3683 else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3684 {
3685 /*
3686 * Custom size - record the min/max values...
3687 */
3688
3689 int lower, upper; /* Range values */
3690
3691 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3692 lower = ippGetRange(x_dim, 0, &upper);
3693 else
3694 lower = upper = ippGetInteger(x_dim, 0);
3695
3696 if (lower < min_width)
3697 min_width = lower;
3698 if (upper > max_width)
3699 max_width = upper;
3700
3701 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3702 lower = ippGetRange(y_dim, 0, &upper);
3703 else
3704 lower = upper = ippGetInteger(y_dim, 0);
3705
3706 if (lower < min_length)
3707 min_length = lower;
3708 if (upper > max_length)
3709 max_length = upper;
3710 }
3711 }
3712
3713 if ((max_width == 0 || max_length == 0) && (attr = ippFindAttribute(supported, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3714 {
3715 /*
3716 * Some printers don't list custom size support in media-col-database...
3717 */
3718
3719 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3720 {
3721 media_size = ippGetCollection(attr, i);
3722 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3723 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3724
3725 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3726 {
3727 /*
3728 * Custom size - record the min/max values...
3729 */
3730
3731 int lower, upper; /* Range values */
3732
3733 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3734 lower = ippGetRange(x_dim, 0, &upper);
3735 else
3736 lower = upper = ippGetInteger(x_dim, 0);
3737
3738 if (lower < min_width)
3739 min_width = lower;
3740 if (upper > max_width)
3741 max_width = upper;
3742
3743 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3744 lower = ippGetRange(y_dim, 0, &upper);
3745 else
3746 lower = upper = ippGetInteger(y_dim, 0);
3747
3748 if (lower < min_length)
3749 min_length = lower;
3750 if (upper > max_length)
3751 max_length = upper;
3752 }
3753 }
3754 }
3755 }
3756 else if ((attr = ippFindAttribute(supported, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3757 {
3758 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3759 {
3760 cups_size_t temp; /* Current size */
3761
3762 media_size = ippGetCollection(attr, i);
3763 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3764 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3765 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3766
3767 if (pwg)
3768 {
3769 temp.width = pwg->width;
3770 temp.length = pwg->length;
3771 temp.bottom = bottom;
3772 temp.left = left;
3773 temp.right = right;
3774 temp.top = top;
3775
3776 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3777 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3778 else
3779 cupsCopyString(temp.media, pwg->ppd, sizeof(temp.media));
3780
3781 if (!cupsArrayFind(sizes, &temp))
3782 cupsArrayAdd(sizes, &temp);
3783 }
3784 else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3785 {
3786 /*
3787 * Custom size - record the min/max values...
3788 */
3789
3790 int lower, upper; /* Range values */
3791
3792 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3793 lower = ippGetRange(x_dim, 0, &upper);
3794 else
3795 lower = upper = ippGetInteger(x_dim, 0);
3796
3797 if (lower < min_width)
3798 min_width = lower;
3799 if (upper > max_width)
3800 max_width = upper;
3801
3802 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3803 lower = ippGetRange(y_dim, 0, &upper);
3804 else
3805 lower = upper = ippGetInteger(y_dim, 0);
3806
3807 if (lower < min_length)
3808 min_length = lower;
3809 if (upper > max_length)
3810 max_length = upper;
3811 }
3812 }
3813 }
3814 else if ((attr = ippFindAttribute(supported, "media-supported", IPP_TAG_ZERO)) != NULL)
3815 {
3816 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3817 {
3818 const char *pwg_size = ippGetString(attr, i, NULL);
3819 /* PWG size name */
3820 cups_size_t temp; /* Current size */
3821
3822 if ((pwg = pwgMediaForPWG(pwg_size)) != NULL)
3823 {
3824 if (strstr(pwg_size, "_max_") || strstr(pwg_size, "_max."))
3825 {
3826 if (pwg->width > max_width)
3827 max_width = pwg->width;
3828 if (pwg->length > max_length)
3829 max_length = pwg->length;
3830 }
3831 else if (strstr(pwg_size, "_min_") || strstr(pwg_size, "_min."))
3832 {
3833 if (pwg->width < min_width)
3834 min_width = pwg->width;
3835 if (pwg->length < min_length)
3836 min_length = pwg->length;
3837 }
3838 else
3839 {
3840 temp.width = pwg->width;
3841 temp.length = pwg->length;
3842 temp.bottom = bottom;
3843 temp.left = left;
3844 temp.right = right;
3845 temp.top = top;
3846
3847 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3848 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3849 else
3850 cupsCopyString(temp.media, pwg->ppd, sizeof(temp.media));
3851
3852 if (!cupsArrayFind(sizes, &temp))
3853 cupsArrayAdd(sizes, &temp);
3854 }
3855 }
3856 }
3857 }
3858
3859 if (cupsArrayCount(sizes) > 0)
3860 {
3861 /*
3862 * List all of the standard sizes...
3863 */
3864
3865 char tleft[256], /* Left string */
3866 tbottom[256], /* Bottom string */
3867 tright[256], /* Right string */
3868 ttop[256], /* Top string */
3869 twidth[256], /* Width string */
3870 tlength[256]; /* Length string */
3871
3872 cupsFilePrintf(fp, "*OpenUI *PageSize: PickOne\n"
3873 "*OrderDependency: 10 AnySetup *PageSize\n"
3874 "*DefaultPageSize: %s\n", ppdname);
3875 for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3876 {
3877 _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3878 _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3879
3880 cupsFilePrintf(fp, "*PageSize %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", size->media, twidth, tlength);
3881 }
3882 cupsFilePuts(fp, "*CloseUI: *PageSize\n");
3883
3884 cupsFilePrintf(fp, "*OpenUI *PageRegion: PickOne\n"
3885 "*OrderDependency: 10 AnySetup *PageRegion\n"
3886 "*DefaultPageRegion: %s\n", ppdname);
3887 for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3888 {
3889 _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3890 _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3891
3892 cupsFilePrintf(fp, "*PageRegion %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", size->media, twidth, tlength);
3893 }
3894 cupsFilePuts(fp, "*CloseUI: *PageRegion\n");
3895
3896 cupsFilePrintf(fp, "*DefaultImageableArea: %s\n"
3897 "*DefaultPaperDimension: %s\n", ppdname, ppdname);
3898
3899 for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3900 {
3901 _cupsStrFormatd(tleft, tleft + sizeof(tleft), size->left * 72.0 / 2540.0, loc);
3902 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), size->bottom * 72.0 / 2540.0, loc);
3903 _cupsStrFormatd(tright, tright + sizeof(tright), (size->width - size->right) * 72.0 / 2540.0, loc);
3904 _cupsStrFormatd(ttop, ttop + sizeof(ttop), (size->length - size->top) * 72.0 / 2540.0, loc);
3905 _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3906 _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3907
3908 cupsFilePrintf(fp, "*ImageableArea %s: \"%s %s %s %s\"\n", size->media, tleft, tbottom, tright, ttop);
3909 cupsFilePrintf(fp, "*PaperDimension %s: \"%s %s\"\n", size->media, twidth, tlength);
3910 }
3911
3912 cupsArrayDelete(sizes);
3913
3914 /*
3915 * Custom size support...
3916 */
3917
3918 if (max_width > 0 && min_width < INT_MAX && max_length > 0 && min_length < INT_MAX)
3919 {
3920 char tmax[256], tmin[256]; /* Min/max values */
3921
3922 _cupsStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc);
3923 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), bottom * 72.0 / 2540.0, loc);
3924 _cupsStrFormatd(tright, tright + sizeof(tright), right * 72.0 / 2540.0, loc);
3925 _cupsStrFormatd(ttop, ttop + sizeof(ttop), top * 72.0 / 2540.0, loc);
3926
3927 cupsFilePrintf(fp, "*HWMargins: \"%s %s %s %s\"\n", tleft, tbottom, tright, ttop);
3928
3929 _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_width * 72.0 / 2540.0, loc);
3930 _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_width * 72.0 / 2540.0, loc);
3931 cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s\n", tmin, tmax);
3932
3933 _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_length * 72.0 / 2540.0, loc);
3934 _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_length * 72.0 / 2540.0, loc);
3935 cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s\n", tmin, tmax);
3936
3937 cupsFilePuts(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0\n");
3938 cupsFilePuts(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0\n");
3939 cupsFilePuts(fp, "*ParamCustomPageSize Orientation: 5 int 0 3\n");
3940 cupsFilePuts(fp, "*CustomPageSize True: \"pop pop pop <</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\"\n");
3941 }
3942 }
3943 else
3944 {
3945 cupsArrayDelete(sizes);
3946 goto bad_ppd;
3947 }
3948
3949 /*
3950 * InputSlot...
3951 */
3952
3953 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source", IPP_TAG_ZERO)) != NULL)
3954 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3955 else
3956 ppdname[0] = '\0';
3957
3958 if ((attr = ippFindAttribute(supported, "media-source-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3959 {
3960 int have_default = ppdname[0] != '\0';
3961 /* Do we have a default InputSlot? */
3962 static const char * const sources[] =
3963 { /* Standard "media-source" strings */
3964 "auto",
3965 "main",
3966 "alternate",
3967 "large-capacity",
3968 "manual",
3969 "envelope",
3970 "disc",
3971 "photo",
3972 "hagaki",
3973 "main-roll",
3974 "alternate-roll",
3975 "top",
3976 "middle",
3977 "bottom",
3978 "side",
3979 "left",
3980 "right",
3981 "center",
3982 "rear",
3983 "by-pass-tray",
3984 "tray-1",
3985 "tray-2",
3986 "tray-3",
3987 "tray-4",
3988 "tray-5",
3989 "tray-6",
3990 "tray-7",
3991 "tray-8",
3992 "tray-9",
3993 "tray-10",
3994 "tray-11",
3995 "tray-12",
3996 "tray-13",
3997 "tray-14",
3998 "tray-15",
3999 "tray-16",
4000 "tray-17",
4001 "tray-18",
4002 "tray-19",
4003 "tray-20",
4004 "roll-1",
4005 "roll-2",
4006 "roll-3",
4007 "roll-4",
4008 "roll-5",
4009 "roll-6",
4010 "roll-7",
4011 "roll-8",
4012 "roll-9",
4013 "roll-10"
4014 };
4015
4016 cupsFilePuts(fp, "*OpenUI *InputSlot: PickOne\n"
4017 "*OrderDependency: 10 AnySetup *InputSlot\n");
4018 if (have_default)
4019 cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
4020
4021 for (i = 0; i < count; i ++)
4022 {
4023 keyword = ippGetString(attr, i, NULL);
4024
4025 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4026
4027 if (i == 0 && !have_default)
4028 cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
4029
4030 for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++)
4031 {
4032 if (!strcmp(sources[j], keyword))
4033 {
4034 snprintf(msgid, sizeof(msgid), "media-source.%s", keyword);
4035
4036 cupsFilePrintf(fp, "*InputSlot %s/%s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)), j);
4037 ppd_put_strings(fp, langs, "InputSlot", ppdname, msgid);
4038 break;
4039 }
4040 }
4041 }
4042 cupsFilePuts(fp, "*CloseUI: *InputSlot\n");
4043 }
4044
4045 /*
4046 * MediaType...
4047 */
4048
4049 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type", IPP_TAG_ZERO)) != NULL)
4050 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
4051 else
4052 cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
4053
4054 if ((attr = ippFindAttribute(supported, "media-type-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
4055 {
4056 cupsFilePrintf(fp, "*OpenUI *MediaType: PickOne\n"
4057 "*OrderDependency: 10 AnySetup *MediaType\n"
4058 "*DefaultMediaType: %s\n", ppdname);
4059 for (i = 0; i < count; i ++)
4060 {
4061 keyword = ippGetString(attr, i, NULL);
4062
4063 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4064
4065 snprintf(msgid, sizeof(msgid), "media-type.%s", keyword);
4066
4067 cupsFilePrintf(fp, "*MediaType %s/%s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)), keyword);
4068 ppd_put_strings(fp, langs, "MediaType", ppdname, msgid);
4069 }
4070 cupsFilePuts(fp, "*CloseUI: *MediaType\n");
4071 }
4072
4073 /*
4074 * cupsPrintQuality and DefaultResolution...
4075 */
4076
4077 quality = ippFindAttribute(supported, "print-quality-supported", IPP_TAG_ENUM);
4078
4079 if ((attr = ippFindAttribute(supported, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
4080 {
4081 int lowdpi = 0, hidpi = 0; /* Lower and higher resolution */
4082
4083 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4084 {
4085 const char *rs = ippGetString(attr, i, NULL);
4086 /* RS value */
4087
4088 if (_cups_strncasecmp(rs, "RS", 2))
4089 continue;
4090
4091 lowdpi = atoi(rs + 2);
4092 if ((rs = strrchr(rs, '-')) != NULL)
4093 hidpi = atoi(rs + 1);
4094 else
4095 hidpi = lowdpi;
4096 break;
4097 }
4098
4099 if (lowdpi == 0)
4100 {
4101 /*
4102 * Invalid "urf-supported" value...
4103 */
4104
4105 goto bad_ppd;
4106 }
4107 else
4108 {
4109 /*
4110 * Generate print qualities based on low and high DPIs...
4111 */
4112
4113 cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi);
4114
4115 cupsFilePuts(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4116 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4117 "*DefaultcupsPrintQuality: Normal\n");
4118 if ((lowdpi & 1) == 0)
4119 {
4120 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", lowdpi, lowdpi / 2);
4121 have_qdraft = 1;
4122 }
4123 else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4124 {
4125 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*", lowdpi, lowdpi);
4126 have_qdraft = 1;
4127 }
4128
4129 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", lowdpi, lowdpi);
4130
4131 if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4132 {
4133 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", hidpi, hidpi);
4134 have_qhigh = 1;
4135 }
4136
4137 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4138 }
4139 }
4140 else if ((attr = ippFindAttribute(supported, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
4141 {
4142 /*
4143 * Make a sorted list of resolutions.
4144 */
4145
4146 count = ippGetCount(attr);
4147 if (count > (int)(sizeof(resolutions) / sizeof(resolutions[0])))
4148 count = (int)(sizeof(resolutions) / sizeof(resolutions[0]));
4149
4150 resolutions[0] = 0; /* Not in loop to silence Clang static analyzer... */
4151 for (i = 1; i < count; i ++)
4152 resolutions[i] = i;
4153
4154 for (i = 0; i < (count - 1); i ++)
4155 {
4156 for (j = i + 1; j < count; j ++)
4157 {
4158 int ix, iy, /* First X and Y resolution */
4159 jx, jy, /* Second X and Y resolution */
4160 temp; /* Swap variable */
4161 ipp_res_t units; /* Resolution units */
4162
4163 ix = ippGetResolution(attr, resolutions[i], &iy, &units);
4164 jx = ippGetResolution(attr, resolutions[j], &jy, &units);
4165
4166 if (ix > jx || (ix == jx && iy > jy))
4167 {
4168 /*
4169 * Swap these two resolutions...
4170 */
4171
4172 temp = resolutions[i];
4173 resolutions[i] = resolutions[j];
4174 resolutions[j] = temp;
4175 }
4176 }
4177 }
4178
4179 /*
4180 * Generate print quality options...
4181 */
4182
4183 pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, ppdname, sizeof(ppdname));
4184 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4185
4186 cupsFilePuts(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4187 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4188 "*DefaultcupsPrintQuality: Normal\n");
4189 if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4190 {
4191 pwg_ppdize_resolution(attr, resolutions[0], &xres, &yres, NULL, 0);
4192 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4193 have_qdraft = 1;
4194 }
4195
4196 pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, NULL, 0);
4197 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4198
4199 if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4200 {
4201 pwg_ppdize_resolution(attr, resolutions[count - 1], &xres, &yres, NULL, 0);
4202 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4203 have_qhigh = 1;
4204 }
4205
4206 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4207 }
4208 else if (is_apple || is_pwg)
4209 {
4210 goto bad_ppd;
4211 }
4212 else
4213 {
4214 if ((attr = ippFindAttribute(supported, "printer-resolution-default", IPP_TAG_RESOLUTION)) != NULL)
4215 {
4216 pwg_ppdize_resolution(attr, 0, &xres, &yres, ppdname, sizeof(ppdname));
4217 }
4218 else
4219 {
4220 xres = yres = 300;
4221 cupsCopyString(ppdname, "300dpi", sizeof(ppdname));
4222 }
4223
4224 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4225
4226 cupsFilePuts(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4227 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4228 "*DefaultcupsPrintQuality: Normal\n");
4229 if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4230 {
4231 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*", xres, yres);
4232 have_qdraft = 1;
4233 }
4234
4235 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4236
4237 if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
4238 {
4239 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4240 have_qhigh = 1;
4241 }
4242 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4243 }
4244
4245 /*
4246 * ColorModel...
4247 */
4248
4249 if ((defattr = ippFindAttribute(supported, "print-color-mode-default", IPP_TAG_KEYWORD)) == NULL)
4250 defattr = ippFindAttribute(supported, "output-mode-default", IPP_TAG_KEYWORD);
4251
4252 if ((attr = ippFindAttribute(supported, "urf-supported", IPP_TAG_KEYWORD)) == NULL)
4253 if ((attr = ippFindAttribute(supported, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
4254 if ((attr = ippFindAttribute(supported, "print-color-mode-supported", IPP_TAG_KEYWORD)) == NULL)
4255 attr = ippFindAttribute(supported, "output-mode-supported", IPP_TAG_KEYWORD);
4256
4257 if (attr)
4258 {
4259 int wrote_color = 0;
4260 const char *default_color = NULL; /* Default */
4261
4262 if ((keyword = ippGetString(defattr, 0, NULL)) != NULL &&
4263 strcmp(keyword, "auto"))
4264 {
4265 if (!strcmp(keyword, "bi-level"))
4266 default_color = "FastGray";
4267 else if (!strcmp(keyword, "monochrome") || !strcmp(keyword, "auto-monochrome"))
4268 default_color = "Gray";
4269 else
4270 default_color = "RGB";
4271 }
4272
4273 cupsFilePrintf(fp, "*%% ColorModel from %s\n", ippGetName(attr));
4274
4275 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4276 {
4277 keyword = ippGetString(attr, i, NULL);
4278
4279 #define PRINTF_COLORMODEL if (!wrote_color) { cupsFilePuts(fp, "*OpenUI *ColorModel: PickOne\n*OrderDependency: 10 AnySetup *ColorModel\n"); wrote_color = 1; }
4280 #define PRINTF_COLOROPTION(name,text,cspace,bpp) cupsFilePrintf(fp, "*ColorModel %s/%s: \"<</cupsColorSpace %d/cupsBitsPerColor %d/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", name, text, cspace, bpp);
4281
4282 if (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
4283 {
4284 PRINTF_COLORMODEL
4285
4286 PRINTF_COLOROPTION("FastGray", _("Fast Grayscale"), CUPS_CSPACE_K, 1)
4287
4288 if (!default_color)
4289 default_color = "FastGray";
4290 }
4291 else if (!strcasecmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
4292 {
4293 PRINTF_COLORMODEL
4294
4295 PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8)
4296
4297 if (!default_color || (!defattr && !strcmp(default_color, "FastGray")))
4298 default_color = "Gray";
4299 }
4300 else if (!strcasecmp(keyword, "sgray_16") || !strcmp(keyword, "W8-16"))
4301 {
4302 PRINTF_COLORMODEL
4303
4304 if (!strcmp(keyword, "W8-16"))
4305 {
4306 PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8)
4307
4308 if (!default_color || (!defattr && !strcmp(default_color, "FastGray")))
4309 default_color = "Gray";
4310 }
4311
4312 PRINTF_COLOROPTION("Gray16", _("Deep Gray"), CUPS_CSPACE_SW, 16)
4313 }
4314 else if (!strcasecmp(keyword, "srgb_8") || !strncmp(keyword, "SRGB24", 6) || !strcmp(keyword, "color"))
4315 {
4316 PRINTF_COLORMODEL
4317
4318 PRINTF_COLOROPTION("RGB", _("Color"), CUPS_CSPACE_SRGB, 8)
4319
4320 if (!default_color)
4321 default_color = "RGB";
4322
4323 // Apparently some printers only advertise color support, so make sure
4324 // we also do grayscale for these printers...
4325 if (!ippContainsString(attr, "sgray_8") && !ippContainsString(attr, "black_1") && !ippContainsString(attr, "black_8") && !ippContainsString(attr, "W8") && !ippContainsString(attr, "W8-16"))
4326 PRINTF_COLOROPTION("Gray", _("GrayScale"), CUPS_CSPACE_SW, 8)
4327 }
4328 else if (!strcasecmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48") || !strcmp(keyword, "ADOBERGB24-48"))
4329 {
4330 PRINTF_COLORMODEL
4331
4332 PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 16)
4333
4334 if (!default_color)
4335 default_color = "AdobeRGB";
4336 }
4337 else if ((!strcasecmp(keyword, "adobe-rgb_8") && !ippContainsString(attr, "adobe-rgb_16")) || !strcmp(keyword, "ADOBERGB24"))
4338 {
4339 PRINTF_COLORMODEL
4340
4341 PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 8)
4342
4343 if (!default_color)
4344 default_color = "AdobeRGB";
4345 }
4346 else if ((!strcasecmp(keyword, "black_8") && !ippContainsString(attr, "black_16")) || !strcmp(keyword, "DEVW8"))
4347 {
4348 PRINTF_COLORMODEL
4349
4350 PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 8)
4351 }
4352 else if (!strcasecmp(keyword, "black_16") || !strcmp(keyword, "DEVW16") || !strcmp(keyword, "DEVW8-16"))
4353 {
4354 PRINTF_COLORMODEL
4355
4356 PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 16)
4357 }
4358 else if ((!strcasecmp(keyword, "cmyk_8") && !ippContainsString(attr, "cmyk_16")) || !strcmp(keyword, "DEVCMYK32"))
4359 {
4360 PRINTF_COLORMODEL
4361
4362 PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 8)
4363 }
4364 else if (!strcasecmp(keyword, "cmyk_16") || !strcmp(keyword, "DEVCMYK32-64") || !strcmp(keyword, "DEVCMYK64"))
4365 {
4366 PRINTF_COLORMODEL
4367
4368 PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 16)
4369 }
4370 else if ((!strcasecmp(keyword, "rgb_8") && ippContainsString(attr, "rgb_16")) || !strcmp(keyword, "DEVRGB24"))
4371 {
4372 PRINTF_COLORMODEL
4373
4374 PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 8)
4375 }
4376 else if (!strcasecmp(keyword, "rgb_16") || !strcmp(keyword, "DEVRGB24-48") || !strcmp(keyword, "DEVRGB48"))
4377 {
4378 PRINTF_COLORMODEL
4379
4380 PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 16)
4381 }
4382 }
4383
4384 if (default_color)
4385 cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color);
4386 if (wrote_color)
4387 cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
4388
4389 if (default_color)
4390 {
4391 // Standard presets for color mode and quality...
4392 if (have_qdraft)
4393 cupsFilePuts(fp,
4394 "*APPrinterPreset Gray_with_Paper_Auto-Detect_-_Draft/Draft B&W: \"\n"
4395 " *cupsPrintQuality Draft *ColorModel Gray\n"
4396 " com.apple.print.preset.graphicsType General\n"
4397 " com.apple.print.preset.quality low\n"
4398 " com.apple.print.preset.media-front-coating autodetect\n"
4399 " com.apple.print.preset.output-mode monochrome\"\n"
4400 "*End\n");
4401 cupsFilePuts(fp,
4402 "*APPrinterPreset Gray_with_Paper_Auto-Detect/Black and White: \"\n"
4403 " *cupsPrintQuality Normal *ColorModel Gray\n"
4404 " com.apple.print.preset.graphicsType General\n"
4405 " com.apple.print.preset.quality mid\n"
4406 " com.apple.print.preset.media-front-coating autodetect\n"
4407 " com.apple.print.preset.output-mode monochrome\"\n"
4408 "*End\n");
4409 if (strcmp(default_color, "Gray"))
4410 cupsFilePuts(fp,
4411 "*APPrinterPreset Color_with_Paper_Auto-Detect/Color: \"\n"
4412 " *cupsPrintQuality Normal *ColorModel RGB\n"
4413 " com.apple.print.preset.graphicsType General\n"
4414 " com.apple.print.preset.quality mid\n"
4415 " com.apple.print.preset.media-front-coating autodetect\n"
4416 " com.apple.print.preset.output-mode color\"\n"
4417 "*End\n");
4418 if (!strcmp(default_color, "AdobeRGB") || have_qhigh)
4419 cupsFilePrintf(fp,
4420 "*APPrinterPreset Photo_with_Paper_Auto-Detect/Photo: \"\n"
4421 " *cupsPrintQuality %s *ColorModel %s\n"
4422 " com.apple.print.preset.graphicsType Photo\n"
4423 " com.apple.print.preset.quality %s\n"
4424 " com.apple.print.preset.media-front-coating autodetect\n"
4425 " com.apple.print.preset.output-mode color\"\n"
4426 "*End\n", have_qhigh ? "High" : "Normal", default_color, have_qhigh ? "high" : "mid");
4427 }
4428 }
4429
4430 /*
4431 * Duplex...
4432 */
4433
4434 if ((attr = ippFindAttribute(supported, "sides-supported", IPP_TAG_KEYWORD)) != NULL && ippContainsString(attr, "two-sided-long-edge"))
4435 {
4436 cupsFilePuts(fp, "*OpenUI *Duplex: PickOne\n"
4437 "*OrderDependency: 10 AnySetup *Duplex\n"
4438 "*DefaultDuplex: None\n"
4439 "*Duplex None: \"<</Duplex false>>setpagedevice\"\n"
4440 "*Duplex DuplexNoTumble: \"<</Duplex true/Tumble false>>setpagedevice\"\n"
4441 "*Duplex DuplexTumble: \"<</Duplex true/Tumble true>>setpagedevice\"\n"
4442 "*CloseUI: *Duplex\n");
4443
4444 if ((attr = ippFindAttribute(supported, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
4445 {
4446 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4447 {
4448 const char *dm = ippGetString(attr, i, NULL);
4449 /* DM value */
4450
4451 if (!_cups_strcasecmp(dm, "DM1"))
4452 {
4453 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
4454 break;
4455 }
4456 else if (!_cups_strcasecmp(dm, "DM2"))
4457 {
4458 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
4459 break;
4460 }
4461 else if (!_cups_strcasecmp(dm, "DM3"))
4462 {
4463 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
4464 break;
4465 }
4466 else if (!_cups_strcasecmp(dm, "DM4"))
4467 {
4468 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
4469 break;
4470 }
4471 }
4472 }
4473 else if ((attr = ippFindAttribute(supported, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
4474 {
4475 keyword = ippGetString(attr, 0, NULL);
4476
4477 if (!strcmp(keyword, "flipped"))
4478 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
4479 else if (!strcmp(keyword, "manual-tumble"))
4480 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
4481 else if (!strcmp(keyword, "normal"))
4482 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
4483 else
4484 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
4485 }
4486 }
4487
4488 /*
4489 * Output bin...
4490 */
4491
4492 if ((attr = ippFindAttribute(supported, "output-bin-default", IPP_TAG_ZERO)) != NULL)
4493 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
4494 else
4495 cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
4496
4497 if ((attr = ippFindAttribute(supported, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 0)
4498 {
4499 ipp_attribute_t *trays = ippFindAttribute(supported, "printer-output-tray", IPP_TAG_STRING);
4500 /* printer-output-tray attribute, if any */
4501 const char *tray_ptr; /* printer-output-tray value */
4502 int tray_len; /* Len of printer-output-tray value */
4503 char tray[IPP_MAX_OCTETSTRING];
4504 /* printer-output-tray string value */
4505
4506 cupsFilePrintf(fp, "*OpenUI *OutputBin: PickOne\n"
4507 "*OrderDependency: 10 AnySetup *OutputBin\n"
4508 "*DefaultOutputBin: %s\n", ppdname);
4509 if (!strcmp(ppdname, "FaceUp"))
4510 cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n");
4511 else
4512 cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n");
4513
4514 for (i = 0; i < count; i ++)
4515 {
4516 keyword = ippGetString(attr, i, NULL);
4517
4518 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4519
4520 snprintf(msgid, sizeof(msgid), "output-bin.%s", keyword);
4521
4522 cupsFilePrintf(fp, "*OutputBin %s/%s: \"\"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)));
4523 ppd_put_strings(fp, langs, "OutputBin", ppdname, msgid);
4524
4525 if ((tray_ptr = ippGetOctetString(trays, i, &tray_len)) != NULL)
4526 {
4527 if (tray_len >= (int)sizeof(tray))
4528 tray_len = (int)sizeof(tray) - 1;
4529
4530 memcpy(tray, tray_ptr, (size_t)tray_len);
4531 tray[tray_len] = '\0';
4532
4533 if (strstr(tray, "stackingorder=lastToFirst;"))
4534 cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
4535 else
4536 cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
4537 }
4538 else if (!strcmp(ppdname, "FaceUp"))
4539 cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
4540 else
4541 cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
4542 }
4543 cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
4544 }
4545
4546 /*
4547 * Finishing options...
4548 */
4549
4550 if ((attr = ippFindAttribute(supported, "finishings-supported", IPP_TAG_ENUM)) != NULL)
4551 {
4552 int value; /* Enum value */
4553 const char *ppd_keyword; /* PPD keyword for enum */
4554 cups_array_t *names; /* Names we've added */
4555 static const char * const base_keywords[] =
4556 { /* Base STD 92 keywords */
4557 NULL, /* none */
4558 "SingleAuto", /* staple */
4559 "SingleAuto", /* punch */
4560 NULL, /* cover */
4561 "BindAuto", /* bind */
4562 "SaddleStitch", /* saddle-stitch */
4563 "EdgeStitchAuto", /* edge-stitch */
4564 "Auto", /* fold */
4565 NULL, /* trim */
4566 NULL, /* bale */
4567 NULL, /* booklet-maker */
4568 NULL, /* jog-offset */
4569 NULL, /* coat */
4570 NULL /* laminate */
4571 };
4572
4573 count = ippGetCount(attr);
4574 names = cupsArrayNew3((cups_array_func_t)_cupsArrayStrcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
4575 fin_options = cupsArrayNew((cups_array_func_t)_cupsArrayStrcmp, NULL);
4576
4577 /*
4578 * Staple/Bind/Stitch
4579 */
4580
4581 for (i = 0; i < count; i ++)
4582 {
4583 value = ippGetInteger(attr, i);
4584 keyword = ippEnumString("finishings", value);
4585
4586 if (!strncmp(keyword, "staple-", 7) || !strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch") || !strcmp(keyword, "staple") || !strcmp(keyword, "bind"))
4587 break;
4588 }
4589
4590 if (i < count)
4591 {
4592 static const char * const staple_keywords[] =
4593 { /* StapleLocation keywords */
4594 "SinglePortrait",
4595 "SingleRevLandscape",
4596 "SingleLandscape",
4597 "SingleRevPortrait",
4598 "EdgeStitchPortrait",
4599 "EdgeStitchLandscape",
4600 "EdgeStitchRevPortrait",
4601 "EdgeStitchRevLandscape",
4602 "DualPortrait",
4603 "DualLandscape",
4604 "DualRevPortrait",
4605 "DualRevLandscape",
4606 "TriplePortrait",
4607 "TripleLandscape",
4608 "TripleRevPortrait",
4609 "TripleRevLandscape"
4610 };
4611 static const char * const bind_keywords[] =
4612 { /* StapleLocation binding keywords */
4613 "BindPortrait",
4614 "BindLandscape",
4615 "BindRevPortrait",
4616 "BindRevLandscape"
4617 };
4618
4619 cupsArrayAdd(fin_options, "*StapleLocation");
4620
4621 cupsFilePrintf(fp, "*OpenUI *StapleLocation/%s: PickOne\n", _cupsLangString(base_lang, "finishings.4"));
4622 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
4623 ppd_put_strings(fp, langs, "Translation", "StapleLocation", "finishings.4");
4624 cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
4625 cupsFilePrintf(fp, "*StapleLocation None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4626 ppd_put_strings(fp, langs, "StapleLocation", "None", "finishings.3");
4627
4628 for (; i < count; i ++)
4629 {
4630 value = ippGetInteger(attr, i);
4631 keyword = ippEnumString("finishings", value);
4632
4633 if (strncmp(keyword, "staple-", 7) && strncmp(keyword, "bind-", 5) && strncmp(keyword, "edge-stitch-", 12) && strcmp(keyword, "saddle-stitch") && strcmp(keyword, "staple") && strcmp(keyword, "bind"))
4634 continue;
4635
4636 if (cupsArrayFind(names, (char *)keyword))
4637 continue; /* Already did this finishing template */
4638
4639 cupsArrayAdd(names, (char *)keyword);
4640
4641 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4642
4643 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4644 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4645 else if (value >= IPP_FINISHINGS_STAPLE_TOP_LEFT && value <= IPP_FINISHINGS_STAPLE_TRIPLE_BOTTOM)
4646 ppd_keyword = staple_keywords[value - IPP_FINISHINGS_STAPLE_TOP_LEFT];
4647 else if (value >= IPP_FINISHINGS_BIND_LEFT && value <= IPP_FINISHINGS_BIND_BOTTOM)
4648 ppd_keyword = bind_keywords[value - IPP_FINISHINGS_BIND_LEFT];
4649 else
4650 ppd_keyword = NULL;
4651
4652 if (!ppd_keyword)
4653 continue;
4654
4655 cupsFilePrintf(fp, "*StapleLocation %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4656 ppd_put_strings(fp, langs, "StapleLocation", ppd_keyword, msgid);
4657 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, keyword, ppd_keyword);
4658 }
4659
4660 cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
4661 }
4662
4663 /*
4664 * Fold
4665 */
4666
4667 for (i = 0; i < count; i ++)
4668 {
4669 value = ippGetInteger(attr, i);
4670 keyword = ippEnumString("finishings", value);
4671
4672 if (!strncmp(keyword, "cups-fold-", 10) || !strcmp(keyword, "fold") || !strncmp(keyword, "fold-", 5))
4673 break;
4674 }
4675
4676 if (i < count)
4677 {
4678 static const char * const fold_keywords[] =
4679 { /* FoldType keywords */
4680 "Accordion",
4681 "DoubleGate",
4682 "Gate",
4683 "Half",
4684 "HalfZ",
4685 "LeftGate",
4686 "Letter",
4687 "Parallel",
4688 "XFold",
4689 "RightGate",
4690 "ZFold",
4691 "EngineeringZ"
4692 };
4693
4694 cupsArrayAdd(fin_options, "*FoldType");
4695
4696 cupsFilePrintf(fp, "*OpenUI *FoldType/%s: PickOne\n", _cupsLangString(base_lang, "finishings.10"));
4697 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
4698 ppd_put_strings(fp, langs, "Translation", "FoldType", "finishings.10");
4699 cupsFilePuts(fp, "*DefaultFoldType: None\n");
4700 cupsFilePrintf(fp, "*FoldType None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4701 ppd_put_strings(fp, langs, "FoldType", "None", "finishings.3");
4702
4703 for (; i < count; i ++)
4704 {
4705 value = ippGetInteger(attr, i);
4706 keyword = ippEnumString("finishings", value);
4707
4708 if (!strncmp(keyword, "cups-fold-", 10))
4709 keyword += 5;
4710 else if (strcmp(keyword, "fold") && strncmp(keyword, "fold-", 5))
4711 continue;
4712
4713 if (cupsArrayFind(names, (char *)keyword))
4714 continue; /* Already did this finishing template */
4715
4716 cupsArrayAdd(names, (char *)keyword);
4717
4718 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4719
4720 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4721 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4722 else if (value >= IPP_FINISHINGS_FOLD_ACCORDION && value <= IPP_FINISHINGS_FOLD_ENGINEERING_Z)
4723 ppd_keyword = fold_keywords[value - IPP_FINISHINGS_FOLD_ACCORDION];
4724 else if (value >= IPP_FINISHINGS_CUPS_FOLD_ACCORDION && value <= IPP_FINISHINGS_CUPS_FOLD_Z)
4725 ppd_keyword = fold_keywords[value - IPP_FINISHINGS_CUPS_FOLD_ACCORDION];
4726 else
4727 ppd_keyword = NULL;
4728
4729 if (!ppd_keyword)
4730 continue;
4731
4732 cupsFilePrintf(fp, "*FoldType %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4733 ppd_put_strings(fp, langs, "FoldType", ppd_keyword, msgid);
4734 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, keyword, ppd_keyword);
4735 }
4736
4737 cupsFilePuts(fp, "*CloseUI: *FoldType\n");
4738 }
4739
4740 /*
4741 * Punch
4742 */
4743
4744 for (i = 0; i < count; i ++)
4745 {
4746 value = ippGetInteger(attr, i);
4747 keyword = ippEnumString("finishings", value);
4748
4749 if (!strcmp(keyword, "punch") || !strncmp(keyword, "cups-punch-", 11) || !strncmp(keyword, "punch-", 6))
4750 break;
4751 }
4752
4753 if (i < count)
4754 {
4755 static const char * const punch_keywords[] =
4756 { /* PunchMedia keywords */
4757 "SinglePortrait",
4758 "SingleRevLandscape",
4759 "SingleLandscape",
4760 "SingleRevPortrait",
4761 "DualPortrait",
4762 "DualLandscape",
4763 "DualRevPortrait",
4764 "DualRevLandscape",
4765 "TriplePortrait",
4766 "TripleLandscape",
4767 "TripleRevPortrait",
4768 "TripleRevLandscape",
4769 "QuadPortrait",
4770 "QuadLandscape",
4771 "QuadRevPortrait",
4772 "QuadRevLandscape",
4773 "MultiplePortrait",
4774 "MultipleLandscape",
4775 "MultipleRevPortrait",
4776 "MultipleRevLandscape"
4777 };
4778
4779 cupsArrayAdd(fin_options, "*PunchMedia");
4780
4781 cupsFilePrintf(fp, "*OpenUI *PunchMedia/%s: PickOne\n", _cupsLangString(base_lang, "finishings.5"));
4782 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
4783 ppd_put_strings(fp, langs, "Translation", "PunchMedia", "finishings.5");
4784 cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
4785 cupsFilePrintf(fp, "*PunchMedia None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4786 ppd_put_strings(fp, langs, "PunchMedia", "None", "finishings.3");
4787
4788 for (i = 0; i < count; i ++)
4789 {
4790 value = ippGetInteger(attr, i);
4791 keyword = ippEnumString("finishings", value);
4792
4793 if (!strncmp(keyword, "cups-punch-", 11))
4794 keyword += 5;
4795 else if (strcmp(keyword, "punch") && strncmp(keyword, "punch-", 6))
4796 continue;
4797
4798 if (cupsArrayFind(names, (char *)keyword))
4799 continue; /* Already did this finishing template */
4800
4801 cupsArrayAdd(names, (char *)keyword);
4802
4803 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4804
4805 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4806 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4807 else if (value >= IPP_FINISHINGS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_PUNCH_MULTIPLE_BOTTOM)
4808 ppd_keyword = punch_keywords[value - IPP_FINISHINGS_PUNCH_TOP_LEFT];
4809 else if (value >= IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM)
4810 ppd_keyword = punch_keywords[value - IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT];
4811 else
4812 ppd_keyword = NULL;
4813
4814 if (!ppd_keyword)
4815 continue;
4816
4817 cupsFilePrintf(fp, "*PunchMedia %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4818 ppd_put_strings(fp, langs, "PunchMedia", ppd_keyword, msgid);
4819 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, keyword, ppd_keyword);
4820 }
4821
4822 cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
4823 }
4824
4825 /*
4826 * Booklet
4827 */
4828
4829 if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER))
4830 {
4831 cupsArrayAdd(fin_options, "*Booklet");
4832
4833 cupsFilePrintf(fp, "*OpenUI *Booklet/%s: Boolean\n", _cupsLangString(base_lang, "finishings.13"));
4834 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
4835 ppd_put_strings(fp, langs, "Translation", "StapleLocation", "finishings.13");
4836 cupsFilePuts(fp, "*DefaultBooklet: False\n");
4837 cupsFilePuts(fp, "*Booklet False: \"\"\n");
4838 cupsFilePuts(fp, "*Booklet True: \"\"\n");
4839 cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n", IPP_FINISHINGS_BOOKLET_MAKER);
4840 cupsFilePuts(fp, "*CloseUI: *Booklet\n");
4841 }
4842
4843 /*
4844 * CutMedia
4845 */
4846
4847 for (i = 0; i < count; i ++)
4848 {
4849 value = ippGetInteger(attr, i);
4850 keyword = ippEnumString("finishings", value);
4851
4852 if (!strcmp(keyword, "trim") || !strncmp(keyword, "trim-", 5))
4853 break;
4854 }
4855
4856 if (i < count)
4857 {
4858 static const char * const trim_keywords[] =
4859 { /* CutMedia keywords */
4860 "EndOfPage",
4861 "EndOfDoc",
4862 "EndOfSet",
4863 "EndOfJob"
4864 };
4865
4866 cupsArrayAdd(fin_options, "*CutMedia");
4867
4868 cupsFilePrintf(fp, "*OpenUI *CutMedia/%s: PickOne\n", _cupsLangString(base_lang, "finishings.11"));
4869 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *CutMedia\n");
4870 ppd_put_strings(fp, langs, "Translation", "CutMedia", "finishings.11");
4871 cupsFilePuts(fp, "*DefaultCutMedia: None\n");
4872 cupsFilePrintf(fp, "*CutMedia None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4873 ppd_put_strings(fp, langs, "CutMedia", "None", "finishings.3");
4874
4875 for (i = 0; i < count; i ++)
4876 {
4877 value = ippGetInteger(attr, i);
4878 keyword = ippEnumString("finishings", value);
4879
4880 if (strcmp(keyword, "trim") && strncmp(keyword, "trim-", 5))
4881 continue;
4882
4883 if (cupsArrayFind(names, (char *)keyword))
4884 continue; /* Already did this finishing template */
4885
4886 cupsArrayAdd(names, (char *)keyword);
4887
4888 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4889
4890 if (value == IPP_FINISHINGS_TRIM)
4891 ppd_keyword = "Auto";
4892 else
4893 ppd_keyword = trim_keywords[value - IPP_FINISHINGS_TRIM_AFTER_PAGES];
4894
4895 cupsFilePrintf(fp, "*CutMedia %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4896 ppd_put_strings(fp, langs, "CutMedia", ppd_keyword, msgid);
4897 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*CutMedia %s\"\n", value, keyword, ppd_keyword);
4898 }
4899
4900 cupsFilePuts(fp, "*CloseUI: *CutMedia\n");
4901 }
4902
4903 cupsArrayDelete(names);
4904 }
4905
4906 if ((attr = ippFindAttribute(supported, "finishings-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4907 {
4908 ipp_t *finishing_col; /* Current finishing collection */
4909 ipp_attribute_t *finishing_attr; /* Current finishing member attribute */
4910 cups_array_t *templates; /* Finishing templates */
4911
4912 cupsFilePrintf(fp, "*OpenUI *cupsFinishingTemplate/%s: PickOne\n", ppd_get_string(base_lang, lang, "finishing-template", ppdtext, sizeof(ppdtext)));
4913 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n");
4914 ppd_put_strings(fp, langs, "Translation", "cupsFinishingTemplate", "finishing-template");
4915 cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n");
4916 cupsFilePrintf(fp, "*cupsFinishingTemplate none/%s: \"\"\n", _cupsLangString(lang, "finishings.3"));
4917 ppd_put_strings(fp, langs, "cupsFinishingTemplate", "none", "finishings.3");
4918
4919 templates = cupsArrayNew((cups_array_func_t)_cupsArrayStrcmp, NULL);
4920 count = ippGetCount(attr);
4921
4922 for (i = 0; i < count; i ++)
4923 {
4924 finishing_col = ippGetCollection(attr, i);
4925 keyword = ippGetString(ippFindAttribute(finishing_col, "finishing-template", IPP_TAG_ZERO), 0, NULL);
4926
4927 if (!keyword || cupsArrayFind(templates, (void *)keyword))
4928 continue;
4929
4930 if (!strcmp(keyword, "none"))
4931 continue;
4932
4933 cupsArrayAdd(templates, (void *)keyword);
4934
4935 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4936
4937 snprintf(msgid, sizeof(msgid), "finishing-template.%s", keyword);
4938
4939 cupsFilePrintf(fp, "*cupsFinishingTemplate %s/%s: \"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)));
4940 for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr; finishing_attr = ippNextAttribute(finishing_col))
4941 {
4942 if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION)
4943 {
4944 const char *name = ippGetName(finishing_attr);
4945 /* Member attribute name */
4946
4947 if (strcmp(name, "media-size"))
4948 cupsFilePrintf(fp, "%% %s\n", name);
4949 }
4950 }
4951 cupsFilePuts(fp, "\"\n");
4952 ppd_put_strings(fp, langs, "cupsFinishingTemplate", ppdname, msgid);
4953 cupsFilePuts(fp, "*End\n");
4954 }
4955
4956 cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n");
4957
4958 if (cupsArrayCount(fin_options))
4959 {
4960 const char *fin_option; /* Current finishing option */
4961
4962 cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate");
4963 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4964 cupsFilePrintf(fp, " %s", fin_option);
4965 cupsFilePuts(fp, "\"\n");
4966
4967 cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate none");
4968 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4969 cupsFilePrintf(fp, " %s None", fin_option);
4970 cupsFilePuts(fp, "\"\n");
4971 }
4972
4973 cupsArrayDelete(templates);
4974 }
4975
4976 cupsArrayDelete(fin_options);
4977
4978 /*
4979 * Presets...
4980 */
4981
4982 if ((attr = ippFindAttribute(supported, "job-presets-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4983 {
4984 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4985 {
4986 ipp_t *preset = ippGetCollection(attr, i);
4987 /* Preset collection */
4988 const char *preset_name = ippGetString(ippFindAttribute(preset, "preset-name", IPP_TAG_ZERO), 0, NULL);
4989 /* Preset name */
4990 ipp_attribute_t *member; /* Member attribute in preset */
4991 const char *member_name; /* Member attribute name */
4992 char member_value[256]; /* Member attribute value */
4993
4994 if (!preset || !preset_name)
4995 continue;
4996
4997 pwg_ppdize_name(preset_name, ppdname, sizeof(ppdname));
4998
4999 cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", ppdname);
5000 for (member = ippFirstAttribute(preset); member; member = ippNextAttribute(preset))
5001 {
5002 member_name = ippGetName(member);
5003
5004 if (!member_name || !strcmp(member_name, "preset-name"))
5005 continue;
5006
5007 if (!strcmp(member_name, "finishings"))
5008 {
5009 for (i = 0, count = ippGetCount(member); i < count; i ++)
5010 {
5011 const char *option = NULL; /* PPD option name */
5012
5013 keyword = ippEnumString("finishings", ippGetInteger(member, i));
5014
5015 if (!strcmp(keyword, "booklet-maker"))
5016 {
5017 option = "Booklet";
5018 keyword = "True";
5019 }
5020 else if (!strncmp(keyword, "fold-", 5))
5021 option = "FoldType";
5022 else if (!strncmp(keyword, "punch-", 6))
5023 option = "PunchMedia";
5024 else if (!strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch") || !strncmp(keyword, "staple-", 7) || !strcmp(keyword, "staple") || !strcmp(keyword, "bind"))
5025 option = "StapleLocation";
5026
5027 if (option && keyword)
5028 cupsFilePrintf(fp, "*%s %s\n", option, keyword);
5029 }
5030 }
5031 else if (!strcmp(member_name, "finishings-col"))
5032 {
5033 ipp_t *fin_col; /* finishings-col value */
5034
5035 for (i = 0, count = ippGetCount(member); i < count; i ++)
5036 {
5037 fin_col = ippGetCollection(member, i);
5038
5039 if ((keyword = ippGetString(ippFindAttribute(fin_col, "finishing-template", IPP_TAG_ZERO), 0, NULL)) != NULL)
5040 {
5041 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
5042 cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", ppdname);
5043 }
5044 }
5045 }
5046 else if (!strcmp(member_name, "media"))
5047 {
5048 /*
5049 * Map media to PageSize...
5050 */
5051
5052 if ((pwg = pwgMediaForPWG(ippGetString(member, 0, NULL))) != NULL && pwg->ppd)
5053 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
5054 }
5055 else if (!strcmp(member_name, "media-col"))
5056 {
5057 media_col = ippGetCollection(member, 0);
5058
5059 if ((media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0)) != NULL)
5060 {
5061 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
5062 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
5063 if ((pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL && pwg->ppd)
5064 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
5065 }
5066
5067 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL)) != NULL)
5068 {
5069 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
5070 cupsFilePrintf(fp, "*InputSlot %s\n", ppdname);
5071 }
5072
5073 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL)) != NULL)
5074 {
5075 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
5076 cupsFilePrintf(fp, "*MediaType %s\n", ppdname);
5077 }
5078 }
5079 else if (!strcmp(member_name, "print-quality"))
5080 {
5081 /*
5082 * Map print-quality to cupsPrintQuality...
5083 */
5084
5085 int qval = ippGetInteger(member, 0);
5086 /* print-quality value */
5087 static const char * const qualities[] = { "Draft", "Normal", "High" };
5088 /* cupsPrintQuality values */
5089
5090 if (qval >= IPP_QUALITY_DRAFT && qval <= IPP_QUALITY_HIGH)
5091 cupsFilePrintf(fp, "*cupsPrintQuality %s\n", qualities[qval - IPP_QUALITY_DRAFT]);
5092 }
5093 else if (!strcmp(member_name, "output-bin"))
5094 {
5095 pwg_ppdize_name(ippGetString(member, 0, NULL), ppdname, sizeof(ppdname));
5096 cupsFilePrintf(fp, "*OutputBin %s\n", ppdname);
5097 }
5098 else if (!strcmp(member_name, "sides"))
5099 {
5100 keyword = ippGetString(member, 0, NULL);
5101 if (keyword && !strcmp(keyword, "one-sided"))
5102 cupsFilePuts(fp, "*Duplex None\n");
5103 else if (keyword && !strcmp(keyword, "two-sided-long-edge"))
5104 cupsFilePuts(fp, "*Duplex DuplexNoTumble\n");
5105 else if (keyword && !strcmp(keyword, "two-sided-short-edge"))
5106 cupsFilePuts(fp, "*Duplex DuplexTumble\n");
5107 }
5108 else
5109 {
5110 /*
5111 * Add attribute name and value as-is...
5112 */
5113
5114 ippAttributeString(member, member_value, sizeof(member_value));
5115 cupsFilePrintf(fp, "*%s %s\n", member_name, member_value);
5116 }
5117 }
5118
5119 cupsFilePuts(fp, "\"\n*End\n");
5120
5121 pwg_ppdize_name(preset_name, ppdname, sizeof(ppdname));
5122 snprintf(msgid, sizeof(msgid), "preset-name.%s", preset_name);
5123 ppd_put_strings(fp, langs, "APPrinterPreset", ppdname, msgid);
5124 }
5125 }
5126
5127 /*
5128 * Add cupsSingleFile to support multiple files printing on printers
5129 * which don't support multiple files in its firmware...
5130 *
5131 * Adding the keyword degrades printing performance (there is 1-2 seconds
5132 * pause between files).
5133 */
5134
5135 cupsFilePuts(fp, "*cupsSingleFile: true\n");
5136
5137 /*
5138 * Add localized printer-state-reasons keywords as needed...
5139 */
5140
5141 for (lang = langs; lang; lang = lang->next)
5142 {
5143 _cups_message_t *msg; // Current message
5144
5145 for (msg = (_cups_message_t *)cupsArrayGetFirst(lang->strings); msg; msg = (_cups_message_t *)cupsArrayGetNext(lang->strings))
5146 {
5147 if (!strncmp(msg->msg, "printer-state-reasons.", 22))
5148 {
5149 if (!strcmp(lang->language, "en"))
5150 cupsFilePrintf(fp, "*cupsIPPReason %s/%s: \"\"\n", msg->msg + 22, msg->str);
5151 else
5152 cupsFilePrintf(fp, "*%s.cupsIPPReason %s/%s: \"\"\n", lang->language, msg->msg + 22, msg->str);
5153 }
5154 }
5155 }
5156
5157 /*
5158 * Close up and return...
5159 */
5160
5161 cupsFileClose(fp);
5162
5163 for (lang = langs; lang;)
5164 {
5165 cups_lang_t *next = lang->next;
5166
5167 cupsArrayDelete(lang->strings);
5168 free(lang);
5169
5170 lang = next;
5171 }
5172
5173 return (buffer);
5174
5175 /*
5176 * If we get here then there was a problem creating the PPD...
5177 */
5178
5179 bad_ppd:
5180
5181 cupsFileClose(fp);
5182
5183 for (lang = langs; lang;)
5184 {
5185 cups_lang_t *next = lang->next;
5186
5187 cupsArrayDelete(lang->strings);
5188 free(lang);
5189
5190 lang = next;
5191 }
5192
5193 unlink(buffer);
5194 *buffer = '\0';
5195
5196 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Printer does not support required IPP attributes or document formats."), 1);
5197
5198 return (NULL);
5199 }
5200
5201
5202 /*
5203 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
5204 * media-source.
5205 */
5206
5207 const char * /* O - InputSlot name */
5208 _pwgInputSlotForSource(
5209 const char *media_source, /* I - PWG media-source */
5210 char *name, /* I - Name buffer */
5211 size_t namesize) /* I - Size of name buffer */
5212 {
5213 /*
5214 * Range check input...
5215 */
5216
5217 if (!media_source || !name || namesize < PPD_MAX_NAME)
5218 return (NULL);
5219
5220 if (_cups_strcasecmp(media_source, "main"))
5221 cupsCopyString(name, "Cassette", namesize);
5222 else if (_cups_strcasecmp(media_source, "alternate"))
5223 cupsCopyString(name, "Multipurpose", namesize);
5224 else if (_cups_strcasecmp(media_source, "large-capacity"))
5225 cupsCopyString(name, "LargeCapacity", namesize);
5226 else if (_cups_strcasecmp(media_source, "bottom"))
5227 cupsCopyString(name, "Lower", namesize);
5228 else if (_cups_strcasecmp(media_source, "middle"))
5229 cupsCopyString(name, "Middle", namesize);
5230 else if (_cups_strcasecmp(media_source, "top"))
5231 cupsCopyString(name, "Upper", namesize);
5232 else if (_cups_strcasecmp(media_source, "rear"))
5233 cupsCopyString(name, "Rear", namesize);
5234 else if (_cups_strcasecmp(media_source, "side"))
5235 cupsCopyString(name, "Side", namesize);
5236 else if (_cups_strcasecmp(media_source, "envelope"))
5237 cupsCopyString(name, "Envelope", namesize);
5238 else if (_cups_strcasecmp(media_source, "main-roll"))
5239 cupsCopyString(name, "Roll", namesize);
5240 else if (_cups_strcasecmp(media_source, "alternate-roll"))
5241 cupsCopyString(name, "Roll2", namesize);
5242 else
5243 pwg_ppdize_name(media_source, name, namesize);
5244
5245 return (name);
5246 }
5247
5248
5249 /*
5250 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
5251 * media-type.
5252 */
5253
5254 const char * /* O - MediaType name */
5255 _pwgMediaTypeForType(
5256 const char *media_type, /* I - PWG media-type */
5257 char *name, /* I - Name buffer */
5258 size_t namesize) /* I - Size of name buffer */
5259 {
5260 /*
5261 * Range check input...
5262 */
5263
5264 if (!media_type || !name || namesize < PPD_MAX_NAME)
5265 return (NULL);
5266
5267 if (_cups_strcasecmp(media_type, "auto"))
5268 cupsCopyString(name, "Auto", namesize);
5269 else if (_cups_strcasecmp(media_type, "cardstock"))
5270 cupsCopyString(name, "Cardstock", namesize);
5271 else if (_cups_strcasecmp(media_type, "envelope"))
5272 cupsCopyString(name, "Envelope", namesize);
5273 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
5274 cupsCopyString(name, "Glossy", namesize);
5275 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
5276 cupsCopyString(name, "HighGloss", namesize);
5277 else if (_cups_strcasecmp(media_type, "photographic-matte"))
5278 cupsCopyString(name, "Matte", namesize);
5279 else if (_cups_strcasecmp(media_type, "stationery"))
5280 cupsCopyString(name, "Plain", namesize);
5281 else if (_cups_strcasecmp(media_type, "stationery-coated"))
5282 cupsCopyString(name, "Coated", namesize);
5283 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
5284 cupsCopyString(name, "Inkjet", namesize);
5285 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
5286 cupsCopyString(name, "Letterhead", namesize);
5287 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
5288 cupsCopyString(name, "Preprinted", namesize);
5289 else if (_cups_strcasecmp(media_type, "transparency"))
5290 cupsCopyString(name, "Transparency", namesize);
5291 else
5292 pwg_ppdize_name(media_type, name, namesize);
5293
5294 return (name);
5295 }
5296
5297
5298 /*
5299 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
5300 */
5301
5302 const char * /* O - PageSize name */
5303 _pwgPageSizeForMedia(
5304 pwg_media_t *media, /* I - Media */
5305 char *name, /* I - PageSize name buffer */
5306 size_t namesize) /* I - Size of name buffer */
5307 {
5308 const char *sizeptr, /* Pointer to size in PWG name */
5309 *dimptr; /* Pointer to dimensions in PWG name */
5310
5311
5312 /*
5313 * Range check input...
5314 */
5315
5316 if (!media || !name || namesize < PPD_MAX_NAME)
5317 return (NULL);
5318
5319 /*
5320 * Copy or generate a PageSize name...
5321 */
5322
5323 if (media->ppd)
5324 {
5325 /*
5326 * Use a standard Adobe name...
5327 */
5328
5329 cupsCopyString(name, media->ppd, namesize);
5330 }
5331 else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
5332 (sizeptr = strchr(media->pwg, '_')) == NULL ||
5333 (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
5334 (size_t)(dimptr - sizeptr) > namesize)
5335 {
5336 /*
5337 * Use a name of the form "wNNNhNNN"...
5338 */
5339
5340 snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
5341 (int)PWG_TO_POINTS(media->length));
5342 }
5343 else
5344 {
5345 /*
5346 * Copy the size name from class_sizename_dimensions...
5347 */
5348
5349 memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
5350 name[dimptr - sizeptr - 1] = '\0';
5351 }
5352
5353 return (name);
5354 }
5355
5356
5357 /*
5358 * 'cups_connect()' - Connect to a URL and get the resource path.
5359 */
5360
5361 static int /* O - 1 on success, 0 on failure */
5362 cups_connect(http_t **http, /* IO - Current HTTP connection */
5363 const char *url, /* I - URL to connect */
5364 char *resource, /* I - Resource path buffer */
5365 size_t ressize) /* I - Size of resource path buffer */
5366 {
5367 char scheme[32], /* URL scheme */
5368 userpass[256], /* URL username:password */
5369 host[256], /* URL host */
5370 curhost[256]; /* Current host */
5371 int port; /* URL port */
5372 http_encryption_t encryption; /* Type of encryption to use */
5373
5374
5375 // Separate the URI...
5376 if (httpSeparateURI(HTTP_URI_CODING_ALL, url, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, ressize) < HTTP_URI_STATUS_OK)
5377 return (0);
5378
5379 // Use encryption as needed..
5380 if (port == 443 || !strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
5381 encryption = HTTP_ENCRYPTION_ALWAYS;
5382 else
5383 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
5384
5385 if (!*http || strcasecmp(host, httpGetHostname(*http, curhost, sizeof(curhost))) || httpAddrPort(httpGetAddress(*http)) != port || httpIsEncrypted(*http) != (encryption == HTTP_ENCRYPTION_ALWAYS))
5386 {
5387 httpClose(*http);
5388 *http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 5000, NULL);
5389 }
5390
5391 return (*http != NULL);
5392 }
5393
5394
5395 /*
5396 * 'cups_get_strings()' - Get the strings for the specified language.
5397 */
5398
5399 static cups_lang_t * /* O - Language data */
5400 cups_get_strings(
5401 http_t **http, /* IO - Current HTTP connection */
5402 const char *printer_uri, /* I - Printer URI */
5403 const char *language) /* I - Language name */
5404 {
5405 cups_lang_t *lang = NULL; /* Language data */
5406 char resource[256]; /* Resource path for printer */
5407 ipp_t *request, /* IPP request */
5408 *response = NULL; /* IPP response */
5409 const char *strings_uri; /* "printer-strings-uri" value */
5410 char strings_file[1024] = "";/* Strings filename */
5411 static int request_id = 1000; // Request ID
5412
5413
5414 /*
5415 * Connect to the printer...
5416 */
5417
5418 if (!cups_connect(http, printer_uri, resource, sizeof(resource)))
5419 goto done;
5420
5421 /*
5422 * Get the URL for the corresponding language strings...
5423 */
5424
5425 request = ippNew();
5426 ippSetOperation(request, IPP_OP_GET_PRINTER_ATTRIBUTES);
5427 ippSetRequestId(request, request_id ++);
5428 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", /*language*/NULL, "utf-8");
5429 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", /*language*/NULL, language);
5430 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", /*language*/NULL, printer_uri);
5431
5432 response = cupsDoRequest(*http, request, resource);
5433
5434 if ((strings_uri = ippGetString(ippFindAttribute(response, "printer-strings-uri", IPP_TAG_URI), 0, NULL)) != NULL)
5435 {
5436 /*
5437 * Download the strings file...
5438 */
5439
5440 if (!cups_get_url(http, strings_uri, ".strings", strings_file, sizeof(strings_file)))
5441 goto done;
5442
5443 /*
5444 * Allocate memory...
5445 */
5446
5447 if ((lang = (cups_lang_t *)calloc(1, sizeof(cups_lang_t))) == NULL)
5448 goto done;
5449
5450 /*
5451 * Load the strings...
5452 */
5453
5454 cupsCopyString(lang->language, language, sizeof(lang->language));
5455 lang->strings = _cupsMessageLoad(NULL, strings_file, _CUPS_MESSAGE_STRINGS);
5456 unlink(strings_file);
5457 }
5458 #if 0
5459 else
5460 {
5461 ipp_attribute_t *attr;
5462 ipp_tag_t group_tag = IPP_TAG_ZERO, value_tag;
5463 const char *name;
5464 char value[2048];
5465
5466 puts("No printer-strings-uri in response.");
5467 printf("Get-Printer-Attributes: %s\n", ippErrorString(ippGetStatusCode(response)));
5468 for (attr = ippGetFirstAttribute(response); attr; attr = ippGetNextAttribute(response))
5469 {
5470 if (ippGetGroupTag(attr) != group_tag)
5471 {
5472 group_tag = ippGetGroupTag(attr);
5473 puts(ippTagString(group_tag));
5474 }
5475
5476 name = ippGetName(attr);
5477 value_tag = ippGetValueTag(attr);
5478
5479 ippAttributeString(attr, value, sizeof(value));
5480
5481 printf(" %s %s = %s\n", name, ippTagString(value_tag), value);
5482 }
5483 }
5484 #endif // 0
5485
5486 done:
5487
5488 ippDelete(response);
5489
5490 return (lang);
5491 }
5492
5493
5494 /*
5495 * 'cups_get_url()' - Get a copy of the file at the given URL.
5496 */
5497
5498 static int /* O - 1 on success, 0 on failure */
5499 cups_get_url(http_t **http, /* IO - Current HTTP connection */
5500 const char *url, /* I - URL to get */
5501 const char *suffix, /* I - Filename suffix */
5502 char *name, /* I - Temporary filename */
5503 size_t namesize) /* I - Size of temporary filename buffer */
5504 {
5505 char resource[256]; /* URL resource */
5506 http_status_t status; /* Status of GET request */
5507 int fd; /* Temporary file */
5508
5509
5510 if (!cups_connect(http, url, resource, sizeof(resource)))
5511 return (0);
5512
5513 if ((fd = cupsCreateTempFd("ippeve", suffix, name, namesize)) < 0)
5514 return (0);
5515
5516 status = cupsGetFd(*http, resource, fd);
5517
5518 close(fd);
5519
5520 if (status != HTTP_STATUS_OK)
5521 {
5522 unlink(name);
5523 *name = '\0';
5524 return (0);
5525 }
5526
5527 return (1);
5528 }
5529
5530
5531 /*
5532 * 'ppd_get_string()' - Get a localized string.
5533 *
5534 * This function looks up the msgid first in the CUPS English localization
5535 * catalog, then the printer's English localization, and finally falls back on
5536 * converting the keyword from "word-like-this" to "Words Like This".
5537 */
5538
5539 static const char * // O - Localized text
5540 ppd_get_string(cups_lang_t *base, // I - Base (CUPS) localization
5541 cups_lang_t *printer, // I - Printer localization
5542 const char *msgid, // I - Message ID
5543 char *buffer, // I - Temporary string buffer
5544 size_t bufsize) // I - Size of temporary string buffer
5545 {
5546 const char *text, // Localized text
5547 *msgptr; // Pointer into message ID
5548 char *bufptr; // Pointer into string buffer
5549
5550
5551 if ((text = _cupsLangString(base, msgid)) == msgid)
5552 {
5553 if ((text = _cupsLangString(printer, msgid)) == msgid)
5554 {
5555 if ((msgptr = strrchr(msgid, '.')) != NULL)
5556 msgptr ++;
5557 else
5558 msgptr = msgid;
5559
5560 if (!*msgptr)
5561 return ("");
5562
5563 cupsCopyString(buffer, msgptr, bufsize);
5564 buffer[0] = (char)toupper(buffer[0] & 255);
5565 for (bufptr = buffer + 1; *bufptr; bufptr ++)
5566 {
5567 if (*bufptr == '-')
5568 {
5569 *bufptr = ' ';
5570 bufptr[1] = (char)toupper(bufptr[1] & 255);
5571 }
5572 }
5573
5574 text = buffer;
5575 }
5576 }
5577
5578 return (text);
5579 }
5580
5581
5582 //
5583 // 'ppd_get_strings()' - Get the strings for a given option and choice.
5584 //
5585
5586 static void
5587 ppd_get_strings(
5588 ppd_file_t *ppd, // I - PPD file
5589 cups_lang_t *langs, // I - Languages
5590 const char *option, // I - PPD option keyword
5591 ppd_choice_t *choice, // I - PPD choice
5592 const char *pwg_msgid) // I - PWG message ID
5593 {
5594 cups_lang_t *lang; // Current language
5595 char ppd_name[PPD_MAX_NAME]; // PPD main keyword
5596 ppd_attr_t *ppd_attr; // PPD attribute
5597 _cups_message_t msg; // Message
5598
5599
5600 msg.msg = (char *)pwg_msgid;
5601
5602 for (lang = langs; lang; lang = lang->next)
5603 {
5604 // See if the string is already localized...
5605 if (cupsArrayFind(lang->strings, &msg))
5606 continue; // Yes
5607
5608 // Otherwise add the text...
5609 if (!strcmp(lang->language, "en"))
5610 {
5611 // English
5612 msg.str = choice->text;
5613 }
5614 else
5615 {
5616 // Other languauge...
5617 snprintf(ppd_name, sizeof(ppd_name), "%s.%s", lang->language, option);
5618 if ((ppd_attr = ppdFindAttr(ppd, ppd_name, choice->choice)) != NULL)
5619 msg.str = ppd_attr->text;
5620 else
5621 continue;
5622 }
5623
5624 cupsArrayAdd(lang->strings, &msg);
5625 }
5626 }
5627
5628
5629 /*
5630 * 'ppd_put_strings()' - Write localization attributes to a PPD file.
5631 */
5632
5633 static void
5634 ppd_put_strings(cups_file_t *fp, /* I - PPD file */
5635 cups_lang_t *langs, /* I - Languages */
5636 const char *ppd_option,/* I - PPD option */
5637 const char *ppd_choice,/* I - PPD choice */
5638 const char *pwg_msgid) /* I - PWG message ID */
5639 {
5640 cups_lang_t *lang; /* Current language */
5641 const char *text; /* Localized text */
5642
5643
5644 for (lang = langs; lang; lang = lang->next)
5645 {
5646 if (strcmp(lang->language, "en") && (text = _cupsLangString(lang, pwg_msgid)) != pwg_msgid)
5647 {
5648 // Add the first line of localized text...
5649 cupsFilePrintf(fp, "*%s.%s %s/", lang->language, ppd_option, ppd_choice);
5650 while (*text && *text != '\n')
5651 {
5652 // Escape ":" and "<"...
5653 if (*text == ':' || *text == '<')
5654 cupsFilePrintf(fp, "<%02X>", *text);
5655 else
5656 cupsFilePutChar(fp, *text);
5657
5658 text ++;
5659 }
5660 cupsFilePuts(fp, ": \"\"\n");
5661 }
5662 }
5663 }
5664
5665
5666 /*
5667 * 'pwg_add_finishing()' - Add a finishings value.
5668 */
5669
5670 static void
5671 pwg_add_finishing(
5672 cups_array_t *finishings, /* I - Finishings array */
5673 ipp_finishings_t template, /* I - Finishing template */
5674 const char *name, /* I - PPD option */
5675 const char *value) /* I - PPD choice */
5676 {
5677 _pwg_finishings_t *f; /* New finishings value */
5678
5679
5680 if ((f = (_pwg_finishings_t *)calloc(1, sizeof(_pwg_finishings_t))) != NULL)
5681 {
5682 f->value = template;
5683 f->num_options = cupsAddOption(name, value, 0, &f->options);
5684
5685 cupsArrayAdd(finishings, f);
5686 }
5687 }
5688
5689
5690 /*
5691 * 'pwg_compare_finishings()' - Compare two finishings values.
5692 */
5693
5694 static int /* O - Result of comparison */
5695 pwg_compare_finishings(
5696 _pwg_finishings_t *a, /* I - First finishings value */
5697 _pwg_finishings_t *b, /* I - Second finishings value */
5698 void *data) /* Unused */
5699 {
5700 (void)data;
5701 return ((int)b->value - (int)a->value);
5702 }
5703
5704
5705 /*
5706 * 'pwg_compare_sizes()' - Compare two media sizes...
5707 */
5708
5709 static int /* O - Result of comparison */
5710 pwg_compare_sizes(cups_size_t *a, /* I - First media size */
5711 cups_size_t *b, /* I - Second media size */
5712 void *data) /* Unused */
5713 {
5714 (void)data;
5715 return (strcmp(a->media, b->media));
5716 }
5717
5718
5719 /*
5720 * 'pwg_copy_size()' - Copy a media size.
5721 */
5722
5723 static cups_size_t * /* O - New media size */
5724 pwg_copy_size(cups_size_t *size, /* I - Media size to copy */
5725 void *data) /* Unused*/
5726 {
5727 cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t));
5728 /* New media size */
5729 (void)data;
5730 if (newsize)
5731 memcpy(newsize, size, sizeof(cups_size_t));
5732
5733 return (newsize);
5734 }
5735
5736
5737 /*
5738 * 'pwg_free_finishings()' - Free a finishings value.
5739 */
5740
5741 static void
5742 pwg_free_finishings(_pwg_finishings_t *f, /* I - Finishings value */
5743 void *data) /* Unused */
5744 {
5745 (void)data;
5746 cupsFreeOptions(f->num_options, f->options);
5747 free(f);
5748 }
5749
5750
5751 /*
5752 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
5753 */
5754
5755 static void
5756 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
5757 char *name, /* I - Name buffer */
5758 size_t namesize) /* I - Size of name buffer */
5759 {
5760 char *ptr, /* Pointer into name buffer */
5761 *end; /* End of name buffer */
5762
5763
5764 if (!ipp || !_cups_isalnum(*ipp))
5765 {
5766 *name = '\0';
5767 return;
5768 }
5769
5770 *name = (char)toupper(*ipp++);
5771
5772 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
5773 {
5774 if (*ipp == '-' && _cups_isalnum(ipp[1]))
5775 {
5776 ipp ++;
5777 *ptr++ = (char)toupper(*ipp++ & 255);
5778 }
5779 else if (*ipp == '_' || *ipp == '.' || *ipp == '-' || _cups_isalnum(*ipp))
5780 {
5781 *ptr++ = *ipp++;
5782 }
5783 else
5784 {
5785 ipp ++;
5786 }
5787 }
5788
5789 *ptr = '\0';
5790 }
5791
5792
5793 /*
5794 * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values.
5795 */
5796
5797 static void
5798 pwg_ppdize_resolution(
5799 ipp_attribute_t *attr, /* I - Attribute to convert */
5800 int element, /* I - Element to convert */
5801 int *xres, /* O - X resolution in DPI */
5802 int *yres, /* O - Y resolution in DPI */
5803 char *name, /* I - Name buffer */
5804 size_t namesize) /* I - Size of name buffer */
5805 {
5806 ipp_res_t units; /* Units for resolution */
5807
5808
5809 *xres = ippGetResolution(attr, element, yres, &units);
5810
5811 if (units == IPP_RES_PER_CM)
5812 {
5813 *xres = (int)(*xres * 2.54);
5814 *yres = (int)(*yres * 2.54);
5815 }
5816
5817 if (name && namesize > 4)
5818 {
5819 if (*xres == *yres)
5820 snprintf(name, namesize, "%ddpi", *xres);
5821 else
5822 snprintf(name, namesize, "%dx%ddpi", *xres, *yres);
5823 }
5824 }
5825
5826
5827 /*
5828 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
5829 */
5830
5831 static void
5832 pwg_unppdize_name(const char *ppd, /* I - PPD keyword */
5833 char *name, /* I - Name buffer */
5834 size_t namesize, /* I - Size of name buffer */
5835 const char *dashchars)/* I - Characters to be replaced by dashes */
5836 {
5837 char *ptr, /* Pointer into name buffer */
5838 *end; /* End of name buffer */
5839 int nodash = 1; /* Next char in IPP name cannot be a
5840 dash (first char or after a dash) */
5841
5842
5843 if (_cups_islower(*ppd))
5844 {
5845 /*
5846 * Already lowercase name, use as-is?
5847 */
5848
5849 const char *ppdptr; /* Pointer into PPD keyword */
5850
5851 for (ppdptr = ppd + 1; *ppdptr; ppdptr ++)
5852 if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr) ||
5853 (*ppdptr == '-' && *(ppdptr - 1) == '-') ||
5854 (*ppdptr == '-' && *(ppdptr + 1) == '\0'))
5855 break;
5856
5857 if (!*ppdptr)
5858 {
5859 cupsCopyString(name, ppd, namesize);
5860 return;
5861 }
5862 }
5863
5864 for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
5865 {
5866 if (_cups_isalnum(*ppd) || *ppd == '.' || *ppd == '_')
5867 {
5868 *ptr++ = (char)tolower(*ppd & 255);
5869 nodash = 0;
5870 }
5871 else if (*ppd == '-' || strchr(dashchars, *ppd))
5872 {
5873 if (nodash == 0)
5874 {
5875 *ptr++ = '-';
5876 nodash = 1;
5877 }
5878 }
5879
5880 if (nodash == 0)
5881 {
5882 if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
5883 _cups_isupper(ppd[1]) && ptr < end)
5884 {
5885 *ptr++ = '-';
5886 nodash = 1;
5887 }
5888 else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
5889 {
5890 *ptr++ = '-';
5891 nodash = 1;
5892 }
5893 }
5894 }
5895
5896 /* Remove trailing dashes */
5897 while (ptr > name && *(ptr - 1) == '-')
5898 ptr --;
5899
5900 *ptr = '\0';
5901 }