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