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