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