]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd-cache.c
Make explicit cast from enum to integer
[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_name);
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)", f->value, ippEnumString("finishings", 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)", f->value, ippEnumString("finishings", 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, "AccountId %s\n", pc->account_id ? "true" : "false");
2961 cupsFilePrintf(fp, "AccountingUserId %s\n",
2962 pc->accounting_user_id ? "true" : "false");
2963
2964 if (pc->password)
2965 cupsFilePutConf(fp, "Password", 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 * Password/PIN printing...
3161 */
3162
3163 if ((attr = ippFindAttribute(response, "job-password-supported", IPP_TAG_INTEGER)) != NULL)
3164 {
3165 char pattern[33]; /* Password pattern */
3166 int maxlen = ippGetInteger(attr, 0);
3167 /* Maximum length */
3168 const char *repertoire = ippGetString(ippFindAttribute(response, "job-password-repertoire-configured", IPP_TAG_KEYWORD), 0, NULL);
3169 /* Type of password */
3170
3171 if (maxlen > (int)(sizeof(pattern) - 1))
3172 maxlen = (int)sizeof(pattern) - 1;
3173
3174 if (!repertoire || !strcmp(repertoire, "iana_us-ascii_digits"))
3175 memset(pattern, '1', (size_t)maxlen);
3176 else if (!strcmp(repertoire, "iana_us-ascii_letters"))
3177 memset(pattern, 'A', (size_t)maxlen);
3178 else if (!strcmp(repertoire, "iana_us-ascii_complex"))
3179 memset(pattern, 'C', (size_t)maxlen);
3180 else if (!strcmp(repertoire, "iana_us-ascii_any"))
3181 memset(pattern, '.', (size_t)maxlen);
3182 else if (!strcmp(repertoire, "iana_utf-8_digits"))
3183 memset(pattern, 'N', (size_t)maxlen);
3184 else if (!strcmp(repertoire, "iana_utf-8_letters"))
3185 memset(pattern, 'U', (size_t)maxlen);
3186 else
3187 memset(pattern, '*', (size_t)maxlen);
3188
3189 pattern[maxlen] = '\0';
3190
3191 cupsFilePrintf(fp, "*cupsPassword: \"%s\"\n", pattern);
3192 }
3193
3194 /*
3195 * Filters...
3196 */
3197
3198 if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL)
3199 {
3200 is_apple = ippContainsString(attr, "image/urf");
3201 is_pdf = ippContainsString(attr, "application/pdf");
3202 is_pwg = ippContainsString(attr, "image/pwg-raster") && !is_apple;
3203
3204 if (ippContainsString(attr, "image/jpeg"))
3205 cupsFilePuts(fp, "*cupsFilter2: \"image/jpeg image/jpeg 0 -\"\n");
3206 if (ippContainsString(attr, "image/png"))
3207 cupsFilePuts(fp, "*cupsFilter2: \"image/png image/png 0 -\"\n");
3208 if (is_pdf)
3209 {
3210 /*
3211 * Don't locally filter PDF content when printing to a CUPS shared
3212 * printer, otherwise the options will be applied twice...
3213 */
3214
3215 if (ippContainsString(attr, "application/vnd.cups-pdf"))
3216 cupsFilePuts(fp, "*cupsFilter2: \"application/pdf application/pdf 0 -\"\n");
3217 else
3218 cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 10 -\"\n");
3219 }
3220 else
3221 cupsFilePuts(fp, "*cupsManualCopies: true\n");
3222 if (is_apple)
3223 cupsFilePuts(fp, "*cupsFilter2: \"image/urf image/urf 100 -\"\n");
3224 if (is_pwg)
3225 cupsFilePuts(fp, "*cupsFilter2: \"image/pwg-raster image/pwg-raster 100 -\"\n");
3226 }
3227
3228 if (!is_apple && !is_pdf && !is_pwg)
3229 goto bad_ppd;
3230
3231 /*
3232 * PageSize/PageRegion/ImageableArea/PaperDimension
3233 */
3234
3235 if ((attr = ippFindAttribute(response, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != NULL)
3236 {
3237 for (i = 1, bottom = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3238 if (ippGetInteger(attr, i) > bottom)
3239 bottom = ippGetInteger(attr, i);
3240 }
3241 else
3242 bottom = 1270;
3243
3244 if ((attr = ippFindAttribute(response, "media-left-margin-supported", IPP_TAG_INTEGER)) != NULL)
3245 {
3246 for (i = 1, left = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3247 if (ippGetInteger(attr, i) > left)
3248 left = ippGetInteger(attr, i);
3249 }
3250 else
3251 left = 635;
3252
3253 if ((attr = ippFindAttribute(response, "media-right-margin-supported", IPP_TAG_INTEGER)) != NULL)
3254 {
3255 for (i = 1, right = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3256 if (ippGetInteger(attr, i) > right)
3257 right = ippGetInteger(attr, i);
3258 }
3259 else
3260 right = 635;
3261
3262 if ((attr = ippFindAttribute(response, "media-top-margin-supported", IPP_TAG_INTEGER)) != NULL)
3263 {
3264 for (i = 1, top = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3265 if (ippGetInteger(attr, i) > top)
3266 top = ippGetInteger(attr, i);
3267 }
3268 else
3269 top = 1270;
3270
3271 if ((defattr = ippFindAttribute(response, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3272 {
3273 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3274 {
3275 media_size = ippGetCollection(attr, 0);
3276 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3277 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3278
3279 if (x_dim && y_dim && (pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL)
3280 strlcpy(ppdname, pwg->ppd, sizeof(ppdname));
3281 else
3282 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3283 }
3284 else
3285 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3286 }
3287 else if ((pwg = pwgMediaForPWG(ippGetString(ippFindAttribute(response, "media-default", IPP_TAG_ZERO), 0, NULL))) != NULL)
3288 strlcpy(ppdname, pwg->ppd, sizeof(ppdname));
3289 else
3290 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3291
3292 sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes, NULL, NULL, 0, (cups_acopy_func_t)pwg_copy_size, (cups_afree_func_t)free);
3293
3294 if ((attr = ippFindAttribute(response, "media-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3295 {
3296 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3297 {
3298 cups_size_t temp; /* Current size */
3299 ipp_attribute_t *margin; /* media-xxx-margin attribute */
3300
3301 media_col = ippGetCollection(attr, i);
3302 media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0);
3303 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3304 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3305 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3306
3307 if (pwg)
3308 {
3309 temp.width = pwg->width;
3310 temp.length = pwg->length;
3311
3312 if ((margin = ippFindAttribute(media_col, "media-bottom-margin", IPP_TAG_INTEGER)) != NULL)
3313 temp.bottom = ippGetInteger(margin, 0);
3314 else
3315 temp.bottom = bottom;
3316
3317 if ((margin = ippFindAttribute(media_col, "media-left-margin", IPP_TAG_INTEGER)) != NULL)
3318 temp.left = ippGetInteger(margin, 0);
3319 else
3320 temp.left = left;
3321
3322 if ((margin = ippFindAttribute(media_col, "media-right-margin", IPP_TAG_INTEGER)) != NULL)
3323 temp.right = ippGetInteger(margin, 0);
3324 else
3325 temp.right = right;
3326
3327 if ((margin = ippFindAttribute(media_col, "media-top-margin", IPP_TAG_INTEGER)) != NULL)
3328 temp.top = ippGetInteger(margin, 0);
3329 else
3330 temp.top = top;
3331
3332 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3333 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3334 else
3335 strlcpy(temp.media, pwg->ppd, sizeof(temp.media));
3336
3337 if (!cupsArrayFind(sizes, &temp))
3338 cupsArrayAdd(sizes, &temp);
3339 }
3340 else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3341 {
3342 /*
3343 * Custom size - record the min/max values...
3344 */
3345
3346 int lower, upper; /* Range values */
3347
3348 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3349 lower = ippGetRange(x_dim, 0, &upper);
3350 else
3351 lower = upper = ippGetInteger(x_dim, 0);
3352
3353 if (lower < min_width)
3354 min_width = lower;
3355 if (upper > max_width)
3356 max_width = upper;
3357
3358 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3359 lower = ippGetRange(y_dim, 0, &upper);
3360 else
3361 lower = upper = ippGetInteger(y_dim, 0);
3362
3363 if (lower < min_length)
3364 min_length = lower;
3365 if (upper > max_length)
3366 max_length = upper;
3367 }
3368 }
3369
3370 if ((max_width == 0 || max_length == 0) && (attr = ippFindAttribute(response, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3371 {
3372 /*
3373 * Some printers don't list custom size support in media-col-database...
3374 */
3375
3376 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3377 {
3378 media_size = ippGetCollection(attr, i);
3379 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3380 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3381
3382 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3383 {
3384 /*
3385 * Custom size - record the min/max values...
3386 */
3387
3388 int lower, upper; /* Range values */
3389
3390 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3391 lower = ippGetRange(x_dim, 0, &upper);
3392 else
3393 lower = upper = ippGetInteger(x_dim, 0);
3394
3395 if (lower < min_width)
3396 min_width = lower;
3397 if (upper > max_width)
3398 max_width = upper;
3399
3400 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3401 lower = ippGetRange(y_dim, 0, &upper);
3402 else
3403 lower = upper = ippGetInteger(y_dim, 0);
3404
3405 if (lower < min_length)
3406 min_length = lower;
3407 if (upper > max_length)
3408 max_length = upper;
3409 }
3410 }
3411 }
3412 }
3413 else if ((attr = ippFindAttribute(response, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3414 {
3415 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3416 {
3417 cups_size_t temp; /* Current size */
3418
3419 media_size = ippGetCollection(attr, i);
3420 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3421 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3422 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3423
3424 if (pwg)
3425 {
3426 temp.width = pwg->width;
3427 temp.length = pwg->length;
3428 temp.bottom = bottom;
3429 temp.left = left;
3430 temp.right = right;
3431 temp.top = top;
3432
3433 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3434 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3435 else
3436 strlcpy(temp.media, pwg->ppd, sizeof(temp.media));
3437
3438 if (!cupsArrayFind(sizes, &temp))
3439 cupsArrayAdd(sizes, &temp);
3440 }
3441 else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3442 {
3443 /*
3444 * Custom size - record the min/max values...
3445 */
3446
3447 int lower, upper; /* Range values */
3448
3449 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3450 lower = ippGetRange(x_dim, 0, &upper);
3451 else
3452 lower = upper = ippGetInteger(x_dim, 0);
3453
3454 if (lower < min_width)
3455 min_width = lower;
3456 if (upper > max_width)
3457 max_width = upper;
3458
3459 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3460 lower = ippGetRange(y_dim, 0, &upper);
3461 else
3462 lower = upper = ippGetInteger(y_dim, 0);
3463
3464 if (lower < min_length)
3465 min_length = lower;
3466 if (upper > max_length)
3467 max_length = upper;
3468 }
3469 }
3470 }
3471 else if ((attr = ippFindAttribute(response, "media-supported", IPP_TAG_ZERO)) != NULL)
3472 {
3473 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3474 {
3475 const char *pwg_size = ippGetString(attr, i, NULL);
3476 /* PWG size name */
3477 cups_size_t temp; /* Current size */
3478
3479 if ((pwg = pwgMediaForPWG(pwg_size)) != NULL)
3480 {
3481 if (strstr(pwg_size, "_max_") || strstr(pwg_size, "_max."))
3482 {
3483 if (pwg->width > max_width)
3484 max_width = pwg->width;
3485 if (pwg->length > max_length)
3486 max_length = pwg->length;
3487 }
3488 else if (strstr(pwg_size, "_min_") || strstr(pwg_size, "_min."))
3489 {
3490 if (pwg->width < min_width)
3491 min_width = pwg->width;
3492 if (pwg->length < min_length)
3493 min_length = pwg->length;
3494 }
3495 else
3496 {
3497 temp.width = pwg->width;
3498 temp.length = pwg->length;
3499 temp.bottom = bottom;
3500 temp.left = left;
3501 temp.right = right;
3502 temp.top = top;
3503
3504 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3505 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3506 else
3507 strlcpy(temp.media, pwg->ppd, sizeof(temp.media));
3508
3509 if (!cupsArrayFind(sizes, &temp))
3510 cupsArrayAdd(sizes, &temp);
3511 }
3512 }
3513 }
3514 }
3515
3516 if (cupsArrayCount(sizes) > 0)
3517 {
3518 /*
3519 * List all of the standard sizes...
3520 */
3521
3522 char tleft[256], /* Left string */
3523 tbottom[256], /* Bottom string */
3524 tright[256], /* Right string */
3525 ttop[256], /* Top string */
3526 twidth[256], /* Width string */
3527 tlength[256]; /* Length string */
3528
3529 cupsFilePrintf(fp, "*OpenUI *PageSize: PickOne\n"
3530 "*OrderDependency: 10 AnySetup *PageSize\n"
3531 "*DefaultPageSize: %s\n", ppdname);
3532 for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3533 {
3534 _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3535 _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3536
3537 cupsFilePrintf(fp, "*PageSize %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", size->media, twidth, tlength);
3538 }
3539 cupsFilePuts(fp, "*CloseUI: *PageSize\n");
3540
3541 cupsFilePrintf(fp, "*OpenUI *PageRegion: PickOne\n"
3542 "*OrderDependency: 10 AnySetup *PageRegion\n"
3543 "*DefaultPageRegion: %s\n", ppdname);
3544 for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3545 {
3546 _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3547 _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3548
3549 cupsFilePrintf(fp, "*PageRegion %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", size->media, twidth, tlength);
3550 }
3551 cupsFilePuts(fp, "*CloseUI: *PageRegion\n");
3552
3553 cupsFilePrintf(fp, "*DefaultImageableArea: %s\n"
3554 "*DefaultPaperDimension: %s\n", ppdname, ppdname);
3555
3556 for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3557 {
3558 _cupsStrFormatd(tleft, tleft + sizeof(tleft), size->left * 72.0 / 2540.0, loc);
3559 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), size->bottom * 72.0 / 2540.0, loc);
3560 _cupsStrFormatd(tright, tright + sizeof(tright), (size->width - size->right) * 72.0 / 2540.0, loc);
3561 _cupsStrFormatd(ttop, ttop + sizeof(ttop), (size->length - size->top) * 72.0 / 2540.0, loc);
3562 _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3563 _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3564
3565 cupsFilePrintf(fp, "*ImageableArea %s: \"%s %s %s %s\"\n", size->media, tleft, tbottom, tright, ttop);
3566 cupsFilePrintf(fp, "*PaperDimension %s: \"%s %s\"\n", size->media, twidth, tlength);
3567 }
3568
3569 cupsArrayDelete(sizes);
3570
3571 /*
3572 * Custom size support...
3573 */
3574
3575 if (max_width > 0 && min_width < INT_MAX && max_length > 0 && min_length < INT_MAX)
3576 {
3577 char tmax[256], tmin[256]; /* Min/max values */
3578
3579 _cupsStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc);
3580 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), bottom * 72.0 / 2540.0, loc);
3581 _cupsStrFormatd(tright, tright + sizeof(tright), right * 72.0 / 2540.0, loc);
3582 _cupsStrFormatd(ttop, ttop + sizeof(ttop), top * 72.0 / 2540.0, loc);
3583
3584 cupsFilePrintf(fp, "*HWMargins: \"%s %s %s %s\"\n", tleft, tbottom, tright, ttop);
3585
3586 _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_width * 72.0 / 2540.0, loc);
3587 _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_width * 72.0 / 2540.0, loc);
3588 cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s\n", tmin, tmax);
3589
3590 _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_length * 72.0 / 2540.0, loc);
3591 _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_length * 72.0 / 2540.0, loc);
3592 cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s\n", tmin, tmax);
3593
3594 cupsFilePuts(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0\n");
3595 cupsFilePuts(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0\n");
3596 cupsFilePuts(fp, "*ParamCustomPageSize Orientation: 5 int 0 3\n");
3597 cupsFilePuts(fp, "*CustomPageSize True: \"pop pop pop <</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\"\n");
3598 }
3599 }
3600 else
3601 {
3602 cupsArrayDelete(sizes);
3603 goto bad_ppd;
3604 }
3605
3606 /*
3607 * InputSlot...
3608 */
3609
3610 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source", IPP_TAG_ZERO)) != NULL)
3611 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3612 else
3613 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3614
3615 if ((attr = ippFindAttribute(response, "media-source-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3616 {
3617 static const char * const sources[] =
3618 { /* Standard "media-source" strings */
3619 "auto",
3620 "main",
3621 "alternate",
3622 "large-capacity",
3623 "manual",
3624 "envelope",
3625 "disc",
3626 "photo",
3627 "hagaki",
3628 "main-roll",
3629 "alternate-roll",
3630 "top",
3631 "middle",
3632 "bottom",
3633 "side",
3634 "left",
3635 "right",
3636 "center",
3637 "rear",
3638 "by-pass-tray",
3639 "tray-1",
3640 "tray-2",
3641 "tray-3",
3642 "tray-4",
3643 "tray-5",
3644 "tray-6",
3645 "tray-7",
3646 "tray-8",
3647 "tray-9",
3648 "tray-10",
3649 "tray-11",
3650 "tray-12",
3651 "tray-13",
3652 "tray-14",
3653 "tray-15",
3654 "tray-16",
3655 "tray-17",
3656 "tray-18",
3657 "tray-19",
3658 "tray-20",
3659 "roll-1",
3660 "roll-2",
3661 "roll-3",
3662 "roll-4",
3663 "roll-5",
3664 "roll-6",
3665 "roll-7",
3666 "roll-8",
3667 "roll-9",
3668 "roll-10"
3669 };
3670
3671 cupsFilePrintf(fp, "*OpenUI *InputSlot: PickOne\n"
3672 "*OrderDependency: 10 AnySetup *InputSlot\n"
3673 "*DefaultInputSlot: %s\n", ppdname);
3674 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3675 {
3676 keyword = ippGetString(attr, i, NULL);
3677
3678 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3679
3680 for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++)
3681 if (!strcmp(sources[j], keyword))
3682 {
3683 snprintf(msgid, sizeof(msgid), "media-source.%s", keyword);
3684 cupsFilePrintf(fp, "*InputSlot %s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, j);
3685 cupsFilePrintf(fp, "*%s.InputSlot %s/%s: \"\"\n", lang->language, ppdname, _cupsLangString(lang, msgid));
3686 break;
3687 }
3688 }
3689 cupsFilePuts(fp, "*CloseUI: *InputSlot\n");
3690 }
3691
3692 /*
3693 * MediaType...
3694 */
3695
3696 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type", IPP_TAG_ZERO)) != NULL)
3697 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3698 else
3699 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3700
3701 if ((attr = ippFindAttribute(response, "media-type-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3702 {
3703 cupsFilePrintf(fp, "*OpenUI *MediaType: PickOne\n"
3704 "*OrderDependency: 10 AnySetup *MediaType\n"
3705 "*DefaultMediaType: %s\n", ppdname);
3706 for (i = 0; i < count; i ++)
3707 {
3708 keyword = ippGetString(attr, i, NULL);
3709
3710 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3711
3712 snprintf(msgid, sizeof(msgid), "media-type.%s", keyword);
3713 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
3714 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
3715 msgstr = keyword;
3716
3717 cupsFilePrintf(fp, "*MediaType %s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, ppdname);
3718 cupsFilePrintf(fp, "*%s.MediaType %s/%s: \"\"\n", lang->language, ppdname, msgstr);
3719 }
3720 cupsFilePuts(fp, "*CloseUI: *MediaType\n");
3721 }
3722
3723 /*
3724 * ColorModel...
3725 */
3726
3727 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) == NULL)
3728 if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
3729 if ((attr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) == NULL)
3730 attr = ippFindAttribute(response, "output-mode-supported", IPP_TAG_KEYWORD);
3731
3732 if (attr)
3733 {
3734 int wrote_color = 0;
3735 const char *default_color = NULL; /* Default */
3736
3737 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3738 {
3739 keyword = ippGetString(attr, i, NULL);
3740
3741 #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; }
3742 #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)); }
3743
3744 if (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
3745 {
3746 PRINTF_COLORMODEL
3747
3748 PRINTF_COLOROPTION("FastGray", _("Fast Grayscale"), CUPS_CSPACE_K, 1)
3749
3750 if (!default_color)
3751 default_color = "FastGray";
3752 }
3753 else if (!strcasecmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
3754 {
3755 PRINTF_COLORMODEL
3756
3757 PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8)
3758
3759 if (!default_color || !strcmp(default_color, "FastGray"))
3760 default_color = "Gray";
3761 }
3762 else if (!strcasecmp(keyword, "sgray_16") || !strcmp(keyword, "W8-16"))
3763 {
3764 PRINTF_COLORMODEL
3765
3766 if (!strcmp(keyword, "W8-16"))
3767 {
3768 PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8)
3769
3770 if (!default_color || !strcmp(default_color, "FastGray"))
3771 default_color = "Gray";
3772 }
3773
3774 PRINTF_COLOROPTION("Gray16", _("Deep Gray"), CUPS_CSPACE_SW, 16)
3775 }
3776 else if (!strcasecmp(keyword, "srgb_8") || !strncmp(keyword, "SRGB24", 7) || !strcmp(keyword, "color"))
3777 {
3778 PRINTF_COLORMODEL
3779
3780 PRINTF_COLOROPTION("RGB", _("Color"), CUPS_CSPACE_SRGB, 8)
3781
3782 default_color = "RGB";
3783 }
3784 else if (!strcasecmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48") || !strcmp(keyword, "ADOBERGB24-48"))
3785 {
3786 PRINTF_COLORMODEL
3787
3788 PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 16)
3789
3790 if (!default_color)
3791 default_color = "AdobeRGB";
3792 }
3793 else if ((!strcasecmp(keyword, "adobe-rgb_8") && !ippContainsString(attr, "adobe-rgb_16")) || !strcmp(keyword, "ADOBERGB24"))
3794 {
3795 PRINTF_COLORMODEL
3796
3797 PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 8)
3798
3799 if (!default_color)
3800 default_color = "AdobeRGB";
3801 }
3802 else if ((!strcasecmp(keyword, "black_8") && !ippContainsString(attr, "black_16")) || !strcmp(keyword, "DEVW8"))
3803 {
3804 PRINTF_COLORMODEL
3805
3806 PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 8)
3807 }
3808 else if (!strcasecmp(keyword, "black_16") || !strcmp(keyword, "DEVW16") || !strcmp(keyword, "DEVW8-16"))
3809 {
3810 PRINTF_COLORMODEL
3811
3812 PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 16)
3813 }
3814 else if ((!strcasecmp(keyword, "cmyk_8") && !ippContainsString(attr, "cmyk_16")) || !strcmp(keyword, "DEVCMYK32"))
3815 {
3816 PRINTF_COLORMODEL
3817
3818 PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 8)
3819 }
3820 else if (!strcasecmp(keyword, "cmyk_16") || !strcmp(keyword, "DEVCMYK32-64") || !strcmp(keyword, "DEVCMYK64"))
3821 {
3822 PRINTF_COLORMODEL
3823
3824 PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 16)
3825 }
3826 else if ((!strcasecmp(keyword, "rgb_8") && ippContainsString(attr, "rgb_16")) || !strcmp(keyword, "DEVRGB24"))
3827 {
3828 PRINTF_COLORMODEL
3829
3830 PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 8)
3831 }
3832 else if (!strcasecmp(keyword, "rgb_16") || !strcmp(keyword, "DEVRGB24-48") || !strcmp(keyword, "DEVRGB48"))
3833 {
3834 PRINTF_COLORMODEL
3835
3836 PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 16)
3837 }
3838 }
3839
3840 if (default_color)
3841 cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color);
3842 if (wrote_color)
3843 cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
3844 }
3845
3846 /*
3847 * Duplex...
3848 */
3849
3850 if ((attr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL && ippContainsString(attr, "two-sided-long-edge"))
3851 {
3852 cupsFilePrintf(fp, "*OpenUI *Duplex: PickOne\n"
3853 "*OrderDependency: 10 AnySetup *Duplex\n"
3854 "*%s.Translation Duplex/%s: \"\"\n"
3855 "*DefaultDuplex: None\n"
3856 "*Duplex None: \"<</Duplex false>>setpagedevice\"\n"
3857 "*%s.Duplex None/%s: \"\"\n"
3858 "*Duplex DuplexNoTumble: \"<</Duplex true/Tumble false>>setpagedevice\"\n"
3859 "*%s.Duplex DuplexNoTumble/%s: \"\"\n"
3860 "*Duplex DuplexTumble: \"<</Duplex true/Tumble true>>setpagedevice\"\n"
3861 "*%s.Duplex DuplexTumble/%s: \"\"\n"
3862 "*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)")));
3863
3864 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
3865 {
3866 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3867 {
3868 const char *dm = ippGetString(attr, i, NULL);
3869 /* DM value */
3870
3871 if (!_cups_strcasecmp(dm, "DM1"))
3872 {
3873 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3874 break;
3875 }
3876 else if (!_cups_strcasecmp(dm, "DM2"))
3877 {
3878 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3879 break;
3880 }
3881 else if (!_cups_strcasecmp(dm, "DM3"))
3882 {
3883 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3884 break;
3885 }
3886 else if (!_cups_strcasecmp(dm, "DM4"))
3887 {
3888 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3889 break;
3890 }
3891 }
3892 }
3893 else if ((attr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
3894 {
3895 keyword = ippGetString(attr, 0, NULL);
3896
3897 if (!strcmp(keyword, "flipped"))
3898 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3899 else if (!strcmp(keyword, "manual-tumble"))
3900 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3901 else if (!strcmp(keyword, "normal"))
3902 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3903 else
3904 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3905 }
3906 }
3907
3908 /*
3909 * Output bin...
3910 */
3911
3912 if ((attr = ippFindAttribute(response, "output-bin-default", IPP_TAG_ZERO)) != NULL)
3913 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3914 else
3915 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3916
3917 if ((attr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3918 {
3919 ipp_attribute_t *trays = ippFindAttribute(response, "printer-output-tray", IPP_TAG_STRING);
3920 /* printer-output-tray attribute, if any */
3921 const char *tray_ptr; /* printer-output-tray value */
3922 int tray_len; /* Len of printer-output-tray value */
3923 char tray[IPP_MAX_OCTETSTRING];
3924 /* printer-output-tray string value */
3925
3926 cupsFilePrintf(fp, "*OpenUI *OutputBin: PickOne\n"
3927 "*OrderDependency: 10 AnySetup *OutputBin\n"
3928 "*DefaultOutputBin: %s\n", ppdname);
3929 if (!strcmp(ppdname, "FaceUp"))
3930 cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n");
3931 else
3932 cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n");
3933
3934 for (i = 0; i < count; i ++)
3935 {
3936 keyword = ippGetString(attr, i, NULL);
3937
3938 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3939
3940 snprintf(msgid, sizeof(msgid), "output-bin.%s", keyword);
3941 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
3942 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
3943 msgstr = keyword;
3944
3945 cupsFilePrintf(fp, "*OutputBin %s: \"\"\n", ppdname);
3946 cupsFilePrintf(fp, "*%s.OutputBin %s/%s: \"\"\n", lang->language, ppdname, msgstr);
3947
3948 if ((tray_ptr = ippGetOctetString(trays, i, &tray_len)) != NULL)
3949 {
3950 if (tray_len >= (int)sizeof(tray))
3951 tray_len = (int)sizeof(tray) - 1;
3952
3953 memcpy(tray, tray_ptr, (size_t)tray_len);
3954 tray[tray_len] = '\0';
3955
3956 if (strstr(tray, "stackingorder=lastToFirst;"))
3957 cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
3958 else
3959 cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
3960 }
3961 else if (!strcmp(ppdname, "FaceUp"))
3962 cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
3963 else
3964 cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
3965 }
3966 cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
3967 }
3968
3969 /*
3970 * Finishing options...
3971 */
3972
3973 if ((attr = ippFindAttribute(response, "finishings-supported", IPP_TAG_ENUM)) != NULL)
3974 {
3975 int value; /* Enum value */
3976 cups_array_t *names; /* Names we've added */
3977
3978 count = ippGetCount(attr);
3979 names = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
3980 fin_options = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3981
3982 /*
3983 * Staple/Bind/Stitch
3984 */
3985
3986 for (i = 0; i < count; i ++)
3987 {
3988 value = ippGetInteger(attr, i);
3989 keyword = ippEnumString("finishings", value);
3990
3991 if (!strncmp(keyword, "staple-", 7) || !strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch"))
3992 break;
3993 }
3994
3995 if (i < count)
3996 {
3997 cupsArrayAdd(fin_options, "*StapleLocation");
3998
3999 cupsFilePuts(fp, "*OpenUI *StapleLocation: PickOne\n");
4000 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
4001 cupsFilePrintf(fp, "*%s.Translation StapleLocation/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Staple")));
4002 cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
4003 cupsFilePuts(fp, "*StapleLocation None: \"\"\n");
4004 cupsFilePrintf(fp, "*%s.StapleLocation None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4005
4006 for (; i < count; i ++)
4007 {
4008 value = ippGetInteger(attr, i);
4009 keyword = ippEnumString("finishings", value);
4010
4011 if (strncmp(keyword, "staple-", 7) && strncmp(keyword, "bind-", 5) && strncmp(keyword, "edge-stitch-", 12) && strcmp(keyword, "saddle-stitch"))
4012 continue;
4013
4014 if (cupsArrayFind(names, (char *)keyword))
4015 continue; /* Already did this finishing template */
4016
4017 cupsArrayAdd(names, (char *)keyword);
4018
4019 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4020 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4021 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4022 msgstr = keyword;
4023
4024 cupsFilePrintf(fp, "*StapleLocation %s: \"\"\n", keyword);
4025 cupsFilePrintf(fp, "*%s.StapleLocation %s/%s: \"\"\n", lang->language, keyword, msgstr);
4026 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, keyword, keyword);
4027 }
4028
4029 cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
4030 }
4031
4032 /*
4033 * Fold
4034 */
4035
4036 for (i = 0; i < count; i ++)
4037 {
4038 value = ippGetInteger(attr, i);
4039 keyword = ippEnumString("finishings", value);
4040
4041 if (!strncmp(keyword, "fold-", 5))
4042 break;
4043 }
4044
4045 if (i < count)
4046 {
4047 cupsArrayAdd(fin_options, "*FoldType");
4048
4049 cupsFilePuts(fp, "*OpenUI *FoldType: PickOne\n");
4050 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
4051 cupsFilePrintf(fp, "*%s.Translation FoldType/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Fold")));
4052 cupsFilePuts(fp, "*DefaultFoldType: None\n");
4053 cupsFilePuts(fp, "*FoldType None: \"\"\n");
4054 cupsFilePrintf(fp, "*%s.FoldType None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4055
4056 for (; i < count; i ++)
4057 {
4058 value = ippGetInteger(attr, i);
4059 keyword = ippEnumString("finishings", value);
4060
4061 if (strncmp(keyword, "fold-", 5))
4062 continue;
4063
4064 if (cupsArrayFind(names, (char *)keyword))
4065 continue; /* Already did this finishing template */
4066
4067 cupsArrayAdd(names, (char *)keyword);
4068
4069 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4070 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4071 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4072 msgstr = keyword;
4073
4074 cupsFilePrintf(fp, "*FoldType %s: \"\"\n", keyword);
4075 cupsFilePrintf(fp, "*%s.FoldType %s/%s: \"\"\n", lang->language, keyword, msgstr);
4076 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, keyword, keyword);
4077 }
4078
4079 cupsFilePuts(fp, "*CloseUI: *FoldType\n");
4080 }
4081
4082 /*
4083 * Punch
4084 */
4085
4086 for (i = 0; i < count; i ++)
4087 {
4088 value = ippGetInteger(attr, i);
4089 keyword = ippEnumString("finishings", value);
4090
4091 if (!strncmp(keyword, "punch-", 6))
4092 break;
4093 }
4094
4095 if (i < count)
4096 {
4097 cupsArrayAdd(fin_options, "*PunchMedia");
4098
4099 cupsFilePuts(fp, "*OpenUI *PunchMedia: PickOne\n");
4100 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
4101 cupsFilePrintf(fp, "*%s.Translation PunchMedia/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Punch")));
4102 cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
4103 cupsFilePuts(fp, "*PunchMedia None: \"\"\n");
4104 cupsFilePrintf(fp, "*%s.PunchMedia None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4105
4106 for (i = 0; i < count; i ++)
4107 {
4108 value = ippGetInteger(attr, i);
4109 keyword = ippEnumString("finishings", value);
4110
4111 if (strncmp(keyword, "punch-", 6))
4112 continue;
4113
4114 if (cupsArrayFind(names, (char *)keyword))
4115 continue; /* Already did this finishing template */
4116
4117 cupsArrayAdd(names, (char *)keyword);
4118
4119 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4120 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4121 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4122 msgstr = keyword;
4123
4124 cupsFilePrintf(fp, "*PunchMedia %s: \"\"\n", keyword);
4125 cupsFilePrintf(fp, "*%s.PunchMedia %s/%s: \"\"\n", lang->language, keyword, msgstr);
4126 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, keyword, keyword);
4127 }
4128
4129 cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
4130 }
4131
4132 /*
4133 * Booklet
4134 */
4135
4136 if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER))
4137 {
4138 cupsArrayAdd(fin_options, "*Booklet");
4139
4140 cupsFilePuts(fp, "*OpenUI *Booklet: Boolean\n");
4141 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
4142 cupsFilePrintf(fp, "*%s.Translation Booklet/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Booklet")));
4143 cupsFilePuts(fp, "*DefaultBooklet: False\n");
4144 cupsFilePuts(fp, "*Booklet False: \"\"\n");
4145 cupsFilePuts(fp, "*Booklet True: \"\"\n");
4146 cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n", IPP_FINISHINGS_BOOKLET_MAKER);
4147 cupsFilePuts(fp, "*CloseUI: *Booklet\n");
4148 }
4149
4150 cupsArrayDelete(names);
4151 }
4152
4153 if ((attr = ippFindAttribute(response, "finishings-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4154 {
4155 ipp_t *finishing_col; /* Current finishing collection */
4156 ipp_attribute_t *finishing_attr; /* Current finishing member attribute */
4157 cups_array_t *templates; /* Finishing templates */
4158
4159 cupsFilePuts(fp, "*OpenUI *cupsFinishingTemplate: PickOne\n");
4160 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n");
4161 cupsFilePrintf(fp, "*%s.Translation cupsFinishingTemplate/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Finishing Preset")));
4162 cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n");
4163 cupsFilePuts(fp, "*cupsFinishingTemplate none: \"\"\n");
4164 cupsFilePrintf(fp, "*%s.cupsFinishingTemplate none/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
4165
4166 templates = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4167 count = ippGetCount(attr);
4168
4169 for (i = 0; i < count; i ++)
4170 {
4171 finishing_col = ippGetCollection(attr, i);
4172 keyword = ippGetString(ippFindAttribute(finishing_col, "finishing-template", IPP_TAG_ZERO), 0, NULL);
4173
4174 if (!keyword || cupsArrayFind(templates, (void *)keyword))
4175 continue;
4176
4177 if (strncmp(keyword, "fold-", 5) && (strstr(keyword, "-bottom") || strstr(keyword, "-left") || strstr(keyword, "-right") || strstr(keyword, "-top")))
4178 continue;
4179
4180 cupsArrayAdd(templates, (void *)keyword);
4181
4182 snprintf(msgid, sizeof(msgid), "finishing-template.%s", keyword);
4183 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4184 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4185 msgstr = keyword;
4186
4187 cupsFilePrintf(fp, "*cupsFinishingTemplate %s: \"\n", keyword);
4188 for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr; finishing_attr = ippNextAttribute(finishing_col))
4189 {
4190 if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION)
4191 {
4192 const char *name = ippGetName(finishing_attr);
4193 /* Member attribute name */
4194
4195 if (strcmp(name, "media-size"))
4196 cupsFilePrintf(fp, "%% %s\n", name);
4197 }
4198 }
4199 cupsFilePuts(fp, "\"\n");
4200 cupsFilePrintf(fp, "*%s.cupsFinishingTemplate %s/%s: \"\"\n", lang->language, keyword, msgstr);
4201 cupsFilePuts(fp, "*End\n");
4202 }
4203
4204 cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n");
4205
4206 if (cupsArrayCount(fin_options))
4207 {
4208 const char *fin_option; /* Current finishing option */
4209
4210 cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate");
4211 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4212 cupsFilePrintf(fp, " %s", fin_option);
4213 cupsFilePuts(fp, "\"\n");
4214
4215 cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate None");
4216 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4217 cupsFilePrintf(fp, " %s None", fin_option);
4218 cupsFilePuts(fp, "\"\n");
4219 }
4220
4221 cupsArrayDelete(templates);
4222 }
4223
4224 cupsArrayDelete(fin_options);
4225
4226 /*
4227 * cupsPrintQuality and DefaultResolution...
4228 */
4229
4230 quality = ippFindAttribute(response, "print-quality-supported", IPP_TAG_ENUM);
4231
4232 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
4233 {
4234 int lowdpi = 0, hidpi = 0; /* Lower and higher resolution */
4235
4236 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4237 {
4238 const char *rs = ippGetString(attr, i, NULL);
4239 /* RS value */
4240
4241 if (_cups_strncasecmp(rs, "RS", 2))
4242 continue;
4243
4244 lowdpi = atoi(rs + 2);
4245 if ((rs = strrchr(rs, '-')) != NULL)
4246 hidpi = atoi(rs + 1);
4247 else
4248 hidpi = lowdpi;
4249 break;
4250 }
4251
4252 if (lowdpi == 0)
4253 {
4254 /*
4255 * Invalid "urf-supported" value...
4256 */
4257
4258 goto bad_ppd;
4259 }
4260 else
4261 {
4262 /*
4263 * Generate print qualities based on low and high DPIs...
4264 */
4265
4266 cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi);
4267
4268 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4269 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4270 "*%s.Translation cupsPrintQuality/%s: \"\"\n"
4271 "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
4272 if ((lowdpi & 1) == 0)
4273 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", lowdpi, lowdpi / 2, lang->language, _cupsLangString(lang, _("Draft")));
4274 else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4275 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", lowdpi, lowdpi, lang->language, _cupsLangString(lang, _("Draft")));
4276
4277 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Normal/%s: \"\"\n", lowdpi, lowdpi, lang->language, _cupsLangString(lang, _("Normal")));
4278
4279 if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4280 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality High/%s: \"\"\n", hidpi, hidpi, lang->language, _cupsLangString(lang, _("High")));
4281 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4282 }
4283 }
4284 else if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
4285 {
4286 /*
4287 * Make a sorted list of resolutions.
4288 */
4289
4290 count = ippGetCount(attr);
4291 if (count > (int)(sizeof(resolutions) / sizeof(resolutions[0])))
4292 count = (int)(sizeof(resolutions) / sizeof(resolutions[0]));
4293
4294 resolutions[0] = 0; /* Not in loop to silence Clang static analyzer... */
4295 for (i = 1; i < count; i ++)
4296 resolutions[i] = i;
4297
4298 for (i = 0; i < (count - 1); i ++)
4299 {
4300 for (j = i + 1; j < count; j ++)
4301 {
4302 int ix, iy, /* First X and Y resolution */
4303 jx, jy, /* Second X and Y resolution */
4304 temp; /* Swap variable */
4305 ipp_res_t units; /* Resolution units */
4306
4307 ix = ippGetResolution(attr, resolutions[i], &iy, &units);
4308 jx = ippGetResolution(attr, resolutions[j], &jy, &units);
4309
4310 if (ix > jx || (ix == jx && iy > jy))
4311 {
4312 /*
4313 * Swap these two resolutions...
4314 */
4315
4316 temp = resolutions[i];
4317 resolutions[i] = resolutions[j];
4318 resolutions[j] = temp;
4319 }
4320 }
4321 }
4322
4323 /*
4324 * Generate print quality options...
4325 */
4326
4327 pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, ppdname, sizeof(ppdname));
4328 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4329
4330 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4331 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4332 "*%s.Translation cupsPrintQuality/%s: \"\"\n"
4333 "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
4334 if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4335 {
4336 pwg_ppdize_resolution(attr, resolutions[0], &xres, &yres, NULL, 0);
4337 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4338 cupsFilePrintf(fp, "*%s.cupsPrintQuality Draft/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Draft")));
4339 }
4340
4341 pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, NULL, 0);
4342 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4343 cupsFilePrintf(fp, "*%s.cupsPrintQuality Normal/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Normal")));
4344
4345 if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4346 {
4347 pwg_ppdize_resolution(attr, resolutions[count - 1], &xres, &yres, NULL, 0);
4348 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4349 cupsFilePrintf(fp, "*%s.cupsPrintQuality High/%s: \"\"\n", lang->language, _cupsLangString(lang, _("High")));
4350 }
4351
4352 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4353 }
4354 else if (is_apple || is_pwg)
4355 goto bad_ppd;
4356 else
4357 {
4358 if ((attr = ippFindAttribute(response, "printer-resolution-default", IPP_TAG_RESOLUTION)) != NULL)
4359 {
4360 pwg_ppdize_resolution(attr, 0, &xres, &yres, ppdname, sizeof(ppdname));
4361 }
4362 else
4363 {
4364 xres = yres = 300;
4365 strlcpy(ppdname, "300dpi", sizeof(ppdname));
4366 }
4367
4368 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4369
4370 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4371 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4372 "*%s.Translation cupsPrintQuality/%s: \"\"\n"
4373 "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
4374 if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4375 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("Draft")));
4376
4377 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Normal/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("Normal")));
4378
4379 if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
4380 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality High/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("High")));
4381 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4382 }
4383
4384 /*
4385 * Presets...
4386 */
4387
4388 if ((attr = ippFindAttribute(response, "job-presets-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4389 {
4390 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4391 {
4392 ipp_t *preset = ippGetCollection(attr, i);
4393 /* Preset collection */
4394 const char *preset_name = ippGetString(ippFindAttribute(preset, "preset-name", IPP_TAG_ZERO), 0, NULL),
4395 /* Preset name */
4396 *localized_name; /* Localized preset name */
4397 ipp_attribute_t *member; /* Member attribute in preset */
4398 const char *member_name; /* Member attribute name */
4399 char member_value[256]; /* Member attribute value */
4400
4401 if (!preset || !preset_name)
4402 continue;
4403
4404 cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", preset_name);
4405 for (member = ippFirstAttribute(preset); member; member = ippNextAttribute(preset))
4406 {
4407 member_name = ippGetName(member);
4408
4409 if (!member_name || !strcmp(member_name, "preset-name"))
4410 continue;
4411
4412 if (!strcmp(member_name, "finishings"))
4413 {
4414 for (i = 0, count = ippGetCount(member); i < count; i ++)
4415 {
4416 const char *option = NULL; /* PPD option name */
4417
4418 keyword = ippEnumString("finishings", ippGetInteger(member, i));
4419
4420 if (!strcmp(keyword, "booklet-maker"))
4421 {
4422 option = "Booklet";
4423 keyword = "True";
4424 }
4425 else if (!strncmp(keyword, "fold-", 5))
4426 option = "FoldType";
4427 else if (!strncmp(keyword, "punch-", 6))
4428 option = "PunchMedia";
4429 else if (!strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch") || !strncmp(keyword, "staple-", 7))
4430 option = "StapleLocation";
4431
4432 if (option && keyword)
4433 cupsFilePrintf(fp, "*%s %s\n", option, keyword);
4434 }
4435 }
4436 else if (!strcmp(member_name, "finishings-col"))
4437 {
4438 ipp_t *fin_col; /* finishings-col value */
4439
4440 for (i = 0, count = ippGetCount(member); i < count; i ++)
4441 {
4442 fin_col = ippGetCollection(member, i);
4443
4444 if ((keyword = ippGetString(ippFindAttribute(fin_col, "finishing-template", IPP_TAG_ZERO), 0, NULL)) != NULL)
4445 cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", keyword);
4446 }
4447 }
4448 else if (!strcmp(member_name, "media"))
4449 {
4450 /*
4451 * Map media to PageSize...
4452 */
4453
4454 if ((pwg = pwgMediaForPWG(ippGetString(member, 0, NULL))) != NULL && pwg->ppd)
4455 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4456 }
4457 else if (!strcmp(member_name, "media-col"))
4458 {
4459 media_col = ippGetCollection(member, 0);
4460
4461 if ((media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0)) != NULL)
4462 {
4463 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
4464 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
4465 if ((pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL && pwg->ppd)
4466 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4467 }
4468
4469 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL)) != NULL)
4470 {
4471 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4472 cupsFilePrintf(fp, "*InputSlot %s\n", keyword);
4473 }
4474
4475 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL)) != NULL)
4476 {
4477 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4478 cupsFilePrintf(fp, "*MediaType %s\n", keyword);
4479 }
4480 }
4481 else if (!strcmp(member_name, "print-quality"))
4482 {
4483 /*
4484 * Map print-quality to cupsPrintQuality...
4485 */
4486
4487 int qval = ippGetInteger(member, 0);
4488 /* print-quality value */
4489 static const char * const qualities[] = { "Draft", "Normal", "High" };
4490 /* cupsPrintQuality values */
4491
4492 if (qval >= IPP_QUALITY_DRAFT && qval <= IPP_QUALITY_HIGH)
4493 cupsFilePrintf(fp, "*cupsPrintQuality %s\n", qualities[qval - IPP_QUALITY_DRAFT]);
4494 }
4495 else if (!strcmp(member_name, "output-bin"))
4496 {
4497 pwg_ppdize_name(ippGetString(member, 0, NULL), ppdname, sizeof(ppdname));
4498 cupsFilePrintf(fp, "*OutputBin %s\n", ppdname);
4499 }
4500 else if (!strcmp(member_name, "sides"))
4501 {
4502 keyword = ippGetString(member, 0, NULL);
4503 if (keyword && !strcmp(keyword, "one-sided"))
4504 cupsFilePuts(fp, "*Duplex None\n");
4505 else if (keyword && !strcmp(keyword, "two-sided-long-edge"))
4506 cupsFilePuts(fp, "*Duplex DuplexNoTumble\n");
4507 else if (keyword && !strcmp(keyword, "two-sided-short-edge"))
4508 cupsFilePuts(fp, "*Duplex DuplexTumble\n");
4509 }
4510 else
4511 {
4512 /*
4513 * Add attribute name and value as-is...
4514 */
4515
4516 ippAttributeString(member, member_value, sizeof(member_value));
4517 cupsFilePrintf(fp, "*%s %s\n", member_name, member_value);
4518 }
4519 }
4520
4521 cupsFilePuts(fp, "\"\n*End\n");
4522
4523 if ((localized_name = _cupsMessageLookup(strings, preset_name)) != preset_name)
4524 cupsFilePrintf(fp, "*%s.APPrinterPreset %s/%s: \"\"\n", lang->language, preset_name, localized_name);
4525 }
4526 }
4527
4528 /*
4529 * Close up and return...
4530 */
4531
4532 cupsFileClose(fp);
4533
4534 return (buffer);
4535
4536 /*
4537 * If we get here then there was a problem creating the PPD...
4538 */
4539
4540 bad_ppd:
4541
4542 cupsFileClose(fp);
4543 unlink(buffer);
4544 *buffer = '\0';
4545
4546 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Printer does not support required IPP attributes or document formats."), 1);
4547
4548 return (NULL);
4549 }
4550
4551
4552 /*
4553 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
4554 * media-source.
4555 */
4556
4557 const char * /* O - InputSlot name */
4558 _pwgInputSlotForSource(
4559 const char *media_source, /* I - PWG media-source */
4560 char *name, /* I - Name buffer */
4561 size_t namesize) /* I - Size of name buffer */
4562 {
4563 /*
4564 * Range check input...
4565 */
4566
4567 if (!media_source || !name || namesize < PPD_MAX_NAME)
4568 return (NULL);
4569
4570 if (_cups_strcasecmp(media_source, "main"))
4571 strlcpy(name, "Cassette", namesize);
4572 else if (_cups_strcasecmp(media_source, "alternate"))
4573 strlcpy(name, "Multipurpose", namesize);
4574 else if (_cups_strcasecmp(media_source, "large-capacity"))
4575 strlcpy(name, "LargeCapacity", namesize);
4576 else if (_cups_strcasecmp(media_source, "bottom"))
4577 strlcpy(name, "Lower", namesize);
4578 else if (_cups_strcasecmp(media_source, "middle"))
4579 strlcpy(name, "Middle", namesize);
4580 else if (_cups_strcasecmp(media_source, "top"))
4581 strlcpy(name, "Upper", namesize);
4582 else if (_cups_strcasecmp(media_source, "rear"))
4583 strlcpy(name, "Rear", namesize);
4584 else if (_cups_strcasecmp(media_source, "side"))
4585 strlcpy(name, "Side", namesize);
4586 else if (_cups_strcasecmp(media_source, "envelope"))
4587 strlcpy(name, "Envelope", namesize);
4588 else if (_cups_strcasecmp(media_source, "main-roll"))
4589 strlcpy(name, "Roll", namesize);
4590 else if (_cups_strcasecmp(media_source, "alternate-roll"))
4591 strlcpy(name, "Roll2", namesize);
4592 else
4593 pwg_ppdize_name(media_source, name, namesize);
4594
4595 return (name);
4596 }
4597
4598
4599 /*
4600 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
4601 * media-type.
4602 */
4603
4604 const char * /* O - MediaType name */
4605 _pwgMediaTypeForType(
4606 const char *media_type, /* I - PWG media-type */
4607 char *name, /* I - Name buffer */
4608 size_t namesize) /* I - Size of name buffer */
4609 {
4610 /*
4611 * Range check input...
4612 */
4613
4614 if (!media_type || !name || namesize < PPD_MAX_NAME)
4615 return (NULL);
4616
4617 if (_cups_strcasecmp(media_type, "auto"))
4618 strlcpy(name, "Auto", namesize);
4619 else if (_cups_strcasecmp(media_type, "cardstock"))
4620 strlcpy(name, "Cardstock", namesize);
4621 else if (_cups_strcasecmp(media_type, "envelope"))
4622 strlcpy(name, "Envelope", namesize);
4623 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
4624 strlcpy(name, "Glossy", namesize);
4625 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
4626 strlcpy(name, "HighGloss", namesize);
4627 else if (_cups_strcasecmp(media_type, "photographic-matte"))
4628 strlcpy(name, "Matte", namesize);
4629 else if (_cups_strcasecmp(media_type, "stationery"))
4630 strlcpy(name, "Plain", namesize);
4631 else if (_cups_strcasecmp(media_type, "stationery-coated"))
4632 strlcpy(name, "Coated", namesize);
4633 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
4634 strlcpy(name, "Inkjet", namesize);
4635 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
4636 strlcpy(name, "Letterhead", namesize);
4637 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
4638 strlcpy(name, "Preprinted", namesize);
4639 else if (_cups_strcasecmp(media_type, "transparency"))
4640 strlcpy(name, "Transparency", namesize);
4641 else
4642 pwg_ppdize_name(media_type, name, namesize);
4643
4644 return (name);
4645 }
4646
4647
4648 /*
4649 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
4650 */
4651
4652 const char * /* O - PageSize name */
4653 _pwgPageSizeForMedia(
4654 pwg_media_t *media, /* I - Media */
4655 char *name, /* I - PageSize name buffer */
4656 size_t namesize) /* I - Size of name buffer */
4657 {
4658 const char *sizeptr, /* Pointer to size in PWG name */
4659 *dimptr; /* Pointer to dimensions in PWG name */
4660
4661
4662 /*
4663 * Range check input...
4664 */
4665
4666 if (!media || !name || namesize < PPD_MAX_NAME)
4667 return (NULL);
4668
4669 /*
4670 * Copy or generate a PageSize name...
4671 */
4672
4673 if (media->ppd)
4674 {
4675 /*
4676 * Use a standard Adobe name...
4677 */
4678
4679 strlcpy(name, media->ppd, namesize);
4680 }
4681 else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
4682 (sizeptr = strchr(media->pwg, '_')) == NULL ||
4683 (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
4684 (size_t)(dimptr - sizeptr) > namesize)
4685 {
4686 /*
4687 * Use a name of the form "wNNNhNNN"...
4688 */
4689
4690 snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
4691 (int)PWG_TO_POINTS(media->length));
4692 }
4693 else
4694 {
4695 /*
4696 * Copy the size name from class_sizename_dimensions...
4697 */
4698
4699 memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
4700 name[dimptr - sizeptr - 1] = '\0';
4701 }
4702
4703 return (name);
4704 }
4705
4706
4707 /*
4708 * 'cups_get_url()' - Get a copy of the file at the given URL.
4709 */
4710
4711 static int /* O - 1 on success, 0 on failure */
4712 cups_get_url(http_t **http, /* IO - Current HTTP connection */
4713 const char *url, /* I - URL to get */
4714 char *name, /* I - Temporary filename */
4715 size_t namesize) /* I - Size of temporary filename buffer */
4716 {
4717 char scheme[32], /* URL scheme */
4718 userpass[256], /* URL username:password */
4719 host[256], /* URL host */
4720 curhost[256], /* Current host */
4721 resource[256]; /* URL resource */
4722 int port; /* URL port */
4723 http_encryption_t encryption; /* Type of encryption to use */
4724 http_status_t status; /* Status of GET request */
4725 int fd; /* Temporary file */
4726
4727
4728 if (httpSeparateURI(HTTP_URI_CODING_ALL, url, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
4729 return (0);
4730
4731 if (port == 443 || !strcmp(scheme, "https"))
4732 encryption = HTTP_ENCRYPTION_ALWAYS;
4733 else
4734 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
4735
4736 if (!*http || strcasecmp(host, httpGetHostname(*http, curhost, sizeof(curhost))) || httpAddrPort(httpGetAddress(*http)) != port)
4737 {
4738 httpClose(*http);
4739 *http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 5000, NULL);
4740 }
4741
4742 if (!*http)
4743 return (0);
4744
4745 if ((fd = cupsTempFd(name, (int)namesize)) < 0)
4746 return (0);
4747
4748 status = cupsGetFd(*http, resource, fd);
4749
4750 close(fd);
4751
4752 if (status != HTTP_STATUS_OK)
4753 {
4754 unlink(name);
4755 *name = '\0';
4756 return (0);
4757 }
4758
4759 return (1);
4760 }
4761
4762
4763 /*
4764 * 'pwg_add_finishing()' - Add a finishings value.
4765 */
4766
4767 static void
4768 pwg_add_finishing(
4769 cups_array_t *finishings, /* I - Finishings array */
4770 ipp_finishings_t template, /* I - Finishing template */
4771 const char *name, /* I - PPD option */
4772 const char *value) /* I - PPD choice */
4773 {
4774 _pwg_finishings_t *f; /* New finishings value */
4775
4776
4777 if ((f = (_pwg_finishings_t *)calloc(1, sizeof(_pwg_finishings_t))) != NULL)
4778 {
4779 f->value = template;
4780 f->num_options = cupsAddOption(name, value, 0, &f->options);
4781
4782 cupsArrayAdd(finishings, f);
4783 }
4784 }
4785
4786
4787 /*
4788 * 'pwg_add_message()' - Add a message to the PPD cached strings.
4789 */
4790
4791 static void
4792 pwg_add_message(cups_array_t *a, /* I - Message catalog */
4793 const char *msg, /* I - Message identifier */
4794 const char *str) /* I - Localized string */
4795 {
4796 _cups_message_t *m; /* New message */
4797
4798
4799 if ((m = calloc(1, sizeof(_cups_message_t))) != NULL)
4800 {
4801 m->msg = strdup(msg);
4802 m->str = strdup(str);
4803 cupsArrayAdd(a, m);
4804 }
4805 }
4806
4807
4808 /*
4809 * 'pwg_compare_finishings()' - Compare two finishings values.
4810 */
4811
4812 static int /* O - Result of comparison */
4813 pwg_compare_finishings(
4814 _pwg_finishings_t *a, /* I - First finishings value */
4815 _pwg_finishings_t *b) /* I - Second finishings value */
4816 {
4817 return ((int)b->value - (int)a->value);
4818 }
4819
4820
4821 /*
4822 * 'pwg_compare_sizes()' - Compare two media sizes...
4823 */
4824
4825 static int /* O - Result of comparison */
4826 pwg_compare_sizes(cups_size_t *a, /* I - First media size */
4827 cups_size_t *b) /* I - Second media size */
4828 {
4829 return (strcmp(a->media, b->media));
4830 }
4831
4832
4833 /*
4834 * 'pwg_copy_size()' - Copy a media size.
4835 */
4836
4837 static cups_size_t * /* O - New media size */
4838 pwg_copy_size(cups_size_t *size) /* I - Media size to copy */
4839 {
4840 cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t));
4841 /* New media size */
4842
4843 if (newsize)
4844 memcpy(newsize, size, sizeof(cups_size_t));
4845
4846 return (newsize);
4847 }
4848
4849
4850 /*
4851 * 'pwg_free_finishings()' - Free a finishings value.
4852 */
4853
4854 static void
4855 pwg_free_finishings(
4856 _pwg_finishings_t *f) /* I - Finishings value */
4857 {
4858 cupsFreeOptions(f->num_options, f->options);
4859 free(f);
4860 }
4861
4862
4863 /*
4864 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
4865 */
4866
4867 static void
4868 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
4869 char *name, /* I - Name buffer */
4870 size_t namesize) /* I - Size of name buffer */
4871 {
4872 char *ptr, /* Pointer into name buffer */
4873 *end; /* End of name buffer */
4874
4875
4876 if (!ipp)
4877 {
4878 *name = '\0';
4879 return;
4880 }
4881
4882 *name = (char)toupper(*ipp++);
4883
4884 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
4885 {
4886 if (*ipp == '-' && _cups_isalnum(ipp[1]))
4887 {
4888 ipp ++;
4889 *ptr++ = (char)toupper(*ipp++ & 255);
4890 }
4891 else
4892 *ptr++ = *ipp++;
4893 }
4894
4895 *ptr = '\0';
4896 }
4897
4898
4899 /*
4900 * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values.
4901 */
4902
4903 static void
4904 pwg_ppdize_resolution(
4905 ipp_attribute_t *attr, /* I - Attribute to convert */
4906 int element, /* I - Element to convert */
4907 int *xres, /* O - X resolution in DPI */
4908 int *yres, /* O - Y resolution in DPI */
4909 char *name, /* I - Name buffer */
4910 size_t namesize) /* I - Size of name buffer */
4911 {
4912 ipp_res_t units; /* Units for resolution */
4913
4914
4915 *xres = ippGetResolution(attr, element, yres, &units);
4916
4917 if (units == IPP_RES_PER_CM)
4918 {
4919 *xres = (int)(*xres * 2.54);
4920 *yres = (int)(*yres * 2.54);
4921 }
4922
4923 if (name && namesize > 4)
4924 {
4925 if (*xres == *yres)
4926 snprintf(name, namesize, "%ddpi", *xres);
4927 else
4928 snprintf(name, namesize, "%dx%ddpi", *xres, *yres);
4929 }
4930 }
4931
4932
4933 /*
4934 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
4935 */
4936
4937 static void
4938 pwg_unppdize_name(const char *ppd, /* I - PPD keyword */
4939 char *name, /* I - Name buffer */
4940 size_t namesize, /* I - Size of name buffer */
4941 const char *dashchars)/* I - Characters to be replaced by dashes */
4942 {
4943 char *ptr, /* Pointer into name buffer */
4944 *end; /* End of name buffer */
4945
4946
4947 if (_cups_islower(*ppd))
4948 {
4949 /*
4950 * Already lowercase name, use as-is?
4951 */
4952
4953 const char *ppdptr; /* Pointer into PPD keyword */
4954
4955 for (ppdptr = ppd + 1; *ppdptr; ppdptr ++)
4956 if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr))
4957 break;
4958
4959 if (!*ppdptr)
4960 {
4961 strlcpy(name, ppd, namesize);
4962 return;
4963 }
4964 }
4965
4966 for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
4967 {
4968 if (_cups_isalnum(*ppd) || *ppd == '-')
4969 *ptr++ = (char)tolower(*ppd & 255);
4970 else if (strchr(dashchars, *ppd))
4971 *ptr++ = '-';
4972 else
4973 *ptr++ = *ppd;
4974
4975 if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
4976 _cups_isupper(ppd[1]) && ptr < end)
4977 *ptr++ = '-';
4978 else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
4979 *ptr++ = '-';
4980 }
4981
4982 *ptr = '\0';
4983 }