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