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