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