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