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