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