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