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