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