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