]> git.ipfire.org Git - thirdparty/cups.git/blame_incremental - cups/ppd-cache.c
Merge pull request #1330 from weblate/weblate-cups-cups
[thirdparty/cups.git] / cups / ppd-cache.c
... / ...
CommitLineData
1/*
2 * PPD cache implementation for CUPS.
3 *
4 * Copyright © 2022-2025 by OpenPrinting.
5 * Copyright © 2010-2021 by Apple Inc.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
9 */
10
11#include "cups-private.h"
12#include "ppd-private.h"
13#include "debug-internal.h"
14#include <math.h>
15#include <ctype.h>
16
17
18/*
19 * Macro to test for two almost-equal PWG measurements.
20 */
21
22#define _PWG_EQUIVALENT(x, y) (abs((x)-(y)) < 2)
23
24
25/*
26 * Local functions...
27 */
28
29static int cups_connect(http_t **http, const char *url, char *resource, size_t ressize);
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);
32static const char *ppd_get_string(cups_lang_t *base, cups_lang_t *printer, const char *msgid, char *buffer, size_t bufsize);
33static void ppd_get_strings(ppd_file_t *ppd, cups_lang_t *langs, const char *option, ppd_choice_t *choice, const char *pwg_msgid);
34static const char *ppd_inputslot_for_keyword(_ppd_cache_t *pc, const char *keyword);
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);
36static void pwg_add_finishing(cups_array_t *finishings, ipp_finishings_t template, const char *name, const char *value);
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);
41static void pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
42static void pwg_ppdize_resolution(ipp_attribute_t *attr, int element, int *xres, int *yres, char *name, size_t namesize);
43static void pwg_unppdize_name(const char *ppd, char *name, size_t namesize,
44 const char *dashchars);
45
46
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
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 */
68{
69 int i; /* Looping var */
70 const char *keyword, /* PWG keyword */
71 *password; /* Password string */
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 */
79 *mandatory, /* Mandatory attributes */
80 *finishing_template; /* Finishing template */
81 int num_finishings = 0, /* Number of finishing values */
82 finishings[10]; /* Finishing enum values */
83 ppd_choice_t *choice; /* Marked choice */
84 int finishings_copies = copies,
85 /* Number of copies for finishings */
86 job_pages = 0, /* job-pages value */
87 number_up = 1; /* number-up value */
88 const char *value; /* Option value */
89
90
91 /*
92 * Send standard IPP attributes...
93 */
94
95 if (pc->password && (password = cupsGetOption("job-password", num_options, options)) != NULL && ippGetOperation(request) != IPP_OP_VALIDATE_JOB)
96 {
97 ipp_attribute_t *attr = NULL; /* job-password attribute */
98
99 if ((keyword = cupsGetOption("job-password-encryption", num_options, options)) == NULL)
100 keyword = "none";
101
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);
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") &&
150 strcmp(mandatory, "finishings-col") &&
151 strcmp(mandatory, "finishing-template") &&
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
210 media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot", num_options, options));
211 media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType", num_options, options));
212 size = _ppdCacheGetSize(pc, keyword, /*ppd_size*/NULL);
213
214 if (media_col_sup && (size || media_source || media_type))
215 {
216 /*
217 * Add a media-col value...
218 */
219
220 media_col = ippNew();
221
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 }
232
233 for (i = 0; i < media_col_sup->num_values; i ++)
234 {
235 if (size && !strcmp(media_col_sup->values[i].string.text, "media-left-margin"))
236 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-left-margin", size->left);
237 else if (size && !strcmp(media_col_sup->values[i].string.text, "media-bottom-margin"))
238 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-bottom-margin", size->bottom);
239 else if (size && !strcmp(media_col_sup->values[i].string.text, "media-right-margin"))
240 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-right-margin", size->right);
241 else if (size && !strcmp(media_col_sup->values[i].string.text, "media-top-margin"))
242 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-top-margin", size->top);
243 else if (media_source && !strcmp(media_col_sup->values[i].string.text, "media-source"))
244 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-source", NULL, media_source);
245 else if (media_type && !strcmp(media_col_sup->values[i].string.text, "media-type"))
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
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)
270 {
271 /*
272 * No ColorModel in options...
273 */
274
275 if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL)
276 {
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";
283 else
284 keyword = "color";
285 }
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";
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 {
332 if (pc->sides_1sided && !_cups_strcasecmp(choice->choice, pc->sides_1sided))
333 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "one-sided");
334 else if (pc->sides_2sided_long && !_cups_strcasecmp(choice->choice, pc->sides_2sided_long))
335 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-long-edge");
336 else if (pc->sides_2sided_short && !_cups_strcasecmp(choice->choice, pc->sides_2sided_short))
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
405 if (copies != finishings_copies)
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)
412 {
413 if ((job_pages = atoi(value)) < 1)
414 job_pages = 1;
415 }
416
417 // Adjust for number-up
418 if ((value = cupsGetOption("number-up", num_options, options)) != NULL)
419 {
420 if ((number_up = atoi(value)) < 1)
421 number_up = 1;
422 }
423
424 job_pages = (job_pages + number_up - 1) / number_up;
425
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...
429 if ((job_pages & 1) && (keyword = cupsGetOption("sides", num_options, options)) != NULL && strcmp(keyword, "one-sided"))
430 job_pages ++;
431 }
432
433 if ((finishing_template = cupsGetOption("cupsFinishingTemplate", num_options, options)) == NULL)
434 finishing_template = cupsGetOption("finishing-template", num_options, options);
435
436 if (finishing_template && strcmp(finishing_template, "none"))
437 {
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);
443
444 if (copies != finishings_copies && job_pages > 0)
445 {
446 /*
447 * Send job-pages-per-set attribute to apply finishings correctly...
448 */
449
450 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", job_pages);
451 }
452 }
453 else
454 {
455 num_finishings = _ppdCacheGetFinishingValues(ppd, pc, (int)(sizeof(finishings) / sizeof(finishings[0])), finishings);
456 if (num_finishings > 0)
457 {
458 ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", num_finishings, finishings);
459
460 if (copies != finishings_copies && job_pages > 0)
461 {
462 /*
463 * Send job-pages-per-set attribute to apply finishings correctly...
464 */
465
466 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", job_pages);
467 }
468 }
469 }
470
471 return (copies);
472}
473
474
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 */
490 pwg_size_t *size; /* Current size */
491 pwg_map_t *map; /* Current map */
492 _pwg_finishings_t *finishings; /* Current finishings option */
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
509 DEBUG_printf("_ppdCacheCreateWithFile(filename=\"%s\")", filename);
510
511 /*
512 * Range check input...
513 */
514
515 if (attrs)
516 *attrs = NULL;
517
518 if (!filename)
519 {
520 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
521 return (NULL);
522 }
523
524 /*
525 * Open the file...
526 */
527
528 if ((fp = cupsFileOpen(filename, "r")) == NULL)
529 {
530 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
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 {
540 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
541 DEBUG_puts("_ppdCacheCreateWithFile: Unable to read first line.");
542 cupsFileClose(fp);
543 return (NULL);
544 }
545
546 if (strncmp(line, "#CUPS-PPD-CACHE-", 16))
547 {
548 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
549 DEBUG_printf("_ppdCacheCreateWithFile: Wrong first line \"%s\".", line);
550 cupsFileClose(fp);
551 return (NULL);
552 }
553
554 if (atoi(line + 16) != _PPD_CACHE_VERSION)
555 {
556 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of date PPD cache file."), 1);
557 DEBUG_printf("_ppdCacheCreateWithFile: Cache file has version %s, expected %d.", line + 16, _PPD_CACHE_VERSION);
558 cupsFileClose(fp);
559 return (NULL);
560 }
561
562 /*
563 * Allocate the mapping data structure...
564 */
565
566 if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
567 {
568 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
569 DEBUG_puts("_ppdCacheCreateWithFile: Unable to allocate _ppd_cache_t.");
570 goto create_error;
571 }
572
573 pc->max_copies = 9999;
574
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 {
587 DEBUG_printf("_ppdCacheCreateWithFile: line=\"%s\", value=\"%s\", linenum=%d", line, value, linenum);
588
589 if (!value)
590 {
591 DEBUG_printf("_ppdCacheCreateWithFile: Missing value on line %d.", linenum);
592 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
593 goto create_error;
594 }
595 else if (!_cups_strcasecmp(line, "Filter"))
596 {
597 if (!pc->filters)
598 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
599
600 cupsArrayAdd(pc->filters, value);
601 }
602 else if (!_cups_strcasecmp(line, "PreFilter"))
603 {
604 if (!pc->prefilters)
605 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
606
607 cupsArrayAdd(pc->prefilters, value);
608 }
609 else if (!_cups_strcasecmp(line, "Product"))
610 {
611 pc->product = strdup(value);
612 }
613 else if (!_cups_strcasecmp(line, "SingleFile"))
614 {
615 pc->single_file = !_cups_strcasecmp(value, "true");
616 }
617 else if (!_cups_strcasecmp(line, "IPP"))
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.");
626 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
627 goto create_error;
628 }
629 else if (length <= 0)
630 {
631 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP length.");
632 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
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,
645 *attrs) != IPP_STATE_DATA)
646 {
647 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
648 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
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.");
664 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
665 goto create_error;
666 }
667 }
668 else if (!_cups_strcasecmp(line, "NumBins"))
669 {
670 if (num_bins > 0)
671 {
672 DEBUG_puts("_ppdCacheCreateWithFile: NumBins listed multiple times.");
673 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
674 goto create_error;
675 }
676
677 if ((num_bins = atoi(value)) <= 0 || num_bins > 65536)
678 {
679 DEBUG_printf("_ppdCacheCreateWithFile: Bad NumBins value %d on line %d.", num_sizes, linenum);
680 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
681 goto create_error;
682 }
683
684 if ((pc->bins = calloc((size_t)num_bins, sizeof(pwg_map_t))) == NULL)
685 {
686 DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d bins.", num_sizes);
687 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
688 goto create_error;
689 }
690 }
691 else if (!_cups_strcasecmp(line, "Bin"))
692 {
693 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
694 {
695 DEBUG_printf("_ppdCacheCreateWithFile: Bad Bin on line %d.", linenum);
696 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
697 goto create_error;
698 }
699
700 if (pc->num_bins >= num_bins)
701 {
702 DEBUG_printf("_ppdCacheCreateWithFile: Too many Bin's on line %d.", linenum);
703 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
704 goto create_error;
705 }
706
707 map = pc->bins + pc->num_bins;
708 map->pwg = strdup(pwg_keyword);
709 map->ppd = strdup(ppd_keyword);
710
711 pc->num_bins ++;
712 }
713 else if (!_cups_strcasecmp(line, "NumSizes"))
714 {
715 if (num_sizes > 0)
716 {
717 DEBUG_puts("_ppdCacheCreateWithFile: NumSizes listed multiple times.");
718 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
719 goto create_error;
720 }
721
722 if ((num_sizes = atoi(value)) < 0 || num_sizes > 65536)
723 {
724 DEBUG_printf("_ppdCacheCreateWithFile: Bad NumSizes value %d on line %d.", num_sizes, linenum);
725 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
726 goto create_error;
727 }
728
729 if (num_sizes > 0)
730 {
731 if ((pc->sizes = calloc((size_t)num_sizes, sizeof(pwg_size_t))) == NULL)
732 {
733 DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d sizes.", num_sizes);
734 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
735 goto create_error;
736 }
737 }
738 }
739 else if (!_cups_strcasecmp(line, "Size"))
740 {
741 if (pc->num_sizes >= num_sizes)
742 {
743 DEBUG_printf("_ppdCacheCreateWithFile: Too many Size's on line %d.", linenum);
744 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
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 {
754 DEBUG_printf("_ppdCacheCreateWithFile: Bad Size on line %d.", linenum);
755 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
756 goto create_error;
757 }
758
759 size->map.pwg = strdup(pwg_keyword);
760 size->map.ppd = strdup(ppd_keyword);
761
762 pc->num_sizes ++;
763 }
764 else if (!_cups_strcasecmp(line, "CustomSize"))
765 {
766 if (pc->custom_max_width > 0)
767 {
768 DEBUG_printf("_ppdCacheCreateWithFile: Too many CustomSize's on line %d.", linenum);
769 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
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 {
779 DEBUG_printf("_ppdCacheCreateWithFile: Bad CustomSize on line %d.", linenum);
780 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
781 goto create_error;
782 }
783
784 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
785 pc->custom_max_width, pc->custom_max_length, NULL);
786 pc->custom_max_keyword = strdup(pwg_keyword);
787
788 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
789 pc->custom_min_width, pc->custom_min_length, NULL);
790 pc->custom_min_keyword = strdup(pwg_keyword);
791 }
792 else if (!_cups_strcasecmp(line, "SourceOption"))
793 {
794 pc->source_option = strdup(value);
795 }
796 else if (!_cups_strcasecmp(line, "NumSources"))
797 {
798 if (num_sources > 0)
799 {
800 DEBUG_puts("_ppdCacheCreateWithFile: NumSources listed multiple "
801 "times.");
802 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
803 goto create_error;
804 }
805
806 if ((num_sources = atoi(value)) <= 0 || num_sources > 65536)
807 {
808 DEBUG_printf("_ppdCacheCreateWithFile: Bad NumSources value %d on line %d.", num_sources, linenum);
809 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
810 goto create_error;
811 }
812
813 if ((pc->sources = calloc((size_t)num_sources, sizeof(pwg_map_t))) == NULL)
814 {
815 DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d sources.", num_sources);
816 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
817 goto create_error;
818 }
819 }
820 else if (!_cups_strcasecmp(line, "Source"))
821 {
822 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
823 {
824 DEBUG_printf("_ppdCacheCreateWithFile: Bad Source on line %d.", linenum);
825 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
826 goto create_error;
827 }
828
829 if (pc->num_sources >= num_sources)
830 {
831 DEBUG_printf("_ppdCacheCreateWithFile: Too many Source's on line %d.", linenum);
832 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
833 goto create_error;
834 }
835
836 map = pc->sources + pc->num_sources;
837 map->pwg = strdup(pwg_keyword);
838 map->ppd = strdup(ppd_keyword);
839
840 pc->num_sources ++;
841 }
842 else if (!_cups_strcasecmp(line, "NumTypes"))
843 {
844 if (num_types > 0)
845 {
846 DEBUG_puts("_ppdCacheCreateWithFile: NumTypes listed multiple times.");
847 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
848 goto create_error;
849 }
850
851 if ((num_types = atoi(value)) <= 0 || num_types > 65536)
852 {
853 DEBUG_printf("_ppdCacheCreateWithFile: Bad NumTypes value %d on line %d.", num_types, linenum);
854 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
855 goto create_error;
856 }
857
858 if ((pc->types = calloc((size_t)num_types, sizeof(pwg_map_t))) == NULL)
859 {
860 DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d types.", num_types);
861 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
862 goto create_error;
863 }
864 }
865 else if (!_cups_strcasecmp(line, "Type"))
866 {
867 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
868 {
869 DEBUG_printf("_ppdCacheCreateWithFile: Bad Type on line %d.", linenum);
870 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
871 goto create_error;
872 }
873
874 if (pc->num_types >= num_types)
875 {
876 DEBUG_printf("_ppdCacheCreateWithFile: Too many Type's on line %d.", linenum);
877 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
878 goto create_error;
879 }
880
881 map = pc->types + pc->num_types;
882 map->pwg = strdup(pwg_keyword);
883 map->ppd = strdup(ppd_keyword);
884
885 pc->num_types ++;
886 }
887 else if (!_cups_strcasecmp(line, "Preset"))
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 {
902 DEBUG_printf("_ppdCacheCreateWithFile: Bad Preset on line %d.", linenum);
903 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
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 }
911 else if (!_cups_strcasecmp(line, "SidesOption"))
912 pc->sides_option = strdup(value);
913 else if (!_cups_strcasecmp(line, "Sides1Sided"))
914 pc->sides_1sided = strdup(value);
915 else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
916 pc->sides_2sided_long = strdup(value);
917 else if (!_cups_strcasecmp(line, "Sides2SidedShort"))
918 pc->sides_2sided_short = strdup(value);
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
930 finishings->value = (ipp_finishings_t)strtol(value, &valueptr, 10);
931 finishings->num_options = cupsParseOptions(valueptr, 0,
932 &(finishings->options));
933
934 cupsArrayAdd(pc->finishings, finishings);
935 }
936 else if (!_cups_strcasecmp(line, "FinishingTemplate"))
937 {
938 if (!pc->templates)
939 pc->templates = cupsArrayNew3((cups_array_func_t)_cupsArrayStrcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
940
941 cupsArrayAdd(pc->templates, value);
942 }
943 else if (!_cups_strcasecmp(line, "MaxCopies"))
944 pc->max_copies = atoi(value);
945 else if (!_cups_strcasecmp(line, "ChargeInfoURI"))
946 pc->charge_info_uri = strdup(value);
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"))
952 pc->password = strdup(value);
953 else if (!_cups_strcasecmp(line, "Mandatory"))
954 {
955 if (pc->mandatory)
956 cupsArrayAddStrings(pc->mandatory, value, ' ');
957 else
958 pc->mandatory = cupsArrayNewStrings(value, ' ');
959 }
960 else if (!_cups_strcasecmp(line, "SupportFile"))
961 {
962 if (!pc->support_files)
963 pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
964
965 cupsArrayAdd(pc->support_files, value);
966 }
967 else
968 {
969 DEBUG_printf("_ppdCacheCreateWithFile: Unknown %s on line %d.", line, linenum);
970 }
971 }
972
973 if (pc->num_sizes < num_sizes)
974 {
975 DEBUG_printf("_ppdCacheCreateWithFile: Not enough sizes (%d < %d).", pc->num_sizes, num_sizes);
976 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
977 goto create_error;
978 }
979
980 if (pc->num_sources < num_sources)
981 {
982 DEBUG_printf("_ppdCacheCreateWithFile: Not enough sources (%d < %d).", pc->num_sources, num_sources);
983 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
984 goto create_error;
985 }
986
987 if (pc->num_types < num_types)
988 {
989 DEBUG_printf("_ppdCacheCreateWithFile: Not enough types (%d < %d).", pc->num_types, num_types);
990 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
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)
1008 {
1009 ippDelete(*attrs);
1010 *attrs = NULL;
1011 }
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 */
1022_ppdCacheCreateWithPPD(
1023 cups_lang_t *langs, /* I - Languages to load */
1024 ppd_file_t *ppd) /* I - PPD file */
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 */
1032 *duplex, /* Duplex option */
1033 *ppd_option; /* Other PPD option */
1034 ppd_choice_t *choice; /* Current InputSlot/MediaType */
1035 pwg_map_t *map; /* Current source/type map */
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 */
1040 pwg_size_t *pwg_size; /* Current PWG size */
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 */
1046 pwg_media_t *pwg_media, /* PWG media data */
1047 pwg_mediatemp; /* PWG media data buffer */
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? */
1053 pwg_size_t *old_size; /* Current old size */
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 */
1066 pwg_size_t *new_size; /* New size to add, if any */
1067 const char *filter; /* Current filter */
1068 _pwg_finishings_t *finishings; /* Current finishings value */
1069 char msg_id[256]; /* Message identifier */
1070 cups_lang_t *lang; /* Current language */
1071 _cups_message_t msg; /* Message */
1072
1073
1074 DEBUG_printf("_ppdCacheCreateWithPPD(ppd=%p)", (void *)ppd);
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
1097 if (ppd->num_sizes > 0)
1098 {
1099 if ((pc->sizes = calloc((size_t)ppd->num_sizes, sizeof(pwg_size_t))) == NULL)
1100 {
1101 DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_size_t's.", ppd->num_sizes);
1102 goto create_error;
1103 }
1104
1105 for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes;
1106 i > 0;
1107 i --, ppd_size ++)
1108 {
1109 /*
1110 * Don't copy over custom size...
1111 */
1112
1113 if (!_cups_strcasecmp(ppd_size->name, "Custom"))
1114 continue;
1115
1116 /*
1117 * Convert the PPD size name to the corresponding PWG keyword name.
1118 */
1119
1120 if ((pwg_media = pwgMediaForSize(PWG_FROM_POINTS(ppd_size->width), PWG_FROM_POINTS(ppd_size->length))) != NULL)
1121 {
1122 /*
1123 * Standard name, do we have conflicts?
1124 */
1125
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 }
1133
1134 if (pwg_media)
1135 {
1136 /*
1137 * Standard name and no conflicts, use it!
1138 */
1139
1140 pwg_name = pwg_media->pwg;
1141 new_known_pwg = 1;
1142 }
1143 else
1144 {
1145 /*
1146 * Not a standard name; convert it to a PWG vendor name of the form:
1147 *
1148 * pp_lowerppd_WIDTHxHEIGHTuu
1149 */
1150
1151 pwg_name = pwg_keyword;
1152 new_known_pwg = 0;
1153
1154 pwg_unppdize_name(ppd_size->name, ppd_name, sizeof(ppd_name), "_.");
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);
1158 }
1159
1160 /*
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...
1164 */
1165
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)
1167 continue;
1168
1169 new_width = pwg_media->width;
1170 new_length = pwg_media->length;
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);
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;
1203 free(old_size->map.ppd);
1204 free(old_size->map.pwg);
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 */
1224
1225 new_size->map.ppd = strdup(ppd_size->name);
1226 new_size->map.pwg = strdup(pwg_name);
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 }
1234 }
1235 }
1236
1237 if (ppd->variable_sizes)
1238 {
1239 /*
1240 * Generate custom size data...
1241 */
1242
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);
1246 pc->custom_max_keyword = strdup(pwg_keyword);
1247 pc->custom_max_width = PWG_FROM_POINTS(ppd->custom_max[0]);
1248 pc->custom_max_length = PWG_FROM_POINTS(ppd->custom_max[1]);
1249
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);
1253 pc->custom_min_keyword = strdup(pwg_keyword);
1254 pc->custom_min_width = PWG_FROM_POINTS(ppd->custom_min[0]);
1255 pc->custom_min_length = PWG_FROM_POINTS(ppd->custom_min[1]);
1256
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]);
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 {
1272 pc->source_option = strdup(input_slot->keyword);
1273
1274 if ((pc->sources = calloc((size_t)input_slot->num_choices, sizeof(pwg_map_t))) == NULL)
1275 {
1276 DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_map_t's for InputSlot.", input_slot->num_choices);
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 {
1287 if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
1288 !_cups_strncasecmp(choice->text, "Auto", 4) ||
1289 !_cups_strcasecmp(choice->choice, "Default") ||
1290 !_cups_strcasecmp(choice->text, "Default"))
1291 pwg_name = "auto";
1292 else if (!_cups_strcasecmp(choice->choice, "Cassette"))
1293 pwg_name = "main";
1294 else if (!_cups_strcasecmp(choice->choice, "PhotoTray"))
1295 pwg_name = "photo";
1296 else if (!_cups_strcasecmp(choice->choice, "CDTray"))
1297 pwg_name = "disc";
1298 else if (!_cups_strncasecmp(choice->choice, "Multipurpose", 12) ||
1299 !_cups_strcasecmp(choice->choice, "MP") ||
1300 !_cups_strcasecmp(choice->choice, "MPTray"))
1301 pwg_name = "by-pass-tray";
1302 else if (!_cups_strcasecmp(choice->choice, "LargeCapacity"))
1303 pwg_name = "large-capacity";
1304 else if (!_cups_strncasecmp(choice->choice, "Lower", 5))
1305 pwg_name = "bottom";
1306 else if (!_cups_strncasecmp(choice->choice, "Middle", 6))
1307 pwg_name = "middle";
1308 else if (!_cups_strncasecmp(choice->choice, "Upper", 5))
1309 pwg_name = "top";
1310 else if (!_cups_strncasecmp(choice->choice, "Side", 4))
1311 pwg_name = "side";
1312 else if (!_cups_strcasecmp(choice->choice, "Roll"))
1313 pwg_name = "main-roll";
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";
1334 else
1335 {
1336 /*
1337 * Convert PPD name to lowercase...
1338 */
1339
1340 pwg_name = pwg_keyword;
1341 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1342 "_");
1343 }
1344
1345 map->pwg = strdup(pwg_name);
1346 map->ppd = strdup(choice->choice);
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);
1353 ppd_get_strings(ppd, langs, "InputSlot", choice, msg_id);
1354 }
1355 }
1356
1357 /*
1358 * Copy and convert MediaType data...
1359 */
1360
1361 if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
1362 {
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 };
1385 const int num_standard_types = (int)(sizeof(standard_types) / sizeof(standard_types[0]));
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
1390 if ((pc->types = calloc((size_t)media_type->num_choices, sizeof(pwg_map_t))) == NULL)
1391 {
1392 DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_map_t's for MediaType.", media_type->num_choices);
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 {
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 }
1415 else if (!_cups_strncasecmp(choice->choice, standard_types[j].ppd_name, (size_t)standard_types[j].match_length))
1416 {
1417 pwg_name = standard_types[j].pwg_name;
1418 match_counts[j] ++;
1419 }
1420 }
1421
1422 if (!pwg_name)
1423 {
1424 /*
1425 * Convert PPD name to lowercase...
1426 */
1427
1428 pwg_name = pwg_keyword;
1429 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1430 "_");
1431 }
1432
1433 map->pwg = strdup(pwg_name);
1434 map->ppd = strdup(choice->choice);
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 }
1463
1464 /*
1465 * Add localized text for PWG keyword to message catalog...
1466 */
1467
1468 snprintf(msg_id, sizeof(msg_id), "media-type.%s", map->pwg);
1469 ppd_get_strings(ppd, langs, "MediaType", choice, msg_id);
1470 }
1471 }
1472
1473 /*
1474 * Copy and convert OutputBin data...
1475 */
1476
1477 if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
1478 {
1479 if ((pc->bins = calloc((size_t)output_bin->num_choices, sizeof(pwg_map_t))) == NULL)
1480 {
1481 DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_map_t's for OutputBin.", output_bin->num_choices);
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 {
1492 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
1493
1494 map->pwg = strdup(pwg_keyword);
1495 map->ppd = strdup(choice->choice);
1496
1497 /*
1498 * Add localized text for PWG keyword to message catalog...
1499 */
1500
1501 snprintf(msg_id, sizeof(msg_id), "output-bin.%s", pwg_keyword);
1502 ppd_get_strings(ppd, langs, "OutputBin", choice, msg_id);
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 {
1520#if 0
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);
1527#endif // 0
1528
1529 /*
1530 * Get the options for this preset...
1531 */
1532
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 {
1593 if (!_cups_strcasecmp(color_model_val, "Gray"))
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];
1681 options = calloc((size_t)num_options, sizeof(cups_option_t));
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 {
1730 pc->sides_option = strdup(duplex->keyword);
1731
1732 for (i = duplex->num_choices, choice = duplex->choices;
1733 i > 0;
1734 i --, choice ++)
1735 {
1736 if ((!_cups_strcasecmp(choice->choice, "None") ||
1737 !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
1738 pc->sides_1sided = strdup(choice->choice);
1739 else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
1740 !_cups_strcasecmp(choice->choice, "LongEdge") ||
1741 !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
1742 pc->sides_2sided_long = strdup(choice->choice);
1743 else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
1744 !_cups_strcasecmp(choice->choice, "ShortEdge") ||
1745 !_cups_strcasecmp(choice->choice, "Bottom")) &&
1746 !pc->sides_2sided_short)
1747 pc->sides_2sided_short = strdup(choice->choice);
1748 }
1749 }
1750
1751 /*
1752 * Copy filters and pre-filters...
1753 */
1754
1755 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
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))
1783 if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
1784 _cups_isspace(filter[28]))
1785 break;
1786
1787 if (!filter &&
1788 ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL ||
1789 _cups_strcasecmp(ppd_attr->value, "none")))
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))
1800 if (!_cups_strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
1801 _cups_isspace(filter[31]))
1802 break;
1803
1804 if (filter)
1805 cupsArrayAdd(pc->filters,
1806 "application/vnd.cups-command application/postscript 100 "
1807 "commandtops");
1808 }
1809
1810 if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
1811 {
1812 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
1813
1814 do
1815 {
1816 cupsArrayAdd(pc->prefilters, ppd_attr->value);
1817 }
1818 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) != NULL);
1819 }
1820
1821 if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL)
1822 pc->single_file = !_cups_strcasecmp(ppd_attr->value, "true");
1823
1824 /*
1825 * Copy the product string, if any...
1826 */
1827
1828 if (ppd->product)
1829 pc->product = strdup(ppd->product);
1830
1831 /*
1832 * Copy finishings mapping data...
1833 */
1834
1835 if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL)
1836 {
1837 /*
1838 * Have proper vendor mapping of IPP finishings values to PPD options...
1839 */
1840
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
1850 finishings->value = (ipp_finishings_t)atoi(ppd_attr->spec);
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 }
1860 else
1861 {
1862 /*
1863 * No IPP mapping data, try to map common/standard PPD keywords...
1864 */
1865
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 }
1966
1967 if ((ppd_option = ppdFindOption(ppd, "cupsFinishingTemplate")) != NULL)
1968 {
1969 pc->templates = cupsArrayNew3((cups_array_func_t)_cupsArrayStrcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
1970
1971 for (choice = ppd_option->choices, i = ppd_option->num_choices; i > 0; choice ++, i --)
1972 {
1973 cupsArrayAdd(pc->templates, (void *)choice->choice);
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);
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 }
2006 }
2007 }
2008
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
2020 /*
2021 * cupsChargeInfoURI, cupsJobAccountId, cupsJobAccountingUserId,
2022 * cupsJobPassword, and cupsMandatory.
2023 */
2024
2025 if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL)
2026 pc->charge_info_uri = strdup(ppd_attr->value);
2027
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)
2035 pc->password = strdup(ppd_attr->value);
2036
2037 if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
2038 pc->mandatory = cupsArrayNewStrings(ppd_attr->value, ' ');
2039
2040 /*
2041 * Support files...
2042 */
2043
2044 pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
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
2051#ifdef HAVE_APPLICATIONSERVICES_H
2052 if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
2053 cupsArrayAdd(pc->support_files, ppd_attr->value);
2054#endif
2055
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
2068 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of memory."), 1);
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 */
2083 pwg_map_t *map; /* Current map */
2084 pwg_size_t *size; /* Current size */
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 {
2102 free(map->pwg);
2103 free(map->ppd);
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 {
2113 free(size->map.pwg);
2114 free(size->map.ppd);
2115 }
2116
2117 free(pc->sizes);
2118 }
2119
2120 free(pc->source_option);
2121
2122 if (pc->sources)
2123 {
2124 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
2125 {
2126 free(map->pwg);
2127 free(map->ppd);
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 {
2137 free(map->pwg);
2138 free(map->ppd);
2139 }
2140
2141 free(pc->types);
2142 }
2143
2144 free(pc->custom_max_keyword);
2145 free(pc->custom_min_keyword);
2146
2147 free(pc->product);
2148 cupsArrayDelete(pc->filters);
2149 cupsArrayDelete(pc->prefilters);
2150 cupsArrayDelete(pc->finishings);
2151
2152 free(pc->charge_info_uri);
2153 free(pc->password);
2154
2155 cupsArrayDelete(pc->mandatory);
2156
2157 cupsArrayDelete(pc->support_files);
2158
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 ++)
2189 if (!_cups_strcasecmp(output_bin, pc->bins[i].ppd) || !_cups_strcasecmp(output_bin, pc->bins[i].pwg))
2190 return (pc->bins[i].pwg);
2191
2192 return (NULL);
2193}
2194
2195
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(
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 */
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 {
2234 key.value = (ipp_finishings_t)ippGetInteger(attr, i);
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(
2271 ppd_file_t *ppd, /* I - Marked PPD file */
2272 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
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 */
2280 ppd_choice_t *choice; /* Marked PPD choice */
2281
2282
2283 /*
2284 * Range check input...
2285 */
2286
2287 DEBUG_printf("_ppdCacheGetFinishingValues(ppd=%p, pc=%p, max_values=%d, values=%p)", (void *)ppd, (void *)pc, max_values, (void *)values);
2288
2289 if (!ppd || !pc || max_values < 1 || !values)
2290 {
2291 DEBUG_puts("_ppdCacheGetFinishingValues: Bad arguments, returning 0.");
2292 return (0);
2293 }
2294 else if (!pc->finishings)
2295 {
2296 DEBUG_puts("_ppdCacheGetFinishingValues: No finishings support, returning 0.");
2297 return (0);
2298 }
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 {
2308 DEBUG_printf("_ppdCacheGetFinishingValues: Checking %d (%s)", (int)f->value, ippEnumString("finishings", (int)f->value));
2309
2310 for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2311 {
2312 DEBUG_printf("_ppdCacheGetFinishingValues: %s=%s?", option->name, option->value);
2313
2314 if ((choice = ppdFindMarkedChoice(ppd, option->name)) == NULL || _cups_strcasecmp(option->value, choice->choice))
2315 {
2316 DEBUG_puts("_ppdCacheGetFinishingValues: NO");
2317 break;
2318 }
2319 }
2320
2321 if (i == 0)
2322 {
2323 DEBUG_printf("_ppdCacheGetFinishingValues: Adding %d (%s)", (int)f->value, ippEnumString("finishings", (int)f->value));
2324
2325 values[num_values ++] = (int)f->value;
2326
2327 if (num_values >= max_values)
2328 break;
2329 }
2330 }
2331
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
2343 DEBUG_printf("_ppdCacheGetFinishingValues: Returning %d.", num_values);
2344
2345 return (num_values);
2346}
2347
2348
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 ++)
2365 if (!_cups_strcasecmp(keyword, pc->sources[i].pwg) || !_cups_strcasecmp(keyword, pc->sources[i].ppd))
2366 return (pc->sources[i].ppd);
2367
2368 return (NULL);
2369}
2370
2371
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 */
2398 pwg_size_t size; /* Dimensional size */
2399 int margins_set; /* Were the margins set? */
2400
2401 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2402 if (media_col &&
2403 (media_source = ippFindAttribute(ippGetCollection(media_col, 0),
2404 "media-source",
2405 IPP_TAG_KEYWORD)) != NULL)
2406 {
2407 /*
2408 * Use the media-source value from media-col...
2409 */
2410
2411 keyword = ippGetString(media_source, 0, NULL);
2412 }
2413 else if (pwgInitSize(&size, job, &margins_set))
2414 {
2415 /*
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.
2419 */
2420
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);
2425 keyword = "photo";
2426 }
2427 }
2428 }
2429
2430 return (ppd_inputslot_for_keyword(pc, keyword));
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 ++)
2480 if (!_cups_strcasecmp(keyword, pc->types[i].pwg) || !_cups_strcasecmp(keyword, pc->types[i].ppd))
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 ++)
2514 if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg) || !_cups_strcasecmp(output_bin, pc->bins[i].ppd))
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 */
2534 pwg_size_t *size, /* Current size */
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
2549 DEBUG_printf("_ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)", (void *)pc, (void *)job, keyword, (void *)exact);
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)
2577 DEBUG_printf("1_ppdCacheGetPageSize: Found attribute %s (%s)", attr->name, ippTagString(attr->value_tag));
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
2587 DEBUG_printf("1_ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name);
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 {
2597 DEBUG_printf("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]", (int)(size - pc->sizes), size->map.pwg, size->map.ppd);
2598
2599 if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
2600 !_cups_strcasecmp(ppd_name, size->map.pwg))
2601 {
2602 if (exact)
2603 *exact = 1;
2604
2605 DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\"", ppd_name);
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
2619 if (!pwgInitSize(&jobsize, job, &margins_set))
2620 return (NULL);
2621 }
2622 else
2623 {
2624 /*
2625 * Get the size using a media keyword...
2626 */
2627
2628 pwg_media_t *media; /* Media definition */
2629
2630
2631 if ((media = pwgMediaForPWG(keyword)) == NULL)
2632 if ((media = pwgMediaForLegacy(keyword)) == NULL)
2633 if ((media = pwgMediaForPPD(keyword)) == NULL)
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
2649 if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
2650 _cups_strncasecmp(ppd_name, "custom_", 7))
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
2698 DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd);
2699
2700 return (size->map.ppd);
2701 }
2702 }
2703
2704 if (closest)
2705 {
2706 DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\" (closest)", closest->map.ppd);
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",
2725 (int)PWG_TO_POINTS(jobsize.width), (int)PWG_TO_POINTS(jobsize.length));
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
2741 DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\" (custom)", pc->custom_ppd_size);
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
2760pwg_size_t * /* O - PWG size or NULL */
2761_ppdCacheGetSize(
2762 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2763 const char *page_size, /* I - PPD PageSize */
2764 ppd_size_t *ppd_size) /* I - PPD page size information */
2765{
2766 int i; /* Looping var */
2767 pwg_media_t *media; /* Media */
2768 pwg_size_t *size; /* Current size */
2769
2770
2771 /*
2772 * Range check input...
2773 */
2774
2775 if (!pc || !page_size)
2776 return (NULL);
2777
2778 if (!_cups_strcasecmp(page_size, "Custom") || !_cups_strncasecmp(page_size, "Custom.", 7))
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
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);
2801
2802 l = (float)_cupsStrScand(ptr + 1, &ptr, loc);
2803 if (!ptr)
2804 return (NULL);
2805
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 }
2836 }
2837 else if (ppd_size)
2838 {
2839 w = ppd_size->width * 2540.0 / 72.0;
2840 l = ppd_size->length * 2540.0 / 72.0;
2841 }
2842 else
2843 {
2844 // No custom size information...
2845 return (NULL);
2846 }
2847
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;
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 ++)
2863 {
2864 if (!_cups_strcasecmp(page_size, size->map.ppd) ||
2865 !_cups_strcasecmp(page_size, size->map.pwg))
2866 return (size);
2867 }
2868
2869 /*
2870 * Look up standard sizes...
2871 */
2872
2873 if ((media = pwgMediaForPPD(page_size)) == NULL)
2874 {
2875 if ((media = pwgMediaForLegacy(page_size)) == NULL)
2876 media = pwgMediaForPWG(page_size);
2877 }
2878
2879 if (media)
2880 {
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;
2885
2886 return (&(pc->custom_size));
2887 }
2888
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 */
2904 pwg_map_t *source; /* Current source */
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 ++)
2915 if (!_cups_strcasecmp(input_slot, source->ppd) || !_cups_strcasecmp(input_slot, source->pwg))
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 */
2933 pwg_map_t *type; /* Current type */
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 ++)
2944 if (!_cups_strcasecmp(media_type, type->ppd) || !_cups_strcasecmp(media_type, type->pwg))
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{
2961 int i, j, k; /* Looping vars */
2962 cups_file_t *fp; /* Output file */
2963 pwg_size_t *size; /* Current size */
2964 pwg_map_t *map; /* Current map */
2965 _pwg_finishings_t *f; /* Current finishing option */
2966 cups_option_t *option; /* Current option */
2967 const char *value; /* String value */
2968 char newfile[1024]; /* New filename */
2969
2970
2971 /*
2972 * Range check input...
2973 */
2974
2975 if (!pc || !filename)
2976 {
2977 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
2978 return (0);
2979 }
2980
2981 /*
2982 * Open the file and write with compression...
2983 */
2984
2985 snprintf(newfile, sizeof(newfile), "%s.N", filename);
2986 if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
2987 {
2988 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
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 ++)
3055 {
3056 for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
3057 {
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 }
3067 }
3068 }
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
3103 cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
3104
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
3119 for (value = (const char *)cupsArrayFirst(pc->templates); value; value = (const char *)cupsArrayNext(pc->templates))
3120 cupsFilePutConf(fp, "FinishingTemplate", value);
3121
3122 /*
3123 * Max copies...
3124 */
3125
3126 cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies);
3127
3128 /*
3129 * Accounting/quota/PIN/managed printing values...
3130 */
3131
3132 if (pc->charge_info_uri)
3133 cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri);
3134
3135 cupsFilePrintf(fp, "JobAccountId %s\n", pc->account_id ? "true" : "false");
3136 cupsFilePrintf(fp, "JobAccountingUserId %s\n",
3137 pc->accounting_user_id ? "true" : "false");
3138
3139 if (pc->password)
3140 cupsFilePutConf(fp, "JobPassword", pc->password);
3141
3142 for (value = (char *)cupsArrayFirst(pc->mandatory);
3143 value;
3144 value = (char *)cupsArrayNext(pc->mandatory))
3145 cupsFilePutConf(fp, "Mandatory", value);
3146
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
3156 /*
3157 * IPP attributes, if any...
3158 */
3159
3160 if (attrs)
3161 {
3162 cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
3163
3164 attrs->state = IPP_STATE_IDLE;
3165 ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
3166 }
3167
3168 /*
3169 * Close and return...
3170 */
3171
3172 if (cupsFileClose(fp))
3173 {
3174 unlink(newfile);
3175 return (0);
3176 }
3177
3178 unlink(filename);
3179 return (!rename(newfile, filename));
3180}
3181
3182
3183/*
3184 * '_ppdCreateFromIPP()' - Create a PPD file describing the capabilities
3185 * of an IPP printer.
3186 */
3187
3188char * /* O - PPD filename or `NULL` on error */
3189_ppdCreateFromIPP(char *buffer, /* I - Filename buffer */
3190 size_t bufsize, /* I - Size of filename buffer */
3191 ipp_t *supported) /* I - Get-Printer-Attributes response */
3192{
3193 const char *printer_uri; /* Printer URI */
3194 http_t *http = NULL; /* HTTP connection */
3195 cups_lang_t *base_lang, /* Base English language */
3196 *lang, /* Current language information */
3197 *langs = NULL; /* Language (strings) files */
3198 const char *prefix; /* Prefix string */
3199 cups_file_t *fp; /* PPD file */
3200 cups_array_t *sizes; /* Media sizes supported by printer */
3201 cups_size_t *size; /* Current media size */
3202 ipp_attribute_t *attr, /* xxx-supported */
3203 *lang_supp, /* printer-strings-languages-supported */
3204 *defattr, /* xxx-default */
3205 *quality, /* print-quality-supported */
3206 *x_dim, *y_dim; /* Media dimensions */
3207 ipp_t *media_col, /* Media collection */
3208 *media_size; /* Media size collection */
3209 char make[256], /* Make and model */
3210 *mptr, /* Pointer into make and model */
3211 ppdname[PPD_MAX_NAME],
3212 /* PPD keyword */
3213 ppdtext[PPD_MAX_TEXT];
3214 /* PPD English text */
3215 const char *model; /* Model name */
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 */
3221 top, /* Largest top margin */
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,
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? */
3230 pwg_media_t *pwg; /* PWG media size */
3231 int xres, yres; /* Resolution values */
3232 int resolutions[1000];
3233 /* Array of resolution indices */
3234 int have_qdraft = 0,/* Have draft quality? */
3235 have_qhigh = 0; /* Have high quality? */
3236 char msgid[256]; /* PWG message identifier (attr.value) */
3237 const char *keyword; /* Keyword value */
3238 struct lconv *loc = localeconv();
3239 /* Locale data */
3240 cups_array_t *fin_options = NULL;
3241 /* Finishing options */
3242
3243
3244 /*
3245 * Range check input...
3246 */
3247
3248 if (buffer)
3249 *buffer = '\0';
3250
3251 if (!buffer || bufsize < 1)
3252 {
3253 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
3254 return (NULL);
3255 }
3256
3257 if (!supported)
3258 {
3259 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No IPP attributes."), 1);
3260 return (NULL);
3261 }
3262
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
3269 /*
3270 * Open a temporary file for the PPD...
3271 */
3272
3273 if ((fp = cupsCreateTempFile("ippeve", ".ppd", buffer, bufsize)) == NULL)
3274 {
3275 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
3276 return (NULL);
3277 }
3278
3279 /*
3280 * Get a sanitized make and model...
3281 */
3282
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 */
3288
3289 cupsCopyString(make, ippGetString(attr, 0, NULL), sizeof(make));
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';
3313 else
3314 break;
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 }
3326 else
3327 {
3328 /*
3329 * Use a default make and model...
3330 */
3331
3332 cupsCopyString(make, "Unknown", sizeof(make));
3333 }
3334
3335 if (!_cups_strncasecmp(make, "Hewlett Packard ", 16) || !_cups_strncasecmp(make, "Hewlett-Packard ", 16))
3336 {
3337 /*
3338 * Normalize HP printer make and model...
3339 */
3340
3341 model = make + 16;
3342 cupsCopyString(make, "HP", sizeof(make));
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;
3357 }
3358 else
3359 {
3360 /*
3361 * No separate model name...
3362 */
3363
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");
3380 cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make);
3381 cupsFilePrintf(fp, "*ModelName: \"%s\"\n", model);
3382 cupsFilePrintf(fp, "*Product: \"(%s)\"\n", model);
3383 cupsFilePrintf(fp, "*NickName: \"%s - IPP Everywhere\"\n", model);
3384 cupsFilePrintf(fp, "*ShortNickName: \"%s - IPP Everywhere\"\n", model);
3385
3386 if (ippGetBoolean(ippFindAttribute(supported, "color-supported", IPP_TAG_BOOLEAN), 0))
3387 cupsFilePuts(fp, "*ColorDevice: True\n");
3388 else
3389 cupsFilePuts(fp, "*ColorDevice: False\n");
3390
3391 cupsFilePrintf(fp, "*cupsVersion: %d.%d\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
3392#ifdef __APPLE__
3393 cupsFilePrintf(fp, "*APAirPrint: True\n");
3394#endif // __APPLE__
3395 cupsFilePuts(fp, "*cupsSNMPSupplies: False\n");
3396
3397 if ((lang_supp = ippFindAttribute(supported, "printer-strings-languages-supported", IPP_TAG_LANGUAGE)) != NULL)
3398 {
3399 /*
3400 * List supported languages...
3401 */
3402
3403 for (i = 0, count = ippGetCount(lang_supp), prefix = "*cupsLanguages: \""; i < count; i ++)
3404 {
3405 keyword = ippGetString(lang_supp, i, NULL);
3406
3407 if ((lang = cups_get_strings(&http, printer_uri, keyword)) != NULL)
3408 {
3409 /*
3410 * Add language...
3411 */
3412
3413 lang->next = langs;
3414 langs = lang;
3415
3416 cupsFilePrintf(fp, "%s%s", prefix, keyword);
3417 prefix = " ";
3418 }
3419 }
3420
3421 if (!strcmp(prefix, " "))
3422 cupsFilePuts(fp, "\"\n");
3423 }
3424
3425 if (http)
3426 {
3427 httpClose(http);
3428 http = NULL;
3429 }
3430
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
3440 if ((attr = ippFindAttribute(supported, "printer-more-info", IPP_TAG_URI)) != NULL && ippValidateAttribute(attr))
3441 cupsFilePrintf(fp, "*APSupplies: \"%s\"\n", ippGetString(attr, 0, NULL));
3442
3443 if ((attr = ippFindAttribute(supported, "printer-charge-info-uri", IPP_TAG_URI)) != NULL && ippValidateAttribute(attr))
3444 cupsFilePrintf(fp, "*cupsChargeInfoURI: \"%s\"\n", ippGetString(attr, 0, NULL));
3445
3446 /*
3447 * Accounting...
3448 */
3449
3450 if (ippGetBoolean(ippFindAttribute(supported, "job-account-id-supported", IPP_TAG_BOOLEAN), 0))
3451 cupsFilePuts(fp, "*cupsJobAccountId: True\n");
3452
3453 if (ippGetBoolean(ippFindAttribute(supported, "job-accounting-user-id-supported", IPP_TAG_BOOLEAN), 0))
3454 cupsFilePuts(fp, "*cupsJobAccountingUserId: True\n");
3455
3456 if ((attr = ippFindAttribute(supported, "printer-privacy-policy-uri", IPP_TAG_URI)) != NULL && ippValidateAttribute(attr))
3457 cupsFilePrintf(fp, "*cupsPrivacyURI: \"%s\"\n", ippGetString(attr, 0, NULL));
3458
3459 if ((attr = ippFindAttribute(supported, "printer-mandatory-job-attributes", IPP_TAG_KEYWORD)) != NULL && ippValidateAttribute(attr))
3460 {
3461 for (i = 0, count = ippGetCount(attr), prefix = "*cupsMandatory: \""; i < count; i ++)
3462 {
3463 keyword = ippGetString(attr, i, NULL);
3464
3465 if (strcmp(keyword, "attributes-charset") && strcmp(keyword, "attributes-natural-language") && strcmp(keyword, "printer-uri"))
3466 {
3467 cupsFilePrintf(fp, "%s%s", prefix, keyword);
3468 prefix = " ";
3469 }
3470 }
3471
3472 if (!strcmp(prefix, " "))
3473 cupsFilePuts(fp, "\"\n");
3474 }
3475
3476 if ((attr = ippFindAttribute(supported, "printer-requested-job-attributes", IPP_TAG_KEYWORD)) != NULL && ippValidateAttribute(attr))
3477 {
3478 for (i = 0, count = ippGetCount(attr), prefix = "*cupsRequested: \""; i < count; i ++)
3479 {
3480 keyword = ippGetString(attr, i, NULL);
3481
3482 if (strcmp(keyword, "attributes-charset") && strcmp(keyword, "attributes-natural-language") && strcmp(keyword, "printer-uri"))
3483 {
3484 cupsFilePrintf(fp, "%s%s", prefix, keyword);
3485 prefix = " ";
3486 }
3487 }
3488
3489 if (!strcmp(prefix, " "))
3490 cupsFilePuts(fp, "\"\n");
3491 }
3492
3493 /*
3494 * Password/PIN printing...
3495 */
3496
3497 if ((attr = ippFindAttribute(supported, "job-password-supported", IPP_TAG_INTEGER)) != NULL)
3498 {
3499 char pattern[33]; /* Password pattern */
3500 int maxlen = ippGetInteger(attr, 0);
3501 /* Maximum length */
3502 const char *repertoire = ippGetString(ippFindAttribute(supported, "job-password-repertoire-configured", IPP_TAG_KEYWORD), 0, NULL);
3503 /* Type of password */
3504
3505 if (maxlen > (int)(sizeof(pattern) - 1))
3506 maxlen = (int)sizeof(pattern) - 1;
3507
3508 if (!repertoire || !strcmp(repertoire, "iana_us-ascii_digits"))
3509 memset(pattern, '1', (size_t)maxlen);
3510 else if (!strcmp(repertoire, "iana_us-ascii_letters"))
3511 memset(pattern, 'A', (size_t)maxlen);
3512 else if (!strcmp(repertoire, "iana_us-ascii_complex"))
3513 memset(pattern, 'C', (size_t)maxlen);
3514 else if (!strcmp(repertoire, "iana_us-ascii_any"))
3515 memset(pattern, '.', (size_t)maxlen);
3516 else if (!strcmp(repertoire, "iana_utf-8_digits"))
3517 memset(pattern, 'N', (size_t)maxlen);
3518 else if (!strcmp(repertoire, "iana_utf-8_letters"))
3519 memset(pattern, 'U', (size_t)maxlen);
3520 else
3521 memset(pattern, '*', (size_t)maxlen);
3522
3523 pattern[maxlen] = '\0';
3524
3525 cupsFilePrintf(fp, "*cupsJobPassword: \"%s\"\n", pattern);
3526 }
3527
3528 /*
3529 * Filters...
3530 */
3531
3532 if ((attr = ippFindAttribute(supported, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL)
3533 {
3534 is_apple = ippContainsString(attr, "image/urf") && (ippFindAttribute(supported, "urf-supported", IPP_TAG_KEYWORD) != NULL);
3535 is_pdf = ippContainsString(attr, "application/pdf");
3536 is_pwg = ippContainsString(attr, "image/pwg-raster") && !is_apple &&
3537 (ippFindAttribute(supported, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION) != NULL) &&
3538 (ippFindAttribute(supported, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD) != NULL);
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)
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 }
3556 else
3557 cupsFilePuts(fp, "*cupsManualCopies: True\n");
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");
3562 }
3563
3564 if (!is_apple && !is_pdf && !is_pwg)
3565 goto bad_ppd;
3566
3567 /*
3568 * PageSize/PageRegion/ImageableArea/PaperDimension
3569 */
3570
3571 if ((attr = ippFindAttribute(supported, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != NULL)
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
3580 if ((attr = ippFindAttribute(supported, "media-left-margin-supported", IPP_TAG_INTEGER)) != NULL)
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
3589 if ((attr = ippFindAttribute(supported, "media-right-margin-supported", IPP_TAG_INTEGER)) != NULL)
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
3598 if ((attr = ippFindAttribute(supported, "media-top-margin-supported", IPP_TAG_INTEGER)) != NULL)
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
3607 if ((defattr = ippFindAttribute(supported, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != NULL)
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
3615 if (x_dim && y_dim && (pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL)
3616 cupsCopyString(ppdname, pwg->ppd, sizeof(ppdname));
3617 else
3618 cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
3619 }
3620 else
3621 cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
3622 }
3623 else if ((pwg = pwgMediaForPWG(ippGetString(ippFindAttribute(supported, "media-default", IPP_TAG_ZERO), 0, NULL))) != NULL)
3624 cupsCopyString(ppdname, pwg->ppd, sizeof(ppdname));
3625 else
3626 cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
3627
3628 sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes, NULL, NULL, 0, (cups_acopy_func_t)pwg_copy_size, _cupsArrayFree);
3629
3630 if ((attr = ippFindAttribute(supported, "media-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3631 {
3632 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3633 {
3634 cups_size_t temp; /* Current size */
3635 ipp_attribute_t *margin; /* media-xxx-margin attribute */
3636
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));
3642
3643 if (pwg)
3644 {
3645 temp.width = pwg->width;
3646 temp.length = pwg->length;
3647
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;
3652
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;
3662
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;
3667
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
3671 cupsCopyString(temp.media, pwg->ppd, sizeof(temp.media));
3672
3673 if (!cupsArrayFind(sizes, &temp))
3674 cupsArrayAdd(sizes, &temp);
3675 }
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 */
3681
3682 int lower, upper; /* Range values */
3683
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
3706 if ((max_width == 0 || max_length == 0) && (attr = ippFindAttribute(supported, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3707 {
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 ++)
3713 {
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 */
3723
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 }
3746 }
3747 }
3748 }
3749 else if ((attr = ippFindAttribute(supported, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
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));
3759
3760 if (pwg)
3761 {
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
3772 cupsCopyString(temp.media, pwg->ppd, sizeof(temp.media));
3773
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 */
3782
3783 int lower, upper; /* Range values */
3784
3785 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3786 lower = ippGetRange(x_dim, 0, &upper);
3787 else
3788 lower = upper = ippGetInteger(x_dim, 0);
3789
3790 if (lower < min_width)
3791 min_width = lower;
3792 if (upper > max_width)
3793 max_width = upper;
3794
3795 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3796 lower = ippGetRange(y_dim, 0, &upper);
3797 else
3798 lower = upper = ippGetInteger(y_dim, 0);
3799
3800 if (lower < min_length)
3801 min_length = lower;
3802 if (upper > max_length)
3803 max_length = upper;
3804 }
3805 }
3806 }
3807 else if ((attr = ippFindAttribute(supported, "media-supported", IPP_TAG_ZERO)) != NULL)
3808 {
3809 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3810 {
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)
3816 {
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
3843 cupsCopyString(temp.media, pwg->ppd, sizeof(temp.media));
3844
3845 if (!cupsArrayFind(sizes, &temp))
3846 cupsArrayAdd(sizes, &temp);
3847 }
3848 }
3849 }
3850 }
3851
3852 if (cupsArrayCount(sizes) > 0)
3853 {
3854 /*
3855 * List all of the standard sizes...
3856 */
3857
3858 char tleft[256], /* Left string */
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
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);
3872
3873 cupsFilePrintf(fp, "*PageSize %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", size->media, twidth, tlength);
3874 }
3875 cupsFilePuts(fp, "*CloseUI: *PageSize\n");
3876
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);
3884
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);
3903 }
3904
3905 cupsArrayDelete(sizes);
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 }
3935 }
3936 else
3937 {
3938 cupsArrayDelete(sizes);
3939 goto bad_ppd;
3940 }
3941
3942 /*
3943 * InputSlot...
3944 */
3945
3946 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source", IPP_TAG_ZERO)) != NULL)
3947 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3948 else
3949 ppdname[0] = '\0';
3950
3951 if ((attr = ippFindAttribute(supported, "media-source-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3952 {
3953 int have_default = ppdname[0] != '\0';
3954 /* Do we have a default InputSlot? */
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"
4007 };
4008
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
4014 for (i = 0; i < count; i ++)
4015 {
4016 keyword = ippGetString(attr, i, NULL);
4017
4018 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4019
4020 if (i == 0 && !have_default)
4021 cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
4022
4023 for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++)
4024 {
4025 if (!strcmp(sources[j], keyword))
4026 {
4027 snprintf(msgid, sizeof(msgid), "media-source.%s", keyword);
4028
4029 cupsFilePrintf(fp, "*InputSlot %s/%s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)), j);
4030 ppd_put_strings(fp, langs, "InputSlot", ppdname, msgid);
4031 break;
4032 }
4033 }
4034 }
4035 cupsFilePuts(fp, "*CloseUI: *InputSlot\n");
4036 }
4037
4038 /*
4039 * MediaType...
4040 */
4041
4042 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type", IPP_TAG_ZERO)) != NULL)
4043 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
4044 else
4045 cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
4046
4047 if ((attr = ippFindAttribute(supported, "media-type-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
4048 {
4049 cupsFilePrintf(fp, "*OpenUI *MediaType: PickOne\n"
4050 "*OrderDependency: 10 AnySetup *MediaType\n"
4051 "*DefaultMediaType: %s\n", ppdname);
4052 for (i = 0; i < count; i ++)
4053 {
4054 keyword = ippGetString(attr, i, NULL);
4055
4056 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4057
4058 snprintf(msgid, sizeof(msgid), "media-type.%s", keyword);
4059
4060 cupsFilePrintf(fp, "*MediaType %s/%s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)), keyword);
4061 ppd_put_strings(fp, langs, "MediaType", ppdname, msgid);
4062 }
4063 cupsFilePuts(fp, "*CloseUI: *MediaType\n");
4064 }
4065
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
4108 cupsFilePuts(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4109 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4110 "*DefaultcupsPrintQuality: Normal\n");
4111 if ((lowdpi & 1) == 0)
4112 {
4113 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", lowdpi, lowdpi / 2);
4114 have_qdraft = 1;
4115 }
4116 else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4117 {
4118 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*", lowdpi, lowdpi);
4119 have_qdraft = 1;
4120 }
4121
4122 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", lowdpi, lowdpi);
4123
4124 if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4125 {
4126 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", hidpi, hidpi);
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
4179 cupsFilePuts(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4180 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4181 "*DefaultcupsPrintQuality: Normal\n");
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);
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);
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);
4196 have_qhigh = 1;
4197 }
4198
4199 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4200 }
4201 else if (is_apple || is_pwg)
4202 {
4203 goto bad_ppd;
4204 }
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;
4214 cupsCopyString(ppdname, "300dpi", sizeof(ppdname));
4215 }
4216
4217 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4218
4219 cupsFilePuts(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4220 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4221 "*DefaultcupsPrintQuality: Normal\n");
4222 if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4223 {
4224 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*", xres, yres);
4225 have_qdraft = 1;
4226 }
4227
4228 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4229
4230 if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
4231 {
4232 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4233 have_qhigh = 1;
4234 }
4235 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4236 }
4237
4238 /*
4239 * ColorModel...
4240 */
4241
4242 if ((defattr = ippFindAttribute(supported, "print-color-mode-default", IPP_TAG_KEYWORD)) == NULL)
4243 defattr = ippFindAttribute(supported, "output-mode-default", IPP_TAG_KEYWORD);
4244
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);
4249
4250 if (attr)
4251 {
4252 int wrote_color = 0;
4253 const char *default_color = NULL; /* Default */
4254
4255 if ((keyword = ippGetString(defattr, 0, NULL)) != NULL &&
4256 strcmp(keyword, "auto"))
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
4266 cupsFilePrintf(fp, "*%% ColorModel from %s\n", ippGetName(attr));
4267
4268 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4269 {
4270 keyword = ippGetString(attr, i, NULL);
4271
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);
4274
4275 if (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
4276 {
4277 PRINTF_COLORMODEL
4278
4279 PRINTF_COLOROPTION("FastGray", _("Fast Grayscale"), CUPS_CSPACE_K, 1)
4280
4281 if (!default_color)
4282 default_color = "FastGray";
4283 }
4284 else if (!strcasecmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
4285 {
4286 PRINTF_COLORMODEL
4287
4288 PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8)
4289
4290 if (!default_color || (!defattr && !strcmp(default_color, "FastGray")))
4291 default_color = "Gray";
4292 }
4293 else if (!strcasecmp(keyword, "sgray_16") || !strcmp(keyword, "W8-16"))
4294 {
4295 PRINTF_COLORMODEL
4296
4297 if (!strcmp(keyword, "W8-16"))
4298 {
4299 PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8)
4300
4301 if (!default_color || (!defattr && !strcmp(default_color, "FastGray")))
4302 default_color = "Gray";
4303 }
4304
4305 PRINTF_COLOROPTION("Gray16", _("Deep Gray"), CUPS_CSPACE_SW, 16)
4306 }
4307 else if (!strcasecmp(keyword, "srgb_8") || !strncmp(keyword, "SRGB24", 6) || !strcmp(keyword, "color"))
4308 {
4309 PRINTF_COLORMODEL
4310
4311 PRINTF_COLOROPTION("RGB", _("Color"), CUPS_CSPACE_SRGB, 8)
4312
4313 if (!default_color)
4314 default_color = "RGB";
4315
4316 // Apparently some printers only advertise color support, so make sure
4317 // we also do grayscale for these printers...
4318 if (!ippContainsString(attr, "sgray_8") && !ippContainsString(attr, "black_1") && !ippContainsString(attr, "black_8") && !ippContainsString(attr, "W8") && !ippContainsString(attr, "W8-16"))
4319 PRINTF_COLOROPTION("Gray", _("GrayScale"), CUPS_CSPACE_SW, 8)
4320 }
4321 else if (!strcasecmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48") || !strcmp(keyword, "ADOBERGB24-48"))
4322 {
4323 PRINTF_COLORMODEL
4324
4325 PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 16)
4326
4327 if (!default_color)
4328 default_color = "AdobeRGB";
4329 }
4330 else if ((!strcasecmp(keyword, "adobe-rgb_8") && !ippContainsString(attr, "adobe-rgb_16")) || !strcmp(keyword, "ADOBERGB24"))
4331 {
4332 PRINTF_COLORMODEL
4333
4334 PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 8)
4335
4336 if (!default_color)
4337 default_color = "AdobeRGB";
4338 }
4339 else if ((!strcasecmp(keyword, "black_8") && !ippContainsString(attr, "black_16")) || !strcmp(keyword, "DEVW8"))
4340 {
4341 PRINTF_COLORMODEL
4342
4343 PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 8)
4344 }
4345 else if (!strcasecmp(keyword, "black_16") || !strcmp(keyword, "DEVW16") || !strcmp(keyword, "DEVW8-16"))
4346 {
4347 PRINTF_COLORMODEL
4348
4349 PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 16)
4350 }
4351 else if ((!strcasecmp(keyword, "cmyk_8") && !ippContainsString(attr, "cmyk_16")) || !strcmp(keyword, "DEVCMYK32"))
4352 {
4353 PRINTF_COLORMODEL
4354
4355 PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 8)
4356 }
4357 else if (!strcasecmp(keyword, "cmyk_16") || !strcmp(keyword, "DEVCMYK32-64") || !strcmp(keyword, "DEVCMYK64"))
4358 {
4359 PRINTF_COLORMODEL
4360
4361 PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 16)
4362 }
4363 else if ((!strcasecmp(keyword, "rgb_8") && ippContainsString(attr, "rgb_16")) || !strcmp(keyword, "DEVRGB24"))
4364 {
4365 PRINTF_COLORMODEL
4366
4367 PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 8)
4368 }
4369 else if (!strcasecmp(keyword, "rgb_16") || !strcmp(keyword, "DEVRGB24-48") || !strcmp(keyword, "DEVRGB48"))
4370 {
4371 PRINTF_COLORMODEL
4372
4373 PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 16)
4374 }
4375 }
4376
4377 if (default_color)
4378 cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color);
4379 if (wrote_color)
4380 cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
4381
4382 if (default_color)
4383 {
4384 // Standard presets for color mode and quality...
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");
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 }
4421 }
4422
4423 /*
4424 * Duplex...
4425 */
4426
4427 if ((attr = ippFindAttribute(supported, "sides-supported", IPP_TAG_KEYWORD)) != NULL && ippContainsString(attr, "two-sided-long-edge"))
4428 {
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");
4436
4437 if ((attr = ippFindAttribute(supported, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
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 }
4466 else if ((attr = ippFindAttribute(supported, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
4467 {
4468 keyword = ippGetString(attr, 0, NULL);
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
4481 /*
4482 * Output bin...
4483 */
4484
4485 if ((attr = ippFindAttribute(supported, "output-bin-default", IPP_TAG_ZERO)) != NULL)
4486 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
4487 else
4488 cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
4489
4490 if ((attr = ippFindAttribute(supported, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 0)
4491 {
4492 ipp_attribute_t *trays = ippFindAttribute(supported, "printer-output-tray", IPP_TAG_STRING);
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
4499 cupsFilePrintf(fp, "*OpenUI *OutputBin: PickOne\n"
4500 "*OrderDependency: 10 AnySetup *OutputBin\n"
4501 "*DefaultOutputBin: %s\n", ppdname);
4502 if (!strcmp(ppdname, "FaceUp"))
4503 cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n");
4504 else
4505 cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n");
4506
4507 for (i = 0; i < count; i ++)
4508 {
4509 keyword = ippGetString(attr, i, NULL);
4510
4511 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4512
4513 snprintf(msgid, sizeof(msgid), "output-bin.%s", keyword);
4514
4515 cupsFilePrintf(fp, "*OutputBin %s/%s: \"\"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)));
4516 ppd_put_strings(fp, langs, "OutputBin", ppdname, msgid);
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
4523 memcpy(tray, tray_ptr, (size_t)tray_len);
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);
4535 }
4536 cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
4537 }
4538
4539 /*
4540 * Finishing options...
4541 */
4542
4543 if ((attr = ippFindAttribute(supported, "finishings-supported", IPP_TAG_ENUM)) != NULL)
4544 {
4545 int value; /* Enum value */
4546 const char *ppd_keyword; /* PPD keyword for enum */
4547 cups_array_t *names; /* Names we've added */
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 };
4565
4566 count = ippGetCount(attr);
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);
4569
4570 /*
4571 * Staple/Bind/Stitch
4572 */
4573
4574 for (i = 0; i < count; i ++)
4575 {
4576 value = ippGetInteger(attr, i);
4577 keyword = ippEnumString("finishings", value);
4578
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"))
4580 break;
4581 }
4582
4583 if (i < count)
4584 {
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
4612 cupsArrayAdd(fin_options, "*StapleLocation");
4613
4614 cupsFilePrintf(fp, "*OpenUI *StapleLocation/%s: PickOne\n", _cupsLangString(base_lang, "finishings.4"));
4615 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
4616 ppd_put_strings(fp, langs, "Translation", "StapleLocation", "finishings.4");
4617 cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
4618 cupsFilePrintf(fp, "*StapleLocation None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4619 ppd_put_strings(fp, langs, "StapleLocation", "None", "finishings.3");
4620
4621 for (; i < count; i ++)
4622 {
4623 value = ippGetInteger(attr, i);
4624 keyword = ippEnumString("finishings", value);
4625
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"))
4627 continue;
4628
4629 if (cupsArrayFind(names, (char *)keyword))
4630 continue; /* Already did this finishing template */
4631
4632 cupsArrayAdd(names, (char *)keyword);
4633
4634 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4635
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
4648 cupsFilePrintf(fp, "*StapleLocation %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4649 ppd_put_strings(fp, langs, "StapleLocation", ppd_keyword, msgid);
4650 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, keyword, ppd_keyword);
4651 }
4652
4653 cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
4654 }
4655
4656 /*
4657 * Fold
4658 */
4659
4660 for (i = 0; i < count; i ++)
4661 {
4662 value = ippGetInteger(attr, i);
4663 keyword = ippEnumString("finishings", value);
4664
4665 if (!strncmp(keyword, "cups-fold-", 10) || !strcmp(keyword, "fold") || !strncmp(keyword, "fold-", 5))
4666 break;
4667 }
4668
4669 if (i < count)
4670 {
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
4687 cupsArrayAdd(fin_options, "*FoldType");
4688
4689 cupsFilePrintf(fp, "*OpenUI *FoldType/%s: PickOne\n", _cupsLangString(base_lang, "finishings.10"));
4690 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
4691 ppd_put_strings(fp, langs, "Translation", "FoldType", "finishings.10");
4692 cupsFilePuts(fp, "*DefaultFoldType: None\n");
4693 cupsFilePrintf(fp, "*FoldType None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4694 ppd_put_strings(fp, langs, "FoldType", "None", "finishings.3");
4695
4696 for (; i < count; i ++)
4697 {
4698 value = ippGetInteger(attr, i);
4699 keyword = ippEnumString("finishings", value);
4700
4701 if (!strncmp(keyword, "cups-fold-", 10))
4702 keyword += 5;
4703 else if (strcmp(keyword, "fold") && strncmp(keyword, "fold-", 5))
4704 continue;
4705
4706 if (cupsArrayFind(names, (char *)keyword))
4707 continue; /* Already did this finishing template */
4708
4709 cupsArrayAdd(names, (char *)keyword);
4710
4711 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4712
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
4725 cupsFilePrintf(fp, "*FoldType %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4726 ppd_put_strings(fp, langs, "FoldType", ppd_keyword, msgid);
4727 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, keyword, ppd_keyword);
4728 }
4729
4730 cupsFilePuts(fp, "*CloseUI: *FoldType\n");
4731 }
4732
4733 /*
4734 * Punch
4735 */
4736
4737 for (i = 0; i < count; i ++)
4738 {
4739 value = ippGetInteger(attr, i);
4740 keyword = ippEnumString("finishings", value);
4741
4742 if (!strcmp(keyword, "punch") || !strncmp(keyword, "cups-punch-", 11) || !strncmp(keyword, "punch-", 6))
4743 break;
4744 }
4745
4746 if (i < count)
4747 {
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",
4761 "TripleRevLandscape",
4762 "QuadPortrait",
4763 "QuadLandscape",
4764 "QuadRevPortrait",
4765 "QuadRevLandscape",
4766 "MultiplePortrait",
4767 "MultipleLandscape",
4768 "MultipleRevPortrait",
4769 "MultipleRevLandscape"
4770 };
4771
4772 cupsArrayAdd(fin_options, "*PunchMedia");
4773
4774 cupsFilePrintf(fp, "*OpenUI *PunchMedia/%s: PickOne\n", _cupsLangString(base_lang, "finishings.5"));
4775 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
4776 ppd_put_strings(fp, langs, "Translation", "PunchMedia", "finishings.5");
4777 cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
4778 cupsFilePrintf(fp, "*PunchMedia None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4779 ppd_put_strings(fp, langs, "PunchMedia", "None", "finishings.3");
4780
4781 for (i = 0; i < count; i ++)
4782 {
4783 value = ippGetInteger(attr, i);
4784 keyword = ippEnumString("finishings", value);
4785
4786 if (!strncmp(keyword, "cups-punch-", 11))
4787 keyword += 5;
4788 else if (strcmp(keyword, "punch") && strncmp(keyword, "punch-", 6))
4789 continue;
4790
4791 if (cupsArrayFind(names, (char *)keyword))
4792 continue; /* Already did this finishing template */
4793
4794 cupsArrayAdd(names, (char *)keyword);
4795
4796 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4797
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
4810 cupsFilePrintf(fp, "*PunchMedia %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4811 ppd_put_strings(fp, langs, "PunchMedia", ppd_keyword, msgid);
4812 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, keyword, ppd_keyword);
4813 }
4814
4815 cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
4816 }
4817
4818 /*
4819 * Booklet
4820 */
4821
4822 if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER))
4823 {
4824 cupsArrayAdd(fin_options, "*Booklet");
4825
4826 cupsFilePrintf(fp, "*OpenUI *Booklet/%s: Boolean\n", _cupsLangString(base_lang, "finishings.13"));
4827 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
4828 ppd_put_strings(fp, langs, "Translation", "StapleLocation", "finishings.13");
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");
4834 }
4835
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
4861 cupsFilePrintf(fp, "*OpenUI *CutMedia/%s: PickOne\n", _cupsLangString(base_lang, "finishings.11"));
4862 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *CutMedia\n");
4863 ppd_put_strings(fp, langs, "Translation", "CutMedia", "finishings.11");
4864 cupsFilePuts(fp, "*DefaultCutMedia: None\n");
4865 cupsFilePrintf(fp, "*CutMedia None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4866 ppd_put_strings(fp, langs, "CutMedia", "None", "finishings.3");
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);
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
4888 cupsFilePrintf(fp, "*CutMedia %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4889 ppd_put_strings(fp, langs, "CutMedia", ppd_keyword, msgid);
4890 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*CutMedia %s\"\n", value, keyword, ppd_keyword);
4891 }
4892
4893 cupsFilePuts(fp, "*CloseUI: *CutMedia\n");
4894 }
4895
4896 cupsArrayDelete(names);
4897 }
4898
4899 if ((attr = ippFindAttribute(supported, "finishings-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4900 {
4901 ipp_t *finishing_col; /* Current finishing collection */
4902 ipp_attribute_t *finishing_attr; /* Current finishing member attribute */
4903 cups_array_t *templates; /* Finishing templates */
4904
4905 cupsFilePrintf(fp, "*OpenUI *cupsFinishingTemplate/%s: PickOne\n", ppd_get_string(base_lang, lang, "finishing-template", ppdtext, sizeof(ppdtext)));
4906 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n");
4907 ppd_put_strings(fp, langs, "Translation", "cupsFinishingTemplate", "finishing-template");
4908 cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n");
4909 cupsFilePrintf(fp, "*cupsFinishingTemplate none/%s: \"\"\n", _cupsLangString(lang, "finishings.3"));
4910 ppd_put_strings(fp, langs, "cupsFinishingTemplate", "none", "finishings.3");
4911
4912 templates = cupsArrayNew((cups_array_func_t)_cupsArrayStrcmp, NULL);
4913 count = ippGetCount(attr);
4914
4915 for (i = 0; i < count; i ++)
4916 {
4917 finishing_col = ippGetCollection(attr, i);
4918 keyword = ippGetString(ippFindAttribute(finishing_col, "finishing-template", IPP_TAG_ZERO), 0, NULL);
4919
4920 if (!keyword || cupsArrayFind(templates, (void *)keyword))
4921 continue;
4922
4923 if (!strcmp(keyword, "none"))
4924 continue;
4925
4926 cupsArrayAdd(templates, (void *)keyword);
4927
4928 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4929
4930 snprintf(msgid, sizeof(msgid), "finishing-template.%s", keyword);
4931
4932 cupsFilePrintf(fp, "*cupsFinishingTemplate %s/%s: \"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)));
4933 for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr; finishing_attr = ippNextAttribute(finishing_col))
4934 {
4935 if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION)
4936 {
4937 const char *name = ippGetName(finishing_attr);
4938 /* Member attribute name */
4939
4940 if (strcmp(name, "media-size"))
4941 cupsFilePrintf(fp, "%% %s\n", name);
4942 }
4943 }
4944 cupsFilePuts(fp, "\"\n");
4945 ppd_put_strings(fp, langs, "cupsFinishingTemplate", ppdname, msgid);
4946 cupsFilePuts(fp, "*End\n");
4947 }
4948
4949 cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n");
4950
4951 if (cupsArrayCount(fin_options))
4952 {
4953 const char *fin_option; /* Current finishing option */
4954
4955 cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate");
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
4960 cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate none");
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
4971 /*
4972 * Presets...
4973 */
4974
4975 if ((attr = ippFindAttribute(supported, "job-presets-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4976 {
4977 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4978 {
4979 ipp_t *preset = ippGetCollection(attr, i);
4980 /* Preset collection */
4981 const char *preset_name = ippGetString(ippFindAttribute(preset, "preset-name", IPP_TAG_ZERO), 0, NULL);
4982 /* Preset name */
4983 ipp_attribute_t *member; /* Member attribute in preset */
4984 const char *member_name; /* Member attribute name */
4985 char member_value[256]; /* Member attribute value */
4986
4987 if (!preset || !preset_name)
4988 continue;
4989
4990 pwg_ppdize_name(preset_name, ppdname, sizeof(ppdname));
4991
4992 cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", ppdname);
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";
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"))
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)
5033 {
5034 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
5035 cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", ppdname);
5036 }
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));
5063 cupsFilePrintf(fp, "*InputSlot %s\n", ppdname);
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));
5069 cupsFilePrintf(fp, "*MediaType %s\n", ppdname);
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");
5113
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);
5117 }
5118 }
5119
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
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
5150 /*
5151 * Close up and return...
5152 */
5153
5154 cupsFileClose(fp);
5155
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 }
5165
5166 return (buffer);
5167
5168 /*
5169 * If we get here then there was a problem creating the PPD...
5170 */
5171
5172 bad_ppd:
5173
5174 cupsFileClose(fp);
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
5186 unlink(buffer);
5187 *buffer = '\0';
5188
5189 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Printer does not support required IPP attributes or document formats."), 1);
5190
5191 return (NULL);
5192}
5193
5194
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
5213 if (_cups_strcasecmp(media_source, "main"))
5214 cupsCopyString(name, "Cassette", namesize);
5215 else if (_cups_strcasecmp(media_source, "alternate"))
5216 cupsCopyString(name, "Multipurpose", namesize);
5217 else if (_cups_strcasecmp(media_source, "large-capacity"))
5218 cupsCopyString(name, "LargeCapacity", namesize);
5219 else if (_cups_strcasecmp(media_source, "bottom"))
5220 cupsCopyString(name, "Lower", namesize);
5221 else if (_cups_strcasecmp(media_source, "middle"))
5222 cupsCopyString(name, "Middle", namesize);
5223 else if (_cups_strcasecmp(media_source, "top"))
5224 cupsCopyString(name, "Upper", namesize);
5225 else if (_cups_strcasecmp(media_source, "rear"))
5226 cupsCopyString(name, "Rear", namesize);
5227 else if (_cups_strcasecmp(media_source, "side"))
5228 cupsCopyString(name, "Side", namesize);
5229 else if (_cups_strcasecmp(media_source, "envelope"))
5230 cupsCopyString(name, "Envelope", namesize);
5231 else if (_cups_strcasecmp(media_source, "main-roll"))
5232 cupsCopyString(name, "Roll", namesize);
5233 else if (_cups_strcasecmp(media_source, "alternate-roll"))
5234 cupsCopyString(name, "Roll2", namesize);
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
5260 if (_cups_strcasecmp(media_type, "auto"))
5261 cupsCopyString(name, "Auto", namesize);
5262 else if (_cups_strcasecmp(media_type, "cardstock"))
5263 cupsCopyString(name, "Cardstock", namesize);
5264 else if (_cups_strcasecmp(media_type, "envelope"))
5265 cupsCopyString(name, "Envelope", namesize);
5266 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
5267 cupsCopyString(name, "Glossy", namesize);
5268 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
5269 cupsCopyString(name, "HighGloss", namesize);
5270 else if (_cups_strcasecmp(media_type, "photographic-matte"))
5271 cupsCopyString(name, "Matte", namesize);
5272 else if (_cups_strcasecmp(media_type, "stationery"))
5273 cupsCopyString(name, "Plain", namesize);
5274 else if (_cups_strcasecmp(media_type, "stationery-coated"))
5275 cupsCopyString(name, "Coated", namesize);
5276 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
5277 cupsCopyString(name, "Inkjet", namesize);
5278 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
5279 cupsCopyString(name, "Letterhead", namesize);
5280 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
5281 cupsCopyString(name, "Preprinted", namesize);
5282 else if (_cups_strcasecmp(media_type, "transparency"))
5283 cupsCopyString(name, "Transparency", namesize);
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(
5297 pwg_media_t *media, /* I - Media */
5298 char *name, /* I - PageSize name buffer */
5299 size_t namesize) /* I - Size of name buffer */
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
5322 cupsCopyString(name, media->ppd, namesize);
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
5333 snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
5334 (int)PWG_TO_POINTS(media->length));
5335 }
5336 else
5337 {
5338 /*
5339 * Copy the size name from class_sizename_dimensions...
5340 */
5341
5342 memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
5343 name[dimptr - sizeptr - 1] = '\0';
5344 }
5345
5346 return (name);
5347}
5348
5349
5350/*
5351 * 'cups_connect()' - Connect to a URL and get the resource path.
5352 */
5353
5354static int /* O - 1 on success, 0 on failure */
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 */
5359{
5360 char scheme[32], /* URL scheme */
5361 userpass[256], /* URL username:password */
5362 host[256], /* URL host */
5363 curhost[256]; /* Current host */
5364 int port; /* URL port */
5365 http_encryption_t encryption; /* Type of encryption to use */
5366
5367
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)
5370 return (0);
5371
5372 // Use encryption as needed..
5373 if (port == 443 || !strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
5374 encryption = HTTP_ENCRYPTION_ALWAYS;
5375 else
5376 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
5377
5378 if (!*http || strcasecmp(host, httpGetHostname(*http, curhost, sizeof(curhost))) || httpAddrPort(httpGetAddress(*http)) != port || httpIsEncrypted(*http) != (encryption == HTTP_ENCRYPTION_ALWAYS))
5379 {
5380 httpClose(*http);
5381 *http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 5000, NULL);
5382 }
5383
5384 return (*http != NULL);
5385}
5386
5387
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 */
5404 static int request_id = 1000; // Request ID
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);
5420 ippSetRequestId(request, request_id ++);
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));
5448 lang->strings = _cupsMessageLoad(NULL, strings_file, _CUPS_MESSAGE_STRINGS);
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 }
5476 }
5477#endif // 0
5478
5479 done:
5480
5481 ippDelete(response);
5482
5483 return (lang);
5484}
5485
5486
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 */
5494 const char *suffix, /* I - Filename suffix */
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)))
5504 return (0);
5505
5506 if ((fd = cupsCreateTempFd("ippeve", suffix, name, namesize)) < 0)
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
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);
5557 buffer[0] = (char)toupper(buffer[0] & 255);
5558 for (bufptr = buffer + 1; *bufptr; bufptr ++)
5559 {
5560 if (*bufptr == '-')
5561 {
5562 *bufptr = ' ';
5563 bufptr[1] = (char)toupper(bufptr[1] & 255);
5564 }
5565 }
5566
5567 text = buffer;
5568 }
5569 }
5570
5571 return (text);
5572}
5573
5574
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
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 {
5639 if (strcmp(lang->language, "en") && (text = _cupsLangString(lang, pwg_msgid)) != pwg_msgid)
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 }
5655 }
5656}
5657
5658
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
5683/*
5684 * 'pwg_compare_finishings()' - Compare two finishings values.
5685 */
5686
5687static int /* O - Result of comparison */
5688pwg_compare_finishings(
5689 _pwg_finishings_t *a, /* I - First finishings value */
5690 _pwg_finishings_t *b, /* I - Second finishings value */
5691 void *data) /* Unused */
5692{
5693 (void)data;
5694 return ((int)b->value - (int)a->value);
5695}
5696
5697
5698/*
5699 * 'pwg_compare_sizes()' - Compare two media sizes...
5700 */
5701
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 */
5706{
5707 (void)data;
5708 return (strcmp(a->media, b->media));
5709}
5710
5711
5712/*
5713 * 'pwg_copy_size()' - Copy a media size.
5714 */
5715
5716static cups_size_t * /* O - New media size */
5717pwg_copy_size(cups_size_t *size, /* I - Media size to copy */
5718 void *data) /* Unused*/
5719{
5720 cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t));
5721 /* New media size */
5722 (void)data;
5723 if (newsize)
5724 memcpy(newsize, size, sizeof(cups_size_t));
5725
5726 return (newsize);
5727}
5728
5729
5730/*
5731 * 'pwg_free_finishings()' - Free a finishings value.
5732 */
5733
5734static void
5735pwg_free_finishings(_pwg_finishings_t *f, /* I - Finishings value */
5736 void *data) /* Unused */
5737{
5738 (void)data;
5739 cupsFreeOptions(f->num_options, f->options);
5740 free(f);
5741}
5742
5743
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
5757 if (!ipp || !_cups_isalnum(*ipp))
5758 {
5759 *name = '\0';
5760 return;
5761 }
5762
5763 *name = (char)toupper(*ipp++);
5764
5765 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
5766 {
5767 if (*ipp == '-' && _cups_isalnum(ipp[1]))
5768 {
5769 ipp ++;
5770 *ptr++ = (char)toupper(*ipp++ & 255);
5771 }
5772 else if (*ipp == '_' || *ipp == '.' || *ipp == '-' || _cups_isalnum(*ipp))
5773 {
5774 *ptr++ = *ipp++;
5775 }
5776 else
5777 {
5778 ipp ++;
5779 }
5780 }
5781
5782 *ptr = '\0';
5783}
5784
5785
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
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 */
5827 size_t namesize, /* I - Size of name buffer */
5828 const char *dashchars)/* I - Characters to be replaced by dashes */
5829{
5830 char *ptr, /* Pointer into name buffer */
5831 *end; /* End of name buffer */
5832 int nodash = 1; /* Next char in IPP name cannot be a
5833 dash (first char or after a dash) */
5834
5835
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 ++)
5845 if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr) ||
5846 (*ppdptr == '-' && *(ppdptr - 1) == '-') ||
5847 (*ppdptr == '-' && *(ppdptr + 1) == '\0'))
5848 break;
5849
5850 if (!*ppdptr)
5851 {
5852 cupsCopyString(name, ppd, namesize);
5853 return;
5854 }
5855 }
5856
5857 for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
5858 {
5859 if (_cups_isalnum(*ppd) || *ppd == '.' || *ppd == '_')
5860 {
5861 *ptr++ = (char)tolower(*ppd & 255);
5862 nodash = 0;
5863 }
5864 else if (*ppd == '-' || strchr(dashchars, *ppd))
5865 {
5866 if (nodash == 0)
5867 {
5868 *ptr++ = '-';
5869 nodash = 1;
5870 }
5871 }
5872
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 }
5887 }
5888
5889 /* Remove trailing dashes */
5890 while (ptr > name && *(ptr - 1) == '-')
5891 ptr --;
5892
5893 *ptr = '\0';
5894}