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