]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd-cache.c
Don't exit under memory pressure (<rdar://problem/23255001>)
[thirdparty/cups.git] / cups / ppd-cache.c
1 /*
2 * "$Id$"
3 *
4 * PPD cache implementation for CUPS.
5 *
6 * Copyright 2010-2015 by Apple Inc.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 */
16
17 /*
18 * Include necessary headers...
19 */
20
21 #include "cups-private.h"
22 #include <math.h>
23
24
25 /*
26 * Macro to test for two almost-equal PWG measurements.
27 */
28
29 #define _PWG_EQUIVALENT(x, y) (abs((x)-(y)) < 2)
30
31
32 /*
33 * Local functions...
34 */
35
36 static int pwg_compare_finishings(_pwg_finishings_t *a,
37 _pwg_finishings_t *b);
38 static void pwg_free_finishings(_pwg_finishings_t *f);
39 static void pwg_free_material(_pwg_material_t *m);
40 static void pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
41 static void pwg_ppdize_resolution(ipp_attribute_t *attr, int element, int *xres, int *yres, char *name, size_t namesize);
42 static void pwg_unppdize_name(const char *ppd, char *name, size_t namesize,
43 const char *dashchars);
44
45
46 /*
47 * '_cupsConvertOptions()' - Convert printer options to standard IPP attributes.
48 *
49 * This functions converts PPD and CUPS-specific options to their standard IPP
50 * attributes and values and adds them to the specified IPP request.
51 */
52
53 int /* O - New number of copies */
54 _cupsConvertOptions(ipp_t *request, /* I - IPP request */
55 ppd_file_t *ppd, /* I - PPD file */
56 _ppd_cache_t *pc, /* I - PPD cache info */
57 ipp_attribute_t *media_col_sup,
58 /* I - media-col-supported values */
59 ipp_attribute_t *doc_handling_sup,
60 /* I - multiple-document-handling-supported values */
61 ipp_attribute_t *print_color_mode_sup,
62 /* I - Printer supports print-color-mode */
63 const char *user, /* I - User info */
64 const char *format, /* I - document-format value */
65 int copies, /* I - Number of copies */
66 int num_options, /* I - Number of options */
67 cups_option_t *options) /* I - Options */
68 {
69 int i; /* Looping var */
70 const char *keyword, /* PWG keyword */
71 *password; /* Password string */
72 pwg_size_t *size; /* PWG media size */
73 ipp_t *media_col, /* media-col value */
74 *media_size; /* media-size value */
75 const char *media_source, /* media-source value */
76 *media_type, /* media-type value */
77 *collate_str, /* multiple-document-handling value */
78 *color_attr_name, /* Supported color attribute */
79 *mandatory; /* Mandatory attributes */
80 int num_finishings = 0, /* Number of finishing values */
81 finishings[10]; /* Finishing enum values */
82 ppd_choice_t *choice; /* Marked choice */
83
84
85 /*
86 * Send standard IPP attributes...
87 */
88
89 if (pc->password && (password = cupsGetOption("job-password", num_options, options)) != NULL && ippGetOperation(request) != IPP_OP_VALIDATE_JOB)
90 {
91 ipp_attribute_t *attr = NULL; /* job-password attribute */
92
93 if ((keyword = cupsGetOption("job-password-encryption", num_options, options)) == NULL)
94 keyword = "none";
95
96 if (!strcmp(keyword, "none"))
97 {
98 /*
99 * Add plain-text job-password...
100 */
101
102 attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", password, (int)strlen(password));
103 }
104 else
105 {
106 /*
107 * Add hashed job-password...
108 */
109
110 unsigned char hash[64]; /* Hash of password */
111 ssize_t hashlen; /* Length of hash */
112
113 if ((hashlen = cupsHashData(keyword, password, strlen(password), hash, sizeof(hash))) > 0)
114 attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", hash, (int)hashlen);
115 }
116
117 if (attr)
118 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "job-password-encryption", NULL, keyword);
119 }
120
121 if (pc->account_id)
122 {
123 if ((keyword = cupsGetOption("job-account-id", num_options, options)) == NULL)
124 keyword = cupsGetOption("job-billing", num_options, options);
125
126 if (keyword)
127 ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id", NULL, keyword);
128 }
129
130 if (pc->accounting_user_id)
131 {
132 if ((keyword = cupsGetOption("job-accounting-user-id", num_options, options)) == NULL)
133 keyword = user;
134
135 if (keyword)
136 ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-accounting-user-id", NULL, keyword);
137 }
138
139 for (mandatory = (const char *)cupsArrayFirst(pc->mandatory); mandatory; mandatory = (const char *)cupsArrayNext(pc->mandatory))
140 {
141 if (strcmp(mandatory, "copies") &&
142 strcmp(mandatory, "destination-uris") &&
143 strcmp(mandatory, "finishings") &&
144 strcmp(mandatory, "job-account-id") &&
145 strcmp(mandatory, "job-accounting-user-id") &&
146 strcmp(mandatory, "job-password") &&
147 strcmp(mandatory, "job-password-encryption") &&
148 strcmp(mandatory, "media") &&
149 strncmp(mandatory, "media-col", 9) &&
150 strcmp(mandatory, "multiple-document-handling") &&
151 strcmp(mandatory, "output-bin") &&
152 strcmp(mandatory, "print-color-mode") &&
153 strcmp(mandatory, "print-quality") &&
154 strcmp(mandatory, "sides") &&
155 (keyword = cupsGetOption(mandatory, num_options, options)) != NULL)
156 {
157 _ipp_option_t *opt = _ippFindOption(mandatory);
158 /* Option type */
159 ipp_tag_t value_tag = opt ? opt->value_tag : IPP_TAG_NAME;
160 /* Value type */
161
162 switch (value_tag)
163 {
164 case IPP_TAG_INTEGER :
165 case IPP_TAG_ENUM :
166 ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory, atoi(keyword));
167 break;
168 case IPP_TAG_BOOLEAN :
169 ippAddBoolean(request, IPP_TAG_JOB, mandatory, !_cups_strcasecmp(keyword, "true"));
170 break;
171 case IPP_TAG_RANGE :
172 {
173 int lower, upper; /* Range */
174
175 if (sscanf(keyword, "%d-%d", &lower, &upper) != 2)
176 lower = upper = atoi(keyword);
177
178 ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper);
179 }
180 break;
181 case IPP_TAG_STRING :
182 ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword, (int)strlen(keyword));
183 break;
184 default :
185 if (!strcmp(mandatory, "print-color-mode") && !strcmp(keyword, "monochrome"))
186 {
187 if (ippContainsString(print_color_mode_sup, "auto-monochrome"))
188 keyword = "auto-monochrome";
189 else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome"))
190 keyword = "process-monochrome";
191 }
192
193 ippAddString(request, IPP_TAG_JOB, value_tag, mandatory, NULL, keyword);
194 break;
195 }
196 }
197 }
198
199 if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
200 keyword = cupsGetOption("media", num_options, options);
201
202 if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
203 {
204 /*
205 * Add a media-col value...
206 */
207
208 media_size = ippNew();
209 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
210 "x-dimension", size->width);
211 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
212 "y-dimension", size->length);
213
214 media_col = ippNew();
215 ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
216
217 media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
218 num_options,
219 options));
220 media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType",
221 num_options,
222 options));
223
224 for (i = 0; i < media_col_sup->num_values; i ++)
225 {
226 if (!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 (!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 (!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 (!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 (!strcmp(media_col_sup->values[i].string.text, "media-source") && media_source)
235 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-source", NULL, media_source);
236 else if (!strcmp(media_col_sup->values[i].string.text, "media-type") && 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 num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options, (int)(sizeof(finishings) / sizeof(finishings[0])), finishings);
366 if (num_finishings > 0)
367 {
368 ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", num_finishings, finishings);
369
370 if (copies > 1 && (keyword = cupsGetOption("job-impressions", num_options, options)) != NULL)
371 {
372 /*
373 * Send job-pages-per-set attribute to apply finishings correctly...
374 */
375
376 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", atoi(keyword) / copies);
377 }
378 }
379
380 return (copies);
381 }
382
383
384 /*
385 * '_ppdCacheCreateWithFile()' - Create PPD cache and mapping data from a
386 * written file.
387 *
388 * Use the @link _ppdCacheWriteFile@ function to write PWG mapping data to a
389 * file.
390 */
391
392 _ppd_cache_t * /* O - PPD cache and mapping data */
393 _ppdCacheCreateWithFile(
394 const char *filename, /* I - File to read */
395 ipp_t **attrs) /* IO - IPP attributes, if any */
396 {
397 cups_file_t *fp; /* File */
398 _ppd_cache_t *pc; /* PWG mapping data */
399 pwg_size_t *size; /* Current size */
400 pwg_map_t *map; /* Current map */
401 _pwg_finishings_t *finishings; /* Current finishings option */
402 int linenum, /* Current line number */
403 num_bins, /* Number of bins in file */
404 num_sizes, /* Number of sizes in file */
405 num_sources, /* Number of sources in file */
406 num_types; /* Number of types in file */
407 char line[2048], /* Current line */
408 *value, /* Pointer to value in line */
409 *valueptr, /* Pointer into value */
410 pwg_keyword[128], /* PWG keyword */
411 ppd_keyword[PPD_MAX_NAME];
412 /* PPD keyword */
413 _pwg_print_color_mode_t print_color_mode;
414 /* Print color mode for preset */
415 _pwg_print_quality_t print_quality; /* Print quality for preset */
416
417
418 DEBUG_printf(("_ppdCacheCreateWithFile(filename=\"%s\")", filename));
419
420 /*
421 * Range check input...
422 */
423
424 if (attrs)
425 *attrs = NULL;
426
427 if (!filename)
428 {
429 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
430 return (NULL);
431 }
432
433 /*
434 * Open the file...
435 */
436
437 if ((fp = cupsFileOpen(filename, "r")) == NULL)
438 {
439 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
440 return (NULL);
441 }
442
443 /*
444 * Read the first line and make sure it has "#CUPS-PPD-CACHE-version" in it...
445 */
446
447 if (!cupsFileGets(fp, line, sizeof(line)))
448 {
449 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
450 DEBUG_puts("_ppdCacheCreateWithFile: Unable to read first line.");
451 cupsFileClose(fp);
452 return (NULL);
453 }
454
455 if (strncmp(line, "#CUPS-PPD-CACHE-", 16))
456 {
457 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
458 DEBUG_printf(("_ppdCacheCreateWithFile: Wrong first line \"%s\".", line));
459 cupsFileClose(fp);
460 return (NULL);
461 }
462
463 if (atoi(line + 16) != _PPD_CACHE_VERSION)
464 {
465 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of date PPD cache file."), 1);
466 DEBUG_printf(("_ppdCacheCreateWithFile: Cache file has version %s, "
467 "expected %d.", line + 16, _PPD_CACHE_VERSION));
468 cupsFileClose(fp);
469 return (NULL);
470 }
471
472 /*
473 * Allocate the mapping data structure...
474 */
475
476 if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
477 {
478 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
479 DEBUG_puts("_ppdCacheCreateWithFile: Unable to allocate _ppd_cache_t.");
480 goto create_error;
481 }
482
483 pc->max_copies = 9999;
484
485 /*
486 * Read the file...
487 */
488
489 linenum = 0;
490 num_bins = 0;
491 num_sizes = 0;
492 num_sources = 0;
493 num_types = 0;
494
495 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
496 {
497 DEBUG_printf(("_ppdCacheCreateWithFile: line=\"%s\", value=\"%s\", "
498 "linenum=%d", line, value, linenum));
499
500 if (!value)
501 {
502 DEBUG_printf(("_ppdCacheCreateWithFile: Missing value on line %d.",
503 linenum));
504 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
505 goto create_error;
506 }
507 else if (!_cups_strcasecmp(line, "3D"))
508 {
509 pc->cups_3d = _cupsStrAlloc(value);
510 }
511 else if (!_cups_strcasecmp(line, "LayerOrder"))
512 {
513 pc->cups_layer_order = _cupsStrAlloc(value);
514 }
515 else if (!_cups_strcasecmp(line, "Accuracy"))
516 {
517 sscanf(value, "%d%d%d", pc->cups_accuracy + 0, pc->cups_accuracy + 1, pc->cups_accuracy + 2);
518 }
519 else if (!_cups_strcasecmp(line, "Volume"))
520 {
521 sscanf(value, "%d%d%d", pc->cups_volume + 0, pc->cups_volume + 1, pc->cups_volume + 2);
522 }
523 else if (!_cups_strcasecmp(line, "Material"))
524 {
525 /*
526 * Material key "name" name=value ... name=value
527 */
528
529 if ((valueptr = strchr(value, ' ')) != NULL)
530 {
531 _pwg_material_t *material = (_pwg_material_t *)calloc(1, sizeof(_pwg_material_t));
532
533 *valueptr++ = '\0';
534
535 material->key = _cupsStrAlloc(value);
536
537 if (*valueptr == '\"')
538 {
539 value = valueptr + 1;
540 if ((valueptr = strchr(value, '\"')) != NULL)
541 {
542 *valueptr++ = '\0';
543 material->name = _cupsStrAlloc(value);
544 material->num_props = cupsParseOptions(valueptr, 0, &material->props);
545 }
546 }
547
548 if (!pc->materials)
549 pc->materials = cupsArrayNew3(NULL, NULL, NULL, 0, NULL, (cups_afree_func_t)pwg_free_material);
550
551 cupsArrayAdd(pc->materials, material);
552 }
553 }
554 else if (!_cups_strcasecmp(line, "Filter"))
555 {
556 if (!pc->filters)
557 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
558 (cups_acopy_func_t)_cupsStrAlloc,
559 (cups_afree_func_t)_cupsStrFree);
560
561 cupsArrayAdd(pc->filters, value);
562 }
563 else if (!_cups_strcasecmp(line, "PreFilter"))
564 {
565 if (!pc->prefilters)
566 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
567 (cups_acopy_func_t)_cupsStrAlloc,
568 (cups_afree_func_t)_cupsStrFree);
569
570 cupsArrayAdd(pc->prefilters, value);
571 }
572 else if (!_cups_strcasecmp(line, "Product"))
573 {
574 pc->product = _cupsStrAlloc(value);
575 }
576 else if (!_cups_strcasecmp(line, "SingleFile"))
577 {
578 pc->single_file = !_cups_strcasecmp(value, "true");
579 }
580 else if (!_cups_strcasecmp(line, "IPP"))
581 {
582 off_t pos = cupsFileTell(fp), /* Position in file */
583 length = strtol(value, NULL, 10);
584 /* Length of IPP attributes */
585
586 if (attrs && *attrs)
587 {
588 DEBUG_puts("_ppdCacheCreateWithFile: IPP listed multiple times.");
589 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
590 goto create_error;
591 }
592 else if (length <= 0)
593 {
594 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP length.");
595 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
596 goto create_error;
597 }
598
599 if (attrs)
600 {
601 /*
602 * Read IPP attributes into the provided variable...
603 */
604
605 *attrs = ippNew();
606
607 if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
608 *attrs) != IPP_STATE_DATA)
609 {
610 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
611 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
612 goto create_error;
613 }
614 }
615 else
616 {
617 /*
618 * Skip the IPP data entirely...
619 */
620
621 cupsFileSeek(fp, pos + length);
622 }
623
624 if (cupsFileTell(fp) != (pos + length))
625 {
626 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
627 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
628 goto create_error;
629 }
630 }
631 else if (!_cups_strcasecmp(line, "NumBins"))
632 {
633 if (num_bins > 0)
634 {
635 DEBUG_puts("_ppdCacheCreateWithFile: NumBins listed multiple times.");
636 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
637 goto create_error;
638 }
639
640 if ((num_bins = atoi(value)) <= 0 || num_bins > 65536)
641 {
642 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumBins value %d on line "
643 "%d.", num_sizes, linenum));
644 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
645 goto create_error;
646 }
647
648 if ((pc->bins = calloc((size_t)num_bins, sizeof(pwg_map_t))) == NULL)
649 {
650 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d bins.",
651 num_sizes));
652 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
653 goto create_error;
654 }
655 }
656 else if (!_cups_strcasecmp(line, "Bin"))
657 {
658 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
659 {
660 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Bin on line %d.", linenum));
661 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
662 goto create_error;
663 }
664
665 if (pc->num_bins >= num_bins)
666 {
667 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Bin's on line %d.",
668 linenum));
669 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
670 goto create_error;
671 }
672
673 map = pc->bins + pc->num_bins;
674 map->pwg = _cupsStrAlloc(pwg_keyword);
675 map->ppd = _cupsStrAlloc(ppd_keyword);
676
677 pc->num_bins ++;
678 }
679 else if (!_cups_strcasecmp(line, "NumSizes"))
680 {
681 if (num_sizes > 0)
682 {
683 DEBUG_puts("_ppdCacheCreateWithFile: NumSizes listed multiple times.");
684 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
685 goto create_error;
686 }
687
688 if ((num_sizes = atoi(value)) < 0 || num_sizes > 65536)
689 {
690 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSizes value %d on line "
691 "%d.", num_sizes, linenum));
692 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
693 goto create_error;
694 }
695
696 if (num_sizes > 0)
697 {
698 if ((pc->sizes = calloc((size_t)num_sizes, sizeof(pwg_size_t))) == NULL)
699 {
700 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sizes.",
701 num_sizes));
702 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
703 goto create_error;
704 }
705 }
706 }
707 else if (!_cups_strcasecmp(line, "Size"))
708 {
709 if (pc->num_sizes >= num_sizes)
710 {
711 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Size's on line %d.",
712 linenum));
713 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
714 goto create_error;
715 }
716
717 size = pc->sizes + pc->num_sizes;
718
719 if (sscanf(value, "%127s%40s%d%d%d%d%d%d", pwg_keyword, ppd_keyword,
720 &(size->width), &(size->length), &(size->left),
721 &(size->bottom), &(size->right), &(size->top)) != 8)
722 {
723 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Size on line %d.",
724 linenum));
725 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
726 goto create_error;
727 }
728
729 size->map.pwg = _cupsStrAlloc(pwg_keyword);
730 size->map.ppd = _cupsStrAlloc(ppd_keyword);
731
732 pc->num_sizes ++;
733 }
734 else if (!_cups_strcasecmp(line, "CustomSize"))
735 {
736 if (pc->custom_max_width > 0)
737 {
738 DEBUG_printf(("_ppdCacheCreateWithFile: Too many CustomSize's on line "
739 "%d.", linenum));
740 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
741 goto create_error;
742 }
743
744 if (sscanf(value, "%d%d%d%d%d%d%d%d", &(pc->custom_max_width),
745 &(pc->custom_max_length), &(pc->custom_min_width),
746 &(pc->custom_min_length), &(pc->custom_size.left),
747 &(pc->custom_size.bottom), &(pc->custom_size.right),
748 &(pc->custom_size.top)) != 8)
749 {
750 DEBUG_printf(("_ppdCacheCreateWithFile: Bad CustomSize on line %d.",
751 linenum));
752 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
753 goto create_error;
754 }
755
756 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
757 pc->custom_max_width, pc->custom_max_length, NULL);
758 pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
759
760 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
761 pc->custom_min_width, pc->custom_min_length, NULL);
762 pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
763 }
764 else if (!_cups_strcasecmp(line, "SourceOption"))
765 {
766 pc->source_option = _cupsStrAlloc(value);
767 }
768 else if (!_cups_strcasecmp(line, "NumSources"))
769 {
770 if (num_sources > 0)
771 {
772 DEBUG_puts("_ppdCacheCreateWithFile: NumSources listed multiple "
773 "times.");
774 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
775 goto create_error;
776 }
777
778 if ((num_sources = atoi(value)) <= 0 || num_sources > 65536)
779 {
780 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSources value %d on "
781 "line %d.", num_sources, linenum));
782 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
783 goto create_error;
784 }
785
786 if ((pc->sources = calloc((size_t)num_sources, sizeof(pwg_map_t))) == NULL)
787 {
788 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sources.",
789 num_sources));
790 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
791 goto create_error;
792 }
793 }
794 else if (!_cups_strcasecmp(line, "Source"))
795 {
796 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
797 {
798 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Source on line %d.",
799 linenum));
800 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
801 goto create_error;
802 }
803
804 if (pc->num_sources >= num_sources)
805 {
806 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Source's on line %d.",
807 linenum));
808 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
809 goto create_error;
810 }
811
812 map = pc->sources + pc->num_sources;
813 map->pwg = _cupsStrAlloc(pwg_keyword);
814 map->ppd = _cupsStrAlloc(ppd_keyword);
815
816 pc->num_sources ++;
817 }
818 else if (!_cups_strcasecmp(line, "NumTypes"))
819 {
820 if (num_types > 0)
821 {
822 DEBUG_puts("_ppdCacheCreateWithFile: NumTypes listed multiple times.");
823 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
824 goto create_error;
825 }
826
827 if ((num_types = atoi(value)) <= 0 || num_types > 65536)
828 {
829 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumTypes value %d on "
830 "line %d.", num_types, linenum));
831 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
832 goto create_error;
833 }
834
835 if ((pc->types = calloc((size_t)num_types, sizeof(pwg_map_t))) == NULL)
836 {
837 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d types.",
838 num_types));
839 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
840 goto create_error;
841 }
842 }
843 else if (!_cups_strcasecmp(line, "Type"))
844 {
845 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
846 {
847 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Type on line %d.",
848 linenum));
849 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
850 goto create_error;
851 }
852
853 if (pc->num_types >= num_types)
854 {
855 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Type's on line %d.",
856 linenum));
857 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
858 goto create_error;
859 }
860
861 map = pc->types + pc->num_types;
862 map->pwg = _cupsStrAlloc(pwg_keyword);
863 map->ppd = _cupsStrAlloc(ppd_keyword);
864
865 pc->num_types ++;
866 }
867 else if (!_cups_strcasecmp(line, "Preset"))
868 {
869 /*
870 * Preset output-mode print-quality name=value ...
871 */
872
873 print_color_mode = (_pwg_print_color_mode_t)strtol(value, &valueptr, 10);
874 print_quality = (_pwg_print_quality_t)strtol(valueptr, &valueptr, 10);
875
876 if (print_color_mode < _PWG_PRINT_COLOR_MODE_MONOCHROME ||
877 print_color_mode >= _PWG_PRINT_COLOR_MODE_MAX ||
878 print_quality < _PWG_PRINT_QUALITY_DRAFT ||
879 print_quality >= _PWG_PRINT_QUALITY_MAX ||
880 valueptr == value || !*valueptr)
881 {
882 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Preset on line %d.",
883 linenum));
884 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
885 goto create_error;
886 }
887
888 pc->num_presets[print_color_mode][print_quality] =
889 cupsParseOptions(valueptr, 0,
890 pc->presets[print_color_mode] + print_quality);
891 }
892 else if (!_cups_strcasecmp(line, "SidesOption"))
893 pc->sides_option = _cupsStrAlloc(value);
894 else if (!_cups_strcasecmp(line, "Sides1Sided"))
895 pc->sides_1sided = _cupsStrAlloc(value);
896 else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
897 pc->sides_2sided_long = _cupsStrAlloc(value);
898 else if (!_cups_strcasecmp(line, "Sides2SidedShort"))
899 pc->sides_2sided_short = _cupsStrAlloc(value);
900 else if (!_cups_strcasecmp(line, "Finishings"))
901 {
902 if (!pc->finishings)
903 pc->finishings =
904 cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
905 NULL, NULL, 0, NULL,
906 (cups_afree_func_t)pwg_free_finishings);
907
908 if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
909 goto create_error;
910
911 finishings->value = (ipp_finishings_t)strtol(value, &valueptr, 10);
912 finishings->num_options = cupsParseOptions(valueptr, 0,
913 &(finishings->options));
914
915 cupsArrayAdd(pc->finishings, finishings);
916 }
917 else if (!_cups_strcasecmp(line, "MaxCopies"))
918 pc->max_copies = atoi(value);
919 else if (!_cups_strcasecmp(line, "ChargeInfoURI"))
920 pc->charge_info_uri = _cupsStrAlloc(value);
921 else if (!_cups_strcasecmp(line, "JobAccountId"))
922 pc->account_id = !_cups_strcasecmp(value, "true");
923 else if (!_cups_strcasecmp(line, "JobAccountingUserId"))
924 pc->accounting_user_id = !_cups_strcasecmp(value, "true");
925 else if (!_cups_strcasecmp(line, "JobPassword"))
926 pc->password = _cupsStrAlloc(value);
927 else if (!_cups_strcasecmp(line, "Mandatory"))
928 {
929 if (pc->mandatory)
930 _cupsArrayAddStrings(pc->mandatory, value, ' ');
931 else
932 pc->mandatory = _cupsArrayNewStrings(value, ' ');
933 }
934 else if (!_cups_strcasecmp(line, "SupportFile"))
935 {
936 if (!pc->support_files)
937 pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
938 (cups_acopy_func_t)_cupsStrAlloc,
939 (cups_afree_func_t)_cupsStrFree);
940
941 cupsArrayAdd(pc->support_files, value);
942 }
943 else
944 {
945 DEBUG_printf(("_ppdCacheCreateWithFile: Unknown %s on line %d.", line,
946 linenum));
947 }
948 }
949
950 if (pc->num_sizes < num_sizes)
951 {
952 DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sizes (%d < %d).",
953 pc->num_sizes, num_sizes));
954 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
955 goto create_error;
956 }
957
958 if (pc->num_sources < num_sources)
959 {
960 DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sources (%d < %d).",
961 pc->num_sources, num_sources));
962 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
963 goto create_error;
964 }
965
966 if (pc->num_types < num_types)
967 {
968 DEBUG_printf(("_ppdCacheCreateWithFile: Not enough types (%d < %d).",
969 pc->num_types, num_types));
970 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
971 goto create_error;
972 }
973
974 cupsFileClose(fp);
975
976 return (pc);
977
978 /*
979 * If we get here the file was bad - free any data and return...
980 */
981
982 create_error:
983
984 cupsFileClose(fp);
985 _ppdCacheDestroy(pc);
986
987 if (attrs)
988 {
989 ippDelete(*attrs);
990 *attrs = NULL;
991 }
992
993 return (NULL);
994 }
995
996
997 /*
998 * '_ppdCacheCreateWithPPD()' - Create PWG mapping data from a PPD file.
999 */
1000
1001 _ppd_cache_t * /* O - PPD cache and mapping data */
1002 _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
1003 {
1004 int i, j, k; /* Looping vars */
1005 _ppd_cache_t *pc; /* PWG mapping data */
1006 ppd_option_t *input_slot, /* InputSlot option */
1007 *media_type, /* MediaType option */
1008 *output_bin, /* OutputBin option */
1009 *color_model, /* ColorModel option */
1010 *duplex; /* Duplex option */
1011 ppd_choice_t *choice; /* Current InputSlot/MediaType */
1012 pwg_map_t *map; /* Current source/type map */
1013 ppd_attr_t *ppd_attr; /* Current PPD preset attribute */
1014 int num_options; /* Number of preset options and props */
1015 cups_option_t *options; /* Preset options and properties */
1016 ppd_size_t *ppd_size; /* Current PPD size */
1017 pwg_size_t *pwg_size; /* Current PWG size */
1018 char pwg_keyword[3 + PPD_MAX_NAME + 1 + 12 + 1 + 12 + 3],
1019 /* PWG keyword string */
1020 ppd_name[PPD_MAX_NAME];
1021 /* Normalized PPD name */
1022 const char *pwg_name; /* Standard PWG media name */
1023 pwg_media_t *pwg_media; /* PWG media data */
1024 _pwg_print_color_mode_t pwg_print_color_mode;
1025 /* print-color-mode index */
1026 _pwg_print_quality_t pwg_print_quality;
1027 /* print-quality index */
1028 int similar; /* Are the old and new size similar? */
1029 pwg_size_t *old_size; /* Current old size */
1030 int old_imageable, /* Old imageable length in 2540ths */
1031 old_borderless, /* Old borderless state */
1032 old_known_pwg; /* Old PWG name is well-known */
1033 int new_width, /* New width in 2540ths */
1034 new_length, /* New length in 2540ths */
1035 new_left, /* New left margin in 2540ths */
1036 new_bottom, /* New bottom margin in 2540ths */
1037 new_right, /* New right margin in 2540ths */
1038 new_top, /* New top margin in 2540ths */
1039 new_imageable, /* New imageable length in 2540ths */
1040 new_borderless, /* New borderless state */
1041 new_known_pwg; /* New PWG name is well-known */
1042 pwg_size_t *new_size; /* New size to add, if any */
1043 const char *filter; /* Current filter */
1044 _pwg_finishings_t *finishings; /* Current finishings value */
1045
1046
1047 DEBUG_printf(("_ppdCacheCreateWithPPD(ppd=%p)", ppd));
1048
1049 /*
1050 * Range check input...
1051 */
1052
1053 if (!ppd)
1054 return (NULL);
1055
1056 /*
1057 * Allocate memory...
1058 */
1059
1060 if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
1061 {
1062 DEBUG_puts("_ppdCacheCreateWithPPD: Unable to allocate _ppd_cache_t.");
1063 goto create_error;
1064 }
1065
1066 /*
1067 * Copy and convert size data...
1068 */
1069
1070 if (ppd->num_sizes > 0)
1071 {
1072 if ((pc->sizes = calloc((size_t)ppd->num_sizes, sizeof(pwg_size_t))) == NULL)
1073 {
1074 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1075 "pwg_size_t's.", ppd->num_sizes));
1076 goto create_error;
1077 }
1078
1079 for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes;
1080 i > 0;
1081 i --, ppd_size ++)
1082 {
1083 /*
1084 * Don't copy over custom size...
1085 */
1086
1087 if (!_cups_strcasecmp(ppd_size->name, "Custom"))
1088 continue;
1089
1090 /*
1091 * Convert the PPD size name to the corresponding PWG keyword name.
1092 */
1093
1094 if ((pwg_media = pwgMediaForPPD(ppd_size->name)) != NULL)
1095 {
1096 /*
1097 * Standard name, do we have conflicts?
1098 */
1099
1100 for (j = 0; j < pc->num_sizes; j ++)
1101 if (!strcmp(pc->sizes[j].map.pwg, pwg_media->pwg))
1102 {
1103 pwg_media = NULL;
1104 break;
1105 }
1106 }
1107
1108 if (pwg_media)
1109 {
1110 /*
1111 * Standard name and no conflicts, use it!
1112 */
1113
1114 pwg_name = pwg_media->pwg;
1115 new_known_pwg = 1;
1116 }
1117 else
1118 {
1119 /*
1120 * Not a standard name; convert it to a PWG vendor name of the form:
1121 *
1122 * pp_lowerppd_WIDTHxHEIGHTuu
1123 */
1124
1125 pwg_name = pwg_keyword;
1126 new_known_pwg = 0;
1127
1128 pwg_unppdize_name(ppd_size->name, ppd_name, sizeof(ppd_name), "_.");
1129 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), NULL, ppd_name,
1130 PWG_FROM_POINTS(ppd_size->width),
1131 PWG_FROM_POINTS(ppd_size->length), NULL);
1132 }
1133
1134 /*
1135 * If we have a similar paper with non-zero margins then we only want to
1136 * keep it if it has a larger imageable area length. The NULL check is for
1137 * dimensions that are <= 0...
1138 */
1139
1140 if ((pwg_media = _pwgMediaNearSize(PWG_FROM_POINTS(ppd_size->width),
1141 PWG_FROM_POINTS(ppd_size->length),
1142 0)) == NULL)
1143 continue;
1144
1145 new_width = pwg_media->width;
1146 new_length = pwg_media->length;
1147 new_left = PWG_FROM_POINTS(ppd_size->left);
1148 new_bottom = PWG_FROM_POINTS(ppd_size->bottom);
1149 new_right = PWG_FROM_POINTS(ppd_size->width - ppd_size->right);
1150 new_top = PWG_FROM_POINTS(ppd_size->length - ppd_size->top);
1151 new_imageable = new_length - new_top - new_bottom;
1152 new_borderless = new_bottom == 0 && new_top == 0 &&
1153 new_left == 0 && new_right == 0;
1154
1155 for (k = pc->num_sizes, similar = 0, old_size = pc->sizes, new_size = NULL;
1156 k > 0 && !similar;
1157 k --, old_size ++)
1158 {
1159 old_imageable = old_size->length - old_size->top - old_size->bottom;
1160 old_borderless = old_size->left == 0 && old_size->bottom == 0 &&
1161 old_size->right == 0 && old_size->top == 0;
1162 old_known_pwg = strncmp(old_size->map.pwg, "oe_", 3) &&
1163 strncmp(old_size->map.pwg, "om_", 3);
1164
1165 similar = old_borderless == new_borderless &&
1166 _PWG_EQUIVALENT(old_size->width, new_width) &&
1167 _PWG_EQUIVALENT(old_size->length, new_length);
1168
1169 if (similar &&
1170 (new_known_pwg || (!old_known_pwg && new_imageable > old_imageable)))
1171 {
1172 /*
1173 * The new paper has a larger imageable area so it could replace
1174 * the older paper. Regardless of the imageable area, we always
1175 * prefer the size with a well-known PWG name.
1176 */
1177
1178 new_size = old_size;
1179 _cupsStrFree(old_size->map.ppd);
1180 _cupsStrFree(old_size->map.pwg);
1181 }
1182 }
1183
1184 if (!similar)
1185 {
1186 /*
1187 * The paper was unique enough to deserve its own entry so add it to the
1188 * end.
1189 */
1190
1191 new_size = pwg_size ++;
1192 pc->num_sizes ++;
1193 }
1194
1195 if (new_size)
1196 {
1197 /*
1198 * Save this size...
1199 */
1200
1201 new_size->map.ppd = _cupsStrAlloc(ppd_size->name);
1202 new_size->map.pwg = _cupsStrAlloc(pwg_name);
1203 new_size->width = new_width;
1204 new_size->length = new_length;
1205 new_size->left = new_left;
1206 new_size->bottom = new_bottom;
1207 new_size->right = new_right;
1208 new_size->top = new_top;
1209 }
1210 }
1211 }
1212
1213 if (ppd->variable_sizes)
1214 {
1215 /*
1216 * Generate custom size data...
1217 */
1218
1219 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
1220 PWG_FROM_POINTS(ppd->custom_max[0]),
1221 PWG_FROM_POINTS(ppd->custom_max[1]), NULL);
1222 pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
1223 pc->custom_max_width = PWG_FROM_POINTS(ppd->custom_max[0]);
1224 pc->custom_max_length = PWG_FROM_POINTS(ppd->custom_max[1]);
1225
1226 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
1227 PWG_FROM_POINTS(ppd->custom_min[0]),
1228 PWG_FROM_POINTS(ppd->custom_min[1]), NULL);
1229 pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
1230 pc->custom_min_width = PWG_FROM_POINTS(ppd->custom_min[0]);
1231 pc->custom_min_length = PWG_FROM_POINTS(ppd->custom_min[1]);
1232
1233 pc->custom_size.left = PWG_FROM_POINTS(ppd->custom_margins[0]);
1234 pc->custom_size.bottom = PWG_FROM_POINTS(ppd->custom_margins[1]);
1235 pc->custom_size.right = PWG_FROM_POINTS(ppd->custom_margins[2]);
1236 pc->custom_size.top = PWG_FROM_POINTS(ppd->custom_margins[3]);
1237 }
1238
1239 /*
1240 * Copy and convert InputSlot data...
1241 */
1242
1243 if ((input_slot = ppdFindOption(ppd, "InputSlot")) == NULL)
1244 input_slot = ppdFindOption(ppd, "HPPaperSource");
1245
1246 if (input_slot)
1247 {
1248 pc->source_option = _cupsStrAlloc(input_slot->keyword);
1249
1250 if ((pc->sources = calloc((size_t)input_slot->num_choices, sizeof(pwg_map_t))) == NULL)
1251 {
1252 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1253 "pwg_map_t's for InputSlot.", input_slot->num_choices));
1254 goto create_error;
1255 }
1256
1257 pc->num_sources = input_slot->num_choices;
1258
1259 for (i = input_slot->num_choices, choice = input_slot->choices,
1260 map = pc->sources;
1261 i > 0;
1262 i --, choice ++, map ++)
1263 {
1264 if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
1265 !_cups_strcasecmp(choice->choice, "Default"))
1266 pwg_name = "auto";
1267 else if (!_cups_strcasecmp(choice->choice, "Cassette"))
1268 pwg_name = "main";
1269 else if (!_cups_strcasecmp(choice->choice, "PhotoTray"))
1270 pwg_name = "photo";
1271 else if (!_cups_strcasecmp(choice->choice, "CDTray"))
1272 pwg_name = "disc";
1273 else if (!_cups_strncasecmp(choice->choice, "Multipurpose", 12) ||
1274 !_cups_strcasecmp(choice->choice, "MP") ||
1275 !_cups_strcasecmp(choice->choice, "MPTray"))
1276 pwg_name = "by-pass-tray";
1277 else if (!_cups_strcasecmp(choice->choice, "LargeCapacity"))
1278 pwg_name = "large-capacity";
1279 else if (!_cups_strncasecmp(choice->choice, "Lower", 5))
1280 pwg_name = "bottom";
1281 else if (!_cups_strncasecmp(choice->choice, "Middle", 6))
1282 pwg_name = "middle";
1283 else if (!_cups_strncasecmp(choice->choice, "Upper", 5))
1284 pwg_name = "top";
1285 else if (!_cups_strncasecmp(choice->choice, "Side", 4))
1286 pwg_name = "side";
1287 else if (!_cups_strcasecmp(choice->choice, "Roll"))
1288 pwg_name = "main-roll";
1289 else
1290 {
1291 /*
1292 * Convert PPD name to lowercase...
1293 */
1294
1295 pwg_name = pwg_keyword;
1296 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1297 "_");
1298 }
1299
1300 map->pwg = _cupsStrAlloc(pwg_name);
1301 map->ppd = _cupsStrAlloc(choice->choice);
1302 }
1303 }
1304
1305 /*
1306 * Copy and convert MediaType data...
1307 */
1308
1309 if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
1310 {
1311 if ((pc->types = calloc((size_t)media_type->num_choices, sizeof(pwg_map_t))) == NULL)
1312 {
1313 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1314 "pwg_map_t's for MediaType.", media_type->num_choices));
1315 goto create_error;
1316 }
1317
1318 pc->num_types = media_type->num_choices;
1319
1320 for (i = media_type->num_choices, choice = media_type->choices,
1321 map = pc->types;
1322 i > 0;
1323 i --, choice ++, map ++)
1324 {
1325 if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
1326 !_cups_strcasecmp(choice->choice, "Any") ||
1327 !_cups_strcasecmp(choice->choice, "Default"))
1328 pwg_name = "auto";
1329 else if (!_cups_strncasecmp(choice->choice, "Card", 4))
1330 pwg_name = "cardstock";
1331 else if (!_cups_strncasecmp(choice->choice, "Env", 3))
1332 pwg_name = "envelope";
1333 else if (!_cups_strncasecmp(choice->choice, "Gloss", 5))
1334 pwg_name = "photographic-glossy";
1335 else if (!_cups_strcasecmp(choice->choice, "HighGloss"))
1336 pwg_name = "photographic-high-gloss";
1337 else if (!_cups_strcasecmp(choice->choice, "Matte"))
1338 pwg_name = "photographic-matte";
1339 else if (!_cups_strncasecmp(choice->choice, "Plain", 5))
1340 pwg_name = "stationery";
1341 else if (!_cups_strncasecmp(choice->choice, "Coated", 6))
1342 pwg_name = "stationery-coated";
1343 else if (!_cups_strcasecmp(choice->choice, "Inkjet"))
1344 pwg_name = "stationery-inkjet";
1345 else if (!_cups_strcasecmp(choice->choice, "Letterhead"))
1346 pwg_name = "stationery-letterhead";
1347 else if (!_cups_strncasecmp(choice->choice, "Preprint", 8))
1348 pwg_name = "stationery-preprinted";
1349 else if (!_cups_strcasecmp(choice->choice, "Recycled"))
1350 pwg_name = "stationery-recycled";
1351 else if (!_cups_strncasecmp(choice->choice, "Transparen", 10))
1352 pwg_name = "transparency";
1353 else
1354 {
1355 /*
1356 * Convert PPD name to lowercase...
1357 */
1358
1359 pwg_name = pwg_keyword;
1360 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1361 "_");
1362 }
1363
1364 map->pwg = _cupsStrAlloc(pwg_name);
1365 map->ppd = _cupsStrAlloc(choice->choice);
1366 }
1367 }
1368
1369 /*
1370 * Copy and convert OutputBin data...
1371 */
1372
1373 if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
1374 {
1375 if ((pc->bins = calloc((size_t)output_bin->num_choices, sizeof(pwg_map_t))) == NULL)
1376 {
1377 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1378 "pwg_map_t's for OutputBin.", output_bin->num_choices));
1379 goto create_error;
1380 }
1381
1382 pc->num_bins = output_bin->num_choices;
1383
1384 for (i = output_bin->num_choices, choice = output_bin->choices,
1385 map = pc->bins;
1386 i > 0;
1387 i --, choice ++, map ++)
1388 {
1389 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
1390
1391 map->pwg = _cupsStrAlloc(pwg_keyword);
1392 map->ppd = _cupsStrAlloc(choice->choice);
1393 }
1394 }
1395
1396 if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
1397 {
1398 /*
1399 * Copy and convert APPrinterPreset (output-mode + print-quality) data...
1400 */
1401
1402 const char *quality, /* com.apple.print.preset.quality value */
1403 *output_mode, /* com.apple.print.preset.output-mode value */
1404 *color_model_val, /* ColorModel choice */
1405 *graphicsType, /* com.apple.print.preset.graphicsType value */
1406 *media_front_coating; /* com.apple.print.preset.media-front-coating value */
1407
1408 do
1409 {
1410 num_options = _ppdParseOptions(ppd_attr->value, 0, &options,
1411 _PPD_PARSE_ALL);
1412
1413 if ((quality = cupsGetOption("com.apple.print.preset.quality",
1414 num_options, options)) != NULL)
1415 {
1416 /*
1417 * Get the print-quality for this preset...
1418 */
1419
1420 if (!strcmp(quality, "low"))
1421 pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1422 else if (!strcmp(quality, "high"))
1423 pwg_print_quality = _PWG_PRINT_QUALITY_HIGH;
1424 else
1425 pwg_print_quality = _PWG_PRINT_QUALITY_NORMAL;
1426
1427 /*
1428 * Ignore graphicsType "Photo" presets that are not high quality.
1429 */
1430
1431 graphicsType = cupsGetOption("com.apple.print.preset.graphicsType",
1432 num_options, options);
1433
1434 if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH && graphicsType &&
1435 !strcmp(graphicsType, "Photo"))
1436 continue;
1437
1438 /*
1439 * Ignore presets for normal and draft quality where the coating
1440 * isn't "none" or "autodetect".
1441 */
1442
1443 media_front_coating = cupsGetOption(
1444 "com.apple.print.preset.media-front-coating",
1445 num_options, options);
1446
1447 if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH &&
1448 media_front_coating &&
1449 strcmp(media_front_coating, "none") &&
1450 strcmp(media_front_coating, "autodetect"))
1451 continue;
1452
1453 /*
1454 * Get the output mode for this preset...
1455 */
1456
1457 output_mode = cupsGetOption("com.apple.print.preset.output-mode",
1458 num_options, options);
1459 color_model_val = cupsGetOption("ColorModel", num_options, options);
1460
1461 if (output_mode)
1462 {
1463 if (!strcmp(output_mode, "monochrome"))
1464 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1465 else
1466 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1467 }
1468 else if (color_model_val)
1469 {
1470 if (!_cups_strcasecmp(color_model_val, "Gray"))
1471 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1472 else
1473 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1474 }
1475 else
1476 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1477
1478 /*
1479 * Save the options for this combination as needed...
1480 */
1481
1482 if (!pc->num_presets[pwg_print_color_mode][pwg_print_quality])
1483 pc->num_presets[pwg_print_color_mode][pwg_print_quality] =
1484 _ppdParseOptions(ppd_attr->value, 0,
1485 pc->presets[pwg_print_color_mode] +
1486 pwg_print_quality, _PPD_PARSE_OPTIONS);
1487 }
1488
1489 cupsFreeOptions(num_options, options);
1490 }
1491 while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) != NULL);
1492 }
1493
1494 if (!pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_DRAFT] &&
1495 !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_NORMAL] &&
1496 !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_HIGH])
1497 {
1498 /*
1499 * Try adding some common color options to create grayscale presets. These
1500 * are listed in order of popularity...
1501 */
1502
1503 const char *color_option = NULL, /* Color control option */
1504 *gray_choice = NULL; /* Choice to select grayscale */
1505
1506 if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL &&
1507 ppdFindChoice(color_model, "Gray"))
1508 {
1509 color_option = "ColorModel";
1510 gray_choice = "Gray";
1511 }
1512 else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL &&
1513 ppdFindChoice(color_model, "grayscale"))
1514 {
1515 color_option = "HPColorMode";
1516 gray_choice = "grayscale";
1517 }
1518 else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL &&
1519 ppdFindChoice(color_model, "Mono"))
1520 {
1521 color_option = "BRMonoColor";
1522 gray_choice = "Mono";
1523 }
1524 else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL &&
1525 ppdFindChoice(color_model, "1"))
1526 {
1527 color_option = "CNIJSGrayScale";
1528 gray_choice = "1";
1529 }
1530 else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL &&
1531 ppdFindChoice(color_model, "True"))
1532 {
1533 color_option = "HPColorAsGray";
1534 gray_choice = "True";
1535 }
1536
1537 if (color_option && gray_choice)
1538 {
1539 /*
1540 * Copy and convert ColorModel (output-mode) data...
1541 */
1542
1543 cups_option_t *coption, /* Color option */
1544 *moption; /* Monochrome option */
1545
1546 for (pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1547 pwg_print_quality < _PWG_PRINT_QUALITY_MAX;
1548 pwg_print_quality ++)
1549 {
1550 if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality])
1551 {
1552 /*
1553 * Copy the color options...
1554 */
1555
1556 num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
1557 [pwg_print_quality];
1558 options = calloc(sizeof(cups_option_t), (size_t)num_options);
1559
1560 if (options)
1561 {
1562 for (i = num_options, moption = options,
1563 coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
1564 [pwg_print_quality];
1565 i > 0;
1566 i --, moption ++, coption ++)
1567 {
1568 moption->name = _cupsStrRetain(coption->name);
1569 moption->value = _cupsStrRetain(coption->value);
1570 }
1571
1572 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1573 num_options;
1574 pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1575 options;
1576 }
1577 }
1578 else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL)
1579 continue;
1580
1581 /*
1582 * Add the grayscale option to the preset...
1583 */
1584
1585 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1586 cupsAddOption(color_option, gray_choice,
1587 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
1588 [pwg_print_quality],
1589 pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME] +
1590 pwg_print_quality);
1591 }
1592 }
1593 }
1594
1595 /*
1596 * Copy and convert Duplex (sides) data...
1597 */
1598
1599 if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
1600 if ((duplex = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1601 if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
1602 if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
1603 duplex = ppdFindOption(ppd, "KD03Duplex");
1604
1605 if (duplex)
1606 {
1607 pc->sides_option = _cupsStrAlloc(duplex->keyword);
1608
1609 for (i = duplex->num_choices, choice = duplex->choices;
1610 i > 0;
1611 i --, choice ++)
1612 {
1613 if ((!_cups_strcasecmp(choice->choice, "None") ||
1614 !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
1615 pc->sides_1sided = _cupsStrAlloc(choice->choice);
1616 else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
1617 !_cups_strcasecmp(choice->choice, "LongEdge") ||
1618 !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
1619 pc->sides_2sided_long = _cupsStrAlloc(choice->choice);
1620 else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
1621 !_cups_strcasecmp(choice->choice, "ShortEdge") ||
1622 !_cups_strcasecmp(choice->choice, "Bottom")) &&
1623 !pc->sides_2sided_short)
1624 pc->sides_2sided_short = _cupsStrAlloc(choice->choice);
1625 }
1626 }
1627
1628 /*
1629 * Copy filters and pre-filters...
1630 */
1631
1632 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
1633 (cups_acopy_func_t)_cupsStrAlloc,
1634 (cups_afree_func_t)_cupsStrFree);
1635
1636 cupsArrayAdd(pc->filters,
1637 "application/vnd.cups-raw application/octet-stream 0 -");
1638
1639 if ((ppd_attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL)
1640 {
1641 do
1642 {
1643 cupsArrayAdd(pc->filters, ppd_attr->value);
1644 }
1645 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
1646 }
1647 else if (ppd->num_filters > 0)
1648 {
1649 for (i = 0; i < ppd->num_filters; i ++)
1650 cupsArrayAdd(pc->filters, ppd->filters[i]);
1651 }
1652 else
1653 cupsArrayAdd(pc->filters, "application/vnd.cups-postscript 0 -");
1654
1655 /*
1656 * See if we have a command filter...
1657 */
1658
1659 for (filter = (const char *)cupsArrayFirst(pc->filters);
1660 filter;
1661 filter = (const char *)cupsArrayNext(pc->filters))
1662 if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
1663 _cups_isspace(filter[28]))
1664 break;
1665
1666 if (!filter &&
1667 ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL ||
1668 _cups_strcasecmp(ppd_attr->value, "none")))
1669 {
1670 /*
1671 * No command filter and no cupsCommands keyword telling us not to use one.
1672 * See if this is a PostScript printer, and if so add a PostScript command
1673 * filter...
1674 */
1675
1676 for (filter = (const char *)cupsArrayFirst(pc->filters);
1677 filter;
1678 filter = (const char *)cupsArrayNext(pc->filters))
1679 if (!_cups_strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
1680 _cups_isspace(filter[31]))
1681 break;
1682
1683 if (filter)
1684 cupsArrayAdd(pc->filters,
1685 "application/vnd.cups-command application/postscript 100 "
1686 "commandtops");
1687 }
1688
1689 if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
1690 {
1691 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
1692 (cups_acopy_func_t)_cupsStrAlloc,
1693 (cups_afree_func_t)_cupsStrFree);
1694
1695 do
1696 {
1697 cupsArrayAdd(pc->prefilters, ppd_attr->value);
1698 }
1699 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) != NULL);
1700 }
1701
1702 if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL)
1703 pc->single_file = !_cups_strcasecmp(ppd_attr->value, "true");
1704
1705 /*
1706 * Copy the product string, if any...
1707 */
1708
1709 if (ppd->product)
1710 pc->product = _cupsStrAlloc(ppd->product);
1711
1712 /*
1713 * Copy finishings mapping data...
1714 */
1715
1716 if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL)
1717 {
1718 pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
1719 NULL, NULL, 0, NULL,
1720 (cups_afree_func_t)pwg_free_finishings);
1721
1722 do
1723 {
1724 if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
1725 goto create_error;
1726
1727 finishings->value = (ipp_finishings_t)atoi(ppd_attr->spec);
1728 finishings->num_options = _ppdParseOptions(ppd_attr->value, 0,
1729 &(finishings->options),
1730 _PPD_PARSE_OPTIONS);
1731
1732 cupsArrayAdd(pc->finishings, finishings);
1733 }
1734 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsIPPFinishings",
1735 NULL)) != NULL);
1736 }
1737
1738 /*
1739 * Max copies...
1740 */
1741
1742 if ((ppd_attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
1743 pc->max_copies = atoi(ppd_attr->value);
1744 else if (ppd->manual_copies)
1745 pc->max_copies = 1;
1746 else
1747 pc->max_copies = 9999;
1748
1749 /*
1750 * cupsChargeInfoURI, cupsJobAccountId, cupsJobAccountingUserId,
1751 * cupsJobPassword, and cupsMandatory.
1752 */
1753
1754 if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL)
1755 pc->charge_info_uri = _cupsStrAlloc(ppd_attr->value);
1756
1757 if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountId", NULL)) != NULL)
1758 pc->account_id = !_cups_strcasecmp(ppd_attr->value, "true");
1759
1760 if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountingUserId", NULL)) != NULL)
1761 pc->accounting_user_id = !_cups_strcasecmp(ppd_attr->value, "true");
1762
1763 if ((ppd_attr = ppdFindAttr(ppd, "cupsJobPassword", NULL)) != NULL)
1764 pc->password = _cupsStrAlloc(ppd_attr->value);
1765
1766 if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
1767 pc->mandatory = _cupsArrayNewStrings(ppd_attr->value, ' ');
1768
1769 /*
1770 * Support files...
1771 */
1772
1773 pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
1774 (cups_acopy_func_t)_cupsStrAlloc,
1775 (cups_afree_func_t)_cupsStrFree);
1776
1777 for (ppd_attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
1778 ppd_attr;
1779 ppd_attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
1780 cupsArrayAdd(pc->support_files, ppd_attr->value);
1781
1782 if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
1783 cupsArrayAdd(pc->support_files, ppd_attr->value);
1784
1785 /*
1786 * 3D stuff...
1787 */
1788
1789 if ((ppd_attr = ppdFindAttr(ppd, "cups3D", NULL)) != NULL)
1790 pc->cups_3d = _cupsStrAlloc(ppd_attr->value);
1791
1792 if ((ppd_attr = ppdFindAttr(ppd, "cupsLayerOrder", NULL)) != NULL)
1793 pc->cups_layer_order = _cupsStrAlloc(ppd_attr->value);
1794
1795 if ((ppd_attr = ppdFindAttr(ppd, "cupsAccuracy", NULL)) != NULL)
1796 sscanf(ppd_attr->value, "%d%d%d", pc->cups_accuracy + 0, pc->cups_accuracy + 1, pc->cups_accuracy + 2);
1797
1798 if ((ppd_attr = ppdFindAttr(ppd, "cupsVolume", NULL)) != NULL)
1799 sscanf(ppd_attr->value, "%d%d%d", pc->cups_volume + 0, pc->cups_volume + 1, pc->cups_volume + 2);
1800
1801 for (ppd_attr = ppdFindAttr(ppd, "cupsMaterial", NULL);
1802 ppd_attr;
1803 ppd_attr = ppdFindNextAttr(ppd, "cupsMaterial", NULL))
1804 {
1805 /*
1806 * *cupsMaterial key/name: "name=value ... name=value"
1807 */
1808
1809 _pwg_material_t *material = (_pwg_material_t *)calloc(1, sizeof(_pwg_material_t));
1810
1811 material->key = _cupsStrAlloc(ppd_attr->name);
1812 material->name = _cupsStrAlloc(ppd_attr->text);
1813 material->num_props = cupsParseOptions(ppd_attr->value, 0, &material->props);
1814
1815 if (!pc->materials)
1816 pc->materials = cupsArrayNew3(NULL, NULL, NULL, 0, NULL, (cups_afree_func_t)pwg_free_material);
1817
1818 cupsArrayAdd(pc->materials, material);
1819 }
1820
1821 /*
1822 * Return the cache data...
1823 */
1824
1825 return (pc);
1826
1827 /*
1828 * If we get here we need to destroy the PWG mapping data and return NULL...
1829 */
1830
1831 create_error:
1832
1833 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of memory."), 1);
1834 _ppdCacheDestroy(pc);
1835
1836 return (NULL);
1837 }
1838
1839
1840 /*
1841 * '_ppdCacheDestroy()' - Free all memory used for PWG mapping data.
1842 */
1843
1844 void
1845 _ppdCacheDestroy(_ppd_cache_t *pc) /* I - PPD cache and mapping data */
1846 {
1847 int i; /* Looping var */
1848 pwg_map_t *map; /* Current map */
1849 pwg_size_t *size; /* Current size */
1850
1851
1852 /*
1853 * Range check input...
1854 */
1855
1856 if (!pc)
1857 return;
1858
1859 /*
1860 * Free memory as needed...
1861 */
1862
1863 if (pc->bins)
1864 {
1865 for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
1866 {
1867 _cupsStrFree(map->pwg);
1868 _cupsStrFree(map->ppd);
1869 }
1870
1871 free(pc->bins);
1872 }
1873
1874 if (pc->sizes)
1875 {
1876 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1877 {
1878 _cupsStrFree(size->map.pwg);
1879 _cupsStrFree(size->map.ppd);
1880 }
1881
1882 free(pc->sizes);
1883 }
1884
1885 if (pc->source_option)
1886 _cupsStrFree(pc->source_option);
1887
1888 if (pc->sources)
1889 {
1890 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
1891 {
1892 _cupsStrFree(map->pwg);
1893 _cupsStrFree(map->ppd);
1894 }
1895
1896 free(pc->sources);
1897 }
1898
1899 if (pc->types)
1900 {
1901 for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
1902 {
1903 _cupsStrFree(map->pwg);
1904 _cupsStrFree(map->ppd);
1905 }
1906
1907 free(pc->types);
1908 }
1909
1910 if (pc->custom_max_keyword)
1911 _cupsStrFree(pc->custom_max_keyword);
1912
1913 if (pc->custom_min_keyword)
1914 _cupsStrFree(pc->custom_min_keyword);
1915
1916 _cupsStrFree(pc->product);
1917 cupsArrayDelete(pc->filters);
1918 cupsArrayDelete(pc->prefilters);
1919 cupsArrayDelete(pc->finishings);
1920
1921 _cupsStrFree(pc->charge_info_uri);
1922 _cupsStrFree(pc->password);
1923
1924 cupsArrayDelete(pc->mandatory);
1925
1926 cupsArrayDelete(pc->support_files);
1927
1928 _cupsStrFree(pc->cups_3d);
1929 _cupsStrFree(pc->cups_layer_order);
1930
1931 cupsArrayDelete(pc->materials);
1932
1933 free(pc);
1934 }
1935
1936
1937 /*
1938 * '_ppdCacheGetBin()' - Get the PWG output-bin keyword associated with a PPD
1939 * OutputBin.
1940 */
1941
1942 const char * /* O - output-bin or NULL */
1943 _ppdCacheGetBin(
1944 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1945 const char *output_bin) /* I - PPD OutputBin string */
1946 {
1947 int i; /* Looping var */
1948
1949
1950 /*
1951 * Range check input...
1952 */
1953
1954 if (!pc || !output_bin)
1955 return (NULL);
1956
1957 /*
1958 * Look up the OutputBin string...
1959 */
1960
1961
1962 for (i = 0; i < pc->num_bins; i ++)
1963 if (!_cups_strcasecmp(output_bin, pc->bins[i].ppd))
1964 return (pc->bins[i].pwg);
1965
1966 return (NULL);
1967 }
1968
1969
1970 /*
1971 * '_ppdCacheGetFinishingOptions()' - Get PPD finishing options for the given
1972 * IPP finishings value(s).
1973 */
1974
1975 int /* O - New number of options */
1976 _ppdCacheGetFinishingOptions(
1977 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1978 ipp_t *job, /* I - Job attributes or NULL */
1979 ipp_finishings_t value, /* I - IPP finishings value of IPP_FINISHINGS_NONE */
1980 int num_options, /* I - Number of options */
1981 cups_option_t **options) /* IO - Options */
1982 {
1983 int i; /* Looping var */
1984 _pwg_finishings_t *f, /* PWG finishings options */
1985 key; /* Search key */
1986 ipp_attribute_t *attr; /* Finishings attribute */
1987 cups_option_t *option; /* Current finishings option */
1988
1989
1990 /*
1991 * Range check input...
1992 */
1993
1994 if (!pc || cupsArrayCount(pc->finishings) == 0 || !options ||
1995 (!job && value == IPP_FINISHINGS_NONE))
1996 return (num_options);
1997
1998 /*
1999 * Apply finishing options...
2000 */
2001
2002 if (job && (attr = ippFindAttribute(job, "finishings", IPP_TAG_ENUM)) != NULL)
2003 {
2004 int num_values = ippGetCount(attr); /* Number of values */
2005
2006 for (i = 0; i < num_values; i ++)
2007 {
2008 key.value = (ipp_finishings_t)ippGetInteger(attr, i);
2009
2010 if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
2011 {
2012 int j; /* Another looping var */
2013
2014 for (j = f->num_options, option = f->options; j > 0; j --, option ++)
2015 num_options = cupsAddOption(option->name, option->value,
2016 num_options, options);
2017 }
2018 }
2019 }
2020 else if (value != IPP_FINISHINGS_NONE)
2021 {
2022 key.value = value;
2023
2024 if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
2025 {
2026 int j; /* Another looping var */
2027
2028 for (j = f->num_options, option = f->options; j > 0; j --, option ++)
2029 num_options = cupsAddOption(option->name, option->value,
2030 num_options, options);
2031 }
2032 }
2033
2034 return (num_options);
2035 }
2036
2037
2038 /*
2039 * '_ppdCacheGetFinishingValues()' - Get IPP finishings value(s) from the given
2040 * PPD options.
2041 */
2042
2043 int /* O - Number of finishings values */
2044 _ppdCacheGetFinishingValues(
2045 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2046 int num_options, /* I - Number of options */
2047 cups_option_t *options, /* I - Options */
2048 int max_values, /* I - Maximum number of finishings values */
2049 int *values) /* O - Finishings values */
2050 {
2051 int i, /* Looping var */
2052 num_values = 0; /* Number of values */
2053 _pwg_finishings_t *f; /* Current finishings option */
2054 cups_option_t *option; /* Current option */
2055 const char *val; /* Value for option */
2056
2057
2058 /*
2059 * Range check input...
2060 */
2061
2062 DEBUG_printf(("_ppdCacheGetFinishingValues(pc=%p, num_options=%d, options=%p, max_values=%d, values=%p)", pc, num_options, options, max_values, values));
2063
2064 if (!pc || !pc->finishings || num_options < 1 || max_values < 1 || !values)
2065 {
2066 DEBUG_puts("_ppdCacheGetFinishingValues: Bad arguments, returning 0.");
2067 return (0);
2068 }
2069
2070 /*
2071 * Go through the finishings options and see what is set...
2072 */
2073
2074 for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
2075 f;
2076 f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
2077 {
2078 DEBUG_printf(("_ppdCacheGetFinishingValues: Checking %d (%s)", f->value, ippEnumString("finishings", f->value)));
2079
2080 for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2081 {
2082 DEBUG_printf(("_ppdCacheGetFinishingValues: %s=%s?", option->name, option->value));
2083
2084 if ((val = cupsGetOption(option->name, num_options, options)) == NULL ||
2085 _cups_strcasecmp(option->value, val))
2086 {
2087 DEBUG_puts("_ppdCacheGetFinishingValues: NO");
2088 break;
2089 }
2090 }
2091
2092 if (i == 0)
2093 {
2094 DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d.", f->value));
2095
2096 values[num_values ++] = f->value;
2097
2098 if (num_values >= max_values)
2099 break;
2100 }
2101 }
2102
2103 DEBUG_printf(("_ppdCacheGetFinishingValues: Returning %d.", num_values));
2104
2105 return (num_values);
2106 }
2107
2108
2109 /*
2110 * '_ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job
2111 * attributes or a keyword string.
2112 */
2113
2114 const char * /* O - PPD InputSlot or NULL */
2115 _ppdCacheGetInputSlot(
2116 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2117 ipp_t *job, /* I - Job attributes or NULL */
2118 const char *keyword) /* I - Keyword string or NULL */
2119 {
2120 /*
2121 * Range check input...
2122 */
2123
2124 if (!pc || pc->num_sources == 0 || (!job && !keyword))
2125 return (NULL);
2126
2127 if (job && !keyword)
2128 {
2129 /*
2130 * Lookup the media-col attribute and any media-source found there...
2131 */
2132
2133 ipp_attribute_t *media_col, /* media-col attribute */
2134 *media_source; /* media-source attribute */
2135 pwg_size_t size; /* Dimensional size */
2136 int margins_set; /* Were the margins set? */
2137
2138 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2139 if (media_col &&
2140 (media_source = ippFindAttribute(ippGetCollection(media_col, 0),
2141 "media-source",
2142 IPP_TAG_KEYWORD)) != NULL)
2143 {
2144 /*
2145 * Use the media-source value from media-col...
2146 */
2147
2148 keyword = ippGetString(media_source, 0, NULL);
2149 }
2150 else if (pwgInitSize(&size, job, &margins_set))
2151 {
2152 /*
2153 * For media <= 5x7, look for a photo tray...
2154 */
2155
2156 if (size.width <= (5 * 2540) && size.length <= (7 * 2540))
2157 keyword = "photo";
2158 }
2159 }
2160
2161 if (keyword)
2162 {
2163 int i; /* Looping var */
2164
2165 for (i = 0; i < pc->num_sources; i ++)
2166 if (!_cups_strcasecmp(keyword, pc->sources[i].pwg))
2167 return (pc->sources[i].ppd);
2168 }
2169
2170 return (NULL);
2171 }
2172
2173
2174 /*
2175 * '_ppdCacheGetMediaType()' - Get the PPD MediaType associated with the job
2176 * attributes or a keyword string.
2177 */
2178
2179 const char * /* O - PPD MediaType or NULL */
2180 _ppdCacheGetMediaType(
2181 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2182 ipp_t *job, /* I - Job attributes or NULL */
2183 const char *keyword) /* I - Keyword string or NULL */
2184 {
2185 /*
2186 * Range check input...
2187 */
2188
2189 if (!pc || pc->num_types == 0 || (!job && !keyword))
2190 return (NULL);
2191
2192 if (job && !keyword)
2193 {
2194 /*
2195 * Lookup the media-col attribute and any media-source found there...
2196 */
2197
2198 ipp_attribute_t *media_col, /* media-col attribute */
2199 *media_type; /* media-type attribute */
2200
2201 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2202 if (media_col)
2203 {
2204 if ((media_type = ippFindAttribute(media_col->values[0].collection,
2205 "media-type",
2206 IPP_TAG_KEYWORD)) == NULL)
2207 media_type = ippFindAttribute(media_col->values[0].collection,
2208 "media-type", IPP_TAG_NAME);
2209
2210 if (media_type)
2211 keyword = media_type->values[0].string.text;
2212 }
2213 }
2214
2215 if (keyword)
2216 {
2217 int i; /* Looping var */
2218
2219 for (i = 0; i < pc->num_types; i ++)
2220 if (!_cups_strcasecmp(keyword, pc->types[i].pwg))
2221 return (pc->types[i].ppd);
2222 }
2223
2224 return (NULL);
2225 }
2226
2227
2228 /*
2229 * '_ppdCacheGetOutputBin()' - Get the PPD OutputBin associated with the keyword
2230 * string.
2231 */
2232
2233 const char * /* O - PPD OutputBin or NULL */
2234 _ppdCacheGetOutputBin(
2235 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2236 const char *output_bin) /* I - Keyword string */
2237 {
2238 int i; /* Looping var */
2239
2240
2241 /*
2242 * Range check input...
2243 */
2244
2245 if (!pc || !output_bin)
2246 return (NULL);
2247
2248 /*
2249 * Look up the OutputBin string...
2250 */
2251
2252
2253 for (i = 0; i < pc->num_bins; i ++)
2254 if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg))
2255 return (pc->bins[i].ppd);
2256
2257 return (NULL);
2258 }
2259
2260
2261 /*
2262 * '_ppdCacheGetPageSize()' - Get the PPD PageSize associated with the job
2263 * attributes or a keyword string.
2264 */
2265
2266 const char * /* O - PPD PageSize or NULL */
2267 _ppdCacheGetPageSize(
2268 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2269 ipp_t *job, /* I - Job attributes or NULL */
2270 const char *keyword, /* I - Keyword string or NULL */
2271 int *exact) /* O - 1 if exact match, 0 otherwise */
2272 {
2273 int i; /* Looping var */
2274 pwg_size_t *size, /* Current size */
2275 *closest, /* Closest size */
2276 jobsize; /* Size data from job */
2277 int margins_set, /* Were the margins set? */
2278 dwidth, /* Difference in width */
2279 dlength, /* Difference in length */
2280 dleft, /* Difference in left margins */
2281 dright, /* Difference in right margins */
2282 dbottom, /* Difference in bottom margins */
2283 dtop, /* Difference in top margins */
2284 dmin, /* Minimum difference */
2285 dclosest; /* Closest difference */
2286 const char *ppd_name; /* PPD media name */
2287
2288
2289 DEBUG_printf(("_ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)",
2290 pc, job, keyword, exact));
2291
2292 /*
2293 * Range check input...
2294 */
2295
2296 if (!pc || (!job && !keyword))
2297 return (NULL);
2298
2299 if (exact)
2300 *exact = 0;
2301
2302 ppd_name = keyword;
2303
2304 if (job)
2305 {
2306 /*
2307 * Try getting the PPD media name from the job attributes...
2308 */
2309
2310 ipp_attribute_t *attr; /* Job attribute */
2311
2312 if ((attr = ippFindAttribute(job, "PageSize", IPP_TAG_ZERO)) == NULL)
2313 if ((attr = ippFindAttribute(job, "PageRegion", IPP_TAG_ZERO)) == NULL)
2314 attr = ippFindAttribute(job, "media", IPP_TAG_ZERO);
2315
2316 #ifdef DEBUG
2317 if (attr)
2318 DEBUG_printf(("1_ppdCacheGetPageSize: Found attribute %s (%s)",
2319 attr->name, ippTagString(attr->value_tag)));
2320 else
2321 DEBUG_puts("1_ppdCacheGetPageSize: Did not find media attribute.");
2322 #endif /* DEBUG */
2323
2324 if (attr && (attr->value_tag == IPP_TAG_NAME ||
2325 attr->value_tag == IPP_TAG_KEYWORD))
2326 ppd_name = attr->values[0].string.text;
2327 }
2328
2329 DEBUG_printf(("1_ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name));
2330
2331 if (ppd_name)
2332 {
2333 /*
2334 * Try looking up the named PPD size first...
2335 */
2336
2337 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2338 {
2339 DEBUG_printf(("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]",
2340 (int)(size - pc->sizes), size->map.pwg, size->map.ppd));
2341
2342 if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
2343 !_cups_strcasecmp(ppd_name, size->map.pwg))
2344 {
2345 if (exact)
2346 *exact = 1;
2347
2348 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", ppd_name));
2349
2350 return (size->map.ppd);
2351 }
2352 }
2353 }
2354
2355 if (job && !keyword)
2356 {
2357 /*
2358 * Get the size using media-col or media, with the preference being
2359 * media-col.
2360 */
2361
2362 if (!pwgInitSize(&jobsize, job, &margins_set))
2363 return (NULL);
2364 }
2365 else
2366 {
2367 /*
2368 * Get the size using a media keyword...
2369 */
2370
2371 pwg_media_t *media; /* Media definition */
2372
2373
2374 if ((media = pwgMediaForPWG(keyword)) == NULL)
2375 if ((media = pwgMediaForLegacy(keyword)) == NULL)
2376 if ((media = pwgMediaForPPD(keyword)) == NULL)
2377 return (NULL);
2378
2379 jobsize.width = media->width;
2380 jobsize.length = media->length;
2381 margins_set = 0;
2382 }
2383
2384 /*
2385 * Now that we have the dimensions and possibly the margins, look at the
2386 * available sizes and find the match...
2387 */
2388
2389 closest = NULL;
2390 dclosest = 999999999;
2391
2392 if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
2393 _cups_strncasecmp(ppd_name, "custom_", 7))
2394 {
2395 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2396 {
2397 /*
2398 * Adobe uses a size matching algorithm with an epsilon of 5 points, which
2399 * is just about 176/2540ths...
2400 */
2401
2402 dwidth = size->width - jobsize.width;
2403 dlength = size->length - jobsize.length;
2404
2405 if (dwidth <= -176 || dwidth >= 176 || dlength <= -176 || dlength >= 176)
2406 continue;
2407
2408 if (margins_set)
2409 {
2410 /*
2411 * Use a tighter epsilon of 1 point (35/2540ths) for margins...
2412 */
2413
2414 dleft = size->left - jobsize.left;
2415 dright = size->right - jobsize.right;
2416 dtop = size->top - jobsize.top;
2417 dbottom = size->bottom - jobsize.bottom;
2418
2419 if (dleft <= -35 || dleft >= 35 || dright <= -35 || dright >= 35 ||
2420 dtop <= -35 || dtop >= 35 || dbottom <= -35 || dbottom >= 35)
2421 {
2422 dleft = dleft < 0 ? -dleft : dleft;
2423 dright = dright < 0 ? -dright : dright;
2424 dbottom = dbottom < 0 ? -dbottom : dbottom;
2425 dtop = dtop < 0 ? -dtop : dtop;
2426 dmin = dleft + dright + dbottom + dtop;
2427
2428 if (dmin < dclosest)
2429 {
2430 dclosest = dmin;
2431 closest = size;
2432 }
2433
2434 continue;
2435 }
2436 }
2437
2438 if (exact)
2439 *exact = 1;
2440
2441 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd));
2442
2443 return (size->map.ppd);
2444 }
2445 }
2446
2447 if (closest)
2448 {
2449 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (closest)",
2450 closest->map.ppd));
2451
2452 return (closest->map.ppd);
2453 }
2454
2455 /*
2456 * If we get here we need to check for custom page size support...
2457 */
2458
2459 if (jobsize.width >= pc->custom_min_width &&
2460 jobsize.width <= pc->custom_max_width &&
2461 jobsize.length >= pc->custom_min_length &&
2462 jobsize.length <= pc->custom_max_length)
2463 {
2464 /*
2465 * In range, format as Custom.WWWWxLLLL (points).
2466 */
2467
2468 snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d",
2469 (int)PWG_TO_POINTS(jobsize.width), (int)PWG_TO_POINTS(jobsize.length));
2470
2471 if (margins_set && exact)
2472 {
2473 dleft = pc->custom_size.left - jobsize.left;
2474 dright = pc->custom_size.right - jobsize.right;
2475 dtop = pc->custom_size.top - jobsize.top;
2476 dbottom = pc->custom_size.bottom - jobsize.bottom;
2477
2478 if (dleft > -35 && dleft < 35 && dright > -35 && dright < 35 &&
2479 dtop > -35 && dtop < 35 && dbottom > -35 && dbottom < 35)
2480 *exact = 1;
2481 }
2482 else if (exact)
2483 *exact = 1;
2484
2485 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (custom)",
2486 pc->custom_ppd_size));
2487
2488 return (pc->custom_ppd_size);
2489 }
2490
2491 /*
2492 * No custom page size support or the size is out of range - return NULL.
2493 */
2494
2495 DEBUG_puts("1_ppdCacheGetPageSize: Returning NULL");
2496
2497 return (NULL);
2498 }
2499
2500
2501 /*
2502 * '_ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize.
2503 */
2504
2505 pwg_size_t * /* O - PWG size or NULL */
2506 _ppdCacheGetSize(
2507 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2508 const char *page_size) /* I - PPD PageSize */
2509 {
2510 int i; /* Looping var */
2511 pwg_media_t *media; /* Media */
2512 pwg_size_t *size; /* Current size */
2513
2514
2515 /*
2516 * Range check input...
2517 */
2518
2519 if (!pc || !page_size)
2520 return (NULL);
2521
2522 if (!_cups_strncasecmp(page_size, "Custom.", 7))
2523 {
2524 /*
2525 * Custom size; size name can be one of the following:
2526 *
2527 * Custom.WIDTHxLENGTHin - Size in inches
2528 * Custom.WIDTHxLENGTHft - Size in feet
2529 * Custom.WIDTHxLENGTHcm - Size in centimeters
2530 * Custom.WIDTHxLENGTHmm - Size in millimeters
2531 * Custom.WIDTHxLENGTHm - Size in meters
2532 * Custom.WIDTHxLENGTH[pt] - Size in points
2533 */
2534
2535 double w, l; /* Width and length of page */
2536 char *ptr; /* Pointer into PageSize */
2537 struct lconv *loc; /* Locale data */
2538
2539 loc = localeconv();
2540 w = (float)_cupsStrScand(page_size + 7, &ptr, loc);
2541 if (!ptr || *ptr != 'x')
2542 return (NULL);
2543
2544 l = (float)_cupsStrScand(ptr + 1, &ptr, loc);
2545 if (!ptr)
2546 return (NULL);
2547
2548 if (!_cups_strcasecmp(ptr, "in"))
2549 {
2550 w *= 2540.0;
2551 l *= 2540.0;
2552 }
2553 else if (!_cups_strcasecmp(ptr, "ft"))
2554 {
2555 w *= 12.0 * 2540.0;
2556 l *= 12.0 * 2540.0;
2557 }
2558 else if (!_cups_strcasecmp(ptr, "mm"))
2559 {
2560 w *= 100.0;
2561 l *= 100.0;
2562 }
2563 else if (!_cups_strcasecmp(ptr, "cm"))
2564 {
2565 w *= 1000.0;
2566 l *= 1000.0;
2567 }
2568 else if (!_cups_strcasecmp(ptr, "m"))
2569 {
2570 w *= 100000.0;
2571 l *= 100000.0;
2572 }
2573 else
2574 {
2575 w *= 2540.0 / 72.0;
2576 l *= 2540.0 / 72.0;
2577 }
2578
2579 pc->custom_size.width = (int)w;
2580 pc->custom_size.length = (int)l;
2581
2582 return (&(pc->custom_size));
2583 }
2584
2585 /*
2586 * Not a custom size - look it up...
2587 */
2588
2589 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2590 if (!_cups_strcasecmp(page_size, size->map.ppd) ||
2591 !_cups_strcasecmp(page_size, size->map.pwg))
2592 return (size);
2593
2594 /*
2595 * Look up standard sizes...
2596 */
2597
2598 if ((media = pwgMediaForPPD(page_size)) == NULL)
2599 if ((media = pwgMediaForLegacy(page_size)) == NULL)
2600 media = pwgMediaForPWG(page_size);
2601
2602 if (media)
2603 {
2604 pc->custom_size.width = media->width;
2605 pc->custom_size.length = media->length;
2606
2607 return (&(pc->custom_size));
2608 }
2609
2610 return (NULL);
2611 }
2612
2613
2614 /*
2615 * '_ppdCacheGetSource()' - Get the PWG media-source associated with a PPD
2616 * InputSlot.
2617 */
2618
2619 const char * /* O - PWG media-source keyword */
2620 _ppdCacheGetSource(
2621 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2622 const char *input_slot) /* I - PPD InputSlot */
2623 {
2624 int i; /* Looping var */
2625 pwg_map_t *source; /* Current source */
2626
2627
2628 /*
2629 * Range check input...
2630 */
2631
2632 if (!pc || !input_slot)
2633 return (NULL);
2634
2635 for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++)
2636 if (!_cups_strcasecmp(input_slot, source->ppd))
2637 return (source->pwg);
2638
2639 return (NULL);
2640 }
2641
2642
2643 /*
2644 * '_ppdCacheGetType()' - Get the PWG media-type associated with a PPD
2645 * MediaType.
2646 */
2647
2648 const char * /* O - PWG media-type keyword */
2649 _ppdCacheGetType(
2650 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2651 const char *media_type) /* I - PPD MediaType */
2652 {
2653 int i; /* Looping var */
2654 pwg_map_t *type; /* Current type */
2655
2656
2657 /*
2658 * Range check input...
2659 */
2660
2661 if (!pc || !media_type)
2662 return (NULL);
2663
2664 for (i = pc->num_types, type = pc->types; i > 0; i --, type ++)
2665 if (!_cups_strcasecmp(media_type, type->ppd))
2666 return (type->pwg);
2667
2668 return (NULL);
2669 }
2670
2671
2672 /*
2673 * '_ppdCacheWriteFile()' - Write PWG mapping data to a file.
2674 */
2675
2676 int /* O - 1 on success, 0 on failure */
2677 _ppdCacheWriteFile(
2678 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2679 const char *filename, /* I - File to write */
2680 ipp_t *attrs) /* I - Attributes to write, if any */
2681 {
2682 int i, j, k; /* Looping vars */
2683 cups_file_t *fp; /* Output file */
2684 pwg_size_t *size; /* Current size */
2685 pwg_map_t *map; /* Current map */
2686 _pwg_finishings_t *f; /* Current finishing option */
2687 cups_option_t *option; /* Current option */
2688 const char *value; /* Filter/pre-filter value */
2689 char newfile[1024]; /* New filename */
2690 _pwg_material_t *m; /* Material */
2691
2692
2693 /*
2694 * Range check input...
2695 */
2696
2697 if (!pc || !filename)
2698 {
2699 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
2700 return (0);
2701 }
2702
2703 /*
2704 * Open the file and write with compression...
2705 */
2706
2707 snprintf(newfile, sizeof(newfile), "%s.N", filename);
2708 if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
2709 {
2710 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
2711 return (0);
2712 }
2713
2714 /*
2715 * Standard header...
2716 */
2717
2718 cupsFilePrintf(fp, "#CUPS-PPD-CACHE-%d\n", _PPD_CACHE_VERSION);
2719
2720 /*
2721 * Output bins...
2722 */
2723
2724 if (pc->num_bins > 0)
2725 {
2726 cupsFilePrintf(fp, "NumBins %d\n", pc->num_bins);
2727 for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
2728 cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd);
2729 }
2730
2731 /*
2732 * Media sizes...
2733 */
2734
2735 cupsFilePrintf(fp, "NumSizes %d\n", pc->num_sizes);
2736 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2737 cupsFilePrintf(fp, "Size %s %s %d %d %d %d %d %d\n", size->map.pwg,
2738 size->map.ppd, size->width, size->length, size->left,
2739 size->bottom, size->right, size->top);
2740 if (pc->custom_max_width > 0)
2741 cupsFilePrintf(fp, "CustomSize %d %d %d %d %d %d %d %d\n",
2742 pc->custom_max_width, pc->custom_max_length,
2743 pc->custom_min_width, pc->custom_min_length,
2744 pc->custom_size.left, pc->custom_size.bottom,
2745 pc->custom_size.right, pc->custom_size.top);
2746
2747 /*
2748 * Media sources...
2749 */
2750
2751 if (pc->source_option)
2752 cupsFilePrintf(fp, "SourceOption %s\n", pc->source_option);
2753
2754 if (pc->num_sources > 0)
2755 {
2756 cupsFilePrintf(fp, "NumSources %d\n", pc->num_sources);
2757 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
2758 cupsFilePrintf(fp, "Source %s %s\n", map->pwg, map->ppd);
2759 }
2760
2761 /*
2762 * Media types...
2763 */
2764
2765 if (pc->num_types > 0)
2766 {
2767 cupsFilePrintf(fp, "NumTypes %d\n", pc->num_types);
2768 for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
2769 cupsFilePrintf(fp, "Type %s %s\n", map->pwg, map->ppd);
2770 }
2771
2772 /*
2773 * Presets...
2774 */
2775
2776 for (i = _PWG_PRINT_COLOR_MODE_MONOCHROME; i < _PWG_PRINT_COLOR_MODE_MAX; i ++)
2777 for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
2778 if (pc->num_presets[i][j])
2779 {
2780 cupsFilePrintf(fp, "Preset %d %d", i, j);
2781 for (k = pc->num_presets[i][j], option = pc->presets[i][j];
2782 k > 0;
2783 k --, option ++)
2784 cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2785 cupsFilePutChar(fp, '\n');
2786 }
2787
2788 /*
2789 * Duplex/sides...
2790 */
2791
2792 if (pc->sides_option)
2793 cupsFilePrintf(fp, "SidesOption %s\n", pc->sides_option);
2794
2795 if (pc->sides_1sided)
2796 cupsFilePrintf(fp, "Sides1Sided %s\n", pc->sides_1sided);
2797
2798 if (pc->sides_2sided_long)
2799 cupsFilePrintf(fp, "Sides2SidedLong %s\n", pc->sides_2sided_long);
2800
2801 if (pc->sides_2sided_short)
2802 cupsFilePrintf(fp, "Sides2SidedShort %s\n", pc->sides_2sided_short);
2803
2804 /*
2805 * Product, cupsFilter, cupsFilter2, and cupsPreFilter...
2806 */
2807
2808 if (pc->product)
2809 cupsFilePutConf(fp, "Product", pc->product);
2810
2811 for (value = (const char *)cupsArrayFirst(pc->filters);
2812 value;
2813 value = (const char *)cupsArrayNext(pc->filters))
2814 cupsFilePutConf(fp, "Filter", value);
2815
2816 for (value = (const char *)cupsArrayFirst(pc->prefilters);
2817 value;
2818 value = (const char *)cupsArrayNext(pc->prefilters))
2819 cupsFilePutConf(fp, "PreFilter", value);
2820
2821 cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
2822
2823 /*
2824 * Finishing options...
2825 */
2826
2827 for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
2828 f;
2829 f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
2830 {
2831 cupsFilePrintf(fp, "Finishings %d", f->value);
2832 for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2833 cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2834 cupsFilePutChar(fp, '\n');
2835 }
2836
2837 /*
2838 * Max copies...
2839 */
2840
2841 cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies);
2842
2843 /*
2844 * Accounting/quota/PIN/managed printing values...
2845 */
2846
2847 if (pc->charge_info_uri)
2848 cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri);
2849
2850 cupsFilePrintf(fp, "AccountId %s\n", pc->account_id ? "true" : "false");
2851 cupsFilePrintf(fp, "AccountingUserId %s\n",
2852 pc->accounting_user_id ? "true" : "false");
2853
2854 if (pc->password)
2855 cupsFilePutConf(fp, "Password", pc->password);
2856
2857 for (value = (char *)cupsArrayFirst(pc->mandatory);
2858 value;
2859 value = (char *)cupsArrayNext(pc->mandatory))
2860 cupsFilePutConf(fp, "Mandatory", value);
2861
2862 /*
2863 * Support files...
2864 */
2865
2866 for (value = (char *)cupsArrayFirst(pc->support_files);
2867 value;
2868 value = (char *)cupsArrayNext(pc->support_files))
2869 cupsFilePutConf(fp, "SupportFile", value);
2870
2871 /*
2872 * 3D stuff...
2873 */
2874
2875 if (pc->cups_3d)
2876 cupsFilePutConf(fp, "3D", pc->cups_3d);
2877
2878 if (pc->cups_layer_order)
2879 cupsFilePutConf(fp, "LayerOrder", pc->cups_layer_order);
2880
2881 if (pc->cups_accuracy[0] || pc->cups_accuracy[0] || pc->cups_accuracy[2])
2882 cupsFilePrintf(fp, "Accuracy %d %d %d\n", pc->cups_accuracy[0], pc->cups_accuracy[1], pc->cups_accuracy[2]);
2883
2884 if (pc->cups_volume[0] || pc->cups_volume[0] || pc->cups_volume[2])
2885 cupsFilePrintf(fp, "Volume %d %d %d\n", pc->cups_volume[0], pc->cups_volume[1], pc->cups_volume[2]);
2886
2887 for (m = (_pwg_material_t *)cupsArrayFirst(pc->materials);
2888 m;
2889 m = (_pwg_material_t *)cupsArrayNext(pc->materials))
2890 {
2891 cupsFilePrintf(fp, "Material %s \"%s\"", m->key, m->name);
2892 for (i = 0; i < m->num_props; i ++)
2893 cupsFilePrintf(fp, " %s=%s", m->props[i].name, m->props[i].value);
2894 cupsFilePuts(fp, "\n");
2895 }
2896
2897 /*
2898 * IPP attributes, if any...
2899 */
2900
2901 if (attrs)
2902 {
2903 cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
2904
2905 attrs->state = IPP_STATE_IDLE;
2906 ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
2907 }
2908
2909 /*
2910 * Close and return...
2911 */
2912
2913 if (cupsFileClose(fp))
2914 {
2915 unlink(newfile);
2916 return (0);
2917 }
2918
2919 unlink(filename);
2920 return (!rename(newfile, filename));
2921 }
2922
2923
2924 /*
2925 * '_ppdCreateFromIPP()' - Create a PPD file describing the capabilities
2926 * of an IPP printer.
2927 */
2928
2929 char * /* O - PPD filename or NULL on error */
2930 _ppdCreateFromIPP(char *buffer, /* I - Filename buffer */
2931 size_t bufsize, /* I - Size of filename buffer */
2932 ipp_t *response) /* I - Get-Printer-Attributes response */
2933 {
2934 cups_file_t *fp; /* PPD file */
2935 ipp_attribute_t *attr, /* xxx-supported */
2936 *defattr, /* xxx-default */
2937 *x_dim, *y_dim; /* Media dimensions */
2938 ipp_t *media_size; /* Media size collection */
2939 char make[256], /* Make and model */
2940 *model, /* Model name */
2941 ppdname[PPD_MAX_NAME];
2942 /* PPD keyword */
2943 int i, j, /* Looping vars */
2944 count, /* Number of values */
2945 bottom, /* Largest bottom margin */
2946 left, /* Largest left margin */
2947 right, /* Largest right margin */
2948 top; /* Largest top margin */
2949 pwg_media_t *pwg; /* PWG media size */
2950 int xres, yres; /* Resolution values */
2951 struct lconv *loc = localeconv();
2952 /* Locale data */
2953
2954
2955 /*
2956 * Range check input...
2957 */
2958
2959 if (buffer)
2960 *buffer = '\0';
2961
2962 if (!buffer || bufsize < 1 || !response)
2963 return (NULL);
2964
2965 /*
2966 * Open a temporary file for the PPD...
2967 */
2968
2969 if ((fp = cupsTempFile2(buffer, (int)bufsize)) == NULL)
2970 return (NULL);
2971
2972 /*
2973 * Standard stuff for PPD file...
2974 */
2975
2976 cupsFilePuts(fp, "*PPD-Adobe: \"4.3\"\n");
2977 cupsFilePuts(fp, "*FormatVersion: \"4.3\"\n");
2978 cupsFilePrintf(fp, "*FileVersion: \"%d.%d\"\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
2979 cupsFilePuts(fp, "*LanguageVersion: English\n");
2980 cupsFilePuts(fp, "*LanguageEncoding: ISOLatin1\n");
2981 cupsFilePuts(fp, "*PSVersion: \"(3010.000) 0\"\n");
2982 cupsFilePuts(fp, "*LanguageLevel: \"3\"\n");
2983 cupsFilePuts(fp, "*FileSystem: False\n");
2984 cupsFilePuts(fp, "*PCFileName: \"ippeve.ppd\"\n");
2985
2986 if ((attr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT)) != NULL)
2987 strlcpy(make, ippGetString(attr, 0, NULL), sizeof(make));
2988 else
2989 strlcpy(make, "Unknown Printer", sizeof(make));
2990
2991 if (!_cups_strncasecmp(make, "Hewlett Packard ", 16) ||
2992 !_cups_strncasecmp(make, "Hewlett-Packard ", 16))
2993 {
2994 model = make + 16;
2995 strlcpy(make, "HP", sizeof(make));
2996 }
2997 else if ((model = strchr(make, ' ')) != NULL)
2998 *model++ = '\0';
2999 else
3000 model = make;
3001
3002 cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make);
3003 cupsFilePrintf(fp, "*ModelName: \"%s\"\n", model);
3004 cupsFilePrintf(fp, "*Product: \"(%s)\"\n", model);
3005 cupsFilePrintf(fp, "*NickName: \"%s\"\n", model);
3006 cupsFilePrintf(fp, "*ShortNickName: \"%s\"\n", model);
3007
3008 if ((attr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN)) != NULL && ippGetBoolean(attr, 0))
3009 cupsFilePuts(fp, "*ColorDevice: True\n");
3010 else
3011 cupsFilePuts(fp, "*ColorDevice: False\n");
3012
3013 cupsFilePrintf(fp, "*cupsVersion: %d.%d\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
3014 cupsFilePuts(fp, "*cupsSNMPSupplies: False\n");
3015 cupsFilePuts(fp, "*cupsLanguages: \"en\"\n");
3016
3017 /*
3018 * Filters...
3019 */
3020
3021 if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL)
3022 {
3023 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3024 {
3025 const char *format = ippGetString(attr, i, NULL);
3026 /* PDL */
3027
3028 if (!_cups_strcasecmp(format, "application/pdf"))
3029 cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 10 -\"\n");
3030 else if (!_cups_strcasecmp(format, "application/postscript"))
3031 cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-postscript application/postscript 10 -\"\n");
3032 else if (_cups_strcasecmp(format, "application/octet-stream") && _cups_strcasecmp(format, "application/vnd.hp-pcl") && _cups_strcasecmp(format, "text/plain"))
3033 cupsFilePrintf(fp, "*cupsFilter2: \"%s %s 10 -\"\n", format, format);
3034 }
3035 }
3036
3037 /*
3038 * PageSize/PageRegion/ImageableArea/PaperDimension
3039 */
3040
3041 if ((attr = ippFindAttribute(response, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != NULL)
3042 {
3043 for (i = 1, bottom = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3044 if (ippGetInteger(attr, i) > bottom)
3045 bottom = ippGetInteger(attr, i);
3046 }
3047 else
3048 bottom = 1270;
3049
3050 if ((attr = ippFindAttribute(response, "media-left-margin-supported", IPP_TAG_INTEGER)) != NULL)
3051 {
3052 for (i = 1, left = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3053 if (ippGetInteger(attr, i) > left)
3054 left = ippGetInteger(attr, i);
3055 }
3056 else
3057 left = 635;
3058
3059 if ((attr = ippFindAttribute(response, "media-right-margin-supported", IPP_TAG_INTEGER)) != NULL)
3060 {
3061 for (i = 1, right = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3062 if (ippGetInteger(attr, i) > right)
3063 right = ippGetInteger(attr, i);
3064 }
3065 else
3066 right = 635;
3067
3068 if ((attr = ippFindAttribute(response, "media-top-margin-supported", IPP_TAG_INTEGER)) != NULL)
3069 {
3070 for (i = 1, top = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3071 if (ippGetInteger(attr, i) > top)
3072 top = ippGetInteger(attr, i);
3073 }
3074 else
3075 top = 1270;
3076
3077 if ((defattr = ippFindAttribute(response, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3078 {
3079 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3080 {
3081 media_size = ippGetCollection(attr, 0);
3082 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3083 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3084
3085 if (x_dim && y_dim)
3086 {
3087 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3088 strlcpy(ppdname, pwg->ppd, sizeof(ppdname));
3089 }
3090 else
3091 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3092 }
3093 else
3094 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3095 }
3096
3097 if ((attr = ippFindAttribute(response, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3098 {
3099 cupsFilePrintf(fp, "*OpenUI *PageSize: PickOne\n"
3100 "*OrderDependency: 10 AnySetup *PageSize\n"
3101 "*DefaultPageSize: %s\n", ppdname);
3102 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3103 {
3104 media_size = ippGetCollection(attr, i);
3105 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3106 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3107
3108 if (x_dim && y_dim)
3109 {
3110 char twidth[256], /* Width string */
3111 tlength[256]; /* Length string */
3112
3113 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3114
3115 _cupsStrFormatd(twidth, twidth + sizeof(twidth), pwg->width * 72.0 / 2540.0, loc);
3116 _cupsStrFormatd(tlength, tlength + sizeof(tlength), pwg->length * 72.0 / 2540.0, loc);
3117
3118 cupsFilePrintf(fp, "*PageSize %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", pwg->ppd, twidth, tlength);
3119 }
3120 }
3121 cupsFilePuts(fp, "*CloseUI: *PageSize\n");
3122
3123 cupsFilePrintf(fp, "*OpenUI *PageRegion: PickOne\n"
3124 "*OrderDependency: 10 AnySetup *PageRegion\n"
3125 "*DefaultPageRegion: %s\n", ppdname);
3126 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3127 {
3128 media_size = ippGetCollection(attr, i);
3129 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3130 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3131
3132 if (x_dim && y_dim)
3133 {
3134 char twidth[256], /* Width string */
3135 tlength[256]; /* Length string */
3136
3137 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3138
3139 _cupsStrFormatd(twidth, twidth + sizeof(twidth), pwg->width * 72.0 / 2540.0, loc);
3140 _cupsStrFormatd(tlength, tlength + sizeof(tlength), pwg->length * 72.0 / 2540.0, loc);
3141
3142 cupsFilePrintf(fp, "*PageRegion %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", pwg->ppd, twidth, tlength);
3143 }
3144 }
3145 cupsFilePuts(fp, "*CloseUI: *PageRegion\n");
3146
3147 cupsFilePrintf(fp, "*DefaultImageableArea: %s\n"
3148 "*DefaultPaperDimension: %s\n", ppdname, ppdname);
3149 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3150 {
3151 media_size = ippGetCollection(attr, i);
3152 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3153 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3154
3155 if (x_dim && y_dim)
3156 {
3157 char tleft[256], /* Left string */
3158 tbottom[256], /* Bottom string */
3159 tright[256], /* Right string */
3160 ttop[256], /* Top string */
3161 twidth[256], /* Width string */
3162 tlength[256]; /* Length string */
3163
3164 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3165
3166 _cupsStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc);
3167 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), bottom * 72.0 / 2540.0, loc);
3168 _cupsStrFormatd(tright, tright + sizeof(tright), (pwg->width - right) * 72.0 / 2540.0, loc);
3169 _cupsStrFormatd(ttop, ttop + sizeof(ttop), (pwg->length - top) * 72.0 / 2540.0, loc);
3170 _cupsStrFormatd(twidth, twidth + sizeof(twidth), pwg->width * 72.0 / 2540.0, loc);
3171 _cupsStrFormatd(tlength, tlength + sizeof(tlength), pwg->length * 72.0 / 2540.0, loc);
3172
3173 cupsFilePrintf(fp, "*ImageableArea %s: \"%s %s %s %s\"\n", pwg->ppd, tleft, tbottom, tright, ttop);
3174 cupsFilePrintf(fp, "*PaperDimension %s: \"%s %s\"\n", pwg->ppd, twidth, tlength);
3175 }
3176 }
3177 }
3178
3179 /*
3180 * InputSlot...
3181 */
3182
3183 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source", IPP_TAG_KEYWORD)) != NULL)
3184 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3185 else
3186 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3187
3188 if ((attr = ippFindAttribute(response, "media-source-supported", IPP_TAG_KEYWORD)) != NULL && (count = ippGetCount(attr)) > 1)
3189 {
3190 static const char * const sources[][2] =
3191 {
3192 { "Auto", "Automatic" },
3193 { "Main", "Main" },
3194 { "Alternate", "Alternate" },
3195 { "LargeCapacity", "Large Capacity" },
3196 { "Manual", "Manual" },
3197 { "Envelope", "Envelope" },
3198 { "Disc", "Disc" },
3199 { "Photo", "Photo" },
3200 { "Hagaki", "Hagaki" },
3201 { "MainRoll", "Main Roll" },
3202 { "AlternateRoll", "Alternate Roll" },
3203 { "Top", "Top" },
3204 { "Middle", "Middle" },
3205 { "Bottom", "Bottom" },
3206 { "Side", "Side" },
3207 { "Left", "Left" },
3208 { "Right", "Right" },
3209 { "Center", "Center" },
3210 { "Rear", "Rear" },
3211 { "ByPassTray", "Multipurpose" },
3212 { "Tray1", "Tray 1" },
3213 { "Tray2", "Tray 2" },
3214 { "Tray3", "Tray 3" },
3215 { "Tray4", "Tray 4" },
3216 { "Tray5", "Tray 5" },
3217 { "Tray6", "Tray 6" },
3218 { "Tray7", "Tray 7" },
3219 { "Tray8", "Tray 8" },
3220 { "Tray9", "Tray 9" },
3221 { "Tray10", "Tray 10" },
3222 { "Tray11", "Tray 11" },
3223 { "Tray12", "Tray 12" },
3224 { "Tray13", "Tray 13" },
3225 { "Tray14", "Tray 14" },
3226 { "Tray15", "Tray 15" },
3227 { "Tray16", "Tray 16" },
3228 { "Tray17", "Tray 17" },
3229 { "Tray18", "Tray 18" },
3230 { "Tray19", "Tray 19" },
3231 { "Tray20", "Tray 20" },
3232 { "Roll1", "Roll 1" },
3233 { "Roll2", "Roll 2" },
3234 { "Roll3", "Roll 3" },
3235 { "Roll4", "Roll 4" },
3236 { "Roll5", "Roll 5" },
3237 { "Roll6", "Roll 6" },
3238 { "Roll7", "Roll 7" },
3239 { "Roll8", "Roll 8" },
3240 { "Roll9", "Roll 9" },
3241 { "Roll10", "Roll 10" }
3242 };
3243
3244 cupsFilePrintf(fp, "*OpenUI *InputSlot: PickOne\n"
3245 "*OrderDependency: 10 AnySetup *InputSlot\n"
3246 "*DefaultInputSlot: %s\n", ppdname);
3247 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3248 {
3249 pwg_ppdize_name(ippGetString(attr, i, NULL), ppdname, sizeof(ppdname));
3250
3251 for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++)
3252 if (!strcmp(sources[j][0], ppdname))
3253 {
3254 cupsFilePrintf(fp, "*InputSlot %s/%s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, sources[j][1], j);
3255 break;
3256 }
3257 }
3258 cupsFilePuts(fp, "*CloseUI: *InputSlot\n");
3259 }
3260
3261 /*
3262 * MediaType...
3263 */
3264
3265 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type", IPP_TAG_KEYWORD)) != NULL)
3266 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3267 else
3268 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3269
3270 if ((attr = ippFindAttribute(response, "media-type-supported", IPP_TAG_KEYWORD)) != NULL && (count = ippGetCount(attr)) > 1)
3271 {
3272 static const char * const types[][2] =
3273 { /* Media type strings (far from complete) */
3274 { "Auto", "Automatic" },
3275 { "Cardstock", "Cardstock" },
3276 { "Disc", "CD/DVD/Bluray" },
3277 { "Envelope", "Envelope" },
3278 { "Labels", "Label" },
3279 { "Other", "Other" },
3280 { "Photographic", "Photo" },
3281 { "PhotographicGlossy", "Glossy Photo" },
3282 { "PhotographicHighGloss", "High-Gloss Photo" },
3283 { "PhotographicMatte", "Matte Photo" },
3284 { "PhotographicSatin", "Satin Photo" },
3285 { "PhotographicSemiGloss", "Semi-Gloss Photo" },
3286 { "Stationery", "Plain Paper" },
3287 { "StationeryLetterhead", "Letterhead" },
3288 { "Transparency", "Transparency" }
3289 };
3290
3291 cupsFilePrintf(fp, "*OpenUI *MediaType: PickOne\n"
3292 "*OrderDependency: 10 AnySetup *MediaType\n"
3293 "*DefaultMediaType: %s\n", ppdname);
3294 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3295 {
3296 pwg_ppdize_name(ippGetString(attr, i, NULL), ppdname, sizeof(ppdname));
3297
3298 for (j = 0; j < (int)(sizeof(types) / sizeof(types[0])); j ++)
3299 if (!strcmp(types[j][0], ppdname))
3300 {
3301 cupsFilePrintf(fp, "*MediaType %s/%s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, types[j][1], ppdname);
3302 break;
3303 }
3304
3305 if (j >= (int)(sizeof(types) / sizeof(types[0])))
3306 cupsFilePrintf(fp, "*MediaType %s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, ppdname);
3307
3308 }
3309 cupsFilePuts(fp, "*CloseUI: *MediaType\n");
3310 }
3311
3312 /*
3313 * ColorModel...
3314 */
3315
3316 if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
3317 attr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD);
3318
3319 if (attr)
3320 {
3321 const char *default_color = NULL; /* Default */
3322
3323 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3324 {
3325 const char *keyword = ippGetString(attr, i, NULL);
3326 /* Keyword for color/bit depth */
3327
3328 if (!strcmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
3329 {
3330 if (!default_color)
3331 cupsFilePuts(fp, "*OpenUI *ColorModel/Color Mode: PickOne\n"
3332 "*OrderDependency: 10 AnySetup *ColorModel\n");
3333
3334 cupsFilePuts(fp, "*ColorModel FastGray/Fast Grayscale: \"<</cupsColorSpace 3/cupsBitsPerColor 1/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n");
3335
3336 if (!default_color)
3337 default_color = "FastGray";
3338 }
3339 else if (!strcmp(keyword, "sgray_8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
3340 {
3341 if (!default_color)
3342 cupsFilePuts(fp, "*OpenUI *ColorModel/Color Mode: PickOne\n"
3343 "*OrderDependency: 10 AnySetup *ColorModel\n");
3344
3345 cupsFilePuts(fp, "*ColorModel Gray/Grayscale: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n");
3346
3347 if (!default_color || !strcmp(default_color, "FastGray"))
3348 default_color = "Gray";
3349 }
3350 else if (!strcmp(keyword, "srgb_8") || !strcmp(keyword, "color"))
3351 {
3352 if (!default_color)
3353 cupsFilePuts(fp, "*OpenUI *ColorModel/Color Mode: PickOne\n"
3354 "*OrderDependency: 10 AnySetup *ColorModel\n");
3355
3356 cupsFilePuts(fp, "*ColorModel RGB/Color: \"<</cupsColorSpace 19/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n");
3357
3358 default_color = "RGB";
3359 }
3360 }
3361
3362 if (default_color)
3363 {
3364 cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color);
3365 cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
3366 }
3367 }
3368
3369 /*
3370 * Duplex...
3371 */
3372
3373 if ((attr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL && ippContainsString(attr, "two-sided-long-edge"))
3374 {
3375 cupsFilePuts(fp, "*OpenUI *Duplex/2-Sided Printing: PickOne\n"
3376 "*OrderDependency: 10 AnySetup *Duplex\n"
3377 "*DefaultDuplex: None\n"
3378 "*Duplex None/Off (1-Sided): \"<</Duplex false>>setpagedevice\"\n"
3379 "*Duplex DuplexNoTumble/Long-Edge (Portrait): \"<</Duplex true/Tumble false>>setpagedevice\"\n"
3380 "*Duplex DuplexTumble/Short-Edge (Landscape): \"<</Duplex true/Tumble true>>setpagedevice\"\n"
3381 "*CloseUI: *Duplex\n");
3382
3383 if ((attr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
3384 {
3385 const char *keyword = ippGetString(attr, 0, NULL);
3386 /* Keyword value */
3387
3388 if (!strcmp(keyword, "flipped"))
3389 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3390 else if (!strcmp(keyword, "manual-tumble"))
3391 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3392 else if (!strcmp(keyword, "normal"))
3393 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3394 else
3395 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3396 }
3397 else if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
3398 {
3399 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3400 {
3401 const char *dm = ippGetString(attr, i, NULL);
3402 /* DM value */
3403
3404 if (!_cups_strcasecmp(dm, "DM1"))
3405 {
3406 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3407 break;
3408 }
3409 else if (!_cups_strcasecmp(dm, "DM2"))
3410 {
3411 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3412 break;
3413 }
3414 else if (!_cups_strcasecmp(dm, "DM3"))
3415 {
3416 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3417 break;
3418 }
3419 else if (!_cups_strcasecmp(dm, "DM4"))
3420 {
3421 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3422 break;
3423 }
3424 }
3425 }
3426 }
3427
3428 /*
3429 * cupsPrintQuality and DefaultResolution...
3430 */
3431
3432 if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
3433 {
3434 count = ippGetCount(attr);
3435
3436 pwg_ppdize_resolution(attr, count / 2, &xres, &yres, ppdname, sizeof(ppdname));
3437 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
3438
3439 cupsFilePuts(fp, "*OpenUI *cupsPrintQuality/Print Quality: PickOne\n"
3440 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
3441 "*DefaultcupsPrintQuality: Normal\n");
3442 if (count > 2)
3443 {
3444 pwg_ppdize_resolution(attr, 0, &xres, &yres, NULL, 0);
3445 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
3446 }
3447 pwg_ppdize_resolution(attr, count / 2, &xres, &yres, NULL, 0);
3448 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
3449 if (count > 1)
3450 {
3451 pwg_ppdize_resolution(attr, count - 1, &xres, &yres, NULL, 0);
3452 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
3453 }
3454
3455 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
3456 }
3457 else if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
3458 {
3459 int lowdpi = 0, hidpi = 0; /* Lower and higher resolution */
3460
3461 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3462 {
3463 const char *rs = ippGetString(attr, i, NULL);
3464 /* RS value */
3465
3466 if (_cups_strncasecmp(rs, "RS", 2))
3467 continue;
3468
3469 lowdpi = atoi(rs + 2);
3470 if ((rs = strrchr(rs, '-')) != NULL)
3471 hidpi = atoi(rs + 1);
3472 else
3473 hidpi = lowdpi;
3474 break;
3475 }
3476
3477 if (lowdpi == 0)
3478 {
3479 /*
3480 * Invalid "urf-supported" value...
3481 */
3482
3483 cupsFilePuts(fp, "*DefaultResolution: 300dpi\n");
3484 }
3485 else
3486 {
3487 /*
3488 * Generate print qualities based on low and high DPIs...
3489 */
3490
3491 cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi);
3492
3493 cupsFilePuts(fp, "*OpenUI *cupsPrintQuality/Print Quality: PickOne\n"
3494 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
3495 "*DefaultcupsPrintQuality: Normal\n");
3496 if ((lowdpi & 1) == 0)
3497 cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", lowdpi, lowdpi / 2);
3498 cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", lowdpi, lowdpi);
3499 if (hidpi > lowdpi)
3500 cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", hidpi, hidpi);
3501 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
3502 }
3503 }
3504 else if ((attr = ippFindAttribute(response, "printer-resolution-default", IPP_TAG_RESOLUTION)) != NULL)
3505 {
3506 pwg_ppdize_resolution(attr, 0, &xres, &yres, ppdname, sizeof(ppdname));
3507 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
3508 }
3509 else
3510 cupsFilePuts(fp, "*DefaultResolution: 300dpi\n");
3511
3512 /*
3513 * Close up and return...
3514 */
3515
3516 cupsFileClose(fp);
3517
3518 return (buffer);
3519 }
3520
3521
3522 /*
3523 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
3524 * media-source.
3525 */
3526
3527 const char * /* O - InputSlot name */
3528 _pwgInputSlotForSource(
3529 const char *media_source, /* I - PWG media-source */
3530 char *name, /* I - Name buffer */
3531 size_t namesize) /* I - Size of name buffer */
3532 {
3533 /*
3534 * Range check input...
3535 */
3536
3537 if (!media_source || !name || namesize < PPD_MAX_NAME)
3538 return (NULL);
3539
3540 if (_cups_strcasecmp(media_source, "main"))
3541 strlcpy(name, "Cassette", namesize);
3542 else if (_cups_strcasecmp(media_source, "alternate"))
3543 strlcpy(name, "Multipurpose", namesize);
3544 else if (_cups_strcasecmp(media_source, "large-capacity"))
3545 strlcpy(name, "LargeCapacity", namesize);
3546 else if (_cups_strcasecmp(media_source, "bottom"))
3547 strlcpy(name, "Lower", namesize);
3548 else if (_cups_strcasecmp(media_source, "middle"))
3549 strlcpy(name, "Middle", namesize);
3550 else if (_cups_strcasecmp(media_source, "top"))
3551 strlcpy(name, "Upper", namesize);
3552 else if (_cups_strcasecmp(media_source, "rear"))
3553 strlcpy(name, "Rear", namesize);
3554 else if (_cups_strcasecmp(media_source, "side"))
3555 strlcpy(name, "Side", namesize);
3556 else if (_cups_strcasecmp(media_source, "envelope"))
3557 strlcpy(name, "Envelope", namesize);
3558 else if (_cups_strcasecmp(media_source, "main-roll"))
3559 strlcpy(name, "Roll", namesize);
3560 else if (_cups_strcasecmp(media_source, "alternate-roll"))
3561 strlcpy(name, "Roll2", namesize);
3562 else
3563 pwg_ppdize_name(media_source, name, namesize);
3564
3565 return (name);
3566 }
3567
3568
3569 /*
3570 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
3571 * media-type.
3572 */
3573
3574 const char * /* O - MediaType name */
3575 _pwgMediaTypeForType(
3576 const char *media_type, /* I - PWG media-type */
3577 char *name, /* I - Name buffer */
3578 size_t namesize) /* I - Size of name buffer */
3579 {
3580 /*
3581 * Range check input...
3582 */
3583
3584 if (!media_type || !name || namesize < PPD_MAX_NAME)
3585 return (NULL);
3586
3587 if (_cups_strcasecmp(media_type, "auto"))
3588 strlcpy(name, "Auto", namesize);
3589 else if (_cups_strcasecmp(media_type, "cardstock"))
3590 strlcpy(name, "Cardstock", namesize);
3591 else if (_cups_strcasecmp(media_type, "envelope"))
3592 strlcpy(name, "Envelope", namesize);
3593 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
3594 strlcpy(name, "Glossy", namesize);
3595 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
3596 strlcpy(name, "HighGloss", namesize);
3597 else if (_cups_strcasecmp(media_type, "photographic-matte"))
3598 strlcpy(name, "Matte", namesize);
3599 else if (_cups_strcasecmp(media_type, "stationery"))
3600 strlcpy(name, "Plain", namesize);
3601 else if (_cups_strcasecmp(media_type, "stationery-coated"))
3602 strlcpy(name, "Coated", namesize);
3603 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
3604 strlcpy(name, "Inkjet", namesize);
3605 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
3606 strlcpy(name, "Letterhead", namesize);
3607 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
3608 strlcpy(name, "Preprinted", namesize);
3609 else if (_cups_strcasecmp(media_type, "transparency"))
3610 strlcpy(name, "Transparency", namesize);
3611 else
3612 pwg_ppdize_name(media_type, name, namesize);
3613
3614 return (name);
3615 }
3616
3617
3618 /*
3619 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
3620 */
3621
3622 const char * /* O - PageSize name */
3623 _pwgPageSizeForMedia(
3624 pwg_media_t *media, /* I - Media */
3625 char *name, /* I - PageSize name buffer */
3626 size_t namesize) /* I - Size of name buffer */
3627 {
3628 const char *sizeptr, /* Pointer to size in PWG name */
3629 *dimptr; /* Pointer to dimensions in PWG name */
3630
3631
3632 /*
3633 * Range check input...
3634 */
3635
3636 if (!media || !name || namesize < PPD_MAX_NAME)
3637 return (NULL);
3638
3639 /*
3640 * Copy or generate a PageSize name...
3641 */
3642
3643 if (media->ppd)
3644 {
3645 /*
3646 * Use a standard Adobe name...
3647 */
3648
3649 strlcpy(name, media->ppd, namesize);
3650 }
3651 else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
3652 (sizeptr = strchr(media->pwg, '_')) == NULL ||
3653 (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
3654 (size_t)(dimptr - sizeptr) > namesize)
3655 {
3656 /*
3657 * Use a name of the form "wNNNhNNN"...
3658 */
3659
3660 snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
3661 (int)PWG_TO_POINTS(media->length));
3662 }
3663 else
3664 {
3665 /*
3666 * Copy the size name from class_sizename_dimensions...
3667 */
3668
3669 memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
3670 name[dimptr - sizeptr - 1] = '\0';
3671 }
3672
3673 return (name);
3674 }
3675
3676
3677 /*
3678 * 'pwg_compare_finishings()' - Compare two finishings values.
3679 */
3680
3681 static int /* O- Result of comparison */
3682 pwg_compare_finishings(
3683 _pwg_finishings_t *a, /* I - First finishings value */
3684 _pwg_finishings_t *b) /* I - Second finishings value */
3685 {
3686 return ((int)b->value - (int)a->value);
3687 }
3688
3689
3690 /*
3691 * 'pwg_free_finishings()' - Free a finishings value.
3692 */
3693
3694 static void
3695 pwg_free_finishings(
3696 _pwg_finishings_t *f) /* I - Finishings value */
3697 {
3698 cupsFreeOptions(f->num_options, f->options);
3699 free(f);
3700 }
3701
3702
3703 /*
3704 * 'pwg_free_material()' - Free a material value.
3705 */
3706
3707 static void
3708 pwg_free_material(_pwg_material_t *m) /* I - Material value */
3709 {
3710 _cupsStrFree(m->key);
3711 _cupsStrFree(m->name);
3712
3713 cupsFreeOptions(m->num_props, m->props);
3714
3715 free(m);
3716 }
3717
3718
3719 /*
3720 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
3721 */
3722
3723 static void
3724 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
3725 char *name, /* I - Name buffer */
3726 size_t namesize) /* I - Size of name buffer */
3727 {
3728 char *ptr, /* Pointer into name buffer */
3729 *end; /* End of name buffer */
3730
3731
3732 *name = (char)toupper(*ipp++);
3733
3734 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
3735 {
3736 if (*ipp == '-' && _cups_isalpha(ipp[1]))
3737 {
3738 ipp ++;
3739 *ptr++ = (char)toupper(*ipp++ & 255);
3740 }
3741 else
3742 *ptr++ = *ipp++;
3743 }
3744
3745 *ptr = '\0';
3746 }
3747
3748
3749 /*
3750 * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values.
3751 */
3752
3753 static void
3754 pwg_ppdize_resolution(
3755 ipp_attribute_t *attr, /* I - Attribute to convert */
3756 int element, /* I - Element to convert */
3757 int *xres, /* O - X resolution in DPI */
3758 int *yres, /* O - Y resolution in DPI */
3759 char *name, /* I - Name buffer */
3760 size_t namesize) /* I - Size of name buffer */
3761 {
3762 ipp_res_t units; /* Units for resolution */
3763
3764
3765 *xres = ippGetResolution(attr, element, yres, &units);
3766
3767 if (units == IPP_RES_PER_CM)
3768 {
3769 *xres = (int)(*xres * 2.54);
3770 *yres = (int)(*yres * 2.54);
3771 }
3772
3773 if (name && namesize > 4)
3774 {
3775 if (*xres == *yres)
3776 snprintf(name, namesize, "%ddpi", *xres);
3777 else
3778 snprintf(name, namesize, "%dx%ddpi", *xres, *yres);
3779 }
3780 }
3781
3782
3783 /*
3784 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
3785 */
3786
3787 static void
3788 pwg_unppdize_name(const char *ppd, /* I - PPD keyword */
3789 char *name, /* I - Name buffer */
3790 size_t namesize, /* I - Size of name buffer */
3791 const char *dashchars)/* I - Characters to be replaced by dashes */
3792 {
3793 char *ptr, /* Pointer into name buffer */
3794 *end; /* End of name buffer */
3795
3796
3797 if (_cups_islower(*ppd))
3798 {
3799 /*
3800 * Already lowercase name, use as-is?
3801 */
3802
3803 const char *ppdptr; /* Pointer into PPD keyword */
3804
3805 for (ppdptr = ppd + 1; *ppdptr; ppdptr ++)
3806 if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr))
3807 break;
3808
3809 if (!*ppdptr)
3810 {
3811 strlcpy(name, ppd, namesize);
3812 return;
3813 }
3814 }
3815
3816 for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
3817 {
3818 if (_cups_isalnum(*ppd) || *ppd == '-')
3819 *ptr++ = (char)tolower(*ppd & 255);
3820 else if (strchr(dashchars, *ppd))
3821 *ptr++ = '-';
3822 else
3823 *ptr++ = *ppd;
3824
3825 if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
3826 _cups_isupper(ppd[1]) && ptr < end)
3827 *ptr++ = '-';
3828 else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
3829 *ptr++ = '-';
3830 }
3831
3832 *ptr = '\0';
3833 }
3834
3835
3836 /*
3837 * End of "$Id$".
3838 */