]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd-cache.c
Changelog
[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
4006 count = ippGetCount(attr);
4007 names = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
4008 fin_options = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4009
4010 /*
4011 * Staple/Bind/Stitch
4012 */
4013
4014 for (i = 0; i < count; i ++)
4015 {
4016 value = ippGetInteger(attr, i);
4017 keyword = ippEnumString("finishings", value);
4018
4019 if (!strncmp(keyword, "staple-", 7) || !strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch"))
4020 break;
4021 }
4022
4023 if (i < count)
4024 {
4025 static const char * const staple_keywords[] =
4026 { /* StapleLocation keywords */
4027 "SinglePortrait",
4028 "SingleRevLandscape",
4029 "SingleLandscape",
4030 "SingleRevPortrait",
4031 "EdgeStitchPortrait",
4032 "EdgeStitchLandscape",
4033 "EdgeStitchRevPortrait",
4034 "EdgeStitchRevLandscape",
4035 "DualPortrait",
4036 "DualLandscape",
4037 "DualRevPortrait",
4038 "DualRevLandscape",
4039 "TriplePortrait",
4040 "TripleLandscape",
4041 "TripleRevPortrait",
4042 "TripleRevLandscape"
4043 };
4044 static const char * const bind_keywords[] =
4045 { /* StapleLocation binding keywords */
4046 "BindPortrait",
4047 "BindLandscape",
4048 "BindRevPortrait",
4049 "BindRevLandscape"
4050 };
4051
4052 cupsArrayAdd(fin_options, "*StapleLocation");
4053
4054 cupsFilePuts(fp, "*OpenUI *StapleLocation: PickOne\n");
4055 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
4056 cupsFilePrintf(fp, "*%s.Translation StapleLocation/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Staple")));
4057 cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
4058 cupsFilePuts(fp, "*StapleLocation None: \"\"\n");
4059 cupsFilePrintf(fp, "*%s.StapleLocation None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4060
4061 for (; i < count; i ++)
4062 {
4063 value = ippGetInteger(attr, i);
4064 keyword = ippEnumString("finishings", value);
4065
4066 if (strncmp(keyword, "staple-", 7) && strncmp(keyword, "bind-", 5) && strncmp(keyword, "edge-stitch-", 12) && strcmp(keyword, "saddle-stitch"))
4067 continue;
4068
4069 if (cupsArrayFind(names, (char *)keyword))
4070 continue; /* Already did this finishing template */
4071
4072 cupsArrayAdd(names, (char *)keyword);
4073
4074 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4075 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4076 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4077 msgstr = keyword;
4078
4079 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4080 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4081 else if (value >= IPP_FINISHINGS_STAPLE_TOP_LEFT && value <= IPP_FINISHINGS_STAPLE_TRIPLE_BOTTOM)
4082 ppd_keyword = staple_keywords[value - IPP_FINISHINGS_STAPLE_TOP_LEFT];
4083 else if (value >= IPP_FINISHINGS_BIND_LEFT && value <= IPP_FINISHINGS_BIND_BOTTOM)
4084 ppd_keyword = bind_keywords[value - IPP_FINISHINGS_BIND_LEFT];
4085 else
4086 ppd_keyword = NULL;
4087
4088 if (!ppd_keyword)
4089 continue;
4090
4091 cupsFilePrintf(fp, "*StapleLocation %s: \"\"\n", ppd_keyword);
4092 cupsFilePrintf(fp, "*%s.StapleLocation %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
4093 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, keyword, ppd_keyword);
4094 }
4095
4096 cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
4097 }
4098
4099 /*
4100 * Fold
4101 */
4102
4103 for (i = 0; i < count; i ++)
4104 {
4105 value = ippGetInteger(attr, i);
4106 keyword = ippEnumString("finishings", value);
4107
4108 if (!strncmp(keyword, "cups-fold-", 10) || !strcmp(keyword, "fold") || !strncmp(keyword, "fold-", 5))
4109 break;
4110 }
4111
4112 if (i < count)
4113 {
4114 static const char * const fold_keywords[] =
4115 { /* FoldType keywords */
4116 "Accordion",
4117 "DoubleGate",
4118 "Gate",
4119 "Half",
4120 "HalfZ",
4121 "LeftGate",
4122 "Letter",
4123 "Parallel",
4124 "XFold",
4125 "RightGate",
4126 "ZFold",
4127 "EngineeringZ"
4128 };
4129
4130 cupsArrayAdd(fin_options, "*FoldType");
4131
4132 cupsFilePuts(fp, "*OpenUI *FoldType: PickOne\n");
4133 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
4134 cupsFilePrintf(fp, "*%s.Translation FoldType/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Fold")));
4135 cupsFilePuts(fp, "*DefaultFoldType: None\n");
4136 cupsFilePuts(fp, "*FoldType None: \"\"\n");
4137 cupsFilePrintf(fp, "*%s.FoldType None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4138
4139 for (; i < count; i ++)
4140 {
4141 value = ippGetInteger(attr, i);
4142 keyword = ippEnumString("finishings", value);
4143
4144 if (!strncmp(keyword, "cups-fold-", 10))
4145 keyword += 5;
4146 else if (strcmp(keyword, "fold") && strncmp(keyword, "fold-", 5))
4147 continue;
4148
4149 if (cupsArrayFind(names, (char *)keyword))
4150 continue; /* Already did this finishing template */
4151
4152 cupsArrayAdd(names, (char *)keyword);
4153
4154 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4155 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4156 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4157 msgstr = keyword;
4158
4159 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4160 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4161 else if (value >= IPP_FINISHINGS_FOLD_ACCORDION && value <= IPP_FINISHINGS_FOLD_ENGINEERING_Z)
4162 ppd_keyword = fold_keywords[value - IPP_FINISHINGS_FOLD_ACCORDION];
4163 else if (value >= IPP_FINISHINGS_CUPS_FOLD_ACCORDION && value <= IPP_FINISHINGS_CUPS_FOLD_Z)
4164 ppd_keyword = fold_keywords[value - IPP_FINISHINGS_CUPS_FOLD_ACCORDION];
4165 else
4166 ppd_keyword = NULL;
4167
4168 if (!ppd_keyword)
4169 continue;
4170
4171 cupsFilePrintf(fp, "*FoldType %s: \"\"\n", ppd_keyword);
4172 cupsFilePrintf(fp, "*%s.FoldType %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
4173 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, keyword, ppd_keyword);
4174 }
4175
4176 cupsFilePuts(fp, "*CloseUI: *FoldType\n");
4177 }
4178
4179 /*
4180 * Punch
4181 */
4182
4183 for (i = 0; i < count; i ++)
4184 {
4185 value = ippGetInteger(attr, i);
4186 keyword = ippEnumString("finishings", value);
4187
4188 if (!strncmp(keyword, "cups-punch-", 11) || !strncmp(keyword, "punch-", 6))
4189 break;
4190 }
4191
4192 if (i < count)
4193 {
4194 static const char * const punch_keywords[] =
4195 { /* PunchMedia keywords */
4196 "SinglePortrait",
4197 "SingleRevLandscape",
4198 "SingleLandscape",
4199 "SingleRevPortrait",
4200 "DualPortrait",
4201 "DualLandscape",
4202 "DualRevPortrait",
4203 "DualRevLandscape",
4204 "TriplePortrait",
4205 "TripleLandscape",
4206 "TripleRevPortrait",
4207 "TripleRevLandscape",
4208 "QuadPortrait",
4209 "QuadLandscape",
4210 "QuadRevPortrait",
4211 "QuadRevLandscape",
4212 "MultiplePortrait",
4213 "MultipleLandscape",
4214 "MultipleRevPortrait",
4215 "MultipleRevLandscape"
4216 };
4217
4218 cupsArrayAdd(fin_options, "*PunchMedia");
4219
4220 cupsFilePuts(fp, "*OpenUI *PunchMedia: PickOne\n");
4221 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
4222 cupsFilePrintf(fp, "*%s.Translation PunchMedia/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Punch")));
4223 cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
4224 cupsFilePuts(fp, "*PunchMedia None: \"\"\n");
4225 cupsFilePrintf(fp, "*%s.PunchMedia None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4226
4227 for (i = 0; i < count; i ++)
4228 {
4229 value = ippGetInteger(attr, i);
4230 keyword = ippEnumString("finishings", value);
4231
4232 if (!strncmp(keyword, "cups-punch-", 11))
4233 keyword += 5;
4234 else if (strncmp(keyword, "punch-", 6))
4235 continue;
4236
4237 if (cupsArrayFind(names, (char *)keyword))
4238 continue; /* Already did this finishing template */
4239
4240 cupsArrayAdd(names, (char *)keyword);
4241
4242 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4243 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4244 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4245 msgstr = keyword;
4246
4247 if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4248 ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4249 else if (value >= IPP_FINISHINGS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_PUNCH_MULTIPLE_BOTTOM)
4250 ppd_keyword = punch_keywords[value - IPP_FINISHINGS_PUNCH_TOP_LEFT];
4251 else if (value >= IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM)
4252 ppd_keyword = punch_keywords[value - IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT];
4253 else
4254 ppd_keyword = NULL;
4255
4256 if (!ppd_keyword)
4257 continue;
4258
4259 cupsFilePrintf(fp, "*PunchMedia %s: \"\"\n", ppd_keyword);
4260 cupsFilePrintf(fp, "*%s.PunchMedia %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
4261 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, keyword, ppd_keyword);
4262 }
4263
4264 cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
4265 }
4266
4267 /*
4268 * Booklet
4269 */
4270
4271 if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER))
4272 {
4273 cupsArrayAdd(fin_options, "*Booklet");
4274
4275 cupsFilePuts(fp, "*OpenUI *Booklet: Boolean\n");
4276 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
4277 cupsFilePrintf(fp, "*%s.Translation Booklet/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Booklet")));
4278 cupsFilePuts(fp, "*DefaultBooklet: False\n");
4279 cupsFilePuts(fp, "*Booklet False: \"\"\n");
4280 cupsFilePuts(fp, "*Booklet True: \"\"\n");
4281 cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n", IPP_FINISHINGS_BOOKLET_MAKER);
4282 cupsFilePuts(fp, "*CloseUI: *Booklet\n");
4283 }
4284
4285 /*
4286 * CutMedia
4287 */
4288
4289 for (i = 0; i < count; i ++)
4290 {
4291 value = ippGetInteger(attr, i);
4292 keyword = ippEnumString("finishings", value);
4293
4294 if (!strcmp(keyword, "trim") || !strncmp(keyword, "trim-", 5))
4295 break;
4296 }
4297
4298 if (i < count)
4299 {
4300 static const char * const trim_keywords[] =
4301 { /* CutMedia keywords */
4302 "EndOfPage",
4303 "EndOfDoc",
4304 "EndOfSet",
4305 "EndOfJob"
4306 };
4307
4308 cupsArrayAdd(fin_options, "*CutMedia");
4309
4310 cupsFilePuts(fp, "*OpenUI *CutMedia: PickOne\n");
4311 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *CutMedia\n");
4312 cupsFilePrintf(fp, "*%s.Translation CutMedia/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Cut")));
4313 cupsFilePuts(fp, "*DefaultCutMedia: None\n");
4314 cupsFilePuts(fp, "*CutMedia None: \"\"\n");
4315 cupsFilePrintf(fp, "*%s.CutMedia None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4316
4317 for (i = 0; i < count; i ++)
4318 {
4319 value = ippGetInteger(attr, i);
4320 keyword = ippEnumString("finishings", value);
4321
4322 if (strcmp(keyword, "trim") && strncmp(keyword, "trim-", 5))
4323 continue;
4324
4325 if (cupsArrayFind(names, (char *)keyword))
4326 continue; /* Already did this finishing template */
4327
4328 cupsArrayAdd(names, (char *)keyword);
4329
4330 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4331 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4332 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4333 msgstr = keyword;
4334
4335 if (value == IPP_FINISHINGS_TRIM)
4336 ppd_keyword = "Auto";
4337 else
4338 ppd_keyword = trim_keywords[value - IPP_FINISHINGS_TRIM_AFTER_PAGES];
4339
4340 cupsFilePrintf(fp, "*CutMedia %s: \"\"\n", ppd_keyword);
4341 cupsFilePrintf(fp, "*%s.CutMedia %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
4342 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*CutMedia %s\"\n", value, keyword, ppd_keyword);
4343 }
4344
4345 cupsFilePuts(fp, "*CloseUI: *CutMedia\n");
4346 }
4347
4348 cupsArrayDelete(names);
4349 }
4350
4351 if ((attr = ippFindAttribute(response, "finishings-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4352 {
4353 ipp_t *finishing_col; /* Current finishing collection */
4354 ipp_attribute_t *finishing_attr; /* Current finishing member attribute */
4355 cups_array_t *templates; /* Finishing templates */
4356
4357 cupsFilePuts(fp, "*OpenUI *cupsFinishingTemplate: PickOne\n");
4358 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n");
4359 cupsFilePrintf(fp, "*%s.Translation cupsFinishingTemplate/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Finishing Preset")));
4360 cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n");
4361 cupsFilePuts(fp, "*cupsFinishingTemplate none: \"\"\n");
4362 cupsFilePrintf(fp, "*%s.cupsFinishingTemplate none/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4363
4364 templates = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4365 count = ippGetCount(attr);
4366
4367 for (i = 0; i < count; i ++)
4368 {
4369 finishing_col = ippGetCollection(attr, i);
4370 keyword = ippGetString(ippFindAttribute(finishing_col, "finishing-template", IPP_TAG_ZERO), 0, NULL);
4371
4372 if (!keyword || cupsArrayFind(templates, (void *)keyword))
4373 continue;
4374
4375 if (!strcmp(keyword, "none"))
4376 continue;
4377
4378 cupsArrayAdd(templates, (void *)keyword);
4379
4380 snprintf(msgid, sizeof(msgid), "finishing-template.%s", keyword);
4381 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4382 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4383 msgstr = keyword;
4384
4385 cupsFilePrintf(fp, "*cupsFinishingTemplate %s: \"\n", keyword);
4386 for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr; finishing_attr = ippNextAttribute(finishing_col))
4387 {
4388 if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION)
4389 {
4390 const char *name = ippGetName(finishing_attr);
4391 /* Member attribute name */
4392
4393 if (strcmp(name, "media-size"))
4394 cupsFilePrintf(fp, "%% %s\n", name);
4395 }
4396 }
4397 cupsFilePuts(fp, "\"\n");
4398 cupsFilePrintf(fp, "*%s.cupsFinishingTemplate %s/%s: \"\"\n", lang->language, keyword, msgstr);
4399 cupsFilePuts(fp, "*End\n");
4400 }
4401
4402 cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n");
4403
4404 if (cupsArrayCount(fin_options))
4405 {
4406 const char *fin_option; /* Current finishing option */
4407
4408 cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate");
4409 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4410 cupsFilePrintf(fp, " %s", fin_option);
4411 cupsFilePuts(fp, "\"\n");
4412
4413 cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate None");
4414 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4415 cupsFilePrintf(fp, " %s None", fin_option);
4416 cupsFilePuts(fp, "\"\n");
4417 }
4418
4419 cupsArrayDelete(templates);
4420 }
4421
4422 cupsArrayDelete(fin_options);
4423
4424 /*
4425 * cupsPrintQuality and DefaultResolution...
4426 */
4427
4428 quality = ippFindAttribute(response, "print-quality-supported", IPP_TAG_ENUM);
4429
4430 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
4431 {
4432 int lowdpi = 0, hidpi = 0; /* Lower and higher resolution */
4433
4434 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4435 {
4436 const char *rs = ippGetString(attr, i, NULL);
4437 /* RS value */
4438
4439 if (_cups_strncasecmp(rs, "RS", 2))
4440 continue;
4441
4442 lowdpi = atoi(rs + 2);
4443 if ((rs = strrchr(rs, '-')) != NULL)
4444 hidpi = atoi(rs + 1);
4445 else
4446 hidpi = lowdpi;
4447 break;
4448 }
4449
4450 if (lowdpi == 0)
4451 {
4452 /*
4453 * Invalid "urf-supported" value...
4454 */
4455
4456 goto bad_ppd;
4457 }
4458 else
4459 {
4460 /*
4461 * Generate print qualities based on low and high DPIs...
4462 */
4463
4464 cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi);
4465
4466 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4467 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4468 "*%s.Translation cupsPrintQuality/%s: \"\"\n"
4469 "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
4470 if ((lowdpi & 1) == 0)
4471 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", lowdpi, lowdpi / 2, lang->language, _cupsLangString(lang, _("Draft")));
4472 else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4473 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", lowdpi, lowdpi, lang->language, _cupsLangString(lang, _("Draft")));
4474
4475 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Normal/%s: \"\"\n", lowdpi, lowdpi, lang->language, _cupsLangString(lang, _("Normal")));
4476
4477 if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4478 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality High/%s: \"\"\n", hidpi, hidpi, lang->language, _cupsLangString(lang, _("High")));
4479 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4480 }
4481 }
4482 else if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
4483 {
4484 /*
4485 * Make a sorted list of resolutions.
4486 */
4487
4488 count = ippGetCount(attr);
4489 if (count > (int)(sizeof(resolutions) / sizeof(resolutions[0])))
4490 count = (int)(sizeof(resolutions) / sizeof(resolutions[0]));
4491
4492 resolutions[0] = 0; /* Not in loop to silence Clang static analyzer... */
4493 for (i = 1; i < count; i ++)
4494 resolutions[i] = i;
4495
4496 for (i = 0; i < (count - 1); i ++)
4497 {
4498 for (j = i + 1; j < count; j ++)
4499 {
4500 int ix, iy, /* First X and Y resolution */
4501 jx, jy, /* Second X and Y resolution */
4502 temp; /* Swap variable */
4503 ipp_res_t units; /* Resolution units */
4504
4505 ix = ippGetResolution(attr, resolutions[i], &iy, &units);
4506 jx = ippGetResolution(attr, resolutions[j], &jy, &units);
4507
4508 if (ix > jx || (ix == jx && iy > jy))
4509 {
4510 /*
4511 * Swap these two resolutions...
4512 */
4513
4514 temp = resolutions[i];
4515 resolutions[i] = resolutions[j];
4516 resolutions[j] = temp;
4517 }
4518 }
4519 }
4520
4521 /*
4522 * Generate print quality options...
4523 */
4524
4525 pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, ppdname, sizeof(ppdname));
4526 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4527
4528 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4529 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4530 "*%s.Translation cupsPrintQuality/%s: \"\"\n"
4531 "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
4532 if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4533 {
4534 pwg_ppdize_resolution(attr, resolutions[0], &xres, &yres, NULL, 0);
4535 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4536 cupsFilePrintf(fp, "*%s.cupsPrintQuality Draft/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Draft")));
4537 }
4538
4539 pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, NULL, 0);
4540 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4541 cupsFilePrintf(fp, "*%s.cupsPrintQuality Normal/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Normal")));
4542
4543 if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4544 {
4545 pwg_ppdize_resolution(attr, resolutions[count - 1], &xres, &yres, NULL, 0);
4546 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4547 cupsFilePrintf(fp, "*%s.cupsPrintQuality High/%s: \"\"\n", lang->language, _cupsLangString(lang, _("High")));
4548 }
4549
4550 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4551 }
4552 else if (is_apple || is_pwg)
4553 goto bad_ppd;
4554 else
4555 {
4556 if ((attr = ippFindAttribute(response, "printer-resolution-default", IPP_TAG_RESOLUTION)) != NULL)
4557 {
4558 pwg_ppdize_resolution(attr, 0, &xres, &yres, ppdname, sizeof(ppdname));
4559 }
4560 else
4561 {
4562 xres = yres = 300;
4563 strlcpy(ppdname, "300dpi", sizeof(ppdname));
4564 }
4565
4566 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4567
4568 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4569 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4570 "*%s.Translation cupsPrintQuality/%s: \"\"\n"
4571 "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
4572 if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4573 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("Draft")));
4574
4575 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Normal/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("Normal")));
4576
4577 if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
4578 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality High/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("High")));
4579 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4580 }
4581
4582 /*
4583 * Presets...
4584 */
4585
4586 if ((attr = ippFindAttribute(response, "job-presets-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4587 {
4588 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4589 {
4590 ipp_t *preset = ippGetCollection(attr, i);
4591 /* Preset collection */
4592 const char *preset_name = ippGetString(ippFindAttribute(preset, "preset-name", IPP_TAG_ZERO), 0, NULL),
4593 /* Preset name */
4594 *localized_name; /* Localized preset name */
4595 ipp_attribute_t *member; /* Member attribute in preset */
4596 const char *member_name; /* Member attribute name */
4597 char member_value[256]; /* Member attribute value */
4598
4599 if (!preset || !preset_name)
4600 continue;
4601
4602 cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", preset_name);
4603 for (member = ippFirstAttribute(preset); member; member = ippNextAttribute(preset))
4604 {
4605 member_name = ippGetName(member);
4606
4607 if (!member_name || !strcmp(member_name, "preset-name"))
4608 continue;
4609
4610 if (!strcmp(member_name, "finishings"))
4611 {
4612 for (i = 0, count = ippGetCount(member); i < count; i ++)
4613 {
4614 const char *option = NULL; /* PPD option name */
4615
4616 keyword = ippEnumString("finishings", ippGetInteger(member, i));
4617
4618 if (!strcmp(keyword, "booklet-maker"))
4619 {
4620 option = "Booklet";
4621 keyword = "True";
4622 }
4623 else if (!strncmp(keyword, "fold-", 5))
4624 option = "FoldType";
4625 else if (!strncmp(keyword, "punch-", 6))
4626 option = "PunchMedia";
4627 else if (!strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch") || !strncmp(keyword, "staple-", 7))
4628 option = "StapleLocation";
4629
4630 if (option && keyword)
4631 cupsFilePrintf(fp, "*%s %s\n", option, keyword);
4632 }
4633 }
4634 else if (!strcmp(member_name, "finishings-col"))
4635 {
4636 ipp_t *fin_col; /* finishings-col value */
4637
4638 for (i = 0, count = ippGetCount(member); i < count; i ++)
4639 {
4640 fin_col = ippGetCollection(member, i);
4641
4642 if ((keyword = ippGetString(ippFindAttribute(fin_col, "finishing-template", IPP_TAG_ZERO), 0, NULL)) != NULL)
4643 cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", keyword);
4644 }
4645 }
4646 else if (!strcmp(member_name, "media"))
4647 {
4648 /*
4649 * Map media to PageSize...
4650 */
4651
4652 if ((pwg = pwgMediaForPWG(ippGetString(member, 0, NULL))) != NULL && pwg->ppd)
4653 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4654 }
4655 else if (!strcmp(member_name, "media-col"))
4656 {
4657 media_col = ippGetCollection(member, 0);
4658
4659 if ((media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0)) != NULL)
4660 {
4661 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
4662 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
4663 if ((pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL && pwg->ppd)
4664 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4665 }
4666
4667 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL)) != NULL)
4668 {
4669 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4670 cupsFilePrintf(fp, "*InputSlot %s\n", keyword);
4671 }
4672
4673 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL)) != NULL)
4674 {
4675 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4676 cupsFilePrintf(fp, "*MediaType %s\n", keyword);
4677 }
4678 }
4679 else if (!strcmp(member_name, "print-quality"))
4680 {
4681 /*
4682 * Map print-quality to cupsPrintQuality...
4683 */
4684
4685 int qval = ippGetInteger(member, 0);
4686 /* print-quality value */
4687 static const char * const qualities[] = { "Draft", "Normal", "High" };
4688 /* cupsPrintQuality values */
4689
4690 if (qval >= IPP_QUALITY_DRAFT && qval <= IPP_QUALITY_HIGH)
4691 cupsFilePrintf(fp, "*cupsPrintQuality %s\n", qualities[qval - IPP_QUALITY_DRAFT]);
4692 }
4693 else if (!strcmp(member_name, "output-bin"))
4694 {
4695 pwg_ppdize_name(ippGetString(member, 0, NULL), ppdname, sizeof(ppdname));
4696 cupsFilePrintf(fp, "*OutputBin %s\n", ppdname);
4697 }
4698 else if (!strcmp(member_name, "sides"))
4699 {
4700 keyword = ippGetString(member, 0, NULL);
4701 if (keyword && !strcmp(keyword, "one-sided"))
4702 cupsFilePuts(fp, "*Duplex None\n");
4703 else if (keyword && !strcmp(keyword, "two-sided-long-edge"))
4704 cupsFilePuts(fp, "*Duplex DuplexNoTumble\n");
4705 else if (keyword && !strcmp(keyword, "two-sided-short-edge"))
4706 cupsFilePuts(fp, "*Duplex DuplexTumble\n");
4707 }
4708 else
4709 {
4710 /*
4711 * Add attribute name and value as-is...
4712 */
4713
4714 ippAttributeString(member, member_value, sizeof(member_value));
4715 cupsFilePrintf(fp, "*%s %s\n", member_name, member_value);
4716 }
4717 }
4718
4719 cupsFilePuts(fp, "\"\n*End\n");
4720
4721 if ((localized_name = _cupsMessageLookup(strings, preset_name)) != preset_name)
4722 cupsFilePrintf(fp, "*%s.APPrinterPreset %s/%s: \"\"\n", lang->language, preset_name, localized_name);
4723 }
4724 }
4725
4726 /*
4727 * Close up and return...
4728 */
4729
4730 cupsFileClose(fp);
4731
4732 return (buffer);
4733
4734 /*
4735 * If we get here then there was a problem creating the PPD...
4736 */
4737
4738 bad_ppd:
4739
4740 cupsFileClose(fp);
4741 unlink(buffer);
4742 *buffer = '\0';
4743
4744 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Printer does not support required IPP attributes or document formats."), 1);
4745
4746 return (NULL);
4747 }
4748
4749
4750 /*
4751 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
4752 * media-source.
4753 */
4754
4755 const char * /* O - InputSlot name */
4756 _pwgInputSlotForSource(
4757 const char *media_source, /* I - PWG media-source */
4758 char *name, /* I - Name buffer */
4759 size_t namesize) /* I - Size of name buffer */
4760 {
4761 /*
4762 * Range check input...
4763 */
4764
4765 if (!media_source || !name || namesize < PPD_MAX_NAME)
4766 return (NULL);
4767
4768 if (_cups_strcasecmp(media_source, "main"))
4769 strlcpy(name, "Cassette", namesize);
4770 else if (_cups_strcasecmp(media_source, "alternate"))
4771 strlcpy(name, "Multipurpose", namesize);
4772 else if (_cups_strcasecmp(media_source, "large-capacity"))
4773 strlcpy(name, "LargeCapacity", namesize);
4774 else if (_cups_strcasecmp(media_source, "bottom"))
4775 strlcpy(name, "Lower", namesize);
4776 else if (_cups_strcasecmp(media_source, "middle"))
4777 strlcpy(name, "Middle", namesize);
4778 else if (_cups_strcasecmp(media_source, "top"))
4779 strlcpy(name, "Upper", namesize);
4780 else if (_cups_strcasecmp(media_source, "rear"))
4781 strlcpy(name, "Rear", namesize);
4782 else if (_cups_strcasecmp(media_source, "side"))
4783 strlcpy(name, "Side", namesize);
4784 else if (_cups_strcasecmp(media_source, "envelope"))
4785 strlcpy(name, "Envelope", namesize);
4786 else if (_cups_strcasecmp(media_source, "main-roll"))
4787 strlcpy(name, "Roll", namesize);
4788 else if (_cups_strcasecmp(media_source, "alternate-roll"))
4789 strlcpy(name, "Roll2", namesize);
4790 else
4791 pwg_ppdize_name(media_source, name, namesize);
4792
4793 return (name);
4794 }
4795
4796
4797 /*
4798 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
4799 * media-type.
4800 */
4801
4802 const char * /* O - MediaType name */
4803 _pwgMediaTypeForType(
4804 const char *media_type, /* I - PWG media-type */
4805 char *name, /* I - Name buffer */
4806 size_t namesize) /* I - Size of name buffer */
4807 {
4808 /*
4809 * Range check input...
4810 */
4811
4812 if (!media_type || !name || namesize < PPD_MAX_NAME)
4813 return (NULL);
4814
4815 if (_cups_strcasecmp(media_type, "auto"))
4816 strlcpy(name, "Auto", namesize);
4817 else if (_cups_strcasecmp(media_type, "cardstock"))
4818 strlcpy(name, "Cardstock", namesize);
4819 else if (_cups_strcasecmp(media_type, "envelope"))
4820 strlcpy(name, "Envelope", namesize);
4821 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
4822 strlcpy(name, "Glossy", namesize);
4823 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
4824 strlcpy(name, "HighGloss", namesize);
4825 else if (_cups_strcasecmp(media_type, "photographic-matte"))
4826 strlcpy(name, "Matte", namesize);
4827 else if (_cups_strcasecmp(media_type, "stationery"))
4828 strlcpy(name, "Plain", namesize);
4829 else if (_cups_strcasecmp(media_type, "stationery-coated"))
4830 strlcpy(name, "Coated", namesize);
4831 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
4832 strlcpy(name, "Inkjet", namesize);
4833 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
4834 strlcpy(name, "Letterhead", namesize);
4835 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
4836 strlcpy(name, "Preprinted", namesize);
4837 else if (_cups_strcasecmp(media_type, "transparency"))
4838 strlcpy(name, "Transparency", namesize);
4839 else
4840 pwg_ppdize_name(media_type, name, namesize);
4841
4842 return (name);
4843 }
4844
4845
4846 /*
4847 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
4848 */
4849
4850 const char * /* O - PageSize name */
4851 _pwgPageSizeForMedia(
4852 pwg_media_t *media, /* I - Media */
4853 char *name, /* I - PageSize name buffer */
4854 size_t namesize) /* I - Size of name buffer */
4855 {
4856 const char *sizeptr, /* Pointer to size in PWG name */
4857 *dimptr; /* Pointer to dimensions in PWG name */
4858
4859
4860 /*
4861 * Range check input...
4862 */
4863
4864 if (!media || !name || namesize < PPD_MAX_NAME)
4865 return (NULL);
4866
4867 /*
4868 * Copy or generate a PageSize name...
4869 */
4870
4871 if (media->ppd)
4872 {
4873 /*
4874 * Use a standard Adobe name...
4875 */
4876
4877 strlcpy(name, media->ppd, namesize);
4878 }
4879 else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
4880 (sizeptr = strchr(media->pwg, '_')) == NULL ||
4881 (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
4882 (size_t)(dimptr - sizeptr) > namesize)
4883 {
4884 /*
4885 * Use a name of the form "wNNNhNNN"...
4886 */
4887
4888 snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
4889 (int)PWG_TO_POINTS(media->length));
4890 }
4891 else
4892 {
4893 /*
4894 * Copy the size name from class_sizename_dimensions...
4895 */
4896
4897 memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
4898 name[dimptr - sizeptr - 1] = '\0';
4899 }
4900
4901 return (name);
4902 }
4903
4904
4905 /*
4906 * 'cups_get_url()' - Get a copy of the file at the given URL.
4907 */
4908
4909 static int /* O - 1 on success, 0 on failure */
4910 cups_get_url(http_t **http, /* IO - Current HTTP connection */
4911 const char *url, /* I - URL to get */
4912 char *name, /* I - Temporary filename */
4913 size_t namesize) /* I - Size of temporary filename buffer */
4914 {
4915 char scheme[32], /* URL scheme */
4916 userpass[256], /* URL username:password */
4917 host[256], /* URL host */
4918 curhost[256], /* Current host */
4919 resource[256]; /* URL resource */
4920 int port; /* URL port */
4921 http_encryption_t encryption; /* Type of encryption to use */
4922 http_status_t status; /* Status of GET request */
4923 int fd; /* Temporary file */
4924
4925
4926 if (httpSeparateURI(HTTP_URI_CODING_ALL, url, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
4927 return (0);
4928
4929 if (port == 443 || !strcmp(scheme, "https"))
4930 encryption = HTTP_ENCRYPTION_ALWAYS;
4931 else
4932 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
4933
4934 if (!*http || strcasecmp(host, httpGetHostname(*http, curhost, sizeof(curhost))) || httpAddrPort(httpGetAddress(*http)) != port)
4935 {
4936 httpClose(*http);
4937 *http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 5000, NULL);
4938 }
4939
4940 if (!*http)
4941 return (0);
4942
4943 if ((fd = cupsTempFd(name, (int)namesize)) < 0)
4944 return (0);
4945
4946 status = cupsGetFd(*http, resource, fd);
4947
4948 close(fd);
4949
4950 if (status != HTTP_STATUS_OK)
4951 {
4952 unlink(name);
4953 *name = '\0';
4954 return (0);
4955 }
4956
4957 return (1);
4958 }
4959
4960
4961 /*
4962 * 'pwg_add_finishing()' - Add a finishings value.
4963 */
4964
4965 static void
4966 pwg_add_finishing(
4967 cups_array_t *finishings, /* I - Finishings array */
4968 ipp_finishings_t template, /* I - Finishing template */
4969 const char *name, /* I - PPD option */
4970 const char *value) /* I - PPD choice */
4971 {
4972 _pwg_finishings_t *f; /* New finishings value */
4973
4974
4975 if ((f = (_pwg_finishings_t *)calloc(1, sizeof(_pwg_finishings_t))) != NULL)
4976 {
4977 f->value = template;
4978 f->num_options = cupsAddOption(name, value, 0, &f->options);
4979
4980 cupsArrayAdd(finishings, f);
4981 }
4982 }
4983
4984
4985 /*
4986 * 'pwg_add_message()' - Add a message to the PPD cached strings.
4987 */
4988
4989 static void
4990 pwg_add_message(cups_array_t *a, /* I - Message catalog */
4991 const char *msg, /* I - Message identifier */
4992 const char *str) /* I - Localized string */
4993 {
4994 _cups_message_t *m; /* New message */
4995
4996
4997 if ((m = calloc(1, sizeof(_cups_message_t))) != NULL)
4998 {
4999 m->msg = strdup(msg);
5000 m->str = strdup(str);
5001 cupsArrayAdd(a, m);
5002 }
5003 }
5004
5005
5006 /*
5007 * 'pwg_compare_finishings()' - Compare two finishings values.
5008 */
5009
5010 static int /* O - Result of comparison */
5011 pwg_compare_finishings(
5012 _pwg_finishings_t *a, /* I - First finishings value */
5013 _pwg_finishings_t *b) /* I - Second finishings value */
5014 {
5015 return ((int)b->value - (int)a->value);
5016 }
5017
5018
5019 /*
5020 * 'pwg_compare_sizes()' - Compare two media sizes...
5021 */
5022
5023 static int /* O - Result of comparison */
5024 pwg_compare_sizes(cups_size_t *a, /* I - First media size */
5025 cups_size_t *b) /* I - Second media size */
5026 {
5027 return (strcmp(a->media, b->media));
5028 }
5029
5030
5031 /*
5032 * 'pwg_copy_size()' - Copy a media size.
5033 */
5034
5035 static cups_size_t * /* O - New media size */
5036 pwg_copy_size(cups_size_t *size) /* I - Media size to copy */
5037 {
5038 cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t));
5039 /* New media size */
5040
5041 if (newsize)
5042 memcpy(newsize, size, sizeof(cups_size_t));
5043
5044 return (newsize);
5045 }
5046
5047
5048 /*
5049 * 'pwg_free_finishings()' - Free a finishings value.
5050 */
5051
5052 static void
5053 pwg_free_finishings(
5054 _pwg_finishings_t *f) /* I - Finishings value */
5055 {
5056 cupsFreeOptions(f->num_options, f->options);
5057 free(f);
5058 }
5059
5060
5061 /*
5062 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
5063 */
5064
5065 static void
5066 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
5067 char *name, /* I - Name buffer */
5068 size_t namesize) /* I - Size of name buffer */
5069 {
5070 char *ptr, /* Pointer into name buffer */
5071 *end; /* End of name buffer */
5072
5073
5074 if (!ipp)
5075 {
5076 *name = '\0';
5077 return;
5078 }
5079
5080 *name = (char)toupper(*ipp++);
5081
5082 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
5083 {
5084 if (*ipp == '-' && _cups_isalnum(ipp[1]))
5085 {
5086 ipp ++;
5087 *ptr++ = (char)toupper(*ipp++ & 255);
5088 }
5089 else
5090 *ptr++ = *ipp++;
5091 }
5092
5093 *ptr = '\0';
5094 }
5095
5096
5097 /*
5098 * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values.
5099 */
5100
5101 static void
5102 pwg_ppdize_resolution(
5103 ipp_attribute_t *attr, /* I - Attribute to convert */
5104 int element, /* I - Element to convert */
5105 int *xres, /* O - X resolution in DPI */
5106 int *yres, /* O - Y resolution in DPI */
5107 char *name, /* I - Name buffer */
5108 size_t namesize) /* I - Size of name buffer */
5109 {
5110 ipp_res_t units; /* Units for resolution */
5111
5112
5113 *xres = ippGetResolution(attr, element, yres, &units);
5114
5115 if (units == IPP_RES_PER_CM)
5116 {
5117 *xres = (int)(*xres * 2.54);
5118 *yres = (int)(*yres * 2.54);
5119 }
5120
5121 if (name && namesize > 4)
5122 {
5123 if (*xres == *yres)
5124 snprintf(name, namesize, "%ddpi", *xres);
5125 else
5126 snprintf(name, namesize, "%dx%ddpi", *xres, *yres);
5127 }
5128 }
5129
5130
5131 /*
5132 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
5133 */
5134
5135 static void
5136 pwg_unppdize_name(const char *ppd, /* I - PPD keyword */
5137 char *name, /* I - Name buffer */
5138 size_t namesize, /* I - Size of name buffer */
5139 const char *dashchars)/* I - Characters to be replaced by dashes */
5140 {
5141 char *ptr, /* Pointer into name buffer */
5142 *end; /* End of name buffer */
5143
5144
5145 if (_cups_islower(*ppd))
5146 {
5147 /*
5148 * Already lowercase name, use as-is?
5149 */
5150
5151 const char *ppdptr; /* Pointer into PPD keyword */
5152
5153 for (ppdptr = ppd + 1; *ppdptr; ppdptr ++)
5154 if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr))
5155 break;
5156
5157 if (!*ppdptr)
5158 {
5159 strlcpy(name, ppd, namesize);
5160 return;
5161 }
5162 }
5163
5164 for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
5165 {
5166 if (_cups_isalnum(*ppd) || *ppd == '-')
5167 *ptr++ = (char)tolower(*ppd & 255);
5168 else if (strchr(dashchars, *ppd))
5169 *ptr++ = '-';
5170 else
5171 *ptr++ = *ppd;
5172
5173 if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
5174 _cups_isupper(ppd[1]) && ptr < end)
5175 *ptr++ = '-';
5176 else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
5177 *ptr++ = '-';
5178 }
5179
5180 *ptr = '\0';
5181 }