]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd-cache.c
Update finishings support to use standard (or standard-inspired) PPD option
[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 = pwgMediaForPPD(ppd_size->name)) != 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 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3624
3625 if ((attr = ippFindAttribute(response, "media-source-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3626 {
3627 static const char * const sources[] =
3628 { /* Standard "media-source" strings */
3629 "auto",
3630 "main",
3631 "alternate",
3632 "large-capacity",
3633 "manual",
3634 "envelope",
3635 "disc",
3636 "photo",
3637 "hagaki",
3638 "main-roll",
3639 "alternate-roll",
3640 "top",
3641 "middle",
3642 "bottom",
3643 "side",
3644 "left",
3645 "right",
3646 "center",
3647 "rear",
3648 "by-pass-tray",
3649 "tray-1",
3650 "tray-2",
3651 "tray-3",
3652 "tray-4",
3653 "tray-5",
3654 "tray-6",
3655 "tray-7",
3656 "tray-8",
3657 "tray-9",
3658 "tray-10",
3659 "tray-11",
3660 "tray-12",
3661 "tray-13",
3662 "tray-14",
3663 "tray-15",
3664 "tray-16",
3665 "tray-17",
3666 "tray-18",
3667 "tray-19",
3668 "tray-20",
3669 "roll-1",
3670 "roll-2",
3671 "roll-3",
3672 "roll-4",
3673 "roll-5",
3674 "roll-6",
3675 "roll-7",
3676 "roll-8",
3677 "roll-9",
3678 "roll-10"
3679 };
3680
3681 cupsFilePrintf(fp, "*OpenUI *InputSlot: PickOne\n"
3682 "*OrderDependency: 10 AnySetup *InputSlot\n"
3683 "*DefaultInputSlot: %s\n", ppdname);
3684 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3685 {
3686 keyword = ippGetString(attr, i, NULL);
3687
3688 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3689
3690 for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++)
3691 if (!strcmp(sources[j], keyword))
3692 {
3693 snprintf(msgid, sizeof(msgid), "media-source.%s", keyword);
3694 cupsFilePrintf(fp, "*InputSlot %s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, j);
3695 cupsFilePrintf(fp, "*%s.InputSlot %s/%s: \"\"\n", lang->language, ppdname, _cupsLangString(lang, msgid));
3696 break;
3697 }
3698 }
3699 cupsFilePuts(fp, "*CloseUI: *InputSlot\n");
3700 }
3701
3702 /*
3703 * MediaType...
3704 */
3705
3706 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type", IPP_TAG_ZERO)) != NULL)
3707 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3708 else
3709 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3710
3711 if ((attr = ippFindAttribute(response, "media-type-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3712 {
3713 cupsFilePrintf(fp, "*OpenUI *MediaType: PickOne\n"
3714 "*OrderDependency: 10 AnySetup *MediaType\n"
3715 "*DefaultMediaType: %s\n", ppdname);
3716 for (i = 0; i < count; i ++)
3717 {
3718 keyword = ippGetString(attr, i, NULL);
3719
3720 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3721
3722 snprintf(msgid, sizeof(msgid), "media-type.%s", keyword);
3723 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
3724 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
3725 msgstr = keyword;
3726
3727 cupsFilePrintf(fp, "*MediaType %s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, ppdname);
3728 cupsFilePrintf(fp, "*%s.MediaType %s/%s: \"\"\n", lang->language, ppdname, msgstr);
3729 }
3730 cupsFilePuts(fp, "*CloseUI: *MediaType\n");
3731 }
3732
3733 /*
3734 * ColorModel...
3735 */
3736
3737 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) == NULL)
3738 if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
3739 if ((attr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) == NULL)
3740 attr = ippFindAttribute(response, "output-mode-supported", IPP_TAG_KEYWORD);
3741
3742 if (attr)
3743 {
3744 int wrote_color = 0;
3745 const char *default_color = NULL; /* Default */
3746
3747 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3748 {
3749 keyword = ippGetString(attr, i, NULL);
3750
3751 #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; }
3752 #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)); }
3753
3754 if (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
3755 {
3756 PRINTF_COLORMODEL
3757
3758 PRINTF_COLOROPTION("FastGray", _("Fast Grayscale"), CUPS_CSPACE_K, 1)
3759
3760 if (!default_color)
3761 default_color = "FastGray";
3762 }
3763 else if (!strcasecmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
3764 {
3765 PRINTF_COLORMODEL
3766
3767 PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8)
3768
3769 if (!default_color || !strcmp(default_color, "FastGray"))
3770 default_color = "Gray";
3771 }
3772 else if (!strcasecmp(keyword, "sgray_16") || !strcmp(keyword, "W8-16"))
3773 {
3774 PRINTF_COLORMODEL
3775
3776 if (!strcmp(keyword, "W8-16"))
3777 {
3778 PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8)
3779
3780 if (!default_color || !strcmp(default_color, "FastGray"))
3781 default_color = "Gray";
3782 }
3783
3784 PRINTF_COLOROPTION("Gray16", _("Deep Gray"), CUPS_CSPACE_SW, 16)
3785 }
3786 else if (!strcasecmp(keyword, "srgb_8") || !strncmp(keyword, "SRGB24", 7) || !strcmp(keyword, "color"))
3787 {
3788 PRINTF_COLORMODEL
3789
3790 PRINTF_COLOROPTION("RGB", _("Color"), CUPS_CSPACE_SRGB, 8)
3791
3792 default_color = "RGB";
3793 }
3794 else if (!strcasecmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48") || !strcmp(keyword, "ADOBERGB24-48"))
3795 {
3796 PRINTF_COLORMODEL
3797
3798 PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 16)
3799
3800 if (!default_color)
3801 default_color = "AdobeRGB";
3802 }
3803 else if ((!strcasecmp(keyword, "adobe-rgb_8") && !ippContainsString(attr, "adobe-rgb_16")) || !strcmp(keyword, "ADOBERGB24"))
3804 {
3805 PRINTF_COLORMODEL
3806
3807 PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 8)
3808
3809 if (!default_color)
3810 default_color = "AdobeRGB";
3811 }
3812 else if ((!strcasecmp(keyword, "black_8") && !ippContainsString(attr, "black_16")) || !strcmp(keyword, "DEVW8"))
3813 {
3814 PRINTF_COLORMODEL
3815
3816 PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 8)
3817 }
3818 else if (!strcasecmp(keyword, "black_16") || !strcmp(keyword, "DEVW16") || !strcmp(keyword, "DEVW8-16"))
3819 {
3820 PRINTF_COLORMODEL
3821
3822 PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 16)
3823 }
3824 else if ((!strcasecmp(keyword, "cmyk_8") && !ippContainsString(attr, "cmyk_16")) || !strcmp(keyword, "DEVCMYK32"))
3825 {
3826 PRINTF_COLORMODEL
3827
3828 PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 8)
3829 }
3830 else if (!strcasecmp(keyword, "cmyk_16") || !strcmp(keyword, "DEVCMYK32-64") || !strcmp(keyword, "DEVCMYK64"))
3831 {
3832 PRINTF_COLORMODEL
3833
3834 PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 16)
3835 }
3836 else if ((!strcasecmp(keyword, "rgb_8") && ippContainsString(attr, "rgb_16")) || !strcmp(keyword, "DEVRGB24"))
3837 {
3838 PRINTF_COLORMODEL
3839
3840 PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 8)
3841 }
3842 else if (!strcasecmp(keyword, "rgb_16") || !strcmp(keyword, "DEVRGB24-48") || !strcmp(keyword, "DEVRGB48"))
3843 {
3844 PRINTF_COLORMODEL
3845
3846 PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 16)
3847 }
3848 }
3849
3850 if (default_color)
3851 cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color);
3852 if (wrote_color)
3853 cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
3854 }
3855
3856 /*
3857 * Duplex...
3858 */
3859
3860 if ((attr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL && ippContainsString(attr, "two-sided-long-edge"))
3861 {
3862 cupsFilePrintf(fp, "*OpenUI *Duplex: PickOne\n"
3863 "*OrderDependency: 10 AnySetup *Duplex\n"
3864 "*%s.Translation Duplex/%s: \"\"\n"
3865 "*DefaultDuplex: None\n"
3866 "*Duplex None: \"<</Duplex false>>setpagedevice\"\n"
3867 "*%s.Duplex None/%s: \"\"\n"
3868 "*Duplex DuplexNoTumble: \"<</Duplex true/Tumble false>>setpagedevice\"\n"
3869 "*%s.Duplex DuplexNoTumble/%s: \"\"\n"
3870 "*Duplex DuplexTumble: \"<</Duplex true/Tumble true>>setpagedevice\"\n"
3871 "*%s.Duplex DuplexTumble/%s: \"\"\n"
3872 "*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)")));
3873
3874 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
3875 {
3876 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3877 {
3878 const char *dm = ippGetString(attr, i, NULL);
3879 /* DM value */
3880
3881 if (!_cups_strcasecmp(dm, "DM1"))
3882 {
3883 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3884 break;
3885 }
3886 else if (!_cups_strcasecmp(dm, "DM2"))
3887 {
3888 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3889 break;
3890 }
3891 else if (!_cups_strcasecmp(dm, "DM3"))
3892 {
3893 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3894 break;
3895 }
3896 else if (!_cups_strcasecmp(dm, "DM4"))
3897 {
3898 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3899 break;
3900 }
3901 }
3902 }
3903 else if ((attr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
3904 {
3905 keyword = ippGetString(attr, 0, NULL);
3906
3907 if (!strcmp(keyword, "flipped"))
3908 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3909 else if (!strcmp(keyword, "manual-tumble"))
3910 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3911 else if (!strcmp(keyword, "normal"))
3912 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3913 else
3914 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3915 }
3916 }
3917
3918 /*
3919 * Output bin...
3920 */
3921
3922 if ((attr = ippFindAttribute(response, "output-bin-default", IPP_TAG_ZERO)) != NULL)
3923 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3924 else
3925 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3926
3927 if ((attr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3928 {
3929 ipp_attribute_t *trays = ippFindAttribute(response, "printer-output-tray", IPP_TAG_STRING);
3930 /* printer-output-tray attribute, if any */
3931 const char *tray_ptr; /* printer-output-tray value */
3932 int tray_len; /* Len of printer-output-tray value */
3933 char tray[IPP_MAX_OCTETSTRING];
3934 /* printer-output-tray string value */
3935
3936 cupsFilePrintf(fp, "*OpenUI *OutputBin: PickOne\n"
3937 "*OrderDependency: 10 AnySetup *OutputBin\n"
3938 "*DefaultOutputBin: %s\n", ppdname);
3939 if (!strcmp(ppdname, "FaceUp"))
3940 cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n");
3941 else
3942 cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n");
3943
3944 for (i = 0; i < count; i ++)
3945 {
3946 keyword = ippGetString(attr, i, NULL);
3947
3948 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3949
3950 snprintf(msgid, sizeof(msgid), "output-bin.%s", keyword);
3951 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
3952 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
3953 msgstr = keyword;
3954
3955 cupsFilePrintf(fp, "*OutputBin %s: \"\"\n", ppdname);
3956 cupsFilePrintf(fp, "*%s.OutputBin %s/%s: \"\"\n", lang->language, ppdname, msgstr);
3957
3958 if ((tray_ptr = ippGetOctetString(trays, i, &tray_len)) != NULL)
3959 {
3960 if (tray_len >= (int)sizeof(tray))
3961 tray_len = (int)sizeof(tray) - 1;
3962
3963 memcpy(tray, tray_ptr, (size_t)tray_len);
3964 tray[tray_len] = '\0';
3965
3966 if (strstr(tray, "stackingorder=lastToFirst;"))
3967 cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
3968 else
3969 cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
3970 }
3971 else if (!strcmp(ppdname, "FaceUp"))
3972 cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
3973 else
3974 cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
3975 }
3976 cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
3977 }
3978
3979 /*
3980 * Finishing options...
3981 */
3982
3983 if ((attr = ippFindAttribute(response, "finishings-supported", IPP_TAG_ENUM)) != NULL)
3984 {
3985 int value; /* Enum value */
3986 const char *ppd_keyword; /* PPD keyword for enum */
3987 cups_array_t *names; /* Names we've added */
3988 static const char * const base_keywords[] =
3989 { /* Base STD 92 keywords */
3990 NULL, /* none */
3991 "SingleAuto", /* staple */
3992 "SingleAuto", /* punch */
3993 NULL, /* cover */
3994 "BindAuto", /* bind */
3995 "SaddleStitch", /* saddle-stitch */
3996 "EdgeStitchAuto", /* edge-stitch */
3997 "Auto", /* fold */
3998 NULL, /* trim */
3999 NULL, /* bale */
4000 NULL, /* booklet-maker */
4001 NULL, /* jog-offset */
4002 NULL, /* coat */
4003 NULL /* laminate */
4004 };
4005 static const char * const staple_keywords[] =
4006 { /* StapleLocation keywords */
4007 "SinglePortrait",
4008 "SingleRevLandscape",
4009 "SingleLandscape",
4010 "SingleRevPortrait",
4011 "EdgeStitchPortrait",
4012 "EdgeStitchLandscape",
4013 "EdgeStitchRevPortrait",
4014 "EdgeStitchRevLandscape",
4015 "DualPortrait",
4016 "DualLandscape",
4017 "DualRevPortrait",
4018 "DualRevLandscape",
4019 "TriplePortrait",
4020 "TripleLandscape",
4021 "TripleRevPortrait",
4022 "TripleRevLandscape"
4023 };
4024 static const char * const bind_keywords[] =
4025 { /* StapleLocation binding keywords */
4026 "BindPortrait",
4027 "BindLandscape",
4028 "BindRevPortrait",
4029 "BindRevLandscape"
4030 };
4031 static const char * const punch_keywords[] =
4032 { /* PunchMedia keywords */
4033 "SinglePortrait",
4034 "SingleRevLandscape",
4035 "SingleLandscape",
4036 "SingleRevPortrait",
4037 "DualPortrait",
4038 "DualLandscape",
4039 "DualRevPortrait",
4040 "DualRevLandscape",
4041 "TriplePortrait",
4042 "TripleLandscape",
4043 "TripleRevPortrait",
4044 "TripleRevLandscape",
4045 "QuadPortrait",
4046 "QuadLandscape",
4047 "QuadRevPortrait",
4048 "QuadRevLandscape",
4049 "MultiplePortrait",
4050 "MultipleLandscape",
4051 "MultipleRevPortrait",
4052 "MultipleRevLandscape"
4053 };
4054 static const char * const fold_keywords[] =
4055 { /* FoldType keywords */
4056 "Accordion",
4057 "DoubleGate",
4058 "Gate",
4059 "Half",
4060 "HalfZ",
4061 "LeftGate",
4062 "Letter",
4063 "Parallel",
4064 "XFold",
4065 "RightGate",
4066 "ZFold",
4067 "EngineeringZ"
4068 };
4069
4070 count = ippGetCount(attr);
4071 names = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
4072 fin_options = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4073
4074 /*
4075 * Staple/Bind/Stitch
4076 */
4077
4078 for (i = 0; i < count; i ++)
4079 {
4080 value = ippGetInteger(attr, i);
4081 keyword = ippEnumString("finishings", value);
4082
4083 if (!strncmp(keyword, "staple-", 7) || !strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch"))
4084 break;
4085 }
4086
4087 if (i < count)
4088 {
4089 cupsArrayAdd(fin_options, "*StapleLocation");
4090
4091 cupsFilePuts(fp, "*OpenUI *StapleLocation: PickOne\n");
4092 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
4093 cupsFilePrintf(fp, "*%s.Translation StapleLocation/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Staple")));
4094 cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
4095 cupsFilePuts(fp, "*StapleLocation None: \"\"\n");
4096 cupsFilePrintf(fp, "*%s.StapleLocation None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4097
4098 for (; i < count; i ++)
4099 {
4100 value = ippGetInteger(attr, i);
4101 keyword = ippEnumString("finishings", value);
4102
4103 if (strncmp(keyword, "staple-", 7) && strncmp(keyword, "bind-", 5) && strncmp(keyword, "edge-stitch-", 12) && strcmp(keyword, "saddle-stitch"))
4104 continue;
4105
4106 if (cupsArrayFind(names, (char *)keyword))
4107 continue; /* Already did this finishing template */
4108
4109 cupsArrayAdd(names, (char *)keyword);
4110
4111 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4112 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4113 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4114 msgstr = keyword;
4115
4116 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4117 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4118 else if (value >= IPP_FINISHINGS_STAPLE_TOP_LEFT && value <= IPP_FINISHINGS_STAPLE_TRIPLE_BOTTOM)
4119 ppd_keyword = staple_keywords[value - IPP_FINISHINGS_STAPLE_TOP_LEFT];
4120 else if (value >= IPP_FINISHINGS_BIND_LEFT && value <= IPP_FINISHINGS_BIND_BOTTOM)
4121 ppd_keyword = bind_keywords[value - IPP_FINISHINGS_BIND_LEFT];
4122 else
4123 ppd_keyword = NULL;
4124
4125 if (!ppd_keyword)
4126 continue;
4127
4128 cupsFilePrintf(fp, "*StapleLocation %s: \"\"\n", ppd_keyword);
4129 cupsFilePrintf(fp, "*%s.StapleLocation %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
4130 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, keyword, ppd_keyword);
4131 }
4132
4133 cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
4134 }
4135
4136 /*
4137 * Fold
4138 */
4139
4140 for (i = 0; i < count; i ++)
4141 {
4142 value = ippGetInteger(attr, i);
4143 keyword = ippEnumString("finishings", value);
4144
4145 if (!strncmp(keyword, "cups-fold-", 10) || !strcmp(keyword, "fold") || !strncmp(keyword, "fold-", 5))
4146 break;
4147 }
4148
4149 if (i < count)
4150 {
4151 cupsArrayAdd(fin_options, "*FoldType");
4152
4153 cupsFilePuts(fp, "*OpenUI *FoldType: PickOne\n");
4154 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
4155 cupsFilePrintf(fp, "*%s.Translation FoldType/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Fold")));
4156 cupsFilePuts(fp, "*DefaultFoldType: None\n");
4157 cupsFilePuts(fp, "*FoldType None: \"\"\n");
4158 cupsFilePrintf(fp, "*%s.FoldType None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4159
4160 for (; i < count; i ++)
4161 {
4162 value = ippGetInteger(attr, i);
4163 keyword = ippEnumString("finishings", value);
4164
4165 if (!strncmp(keyword, "cups-fold-", 10))
4166 keyword += 5;
4167 else if (strcmp(keyword, "fold") && strncmp(keyword, "fold-", 5))
4168 continue;
4169
4170 if (cupsArrayFind(names, (char *)keyword))
4171 continue; /* Already did this finishing template */
4172
4173 cupsArrayAdd(names, (char *)keyword);
4174
4175 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4176 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4177 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4178 msgstr = keyword;
4179
4180 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4181 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4182 else if (value >= IPP_FINISHINGS_FOLD_ACCORDION && value <= IPP_FINISHINGS_FOLD_ENGINEERING_Z)
4183 ppd_keyword = fold_keywords[value - IPP_FINISHINGS_FOLD_ACCORDION];
4184 else if (value >= IPP_FINISHINGS_CUPS_FOLD_ACCORDION && value <= IPP_FINISHINGS_CUPS_FOLD_Z)
4185 ppd_keyword = fold_keywords[value - IPP_FINISHINGS_CUPS_FOLD_ACCORDION];
4186 else
4187 ppd_keyword = NULL;
4188
4189 if (!ppd_keyword)
4190 continue;
4191
4192 cupsFilePrintf(fp, "*FoldType %s: \"\"\n", ppd_keyword);
4193 cupsFilePrintf(fp, "*%s.FoldType %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
4194 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, keyword, ppd_keyword);
4195 }
4196
4197 cupsFilePuts(fp, "*CloseUI: *FoldType\n");
4198 }
4199
4200 /*
4201 * Punch
4202 */
4203
4204 for (i = 0; i < count; i ++)
4205 {
4206 value = ippGetInteger(attr, i);
4207 keyword = ippEnumString("finishings", value);
4208
4209 if (!strncmp(keyword, "cups-punch-", 11) || !strncmp(keyword, "punch-", 6))
4210 break;
4211 }
4212
4213 if (i < count)
4214 {
4215 cupsArrayAdd(fin_options, "*PunchMedia");
4216
4217 cupsFilePuts(fp, "*OpenUI *PunchMedia: PickOne\n");
4218 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
4219 cupsFilePrintf(fp, "*%s.Translation PunchMedia/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Punch")));
4220 cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
4221 cupsFilePuts(fp, "*PunchMedia None: \"\"\n");
4222 cupsFilePrintf(fp, "*%s.PunchMedia None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4223
4224 for (i = 0; i < count; i ++)
4225 {
4226 value = ippGetInteger(attr, i);
4227 keyword = ippEnumString("finishings", value);
4228
4229 if (!strncmp(keyword, "cups-punch-", 11))
4230 keyword += 5;
4231 else if (strncmp(keyword, "punch-", 6))
4232 continue;
4233
4234 if (cupsArrayFind(names, (char *)keyword))
4235 continue; /* Already did this finishing template */
4236
4237 cupsArrayAdd(names, (char *)keyword);
4238
4239 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4240 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4241 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4242 msgstr = keyword;
4243
4244 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4245 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4246 else if (value >= IPP_FINISHINGS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_PUNCH_MULTIPLE_BOTTOM)
4247 ppd_keyword = punch_keywords[value - IPP_FINISHINGS_PUNCH_TOP_LEFT];
4248 else if (value >= IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM)
4249 ppd_keyword = punch_keywords[value - IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT];
4250 else
4251 ppd_keyword = NULL;
4252
4253 if (!ppd_keyword)
4254 continue;
4255
4256 cupsFilePrintf(fp, "*PunchMedia %s: \"\"\n", ppd_keyword);
4257 cupsFilePrintf(fp, "*%s.PunchMedia %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
4258 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, keyword, ppd_keyword);
4259 }
4260
4261 cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
4262 }
4263
4264 /*
4265 * Booklet
4266 */
4267
4268 if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER))
4269 {
4270 cupsArrayAdd(fin_options, "*Booklet");
4271
4272 cupsFilePuts(fp, "*OpenUI *Booklet: Boolean\n");
4273 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
4274 cupsFilePrintf(fp, "*%s.Translation Booklet/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Booklet")));
4275 cupsFilePuts(fp, "*DefaultBooklet: False\n");
4276 cupsFilePuts(fp, "*Booklet False: \"\"\n");
4277 cupsFilePuts(fp, "*Booklet True: \"\"\n");
4278 cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n", IPP_FINISHINGS_BOOKLET_MAKER);
4279 cupsFilePuts(fp, "*CloseUI: *Booklet\n");
4280 }
4281
4282 cupsArrayDelete(names);
4283 }
4284
4285 if ((attr = ippFindAttribute(response, "finishings-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4286 {
4287 ipp_t *finishing_col; /* Current finishing collection */
4288 ipp_attribute_t *finishing_attr; /* Current finishing member attribute */
4289 cups_array_t *templates; /* Finishing templates */
4290
4291 cupsFilePuts(fp, "*OpenUI *cupsFinishingTemplate: PickOne\n");
4292 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n");
4293 cupsFilePrintf(fp, "*%s.Translation cupsFinishingTemplate/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Finishing Preset")));
4294 cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n");
4295 cupsFilePuts(fp, "*cupsFinishingTemplate none: \"\"\n");
4296 cupsFilePrintf(fp, "*%s.cupsFinishingTemplate none/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4297
4298 templates = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4299 count = ippGetCount(attr);
4300
4301 for (i = 0; i < count; i ++)
4302 {
4303 finishing_col = ippGetCollection(attr, i);
4304 keyword = ippGetString(ippFindAttribute(finishing_col, "finishing-template", IPP_TAG_ZERO), 0, NULL);
4305
4306 if (!keyword || cupsArrayFind(templates, (void *)keyword))
4307 continue;
4308
4309 if (!strcmp(keyword, "none"))
4310 continue;
4311
4312 cupsArrayAdd(templates, (void *)keyword);
4313
4314 snprintf(msgid, sizeof(msgid), "finishing-template.%s", keyword);
4315 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4316 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4317 msgstr = keyword;
4318
4319 cupsFilePrintf(fp, "*cupsFinishingTemplate %s: \"\n", keyword);
4320 for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr; finishing_attr = ippNextAttribute(finishing_col))
4321 {
4322 if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION)
4323 {
4324 const char *name = ippGetName(finishing_attr);
4325 /* Member attribute name */
4326
4327 if (strcmp(name, "media-size"))
4328 cupsFilePrintf(fp, "%% %s\n", name);
4329 }
4330 }
4331 cupsFilePuts(fp, "\"\n");
4332 cupsFilePrintf(fp, "*%s.cupsFinishingTemplate %s/%s: \"\"\n", lang->language, keyword, msgstr);
4333 cupsFilePuts(fp, "*End\n");
4334 }
4335
4336 cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n");
4337
4338 if (cupsArrayCount(fin_options))
4339 {
4340 const char *fin_option; /* Current finishing option */
4341
4342 cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate");
4343 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4344 cupsFilePrintf(fp, " %s", fin_option);
4345 cupsFilePuts(fp, "\"\n");
4346
4347 cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate None");
4348 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4349 cupsFilePrintf(fp, " %s None", fin_option);
4350 cupsFilePuts(fp, "\"\n");
4351 }
4352
4353 cupsArrayDelete(templates);
4354 }
4355
4356 cupsArrayDelete(fin_options);
4357
4358 /*
4359 * cupsPrintQuality and DefaultResolution...
4360 */
4361
4362 quality = ippFindAttribute(response, "print-quality-supported", IPP_TAG_ENUM);
4363
4364 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
4365 {
4366 int lowdpi = 0, hidpi = 0; /* Lower and higher resolution */
4367
4368 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4369 {
4370 const char *rs = ippGetString(attr, i, NULL);
4371 /* RS value */
4372
4373 if (_cups_strncasecmp(rs, "RS", 2))
4374 continue;
4375
4376 lowdpi = atoi(rs + 2);
4377 if ((rs = strrchr(rs, '-')) != NULL)
4378 hidpi = atoi(rs + 1);
4379 else
4380 hidpi = lowdpi;
4381 break;
4382 }
4383
4384 if (lowdpi == 0)
4385 {
4386 /*
4387 * Invalid "urf-supported" value...
4388 */
4389
4390 goto bad_ppd;
4391 }
4392 else
4393 {
4394 /*
4395 * Generate print qualities based on low and high DPIs...
4396 */
4397
4398 cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi);
4399
4400 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4401 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4402 "*%s.Translation cupsPrintQuality/%s: \"\"\n"
4403 "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
4404 if ((lowdpi & 1) == 0)
4405 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", lowdpi, lowdpi / 2, lang->language, _cupsLangString(lang, _("Draft")));
4406 else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4407 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", lowdpi, lowdpi, lang->language, _cupsLangString(lang, _("Draft")));
4408
4409 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Normal/%s: \"\"\n", lowdpi, lowdpi, lang->language, _cupsLangString(lang, _("Normal")));
4410
4411 if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4412 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality High/%s: \"\"\n", hidpi, hidpi, lang->language, _cupsLangString(lang, _("High")));
4413 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4414 }
4415 }
4416 else if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
4417 {
4418 /*
4419 * Make a sorted list of resolutions.
4420 */
4421
4422 count = ippGetCount(attr);
4423 if (count > (int)(sizeof(resolutions) / sizeof(resolutions[0])))
4424 count = (int)(sizeof(resolutions) / sizeof(resolutions[0]));
4425
4426 resolutions[0] = 0; /* Not in loop to silence Clang static analyzer... */
4427 for (i = 1; i < count; i ++)
4428 resolutions[i] = i;
4429
4430 for (i = 0; i < (count - 1); i ++)
4431 {
4432 for (j = i + 1; j < count; j ++)
4433 {
4434 int ix, iy, /* First X and Y resolution */
4435 jx, jy, /* Second X and Y resolution */
4436 temp; /* Swap variable */
4437 ipp_res_t units; /* Resolution units */
4438
4439 ix = ippGetResolution(attr, resolutions[i], &iy, &units);
4440 jx = ippGetResolution(attr, resolutions[j], &jy, &units);
4441
4442 if (ix > jx || (ix == jx && iy > jy))
4443 {
4444 /*
4445 * Swap these two resolutions...
4446 */
4447
4448 temp = resolutions[i];
4449 resolutions[i] = resolutions[j];
4450 resolutions[j] = temp;
4451 }
4452 }
4453 }
4454
4455 /*
4456 * Generate print quality options...
4457 */
4458
4459 pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, ppdname, sizeof(ppdname));
4460 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4461
4462 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4463 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4464 "*%s.Translation cupsPrintQuality/%s: \"\"\n"
4465 "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
4466 if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4467 {
4468 pwg_ppdize_resolution(attr, resolutions[0], &xres, &yres, NULL, 0);
4469 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4470 cupsFilePrintf(fp, "*%s.cupsPrintQuality Draft/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Draft")));
4471 }
4472
4473 pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, NULL, 0);
4474 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4475 cupsFilePrintf(fp, "*%s.cupsPrintQuality Normal/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Normal")));
4476
4477 if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4478 {
4479 pwg_ppdize_resolution(attr, resolutions[count - 1], &xres, &yres, NULL, 0);
4480 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4481 cupsFilePrintf(fp, "*%s.cupsPrintQuality High/%s: \"\"\n", lang->language, _cupsLangString(lang, _("High")));
4482 }
4483
4484 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4485 }
4486 else if (is_apple || is_pwg)
4487 goto bad_ppd;
4488 else
4489 {
4490 if ((attr = ippFindAttribute(response, "printer-resolution-default", IPP_TAG_RESOLUTION)) != NULL)
4491 {
4492 pwg_ppdize_resolution(attr, 0, &xres, &yres, ppdname, sizeof(ppdname));
4493 }
4494 else
4495 {
4496 xres = yres = 300;
4497 strlcpy(ppdname, "300dpi", sizeof(ppdname));
4498 }
4499
4500 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4501
4502 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4503 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4504 "*%s.Translation cupsPrintQuality/%s: \"\"\n"
4505 "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
4506 if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4507 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("Draft")));
4508
4509 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Normal/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("Normal")));
4510
4511 if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
4512 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality High/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("High")));
4513 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4514 }
4515
4516 /*
4517 * Presets...
4518 */
4519
4520 if ((attr = ippFindAttribute(response, "job-presets-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4521 {
4522 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4523 {
4524 ipp_t *preset = ippGetCollection(attr, i);
4525 /* Preset collection */
4526 const char *preset_name = ippGetString(ippFindAttribute(preset, "preset-name", IPP_TAG_ZERO), 0, NULL),
4527 /* Preset name */
4528 *localized_name; /* Localized preset name */
4529 ipp_attribute_t *member; /* Member attribute in preset */
4530 const char *member_name; /* Member attribute name */
4531 char member_value[256]; /* Member attribute value */
4532
4533 if (!preset || !preset_name)
4534 continue;
4535
4536 cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", preset_name);
4537 for (member = ippFirstAttribute(preset); member; member = ippNextAttribute(preset))
4538 {
4539 member_name = ippGetName(member);
4540
4541 if (!member_name || !strcmp(member_name, "preset-name"))
4542 continue;
4543
4544 if (!strcmp(member_name, "finishings"))
4545 {
4546 for (i = 0, count = ippGetCount(member); i < count; i ++)
4547 {
4548 const char *option = NULL; /* PPD option name */
4549
4550 keyword = ippEnumString("finishings", ippGetInteger(member, i));
4551
4552 if (!strcmp(keyword, "booklet-maker"))
4553 {
4554 option = "Booklet";
4555 keyword = "True";
4556 }
4557 else if (!strncmp(keyword, "fold-", 5))
4558 option = "FoldType";
4559 else if (!strncmp(keyword, "punch-", 6))
4560 option = "PunchMedia";
4561 else if (!strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch") || !strncmp(keyword, "staple-", 7))
4562 option = "StapleLocation";
4563
4564 if (option && keyword)
4565 cupsFilePrintf(fp, "*%s %s\n", option, keyword);
4566 }
4567 }
4568 else if (!strcmp(member_name, "finishings-col"))
4569 {
4570 ipp_t *fin_col; /* finishings-col value */
4571
4572 for (i = 0, count = ippGetCount(member); i < count; i ++)
4573 {
4574 fin_col = ippGetCollection(member, i);
4575
4576 if ((keyword = ippGetString(ippFindAttribute(fin_col, "finishing-template", IPP_TAG_ZERO), 0, NULL)) != NULL)
4577 cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", keyword);
4578 }
4579 }
4580 else if (!strcmp(member_name, "media"))
4581 {
4582 /*
4583 * Map media to PageSize...
4584 */
4585
4586 if ((pwg = pwgMediaForPWG(ippGetString(member, 0, NULL))) != NULL && pwg->ppd)
4587 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4588 }
4589 else if (!strcmp(member_name, "media-col"))
4590 {
4591 media_col = ippGetCollection(member, 0);
4592
4593 if ((media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0)) != NULL)
4594 {
4595 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
4596 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
4597 if ((pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL && pwg->ppd)
4598 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4599 }
4600
4601 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL)) != NULL)
4602 {
4603 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4604 cupsFilePrintf(fp, "*InputSlot %s\n", keyword);
4605 }
4606
4607 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL)) != NULL)
4608 {
4609 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4610 cupsFilePrintf(fp, "*MediaType %s\n", keyword);
4611 }
4612 }
4613 else if (!strcmp(member_name, "print-quality"))
4614 {
4615 /*
4616 * Map print-quality to cupsPrintQuality...
4617 */
4618
4619 int qval = ippGetInteger(member, 0);
4620 /* print-quality value */
4621 static const char * const qualities[] = { "Draft", "Normal", "High" };
4622 /* cupsPrintQuality values */
4623
4624 if (qval >= IPP_QUALITY_DRAFT && qval <= IPP_QUALITY_HIGH)
4625 cupsFilePrintf(fp, "*cupsPrintQuality %s\n", qualities[qval - IPP_QUALITY_DRAFT]);
4626 }
4627 else if (!strcmp(member_name, "output-bin"))
4628 {
4629 pwg_ppdize_name(ippGetString(member, 0, NULL), ppdname, sizeof(ppdname));
4630 cupsFilePrintf(fp, "*OutputBin %s\n", ppdname);
4631 }
4632 else if (!strcmp(member_name, "sides"))
4633 {
4634 keyword = ippGetString(member, 0, NULL);
4635 if (keyword && !strcmp(keyword, "one-sided"))
4636 cupsFilePuts(fp, "*Duplex None\n");
4637 else if (keyword && !strcmp(keyword, "two-sided-long-edge"))
4638 cupsFilePuts(fp, "*Duplex DuplexNoTumble\n");
4639 else if (keyword && !strcmp(keyword, "two-sided-short-edge"))
4640 cupsFilePuts(fp, "*Duplex DuplexTumble\n");
4641 }
4642 else
4643 {
4644 /*
4645 * Add attribute name and value as-is...
4646 */
4647
4648 ippAttributeString(member, member_value, sizeof(member_value));
4649 cupsFilePrintf(fp, "*%s %s\n", member_name, member_value);
4650 }
4651 }
4652
4653 cupsFilePuts(fp, "\"\n*End\n");
4654
4655 if ((localized_name = _cupsMessageLookup(strings, preset_name)) != preset_name)
4656 cupsFilePrintf(fp, "*%s.APPrinterPreset %s/%s: \"\"\n", lang->language, preset_name, localized_name);
4657 }
4658 }
4659
4660 /*
4661 * Close up and return...
4662 */
4663
4664 cupsFileClose(fp);
4665
4666 return (buffer);
4667
4668 /*
4669 * If we get here then there was a problem creating the PPD...
4670 */
4671
4672 bad_ppd:
4673
4674 cupsFileClose(fp);
4675 unlink(buffer);
4676 *buffer = '\0';
4677
4678 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Printer does not support required IPP attributes or document formats."), 1);
4679
4680 return (NULL);
4681 }
4682
4683
4684 /*
4685 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
4686 * media-source.
4687 */
4688
4689 const char * /* O - InputSlot name */
4690 _pwgInputSlotForSource(
4691 const char *media_source, /* I - PWG media-source */
4692 char *name, /* I - Name buffer */
4693 size_t namesize) /* I - Size of name buffer */
4694 {
4695 /*
4696 * Range check input...
4697 */
4698
4699 if (!media_source || !name || namesize < PPD_MAX_NAME)
4700 return (NULL);
4701
4702 if (_cups_strcasecmp(media_source, "main"))
4703 strlcpy(name, "Cassette", namesize);
4704 else if (_cups_strcasecmp(media_source, "alternate"))
4705 strlcpy(name, "Multipurpose", namesize);
4706 else if (_cups_strcasecmp(media_source, "large-capacity"))
4707 strlcpy(name, "LargeCapacity", namesize);
4708 else if (_cups_strcasecmp(media_source, "bottom"))
4709 strlcpy(name, "Lower", namesize);
4710 else if (_cups_strcasecmp(media_source, "middle"))
4711 strlcpy(name, "Middle", namesize);
4712 else if (_cups_strcasecmp(media_source, "top"))
4713 strlcpy(name, "Upper", namesize);
4714 else if (_cups_strcasecmp(media_source, "rear"))
4715 strlcpy(name, "Rear", namesize);
4716 else if (_cups_strcasecmp(media_source, "side"))
4717 strlcpy(name, "Side", namesize);
4718 else if (_cups_strcasecmp(media_source, "envelope"))
4719 strlcpy(name, "Envelope", namesize);
4720 else if (_cups_strcasecmp(media_source, "main-roll"))
4721 strlcpy(name, "Roll", namesize);
4722 else if (_cups_strcasecmp(media_source, "alternate-roll"))
4723 strlcpy(name, "Roll2", namesize);
4724 else
4725 pwg_ppdize_name(media_source, name, namesize);
4726
4727 return (name);
4728 }
4729
4730
4731 /*
4732 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
4733 * media-type.
4734 */
4735
4736 const char * /* O - MediaType name */
4737 _pwgMediaTypeForType(
4738 const char *media_type, /* I - PWG media-type */
4739 char *name, /* I - Name buffer */
4740 size_t namesize) /* I - Size of name buffer */
4741 {
4742 /*
4743 * Range check input...
4744 */
4745
4746 if (!media_type || !name || namesize < PPD_MAX_NAME)
4747 return (NULL);
4748
4749 if (_cups_strcasecmp(media_type, "auto"))
4750 strlcpy(name, "Auto", namesize);
4751 else if (_cups_strcasecmp(media_type, "cardstock"))
4752 strlcpy(name, "Cardstock", namesize);
4753 else if (_cups_strcasecmp(media_type, "envelope"))
4754 strlcpy(name, "Envelope", namesize);
4755 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
4756 strlcpy(name, "Glossy", namesize);
4757 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
4758 strlcpy(name, "HighGloss", namesize);
4759 else if (_cups_strcasecmp(media_type, "photographic-matte"))
4760 strlcpy(name, "Matte", namesize);
4761 else if (_cups_strcasecmp(media_type, "stationery"))
4762 strlcpy(name, "Plain", namesize);
4763 else if (_cups_strcasecmp(media_type, "stationery-coated"))
4764 strlcpy(name, "Coated", namesize);
4765 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
4766 strlcpy(name, "Inkjet", namesize);
4767 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
4768 strlcpy(name, "Letterhead", namesize);
4769 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
4770 strlcpy(name, "Preprinted", namesize);
4771 else if (_cups_strcasecmp(media_type, "transparency"))
4772 strlcpy(name, "Transparency", namesize);
4773 else
4774 pwg_ppdize_name(media_type, name, namesize);
4775
4776 return (name);
4777 }
4778
4779
4780 /*
4781 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
4782 */
4783
4784 const char * /* O - PageSize name */
4785 _pwgPageSizeForMedia(
4786 pwg_media_t *media, /* I - Media */
4787 char *name, /* I - PageSize name buffer */
4788 size_t namesize) /* I - Size of name buffer */
4789 {
4790 const char *sizeptr, /* Pointer to size in PWG name */
4791 *dimptr; /* Pointer to dimensions in PWG name */
4792
4793
4794 /*
4795 * Range check input...
4796 */
4797
4798 if (!media || !name || namesize < PPD_MAX_NAME)
4799 return (NULL);
4800
4801 /*
4802 * Copy or generate a PageSize name...
4803 */
4804
4805 if (media->ppd)
4806 {
4807 /*
4808 * Use a standard Adobe name...
4809 */
4810
4811 strlcpy(name, media->ppd, namesize);
4812 }
4813 else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
4814 (sizeptr = strchr(media->pwg, '_')) == NULL ||
4815 (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
4816 (size_t)(dimptr - sizeptr) > namesize)
4817 {
4818 /*
4819 * Use a name of the form "wNNNhNNN"...
4820 */
4821
4822 snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
4823 (int)PWG_TO_POINTS(media->length));
4824 }
4825 else
4826 {
4827 /*
4828 * Copy the size name from class_sizename_dimensions...
4829 */
4830
4831 memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
4832 name[dimptr - sizeptr - 1] = '\0';
4833 }
4834
4835 return (name);
4836 }
4837
4838
4839 /*
4840 * 'cups_get_url()' - Get a copy of the file at the given URL.
4841 */
4842
4843 static int /* O - 1 on success, 0 on failure */
4844 cups_get_url(http_t **http, /* IO - Current HTTP connection */
4845 const char *url, /* I - URL to get */
4846 char *name, /* I - Temporary filename */
4847 size_t namesize) /* I - Size of temporary filename buffer */
4848 {
4849 char scheme[32], /* URL scheme */
4850 userpass[256], /* URL username:password */
4851 host[256], /* URL host */
4852 curhost[256], /* Current host */
4853 resource[256]; /* URL resource */
4854 int port; /* URL port */
4855 http_encryption_t encryption; /* Type of encryption to use */
4856 http_status_t status; /* Status of GET request */
4857 int fd; /* Temporary file */
4858
4859
4860 if (httpSeparateURI(HTTP_URI_CODING_ALL, url, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
4861 return (0);
4862
4863 if (port == 443 || !strcmp(scheme, "https"))
4864 encryption = HTTP_ENCRYPTION_ALWAYS;
4865 else
4866 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
4867
4868 if (!*http || strcasecmp(host, httpGetHostname(*http, curhost, sizeof(curhost))) || httpAddrPort(httpGetAddress(*http)) != port)
4869 {
4870 httpClose(*http);
4871 *http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 5000, NULL);
4872 }
4873
4874 if (!*http)
4875 return (0);
4876
4877 if ((fd = cupsTempFd(name, (int)namesize)) < 0)
4878 return (0);
4879
4880 status = cupsGetFd(*http, resource, fd);
4881
4882 close(fd);
4883
4884 if (status != HTTP_STATUS_OK)
4885 {
4886 unlink(name);
4887 *name = '\0';
4888 return (0);
4889 }
4890
4891 return (1);
4892 }
4893
4894
4895 /*
4896 * 'pwg_add_finishing()' - Add a finishings value.
4897 */
4898
4899 static void
4900 pwg_add_finishing(
4901 cups_array_t *finishings, /* I - Finishings array */
4902 ipp_finishings_t template, /* I - Finishing template */
4903 const char *name, /* I - PPD option */
4904 const char *value) /* I - PPD choice */
4905 {
4906 _pwg_finishings_t *f; /* New finishings value */
4907
4908
4909 if ((f = (_pwg_finishings_t *)calloc(1, sizeof(_pwg_finishings_t))) != NULL)
4910 {
4911 f->value = template;
4912 f->num_options = cupsAddOption(name, value, 0, &f->options);
4913
4914 cupsArrayAdd(finishings, f);
4915 }
4916 }
4917
4918
4919 /*
4920 * 'pwg_add_message()' - Add a message to the PPD cached strings.
4921 */
4922
4923 static void
4924 pwg_add_message(cups_array_t *a, /* I - Message catalog */
4925 const char *msg, /* I - Message identifier */
4926 const char *str) /* I - Localized string */
4927 {
4928 _cups_message_t *m; /* New message */
4929
4930
4931 if ((m = calloc(1, sizeof(_cups_message_t))) != NULL)
4932 {
4933 m->msg = strdup(msg);
4934 m->str = strdup(str);
4935 cupsArrayAdd(a, m);
4936 }
4937 }
4938
4939
4940 /*
4941 * 'pwg_compare_finishings()' - Compare two finishings values.
4942 */
4943
4944 static int /* O - Result of comparison */
4945 pwg_compare_finishings(
4946 _pwg_finishings_t *a, /* I - First finishings value */
4947 _pwg_finishings_t *b) /* I - Second finishings value */
4948 {
4949 return ((int)b->value - (int)a->value);
4950 }
4951
4952
4953 /*
4954 * 'pwg_compare_sizes()' - Compare two media sizes...
4955 */
4956
4957 static int /* O - Result of comparison */
4958 pwg_compare_sizes(cups_size_t *a, /* I - First media size */
4959 cups_size_t *b) /* I - Second media size */
4960 {
4961 return (strcmp(a->media, b->media));
4962 }
4963
4964
4965 /*
4966 * 'pwg_copy_size()' - Copy a media size.
4967 */
4968
4969 static cups_size_t * /* O - New media size */
4970 pwg_copy_size(cups_size_t *size) /* I - Media size to copy */
4971 {
4972 cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t));
4973 /* New media size */
4974
4975 if (newsize)
4976 memcpy(newsize, size, sizeof(cups_size_t));
4977
4978 return (newsize);
4979 }
4980
4981
4982 /*
4983 * 'pwg_free_finishings()' - Free a finishings value.
4984 */
4985
4986 static void
4987 pwg_free_finishings(
4988 _pwg_finishings_t *f) /* I - Finishings value */
4989 {
4990 cupsFreeOptions(f->num_options, f->options);
4991 free(f);
4992 }
4993
4994
4995 /*
4996 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
4997 */
4998
4999 static void
5000 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
5001 char *name, /* I - Name buffer */
5002 size_t namesize) /* I - Size of name buffer */
5003 {
5004 char *ptr, /* Pointer into name buffer */
5005 *end; /* End of name buffer */
5006
5007
5008 if (!ipp)
5009 {
5010 *name = '\0';
5011 return;
5012 }
5013
5014 *name = (char)toupper(*ipp++);
5015
5016 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
5017 {
5018 if (*ipp == '-' && _cups_isalnum(ipp[1]))
5019 {
5020 ipp ++;
5021 *ptr++ = (char)toupper(*ipp++ & 255);
5022 }
5023 else
5024 *ptr++ = *ipp++;
5025 }
5026
5027 *ptr = '\0';
5028 }
5029
5030
5031 /*
5032 * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values.
5033 */
5034
5035 static void
5036 pwg_ppdize_resolution(
5037 ipp_attribute_t *attr, /* I - Attribute to convert */
5038 int element, /* I - Element to convert */
5039 int *xres, /* O - X resolution in DPI */
5040 int *yres, /* O - Y resolution in DPI */
5041 char *name, /* I - Name buffer */
5042 size_t namesize) /* I - Size of name buffer */
5043 {
5044 ipp_res_t units; /* Units for resolution */
5045
5046
5047 *xres = ippGetResolution(attr, element, yres, &units);
5048
5049 if (units == IPP_RES_PER_CM)
5050 {
5051 *xres = (int)(*xres * 2.54);
5052 *yres = (int)(*yres * 2.54);
5053 }
5054
5055 if (name && namesize > 4)
5056 {
5057 if (*xres == *yres)
5058 snprintf(name, namesize, "%ddpi", *xres);
5059 else
5060 snprintf(name, namesize, "%dx%ddpi", *xres, *yres);
5061 }
5062 }
5063
5064
5065 /*
5066 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
5067 */
5068
5069 static void
5070 pwg_unppdize_name(const char *ppd, /* I - PPD keyword */
5071 char *name, /* I - Name buffer */
5072 size_t namesize, /* I - Size of name buffer */
5073 const char *dashchars)/* I - Characters to be replaced by dashes */
5074 {
5075 char *ptr, /* Pointer into name buffer */
5076 *end; /* End of name buffer */
5077
5078
5079 if (_cups_islower(*ppd))
5080 {
5081 /*
5082 * Already lowercase name, use as-is?
5083 */
5084
5085 const char *ppdptr; /* Pointer into PPD keyword */
5086
5087 for (ppdptr = ppd + 1; *ppdptr; ppdptr ++)
5088 if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr))
5089 break;
5090
5091 if (!*ppdptr)
5092 {
5093 strlcpy(name, ppd, namesize);
5094 return;
5095 }
5096 }
5097
5098 for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
5099 {
5100 if (_cups_isalnum(*ppd) || *ppd == '-')
5101 *ptr++ = (char)tolower(*ppd & 255);
5102 else if (strchr(dashchars, *ppd))
5103 *ptr++ = '-';
5104 else
5105 *ptr++ = *ppd;
5106
5107 if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
5108 _cups_isupper(ppd[1]) && ptr < end)
5109 *ptr++ = '-';
5110 else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
5111 *ptr++ = '-';
5112 }
5113
5114 *ptr = '\0';
5115 }