]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd-cache.c
cf34a00bd5dff9c2367701272d78eab656014f2f
[thirdparty/cups.git] / cups / ppd-cache.c
1 /*
2 * "$Id$"
3 *
4 * PPD cache implementation for CUPS.
5 *
6 * Copyright 2010-2012 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 * Contents:
17 *
18 * _ppdCacheCreateWithFile() - Create PPD cache and mapping data from a
19 * written file.
20 * _ppdCacheCreateWithPPD() - Create PWG mapping data from a PPD file.
21 * _ppdCacheDestroy() - Free all memory used for PWG mapping data.
22 * _ppdCacheGetBin() - Get the PWG output-bin keyword associated with
23 * a PPD OutputBin.
24 * _ppdCacheGetInputSlot() - Get the PPD InputSlot associated with the job
25 * attributes or a keyword string.
26 * _ppdCacheGetMediaType() - Get the PPD MediaType associated with the job
27 * attributes or a keyword string.
28 * _ppdCacheGetOutputBin() - Get the PPD OutputBin associated with the
29 * keyword string.
30 * _ppdCacheGetPageSize() - Get the PPD PageSize associated with the job
31 * attributes or a keyword string.
32 * _ppdCacheGetSize() - Get the PWG size associated with a PPD
33 * PageSize.
34 * _ppdCacheGetSource() - Get the PWG media-source associated with a PPD
35 * InputSlot.
36 * _ppdCacheGetType() - Get the PWG media-type associated with a PPD
37 * MediaType.
38 * _ppdCacheWriteFile() - Write PWG mapping data to a file.
39 * _pwgInputSlotForSource() - Get the InputSlot name for the given PWG
40 * media-source.
41 * _pwgMediaTypeForType() - Get the MediaType name for the given PWG
42 * media-type.
43 * _pwgPageSizeForMedia() - Get the PageSize name for the given media.
44 * pwg_ppdize_name() - Convert an IPP keyword to a PPD keyword.
45 * pwg_unppdize_name() - Convert a PPD keyword to a lowercase IPP
46 * keyword.
47 */
48
49 /*
50 * Include necessary headers...
51 */
52
53 #include "cups-private.h"
54 #include <math.h>
55
56
57 /*
58 * Macro to test for two almost-equal PWG measurements.
59 */
60
61 #define _PWG_EQUIVALENT(x, y) (abs((x)-(y)) < 2)
62
63
64 /*
65 * Local functions...
66 */
67
68 static int pwg_compare_finishings(_pwg_finishings_t *a,
69 _pwg_finishings_t *b);
70 static void pwg_free_finishings(_pwg_finishings_t *f);
71 static void pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
72 static void pwg_unppdize_name(const char *ppd, char *name, size_t namesize);
73
74
75 /*
76 * '_ppdCacheCreateWithFile()' - Create PPD cache and mapping data from a
77 * written file.
78 *
79 * Use the @link _ppdCacheWriteFile@ function to write PWG mapping data to a
80 * file.
81 */
82
83 _ppd_cache_t * /* O - PPD cache and mapping data */
84 _ppdCacheCreateWithFile(
85 const char *filename, /* I - File to read */
86 ipp_t **attrs) /* IO - IPP attributes, if any */
87 {
88 cups_file_t *fp; /* File */
89 _ppd_cache_t *pc; /* PWG mapping data */
90 _pwg_size_t *size; /* Current size */
91 _pwg_map_t *map; /* Current map */
92 _pwg_finishings_t *finishings; /* Current finishings option */
93 int linenum, /* Current line number */
94 num_bins, /* Number of bins in file */
95 num_sizes, /* Number of sizes in file */
96 num_sources, /* Number of sources in file */
97 num_types; /* Number of types in file */
98 char line[2048], /* Current line */
99 *value, /* Pointer to value in line */
100 *valueptr, /* Pointer into value */
101 pwg_keyword[128], /* PWG keyword */
102 ppd_keyword[PPD_MAX_NAME];
103 /* PPD keyword */
104 _pwg_print_color_mode_t print_color_mode;
105 /* Print color mode for preset */
106 _pwg_print_quality_t print_quality; /* Print quality for preset */
107
108
109 DEBUG_printf(("_ppdCacheCreateWithFile(filename=\"%s\")", filename));
110
111 /*
112 * Range check input...
113 */
114
115 if (attrs)
116 *attrs = NULL;
117
118 if (!filename)
119 {
120 _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
121 return (NULL);
122 }
123
124 /*
125 * Open the file...
126 */
127
128 if ((fp = cupsFileOpen(filename, "r")) == NULL)
129 {
130 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
131 return (NULL);
132 }
133
134 /*
135 * Read the first line and make sure it has "#CUPS-PPD-CACHE-version" in it...
136 */
137
138 if (!cupsFileGets(fp, line, sizeof(line)))
139 {
140 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
141 DEBUG_puts("_ppdCacheCreateWithFile: Unable to read first line.");
142 cupsFileClose(fp);
143 return (NULL);
144 }
145
146 if (strncmp(line, "#CUPS-PPD-CACHE-", 16))
147 {
148 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
149 DEBUG_printf(("_ppdCacheCreateWithFile: Wrong first line \"%s\".", line));
150 cupsFileClose(fp);
151 return (NULL);
152 }
153
154 if (atoi(line + 16) != _PPD_CACHE_VERSION)
155 {
156 _cupsSetError(IPP_INTERNAL_ERROR, _("Out of date PPD cache file."), 1);
157 DEBUG_printf(("_ppdCacheCreateWithFile: Cache file has version %s, "
158 "expected %d.", line + 16, _PPD_CACHE_VERSION));
159 cupsFileClose(fp);
160 return (NULL);
161 }
162
163 /*
164 * Allocate the mapping data structure...
165 */
166
167 if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
168 {
169 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
170 DEBUG_puts("_ppdCacheCreateWithFile: Unable to allocate _ppd_cache_t.");
171 goto create_error;
172 }
173
174 pc->max_copies = 9999;
175
176 /*
177 * Read the file...
178 */
179
180 linenum = 0;
181 num_bins = 0;
182 num_sizes = 0;
183 num_sources = 0;
184 num_types = 0;
185
186 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
187 {
188 DEBUG_printf(("_ppdCacheCreateWithFile: line=\"%s\", value=\"%s\", "
189 "linenum=%d", line, value, linenum));
190
191 if (!value)
192 {
193 DEBUG_printf(("_ppdCacheCreateWithFile: Missing value on line %d.",
194 linenum));
195 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
196 goto create_error;
197 }
198 else if (!_cups_strcasecmp(line, "Filter"))
199 {
200 if (!pc->filters)
201 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
202 (cups_acopy_func_t)_cupsStrAlloc,
203 (cups_afree_func_t)_cupsStrFree);
204
205 cupsArrayAdd(pc->filters, value);
206 }
207 else if (!_cups_strcasecmp(line, "PreFilter"))
208 {
209 if (!pc->prefilters)
210 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
211 (cups_acopy_func_t)_cupsStrAlloc,
212 (cups_afree_func_t)_cupsStrFree);
213
214 cupsArrayAdd(pc->prefilters, value);
215 }
216 else if (!_cups_strcasecmp(line, "Product"))
217 {
218 pc->product = _cupsStrAlloc(value);
219 }
220 else if (!_cups_strcasecmp(line, "SingleFile"))
221 {
222 pc->single_file = !_cups_strcasecmp(value, "true");
223 }
224 else if (!_cups_strcasecmp(line, "IPP"))
225 {
226 off_t pos = cupsFileTell(fp), /* Position in file */
227 length = strtol(value, NULL, 10);
228 /* Length of IPP attributes */
229
230 if (attrs && *attrs)
231 {
232 DEBUG_puts("_ppdCacheCreateWithFile: IPP listed multiple times.");
233 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
234 goto create_error;
235 }
236 else if (length <= 0)
237 {
238 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP length.");
239 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
240 goto create_error;
241 }
242
243 if (attrs)
244 {
245 /*
246 * Read IPP attributes into the provided variable...
247 */
248
249 *attrs = ippNew();
250
251 if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
252 *attrs) != IPP_DATA)
253 {
254 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
255 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
256 goto create_error;
257 }
258 }
259 else
260 {
261 /*
262 * Skip the IPP data entirely...
263 */
264
265 cupsFileSeek(fp, pos + length);
266 }
267
268 if (cupsFileTell(fp) != (pos + length))
269 {
270 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
271 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
272 goto create_error;
273 }
274 }
275 else if (!_cups_strcasecmp(line, "NumBins"))
276 {
277 if (num_bins > 0)
278 {
279 DEBUG_puts("_ppdCacheCreateWithFile: NumBins listed multiple times.");
280 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
281 goto create_error;
282 }
283
284 if ((num_bins = atoi(value)) <= 0 || num_bins > 65536)
285 {
286 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumBins value %d on line "
287 "%d.", num_sizes, linenum));
288 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
289 goto create_error;
290 }
291
292 if ((pc->bins = calloc(num_bins, sizeof(_pwg_map_t))) == NULL)
293 {
294 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d bins.",
295 num_sizes));
296 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
297 goto create_error;
298 }
299 }
300 else if (!_cups_strcasecmp(line, "Bin"))
301 {
302 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
303 {
304 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Bin on line %d.", linenum));
305 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
306 goto create_error;
307 }
308
309 if (pc->num_bins >= num_bins)
310 {
311 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Bin's on line %d.",
312 linenum));
313 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
314 goto create_error;
315 }
316
317 map = pc->bins + pc->num_bins;
318 map->pwg = _cupsStrAlloc(pwg_keyword);
319 map->ppd = _cupsStrAlloc(ppd_keyword);
320
321 pc->num_bins ++;
322 }
323 else if (!_cups_strcasecmp(line, "NumSizes"))
324 {
325 if (num_sizes > 0)
326 {
327 DEBUG_puts("_ppdCacheCreateWithFile: NumSizes listed multiple times.");
328 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
329 goto create_error;
330 }
331
332 if ((num_sizes = atoi(value)) <= 0 || num_sizes > 65536)
333 {
334 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSizes value %d on line "
335 "%d.", num_sizes, linenum));
336 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
337 goto create_error;
338 }
339
340 if ((pc->sizes = calloc(num_sizes, sizeof(_pwg_size_t))) == NULL)
341 {
342 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sizes.",
343 num_sizes));
344 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
345 goto create_error;
346 }
347 }
348 else if (!_cups_strcasecmp(line, "Size"))
349 {
350 if (pc->num_sizes >= num_sizes)
351 {
352 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Size's on line %d.",
353 linenum));
354 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
355 goto create_error;
356 }
357
358 size = pc->sizes + pc->num_sizes;
359
360 if (sscanf(value, "%127s%40s%d%d%d%d%d%d", pwg_keyword, ppd_keyword,
361 &(size->width), &(size->length), &(size->left),
362 &(size->bottom), &(size->right), &(size->top)) != 8)
363 {
364 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Size on line %d.",
365 linenum));
366 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
367 goto create_error;
368 }
369
370 size->map.pwg = _cupsStrAlloc(pwg_keyword);
371 size->map.ppd = _cupsStrAlloc(ppd_keyword);
372
373 pc->num_sizes ++;
374 }
375 else if (!_cups_strcasecmp(line, "CustomSize"))
376 {
377 if (pc->custom_max_width > 0)
378 {
379 DEBUG_printf(("_ppdCacheCreateWithFile: Too many CustomSize's on line "
380 "%d.", linenum));
381 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
382 goto create_error;
383 }
384
385 if (sscanf(value, "%d%d%d%d%d%d%d%d", &(pc->custom_max_width),
386 &(pc->custom_max_length), &(pc->custom_min_width),
387 &(pc->custom_min_length), &(pc->custom_size.left),
388 &(pc->custom_size.bottom), &(pc->custom_size.right),
389 &(pc->custom_size.top)) != 8)
390 {
391 DEBUG_printf(("_ppdCacheCreateWithFile: Bad CustomSize on line %d.",
392 linenum));
393 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
394 goto create_error;
395 }
396
397 _pwgGenerateSize(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
398 pc->custom_max_width, pc->custom_max_length);
399 pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
400
401 _pwgGenerateSize(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
402 pc->custom_min_width, pc->custom_min_length);
403 pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
404 }
405 else if (!_cups_strcasecmp(line, "SourceOption"))
406 {
407 pc->source_option = _cupsStrAlloc(value);
408 }
409 else if (!_cups_strcasecmp(line, "NumSources"))
410 {
411 if (num_sources > 0)
412 {
413 DEBUG_puts("_ppdCacheCreateWithFile: NumSources listed multiple "
414 "times.");
415 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
416 goto create_error;
417 }
418
419 if ((num_sources = atoi(value)) <= 0 || num_sources > 65536)
420 {
421 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSources value %d on "
422 "line %d.", num_sources, linenum));
423 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
424 goto create_error;
425 }
426
427 if ((pc->sources = calloc(num_sources, sizeof(_pwg_map_t))) == NULL)
428 {
429 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sources.",
430 num_sources));
431 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
432 goto create_error;
433 }
434 }
435 else if (!_cups_strcasecmp(line, "Source"))
436 {
437 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
438 {
439 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Source on line %d.",
440 linenum));
441 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
442 goto create_error;
443 }
444
445 if (pc->num_sources >= num_sources)
446 {
447 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Source's on line %d.",
448 linenum));
449 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
450 goto create_error;
451 }
452
453 map = pc->sources + pc->num_sources;
454 map->pwg = _cupsStrAlloc(pwg_keyword);
455 map->ppd = _cupsStrAlloc(ppd_keyword);
456
457 pc->num_sources ++;
458 }
459 else if (!_cups_strcasecmp(line, "NumTypes"))
460 {
461 if (num_types > 0)
462 {
463 DEBUG_puts("_ppdCacheCreateWithFile: NumTypes listed multiple times.");
464 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
465 goto create_error;
466 }
467
468 if ((num_types = atoi(value)) <= 0 || num_types > 65536)
469 {
470 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumTypes value %d on "
471 "line %d.", num_types, linenum));
472 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
473 goto create_error;
474 }
475
476 if ((pc->types = calloc(num_types, sizeof(_pwg_map_t))) == NULL)
477 {
478 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d types.",
479 num_types));
480 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
481 goto create_error;
482 }
483 }
484 else if (!_cups_strcasecmp(line, "Type"))
485 {
486 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
487 {
488 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Type on line %d.",
489 linenum));
490 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
491 goto create_error;
492 }
493
494 if (pc->num_types >= num_types)
495 {
496 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Type's on line %d.",
497 linenum));
498 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
499 goto create_error;
500 }
501
502 map = pc->types + pc->num_types;
503 map->pwg = _cupsStrAlloc(pwg_keyword);
504 map->ppd = _cupsStrAlloc(ppd_keyword);
505
506 pc->num_types ++;
507 }
508 else if (!_cups_strcasecmp(line, "Preset"))
509 {
510 /*
511 * Preset output-mode print-quality name=value ...
512 */
513
514 print_color_mode = (_pwg_print_color_mode_t)strtol(value, &valueptr, 10);
515 print_quality = (_pwg_print_quality_t)strtol(valueptr, &valueptr, 10);
516
517 if (print_color_mode < _PWG_PRINT_COLOR_MODE_MONOCHROME ||
518 print_color_mode >= _PWG_PRINT_COLOR_MODE_MAX ||
519 print_quality < _PWG_PRINT_QUALITY_DRAFT ||
520 print_quality >= _PWG_PRINT_QUALITY_MAX ||
521 valueptr == value || !*valueptr)
522 {
523 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Preset on line %d.",
524 linenum));
525 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
526 goto create_error;
527 }
528
529 pc->num_presets[print_color_mode][print_quality] =
530 cupsParseOptions(valueptr, 0,
531 pc->presets[print_color_mode] + print_quality);
532 }
533 else if (!_cups_strcasecmp(line, "SidesOption"))
534 pc->sides_option = _cupsStrAlloc(value);
535 else if (!_cups_strcasecmp(line, "Sides1Sided"))
536 pc->sides_1sided = _cupsStrAlloc(value);
537 else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
538 pc->sides_2sided_long = _cupsStrAlloc(value);
539 else if (!_cups_strcasecmp(line, "Sides2SidedShort"))
540 pc->sides_2sided_short = _cupsStrAlloc(value);
541 else if (!_cups_strcasecmp(line, "Finishings"))
542 {
543 if (!pc->finishings)
544 pc->finishings =
545 cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
546 NULL, NULL, 0, NULL,
547 (cups_afree_func_t)pwg_free_finishings);
548
549 if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
550 goto create_error;
551
552 finishings->value = strtol(value, &valueptr, 10);
553 finishings->num_options = cupsParseOptions(valueptr, 0,
554 &(finishings->options));
555
556 cupsArrayAdd(pc->finishings, finishings);
557 }
558 else if (!_cups_strcasecmp(line, "MaxCopies"))
559 pc->max_copies = atoi(value);
560 else
561 {
562 DEBUG_printf(("_ppdCacheCreateWithFile: Unknown %s on line %d.", line,
563 linenum));
564 }
565 }
566
567 if (pc->num_sizes < num_sizes)
568 {
569 DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sizes (%d < %d).",
570 pc->num_sizes, num_sizes));
571 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
572 goto create_error;
573 }
574
575 if (pc->num_sources < num_sources)
576 {
577 DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sources (%d < %d).",
578 pc->num_sources, num_sources));
579 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
580 goto create_error;
581 }
582
583 if (pc->num_types < num_types)
584 {
585 DEBUG_printf(("_ppdCacheCreateWithFile: Not enough types (%d < %d).",
586 pc->num_types, num_types));
587 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
588 goto create_error;
589 }
590
591 cupsFileClose(fp);
592
593 return (pc);
594
595 /*
596 * If we get here the file was bad - free any data and return...
597 */
598
599 create_error:
600
601 cupsFileClose(fp);
602 _ppdCacheDestroy(pc);
603
604 if (attrs)
605 {
606 ippDelete(*attrs);
607 *attrs = NULL;
608 }
609
610 return (NULL);
611 }
612
613
614 /*
615 * '_ppdCacheCreateWithPPD()' - Create PWG mapping data from a PPD file.
616 */
617
618 _ppd_cache_t * /* O - PPD cache and mapping data */
619 _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
620 {
621 int i, j, k; /* Looping vars */
622 _ppd_cache_t *pc; /* PWG mapping data */
623 ppd_option_t *input_slot, /* InputSlot option */
624 *media_type, /* MediaType option */
625 *output_bin, /* OutputBin option */
626 *color_model, /* ColorModel option */
627 *duplex; /* Duplex option */
628 ppd_choice_t *choice; /* Current InputSlot/MediaType */
629 _pwg_map_t *map; /* Current source/type map */
630 ppd_attr_t *ppd_attr; /* Current PPD preset attribute */
631 int num_options; /* Number of preset options and props */
632 cups_option_t *options; /* Preset options and properties */
633 ppd_size_t *ppd_size; /* Current PPD size */
634 _pwg_size_t *pwg_size; /* Current PWG size */
635 char pwg_keyword[3 + PPD_MAX_NAME + 1 + 12 + 1 + 12 + 3],
636 /* PWG keyword string */
637 ppd_name[PPD_MAX_NAME];
638 /* Normalized PPD name */
639 const char *pwg_name; /* Standard PWG media name */
640 _pwg_media_t *pwg_media; /* PWG media data */
641 _pwg_print_color_mode_t pwg_print_color_mode;
642 /* print-color-mode index */
643 _pwg_print_quality_t pwg_print_quality;
644 /* print-quality index */
645 int similar; /* Are the old and new size similar? */
646 _pwg_size_t *old_size; /* Current old size */
647 int old_imageable, /* Old imageable length in 2540ths */
648 old_borderless, /* Old borderless state */
649 old_known_pwg; /* Old PWG name is well-known */
650 int new_width, /* New width in 2540ths */
651 new_length, /* New length in 2540ths */
652 new_left, /* New left margin in 2540ths */
653 new_bottom, /* New bottom margin in 2540ths */
654 new_right, /* New right margin in 2540ths */
655 new_top, /* New top margin in 2540ths */
656 new_imageable, /* New imageable length in 2540ths */
657 new_borderless, /* New borderless state */
658 new_known_pwg; /* New PWG name is well-known */
659 _pwg_size_t *new_size; /* New size to add, if any */
660 const char *filter; /* Current filter */
661 _pwg_finishings_t *finishings; /* Current finishings value */
662
663
664 DEBUG_printf(("_ppdCacheCreateWithPPD(ppd=%p)", ppd));
665
666 /*
667 * Range check input...
668 */
669
670 if (!ppd)
671 return (NULL);
672
673 /*
674 * Allocate memory...
675 */
676
677 if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
678 {
679 DEBUG_puts("_ppdCacheCreateWithPPD: Unable to allocate _ppd_cache_t.");
680 goto create_error;
681 }
682
683 /*
684 * Copy and convert size data...
685 */
686
687 if (ppd->num_sizes == 0)
688 {
689 DEBUG_puts("_ppdCacheCreateWithPPD: No page sizes in PPD.");
690 goto create_error;
691 }
692
693 if ((pc->sizes = calloc(ppd->num_sizes, sizeof(_pwg_size_t))) == NULL)
694 {
695 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
696 "_pwg_size_t's.", ppd->num_sizes));
697 goto create_error;
698 }
699
700 for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes;
701 i > 0;
702 i --, ppd_size ++)
703 {
704 /*
705 * Don't copy over custom size...
706 */
707
708 if (!_cups_strcasecmp(ppd_size->name, "Custom"))
709 continue;
710
711 /*
712 * Convert the PPD size name to the corresponding PWG keyword name.
713 */
714
715 if ((pwg_media = _pwgMediaForPPD(ppd_size->name)) != NULL)
716 {
717 /*
718 * Standard name, do we have conflicts?
719 */
720
721 for (j = 0; j < pc->num_sizes; j ++)
722 if (!strcmp(pc->sizes[j].map.pwg, pwg_media->pwg))
723 {
724 pwg_media = NULL;
725 break;
726 }
727 }
728
729 if (pwg_media)
730 {
731 /*
732 * Standard name and no conflicts, use it!
733 */
734
735 pwg_name = pwg_media->pwg;
736 new_known_pwg = 1;
737 }
738 else
739 {
740 /*
741 * Not a standard name; convert it to a PWG vendor name of the form:
742 *
743 * pp_lowerppd_WIDTHxHEIGHTuu
744 */
745
746 pwg_name = pwg_keyword;
747 new_known_pwg = 0;
748
749 pwg_unppdize_name(ppd_size->name, ppd_name, sizeof(ppd_name));
750 _pwgGenerateSize(pwg_keyword, sizeof(pwg_keyword), NULL, ppd_name,
751 _PWG_FROMPTS(ppd_size->width),
752 _PWG_FROMPTS(ppd_size->length));
753 }
754
755 /*
756 * If we have a similar paper with non-zero margins then we only
757 * want to keep it if it has a larger imageable area length.
758 */
759
760 pwg_media = _pwgMediaForSize(_PWG_FROMPTS(ppd_size->width),
761 _PWG_FROMPTS(ppd_size->length));
762 new_width = pwg_media->width;
763 new_length = pwg_media->length;
764 new_left = _PWG_FROMPTS(ppd_size->left);
765 new_bottom = _PWG_FROMPTS(ppd_size->bottom);
766 new_right = _PWG_FROMPTS(ppd_size->width - ppd_size->right);
767 new_top = _PWG_FROMPTS(ppd_size->length - ppd_size->top);
768 new_imageable = new_length - new_top - new_bottom;
769 new_borderless = new_bottom == 0 && new_top == 0 &&
770 new_left == 0 && new_right == 0;
771
772 for (k = pc->num_sizes, similar = 0, old_size = pc->sizes, new_size = NULL;
773 k > 0 && !similar;
774 k --, old_size ++)
775 {
776 old_imageable = old_size->length - old_size->top - old_size->bottom;
777 old_borderless = old_size->left == 0 && old_size->bottom == 0 &&
778 old_size->right == 0 && old_size->top == 0;
779 old_known_pwg = strncmp(old_size->map.pwg, "oe_", 3) &&
780 strncmp(old_size->map.pwg, "om_", 3);
781
782 similar = old_borderless == new_borderless &&
783 _PWG_EQUIVALENT(old_size->width, new_width) &&
784 _PWG_EQUIVALENT(old_size->length, new_length);
785
786 if (similar &&
787 (new_known_pwg || (!old_known_pwg && new_imageable > old_imageable)))
788 {
789 /*
790 * The new paper has a larger imageable area so it could replace
791 * the older paper. Regardless of the imageable area, we always
792 * prefer the size with a well-known PWG name.
793 */
794
795 new_size = old_size;
796 _cupsStrFree(old_size->map.ppd);
797 _cupsStrFree(old_size->map.pwg);
798 }
799 }
800
801 if (!similar)
802 {
803 /*
804 * The paper was unique enough to deserve its own entry so add it to the
805 * end.
806 */
807
808 new_size = pwg_size ++;
809 pc->num_sizes ++;
810 }
811
812 if (new_size)
813 {
814 /*
815 * Save this size...
816 */
817
818 new_size->map.ppd = _cupsStrAlloc(ppd_size->name);
819 new_size->map.pwg = _cupsStrAlloc(pwg_name);
820 new_size->width = new_width;
821 new_size->length = new_length;
822 new_size->left = new_left;
823 new_size->bottom = new_bottom;
824 new_size->right = new_right;
825 new_size->top = new_top;
826 }
827 }
828
829 if (ppd->variable_sizes)
830 {
831 /*
832 * Generate custom size data...
833 */
834
835 _pwgGenerateSize(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
836 _PWG_FROMPTS(ppd->custom_max[0]),
837 _PWG_FROMPTS(ppd->custom_max[1]));
838 pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
839 pc->custom_max_width = _PWG_FROMPTS(ppd->custom_max[0]);
840 pc->custom_max_length = _PWG_FROMPTS(ppd->custom_max[1]);
841
842 _pwgGenerateSize(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
843 _PWG_FROMPTS(ppd->custom_min[0]),
844 _PWG_FROMPTS(ppd->custom_min[1]));
845 pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
846 pc->custom_min_width = _PWG_FROMPTS(ppd->custom_min[0]);
847 pc->custom_min_length = _PWG_FROMPTS(ppd->custom_min[1]);
848
849 pc->custom_size.left = _PWG_FROMPTS(ppd->custom_margins[0]);
850 pc->custom_size.bottom = _PWG_FROMPTS(ppd->custom_margins[1]);
851 pc->custom_size.right = _PWG_FROMPTS(ppd->custom_margins[2]);
852 pc->custom_size.top = _PWG_FROMPTS(ppd->custom_margins[3]);
853 }
854
855 /*
856 * Copy and convert InputSlot data...
857 */
858
859 if ((input_slot = ppdFindOption(ppd, "InputSlot")) == NULL)
860 input_slot = ppdFindOption(ppd, "HPPaperSource");
861
862 if (input_slot)
863 {
864 pc->source_option = _cupsStrAlloc(input_slot->keyword);
865
866 if ((pc->sources = calloc(input_slot->num_choices,
867 sizeof(_pwg_map_t))) == NULL)
868 {
869 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
870 "_pwg_map_t's for InputSlot.", input_slot->num_choices));
871 goto create_error;
872 }
873
874 pc->num_sources = input_slot->num_choices;
875
876 for (i = input_slot->num_choices, choice = input_slot->choices,
877 map = pc->sources;
878 i > 0;
879 i --, choice ++, map ++)
880 {
881 if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
882 !_cups_strcasecmp(choice->choice, "Default"))
883 pwg_name = "auto";
884 else if (!_cups_strcasecmp(choice->choice, "Cassette"))
885 pwg_name = "main";
886 else if (!_cups_strcasecmp(choice->choice, "PhotoTray"))
887 pwg_name = "photo";
888 else if (!_cups_strcasecmp(choice->choice, "CDTray"))
889 pwg_name = "disc";
890 else if (!_cups_strncasecmp(choice->choice, "Multipurpose", 12) ||
891 !_cups_strcasecmp(choice->choice, "MP") ||
892 !_cups_strcasecmp(choice->choice, "MPTray"))
893 pwg_name = "by-pass-tray";
894 else if (!_cups_strcasecmp(choice->choice, "LargeCapacity"))
895 pwg_name = "large-capacity";
896 else if (!_cups_strncasecmp(choice->choice, "Lower", 5))
897 pwg_name = "bottom";
898 else if (!_cups_strncasecmp(choice->choice, "Middle", 6))
899 pwg_name = "middle";
900 else if (!_cups_strncasecmp(choice->choice, "Upper", 5))
901 pwg_name = "top";
902 else if (!_cups_strncasecmp(choice->choice, "Side", 4))
903 pwg_name = "side";
904 else if (!_cups_strcasecmp(choice->choice, "Roll"))
905 pwg_name = "main-roll";
906 else
907 {
908 /*
909 * Convert PPD name to lowercase...
910 */
911
912 pwg_name = pwg_keyword;
913 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword));
914 }
915
916 map->pwg = _cupsStrAlloc(pwg_name);
917 map->ppd = _cupsStrAlloc(choice->choice);
918 }
919 }
920
921 /*
922 * Copy and convert MediaType data...
923 */
924
925 if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
926 {
927 if ((pc->types = calloc(media_type->num_choices,
928 sizeof(_pwg_map_t))) == NULL)
929 {
930 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
931 "_pwg_map_t's for MediaType.", media_type->num_choices));
932 goto create_error;
933 }
934
935 pc->num_types = media_type->num_choices;
936
937 for (i = media_type->num_choices, choice = media_type->choices,
938 map = pc->types;
939 i > 0;
940 i --, choice ++, map ++)
941 {
942 if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
943 !_cups_strcasecmp(choice->choice, "Any") ||
944 !_cups_strcasecmp(choice->choice, "Default"))
945 pwg_name = "auto";
946 else if (!_cups_strncasecmp(choice->choice, "Card", 4))
947 pwg_name = "cardstock";
948 else if (!_cups_strncasecmp(choice->choice, "Env", 3))
949 pwg_name = "envelope";
950 else if (!_cups_strncasecmp(choice->choice, "Gloss", 5))
951 pwg_name = "photographic-glossy";
952 else if (!_cups_strcasecmp(choice->choice, "HighGloss"))
953 pwg_name = "photographic-high-gloss";
954 else if (!_cups_strcasecmp(choice->choice, "Matte"))
955 pwg_name = "photographic-matte";
956 else if (!_cups_strncasecmp(choice->choice, "Plain", 5))
957 pwg_name = "stationery";
958 else if (!_cups_strncasecmp(choice->choice, "Coated", 6))
959 pwg_name = "stationery-coated";
960 else if (!_cups_strcasecmp(choice->choice, "Inkjet"))
961 pwg_name = "stationery-inkjet";
962 else if (!_cups_strcasecmp(choice->choice, "Letterhead"))
963 pwg_name = "stationery-letterhead";
964 else if (!_cups_strncasecmp(choice->choice, "Preprint", 8))
965 pwg_name = "stationery-preprinted";
966 else if (!_cups_strcasecmp(choice->choice, "Recycled"))
967 pwg_name = "stationery-recycled";
968 else if (!_cups_strncasecmp(choice->choice, "Transparen", 10))
969 pwg_name = "transparency";
970 else
971 {
972 /*
973 * Convert PPD name to lowercase...
974 */
975
976 pwg_name = pwg_keyword;
977 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword));
978 }
979
980 map->pwg = _cupsStrAlloc(pwg_name);
981 map->ppd = _cupsStrAlloc(choice->choice);
982 }
983 }
984
985 /*
986 * Copy and convert OutputBin data...
987 */
988
989 if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
990 {
991 if ((pc->bins = calloc(output_bin->num_choices,
992 sizeof(_pwg_map_t))) == NULL)
993 {
994 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
995 "_pwg_map_t's for OutputBin.", output_bin->num_choices));
996 goto create_error;
997 }
998
999 pc->num_bins = output_bin->num_choices;
1000
1001 for (i = output_bin->num_choices, choice = output_bin->choices,
1002 map = pc->bins;
1003 i > 0;
1004 i --, choice ++, map ++)
1005 {
1006 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword));
1007
1008 map->pwg = _cupsStrAlloc(pwg_keyword);
1009 map->ppd = _cupsStrAlloc(choice->choice);
1010 }
1011 }
1012
1013 if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
1014 {
1015 /*
1016 * Copy and convert APPrinterPreset (output-mode + print-quality) data...
1017 */
1018
1019 const char *quality, /* com.apple.print.preset.quality value */
1020 *output_mode, /* com.apple.print.preset.output-mode value */
1021 *color_model_val, /* ColorModel choice */
1022 *graphicsType, /* com.apple.print.preset.graphicsType value */
1023 *media_front_coating; /* com.apple.print.preset.media-front-coating value */
1024
1025 do
1026 {
1027 num_options = _ppdParseOptions(ppd_attr->value, 0, &options,
1028 _PPD_PARSE_ALL);
1029
1030 if ((quality = cupsGetOption("com.apple.print.preset.quality",
1031 num_options, options)) != NULL)
1032 {
1033 /*
1034 * Get the print-quality for this preset...
1035 */
1036
1037 if (!strcmp(quality, "low"))
1038 pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1039 else if (!strcmp(quality, "high"))
1040 pwg_print_quality = _PWG_PRINT_QUALITY_HIGH;
1041 else
1042 pwg_print_quality = _PWG_PRINT_QUALITY_NORMAL;
1043
1044 /*
1045 * Ignore graphicsType "Photo" presets that are not high quality.
1046 */
1047
1048 graphicsType = cupsGetOption("com.apple.print.preset.graphicsType",
1049 num_options, options);
1050
1051 if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH && graphicsType &&
1052 !strcmp(graphicsType, "Photo"))
1053 continue;
1054
1055 /*
1056 * Ignore presets for normal and draft quality where the coating
1057 * isn't "none" or "autodetect".
1058 */
1059
1060 media_front_coating = cupsGetOption(
1061 "com.apple.print.preset.media-front-coating",
1062 num_options, options);
1063
1064 if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH &&
1065 media_front_coating &&
1066 strcmp(media_front_coating, "none") &&
1067 strcmp(media_front_coating, "autodetect"))
1068 continue;
1069
1070 /*
1071 * Get the output mode for this preset...
1072 */
1073
1074 output_mode = cupsGetOption("com.apple.print.preset.output-mode",
1075 num_options, options);
1076 color_model_val = cupsGetOption("ColorModel", num_options, options);
1077
1078 if (output_mode)
1079 {
1080 if (!strcmp(output_mode, "monochrome"))
1081 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1082 else
1083 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1084 }
1085 else if (color_model_val)
1086 {
1087 if (!_cups_strcasecmp(color_model_val, "Gray"))
1088 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1089 else
1090 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1091 }
1092 else
1093 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1094
1095 /*
1096 * Save the options for this combination as needed...
1097 */
1098
1099 if (!pc->num_presets[pwg_print_color_mode][pwg_print_quality])
1100 pc->num_presets[pwg_print_color_mode][pwg_print_quality] =
1101 _ppdParseOptions(ppd_attr->value, 0,
1102 pc->presets[pwg_print_color_mode] +
1103 pwg_print_quality, _PPD_PARSE_OPTIONS);
1104 }
1105
1106 cupsFreeOptions(num_options, options);
1107 }
1108 while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) != NULL);
1109 }
1110
1111 if (!pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_DRAFT] &&
1112 !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_NORMAL] &&
1113 !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_HIGH])
1114 {
1115 /*
1116 * Try adding some common color options to create grayscale presets. These
1117 * are listed in order of popularity...
1118 */
1119
1120 const char *color_option = NULL, /* Color control option */
1121 *gray_choice = NULL; /* Choice to select grayscale */
1122
1123 if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL &&
1124 ppdFindChoice(color_model, "Gray"))
1125 {
1126 color_option = "ColorModel";
1127 gray_choice = "Gray";
1128 }
1129 else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL &&
1130 ppdFindChoice(color_model, "grayscale"))
1131 {
1132 color_option = "HPColorMode";
1133 gray_choice = "grayscale";
1134 }
1135 else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL &&
1136 ppdFindChoice(color_model, "Mono"))
1137 {
1138 color_option = "BRMonoColor";
1139 gray_choice = "Mono";
1140 }
1141 else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL &&
1142 ppdFindChoice(color_model, "1"))
1143 {
1144 color_option = "CNIJSGrayScale";
1145 gray_choice = "1";
1146 }
1147 else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL &&
1148 ppdFindChoice(color_model, "True"))
1149 {
1150 color_option = "HPColorAsGray";
1151 gray_choice = "True";
1152 }
1153
1154 if (color_option && gray_choice)
1155 {
1156 /*
1157 * Copy and convert ColorModel (output-mode) data...
1158 */
1159
1160 cups_option_t *coption, /* Color option */
1161 *moption; /* Monochrome option */
1162
1163 for (pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1164 pwg_print_quality < _PWG_PRINT_QUALITY_MAX;
1165 pwg_print_quality ++)
1166 {
1167 if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality])
1168 {
1169 /*
1170 * Copy the color options...
1171 */
1172
1173 num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
1174 [pwg_print_quality];
1175 options = calloc(sizeof(cups_option_t), num_options);
1176
1177 if (options)
1178 {
1179 for (i = num_options, moption = options,
1180 coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
1181 [pwg_print_quality];
1182 i > 0;
1183 i --, moption ++, coption ++)
1184 {
1185 moption->name = _cupsStrRetain(coption->name);
1186 moption->value = _cupsStrRetain(coption->value);
1187 }
1188
1189 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1190 num_options;
1191 pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1192 options;
1193 }
1194 }
1195 else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL)
1196 continue;
1197
1198 /*
1199 * Add the grayscale option to the preset...
1200 */
1201
1202 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1203 cupsAddOption(color_option, gray_choice,
1204 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
1205 [pwg_print_quality],
1206 pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME] +
1207 pwg_print_quality);
1208 }
1209 }
1210 }
1211
1212 /*
1213 * Copy and convert Duplex (sides) data...
1214 */
1215
1216 if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
1217 if ((duplex = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1218 if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
1219 if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
1220 duplex = ppdFindOption(ppd, "KD03Duplex");
1221
1222 if (duplex)
1223 {
1224 pc->sides_option = _cupsStrAlloc(duplex->keyword);
1225
1226 for (i = duplex->num_choices, choice = duplex->choices;
1227 i > 0;
1228 i --, choice ++)
1229 {
1230 if ((!_cups_strcasecmp(choice->choice, "None") ||
1231 !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
1232 pc->sides_1sided = _cupsStrAlloc(choice->choice);
1233 else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
1234 !_cups_strcasecmp(choice->choice, "LongEdge") ||
1235 !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
1236 pc->sides_2sided_long = _cupsStrAlloc(choice->choice);
1237 else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
1238 !_cups_strcasecmp(choice->choice, "ShortEdge") ||
1239 !_cups_strcasecmp(choice->choice, "Bottom")) &&
1240 !pc->sides_2sided_short)
1241 pc->sides_2sided_short = _cupsStrAlloc(choice->choice);
1242 }
1243 }
1244
1245 /*
1246 * Copy filters and pre-filters...
1247 */
1248
1249 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
1250 (cups_acopy_func_t)_cupsStrAlloc,
1251 (cups_afree_func_t)_cupsStrFree);
1252
1253 cupsArrayAdd(pc->filters,
1254 "application/vnd.cups-raw application/octet-stream 0 -");
1255
1256 if ((ppd_attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL)
1257 {
1258 do
1259 {
1260 cupsArrayAdd(pc->filters, ppd_attr->value);
1261 }
1262 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
1263 }
1264 else if (ppd->num_filters > 0)
1265 {
1266 for (i = 0; i < ppd->num_filters; i ++)
1267 cupsArrayAdd(pc->filters, ppd->filters[i]);
1268 }
1269 else
1270 cupsArrayAdd(pc->filters, "application/vnd.cups-postscript 0 -");
1271
1272 /*
1273 * See if we have a command filter...
1274 */
1275
1276 for (filter = (const char *)cupsArrayFirst(pc->filters);
1277 filter;
1278 filter = (const char *)cupsArrayNext(pc->filters))
1279 if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
1280 _cups_isspace(filter[28]))
1281 break;
1282
1283 if (!filter &&
1284 ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL ||
1285 _cups_strcasecmp(ppd_attr->value, "none")))
1286 {
1287 /*
1288 * No command filter and no cupsCommands keyword telling us not to use one.
1289 * See if this is a PostScript printer, and if so add a PostScript command
1290 * filter...
1291 */
1292
1293 for (filter = (const char *)cupsArrayFirst(pc->filters);
1294 filter;
1295 filter = (const char *)cupsArrayNext(pc->filters))
1296 if (!_cups_strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
1297 _cups_isspace(filter[31]))
1298 break;
1299
1300 if (filter)
1301 cupsArrayAdd(pc->filters,
1302 "application/vnd.cups-command application/postscript 100 "
1303 "commandtops");
1304 }
1305
1306 if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
1307 {
1308 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
1309 (cups_acopy_func_t)_cupsStrAlloc,
1310 (cups_afree_func_t)_cupsStrFree);
1311
1312 do
1313 {
1314 cupsArrayAdd(pc->prefilters, ppd_attr->value);
1315 }
1316 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) != NULL);
1317 }
1318
1319 if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL)
1320 pc->single_file = !_cups_strcasecmp(ppd_attr->value, "true");
1321
1322 /*
1323 * Copy the product string, if any...
1324 */
1325
1326 if (ppd->product)
1327 pc->product = _cupsStrAlloc(ppd->product);
1328
1329 /*
1330 * Copy finishings mapping data...
1331 */
1332
1333 if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL)
1334 {
1335 pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
1336 NULL, NULL, 0, NULL,
1337 (cups_afree_func_t)pwg_free_finishings);
1338
1339 do
1340 {
1341 if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
1342 goto create_error;
1343
1344 finishings->value = atoi(ppd_attr->spec);
1345 finishings->num_options = _ppdParseOptions(ppd_attr->value, 0,
1346 &(finishings->options),
1347 _PPD_PARSE_OPTIONS);
1348
1349 cupsArrayAdd(pc->finishings, finishings);
1350 }
1351 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsIPPFinishings",
1352 NULL)) != NULL);
1353 }
1354
1355 /*
1356 * Max copies...
1357 */
1358
1359 if ((ppd_attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
1360 pc->max_copies = atoi(ppd_attr->value);
1361 else if (ppd->manual_copies)
1362 pc->max_copies = 1;
1363 else
1364 pc->max_copies = 9999;
1365
1366 /*
1367 * Return the cache data...
1368 */
1369
1370 return (pc);
1371
1372 /*
1373 * If we get here we need to destroy the PWG mapping data and return NULL...
1374 */
1375
1376 create_error:
1377
1378 _cupsSetError(IPP_INTERNAL_ERROR, _("Out of memory."), 1);
1379 _ppdCacheDestroy(pc);
1380
1381 return (NULL);
1382 }
1383
1384
1385 /*
1386 * '_ppdCacheDestroy()' - Free all memory used for PWG mapping data.
1387 */
1388
1389 void
1390 _ppdCacheDestroy(_ppd_cache_t *pc) /* I - PPD cache and mapping data */
1391 {
1392 int i; /* Looping var */
1393 _pwg_map_t *map; /* Current map */
1394 _pwg_size_t *size; /* Current size */
1395
1396
1397 /*
1398 * Range check input...
1399 */
1400
1401 if (!pc)
1402 return;
1403
1404 /*
1405 * Free memory as needed...
1406 */
1407
1408 if (pc->bins)
1409 {
1410 for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
1411 {
1412 _cupsStrFree(map->pwg);
1413 _cupsStrFree(map->ppd);
1414 }
1415
1416 free(pc->bins);
1417 }
1418
1419 if (pc->sizes)
1420 {
1421 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1422 {
1423 _cupsStrFree(size->map.pwg);
1424 _cupsStrFree(size->map.ppd);
1425 }
1426
1427 free(pc->sizes);
1428 }
1429
1430 if (pc->source_option)
1431 _cupsStrFree(pc->source_option);
1432
1433 if (pc->sources)
1434 {
1435 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
1436 {
1437 _cupsStrFree(map->pwg);
1438 _cupsStrFree(map->ppd);
1439 }
1440
1441 free(pc->sources);
1442 }
1443
1444 if (pc->types)
1445 {
1446 for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
1447 {
1448 _cupsStrFree(map->pwg);
1449 _cupsStrFree(map->ppd);
1450 }
1451
1452 free(pc->types);
1453 }
1454
1455 if (pc->custom_max_keyword)
1456 _cupsStrFree(pc->custom_max_keyword);
1457
1458 if (pc->custom_min_keyword)
1459 _cupsStrFree(pc->custom_min_keyword);
1460
1461 _cupsStrFree(pc->product);
1462 cupsArrayDelete(pc->filters);
1463 cupsArrayDelete(pc->prefilters);
1464 cupsArrayDelete(pc->finishings);
1465
1466 free(pc);
1467 }
1468
1469
1470 /*
1471 * '_ppdCacheGetBin()' - Get the PWG output-bin keyword associated with a PPD
1472 * OutputBin.
1473 */
1474
1475 const char * /* O - output-bin or NULL */
1476 _ppdCacheGetBin(
1477 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1478 const char *output_bin) /* I - PPD OutputBin string */
1479 {
1480 int i; /* Looping var */
1481
1482
1483 /*
1484 * Range check input...
1485 */
1486
1487 if (!pc || !output_bin)
1488 return (NULL);
1489
1490 /*
1491 * Look up the OutputBin string...
1492 */
1493
1494
1495 for (i = 0; i < pc->num_bins; i ++)
1496 if (!_cups_strcasecmp(output_bin, pc->bins[i].ppd))
1497 return (pc->bins[i].pwg);
1498
1499 return (NULL);
1500 }
1501
1502
1503 /*
1504 * '_ppdCacheGetFinishingOptions()' - Get PPD finishing options for the given
1505 * IPP finishings value(s).
1506 */
1507
1508 int /* O - New number of options */
1509 _ppdCacheGetFinishingOptions(
1510 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1511 ipp_t *job, /* I - Job attributes or NULL */
1512 ipp_finish_t value, /* I - IPP finishings value of IPP_FINISHINGS_NONE */
1513 int num_options, /* I - Number of options */
1514 cups_option_t **options) /* IO - Options */
1515 {
1516 int i; /* Looping var */
1517 _pwg_finishings_t *f, /* PWG finishings options */
1518 key; /* Search key */
1519 ipp_attribute_t *attr; /* Finishings attribute */
1520 cups_option_t *option; /* Current finishings option */
1521
1522
1523 /*
1524 * Range check input...
1525 */
1526
1527 if (!pc || cupsArrayCount(pc->finishings) == 0 || !options ||
1528 (!job && value == IPP_FINISHINGS_NONE))
1529 return (num_options);
1530
1531 /*
1532 * Apply finishing options...
1533 */
1534
1535 if (job && (attr = ippFindAttribute(job, "finishings", IPP_TAG_ENUM)) != NULL)
1536 {
1537 int num_values = ippGetCount(attr); /* Number of values */
1538
1539 for (i = 0; i < num_values; i ++)
1540 {
1541 key.value = ippGetInteger(attr, i);
1542
1543 if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
1544 {
1545 int j; /* Another looping var */
1546
1547 for (j = f->num_options, option = f->options; j > 0; j --, option ++)
1548 num_options = cupsAddOption(option->name, option->value,
1549 num_options, options);
1550 }
1551 }
1552 }
1553 else if (value != IPP_FINISHINGS_NONE)
1554 {
1555 key.value = value;
1556
1557 if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
1558 {
1559 int j; /* Another looping var */
1560
1561 for (j = f->num_options, option = f->options; j > 0; j --, option ++)
1562 num_options = cupsAddOption(option->name, option->value,
1563 num_options, options);
1564 }
1565 }
1566
1567 return (num_options);
1568 }
1569
1570
1571 /*
1572 * '_ppdCacheGetFinishingValues()' - Get IPP finishings value(s) from the given
1573 * PPD options.
1574 */
1575
1576 int /* O - Number of finishings values */
1577 _ppdCacheGetFinishingValues(
1578 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1579 int num_options, /* I - Number of options */
1580 cups_option_t *options, /* I - Options */
1581 int max_values, /* I - Maximum number of finishings values */
1582 int *values) /* O - Finishings values */
1583 {
1584 int i, /* Looping var */
1585 num_values = 0; /* Number of values */
1586 _pwg_finishings_t *f; /* Current finishings option */
1587 cups_option_t *option; /* Current option */
1588 const char *val; /* Value for option */
1589
1590
1591 /*
1592 * Range check input...
1593 */
1594
1595 if (!pc || !pc->finishings || num_options < 1 || max_values < 1 || !values)
1596 return (0);
1597
1598 /*
1599 * Go through the finishings options and see what is set...
1600 */
1601
1602 for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
1603 f;
1604 f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
1605 {
1606 for (i = f->num_options, option = f->options; i > 0; i --, option ++)
1607 if ((val = cupsGetOption(option->name, num_options, options)) == NULL ||
1608 _cups_strcasecmp(option->value, val))
1609 break;
1610
1611 if (i == 0)
1612 {
1613 values[num_values ++] = f->value;
1614
1615 if (num_values >= max_values)
1616 break;
1617 }
1618 }
1619
1620 return (num_values);
1621 }
1622
1623
1624 /*
1625 * '_ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job
1626 * attributes or a keyword string.
1627 */
1628
1629 const char * /* O - PPD InputSlot or NULL */
1630 _ppdCacheGetInputSlot(
1631 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1632 ipp_t *job, /* I - Job attributes or NULL */
1633 const char *keyword) /* I - Keyword string or NULL */
1634 {
1635 /*
1636 * Range check input...
1637 */
1638
1639 if (!pc || pc->num_sources == 0 || (!job && !keyword))
1640 return (NULL);
1641
1642 if (job && !keyword)
1643 {
1644 /*
1645 * Lookup the media-col attribute and any media-source found there...
1646 */
1647
1648 ipp_attribute_t *media_col, /* media-col attribute */
1649 *media_source; /* media-source attribute */
1650 _pwg_size_t size; /* Dimensional size */
1651 int margins_set; /* Were the margins set? */
1652
1653 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
1654 if (media_col &&
1655 (media_source = ippFindAttribute(ippGetCollection(media_col, 0),
1656 "media-source",
1657 IPP_TAG_KEYWORD)) != NULL)
1658 {
1659 /*
1660 * Use the media-source value from media-col...
1661 */
1662
1663 keyword = ippGetString(media_source, 0, NULL);
1664 }
1665 else if (_pwgInitSize(&size, job, &margins_set))
1666 {
1667 /*
1668 * For media <= 5x7, look for a photo tray...
1669 */
1670
1671 if (size.width <= (5 * 2540) && size.length <= (7 * 2540))
1672 keyword = "photo";
1673 }
1674 }
1675
1676 if (keyword)
1677 {
1678 int i; /* Looping var */
1679
1680 for (i = 0; i < pc->num_sources; i ++)
1681 if (!_cups_strcasecmp(keyword, pc->sources[i].pwg))
1682 return (pc->sources[i].ppd);
1683 }
1684
1685 return (NULL);
1686 }
1687
1688
1689 /*
1690 * '_ppdCacheGetMediaType()' - Get the PPD MediaType associated with the job
1691 * attributes or a keyword string.
1692 */
1693
1694 const char * /* O - PPD MediaType or NULL */
1695 _ppdCacheGetMediaType(
1696 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1697 ipp_t *job, /* I - Job attributes or NULL */
1698 const char *keyword) /* I - Keyword string or NULL */
1699 {
1700 /*
1701 * Range check input...
1702 */
1703
1704 if (!pc || pc->num_types == 0 || (!job && !keyword))
1705 return (NULL);
1706
1707 if (job && !keyword)
1708 {
1709 /*
1710 * Lookup the media-col attribute and any media-source found there...
1711 */
1712
1713 ipp_attribute_t *media_col, /* media-col attribute */
1714 *media_type; /* media-type attribute */
1715
1716 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
1717 if (media_col)
1718 {
1719 if ((media_type = ippFindAttribute(media_col->values[0].collection,
1720 "media-type",
1721 IPP_TAG_KEYWORD)) == NULL)
1722 media_type = ippFindAttribute(media_col->values[0].collection,
1723 "media-type", IPP_TAG_NAME);
1724
1725 if (media_type)
1726 keyword = media_type->values[0].string.text;
1727 }
1728 }
1729
1730 if (keyword)
1731 {
1732 int i; /* Looping var */
1733
1734 for (i = 0; i < pc->num_types; i ++)
1735 if (!_cups_strcasecmp(keyword, pc->types[i].pwg))
1736 return (pc->types[i].ppd);
1737 }
1738
1739 return (NULL);
1740 }
1741
1742
1743 /*
1744 * '_ppdCacheGetOutputBin()' - Get the PPD OutputBin associated with the keyword
1745 * string.
1746 */
1747
1748 const char * /* O - PPD OutputBin or NULL */
1749 _ppdCacheGetOutputBin(
1750 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1751 const char *output_bin) /* I - Keyword string */
1752 {
1753 int i; /* Looping var */
1754
1755
1756 /*
1757 * Range check input...
1758 */
1759
1760 if (!pc || !output_bin)
1761 return (NULL);
1762
1763 /*
1764 * Look up the OutputBin string...
1765 */
1766
1767
1768 for (i = 0; i < pc->num_bins; i ++)
1769 if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg))
1770 return (pc->bins[i].ppd);
1771
1772 return (NULL);
1773 }
1774
1775
1776 /*
1777 * '_ppdCacheGetPageSize()' - Get the PPD PageSize associated with the job
1778 * attributes or a keyword string.
1779 */
1780
1781 const char * /* O - PPD PageSize or NULL */
1782 _ppdCacheGetPageSize(
1783 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1784 ipp_t *job, /* I - Job attributes or NULL */
1785 const char *keyword, /* I - Keyword string or NULL */
1786 int *exact) /* O - 1 if exact match, 0 otherwise */
1787 {
1788 int i; /* Looping var */
1789 _pwg_size_t *size, /* Current size */
1790 *closest, /* Closest size */
1791 jobsize; /* Size data from job */
1792 int margins_set, /* Were the margins set? */
1793 dwidth, /* Difference in width */
1794 dlength, /* Difference in length */
1795 dleft, /* Difference in left margins */
1796 dright, /* Difference in right margins */
1797 dbottom, /* Difference in bottom margins */
1798 dtop, /* Difference in top margins */
1799 dmin, /* Minimum difference */
1800 dclosest; /* Closest difference */
1801 const char *ppd_name; /* PPD media name */
1802
1803
1804 DEBUG_printf(("_ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)",
1805 pc, job, keyword, exact));
1806
1807 /*
1808 * Range check input...
1809 */
1810
1811 if (!pc || (!job && !keyword))
1812 return (NULL);
1813
1814 if (exact)
1815 *exact = 0;
1816
1817 ppd_name = keyword;
1818
1819 if (job)
1820 {
1821 /*
1822 * Try getting the PPD media name from the job attributes...
1823 */
1824
1825 ipp_attribute_t *attr; /* Job attribute */
1826
1827 if ((attr = ippFindAttribute(job, "PageSize", IPP_TAG_ZERO)) == NULL)
1828 if ((attr = ippFindAttribute(job, "PageRegion", IPP_TAG_ZERO)) == NULL)
1829 attr = ippFindAttribute(job, "media", IPP_TAG_ZERO);
1830
1831 #ifdef DEBUG
1832 if (attr)
1833 DEBUG_printf(("1_ppdCacheGetPageSize: Found attribute %s (%s)",
1834 attr->name, ippTagString(attr->value_tag)));
1835 else
1836 DEBUG_puts("1_ppdCacheGetPageSize: Did not find media attribute.");
1837 #endif /* DEBUG */
1838
1839 if (attr && (attr->value_tag == IPP_TAG_NAME ||
1840 attr->value_tag == IPP_TAG_KEYWORD))
1841 ppd_name = attr->values[0].string.text;
1842 }
1843
1844 DEBUG_printf(("1_ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name));
1845
1846 if (ppd_name)
1847 {
1848 /*
1849 * Try looking up the named PPD size first...
1850 */
1851
1852 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1853 {
1854 DEBUG_printf(("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]",
1855 (int)(size - pc->sizes), size->map.pwg, size->map.ppd));
1856
1857 if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
1858 !_cups_strcasecmp(ppd_name, size->map.pwg))
1859 {
1860 if (exact)
1861 *exact = 1;
1862
1863 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", ppd_name));
1864
1865 return (size->map.ppd);
1866 }
1867 }
1868 }
1869
1870 if (job && !keyword)
1871 {
1872 /*
1873 * Get the size using media-col or media, with the preference being
1874 * media-col.
1875 */
1876
1877 if (!_pwgInitSize(&jobsize, job, &margins_set))
1878 return (NULL);
1879 }
1880 else
1881 {
1882 /*
1883 * Get the size using a media keyword...
1884 */
1885
1886 _pwg_media_t *media; /* Media definition */
1887
1888
1889 if ((media = _pwgMediaForPWG(keyword)) == NULL)
1890 if ((media = _pwgMediaForLegacy(keyword)) == NULL)
1891 if ((media = _pwgMediaForPPD(keyword)) == NULL)
1892 return (NULL);
1893
1894 jobsize.width = media->width;
1895 jobsize.length = media->length;
1896 margins_set = 0;
1897 }
1898
1899 /*
1900 * Now that we have the dimensions and possibly the margins, look at the
1901 * available sizes and find the match...
1902 */
1903
1904 closest = NULL;
1905 dclosest = 999999999;
1906
1907 if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
1908 _cups_strncasecmp(ppd_name, "custom_", 7))
1909 {
1910 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1911 {
1912 /*
1913 * Adobe uses a size matching algorithm with an epsilon of 5 points, which
1914 * is just about 176/2540ths...
1915 */
1916
1917 dwidth = size->width - jobsize.width;
1918 dlength = size->length - jobsize.length;
1919
1920 if (dwidth <= -176 || dwidth >= 176 || dlength <= -176 || dlength >= 176)
1921 continue;
1922
1923 if (margins_set)
1924 {
1925 /*
1926 * Use a tighter epsilon of 1 point (35/2540ths) for margins...
1927 */
1928
1929 dleft = size->left - jobsize.left;
1930 dright = size->right - jobsize.right;
1931 dtop = size->top - jobsize.top;
1932 dbottom = size->bottom - jobsize.bottom;
1933
1934 if (dleft <= -35 || dleft >= 35 || dright <= -35 || dright >= 35 ||
1935 dtop <= -35 || dtop >= 35 || dbottom <= -35 || dbottom >= 35)
1936 {
1937 dleft = dleft < 0 ? -dleft : dleft;
1938 dright = dright < 0 ? -dright : dright;
1939 dbottom = dbottom < 0 ? -dbottom : dbottom;
1940 dtop = dtop < 0 ? -dtop : dtop;
1941 dmin = dleft + dright + dbottom + dtop;
1942
1943 if (dmin < dclosest)
1944 {
1945 dclosest = dmin;
1946 closest = size;
1947 }
1948
1949 continue;
1950 }
1951 }
1952
1953 if (exact)
1954 *exact = 1;
1955
1956 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd));
1957
1958 return (size->map.ppd);
1959 }
1960 }
1961
1962 if (closest)
1963 {
1964 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (closest)",
1965 closest->map.ppd));
1966
1967 return (closest->map.ppd);
1968 }
1969
1970 /*
1971 * If we get here we need to check for custom page size support...
1972 */
1973
1974 if (jobsize.width >= pc->custom_min_width &&
1975 jobsize.width <= pc->custom_max_width &&
1976 jobsize.length >= pc->custom_min_length &&
1977 jobsize.length <= pc->custom_max_length)
1978 {
1979 /*
1980 * In range, format as Custom.WWWWxLLLL (points).
1981 */
1982
1983 snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d",
1984 (int)_PWG_TOPTS(jobsize.width), (int)_PWG_TOPTS(jobsize.length));
1985
1986 if (margins_set && exact)
1987 {
1988 dleft = pc->custom_size.left - jobsize.left;
1989 dright = pc->custom_size.right - jobsize.right;
1990 dtop = pc->custom_size.top - jobsize.top;
1991 dbottom = pc->custom_size.bottom - jobsize.bottom;
1992
1993 if (dleft > -35 && dleft < 35 && dright > -35 && dright < 35 &&
1994 dtop > -35 && dtop < 35 && dbottom > -35 && dbottom < 35)
1995 *exact = 1;
1996 }
1997 else if (exact)
1998 *exact = 1;
1999
2000 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (custom)",
2001 pc->custom_ppd_size));
2002
2003 return (pc->custom_ppd_size);
2004 }
2005
2006 /*
2007 * No custom page size support or the size is out of range - return NULL.
2008 */
2009
2010 DEBUG_puts("1_ppdCacheGetPageSize: Returning NULL");
2011
2012 return (NULL);
2013 }
2014
2015
2016 /*
2017 * '_ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize.
2018 */
2019
2020 _pwg_size_t * /* O - PWG size or NULL */
2021 _ppdCacheGetSize(
2022 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2023 const char *page_size) /* I - PPD PageSize */
2024 {
2025 int i; /* Looping var */
2026 _pwg_media_t *media; /* Media */
2027 _pwg_size_t *size; /* Current size */
2028
2029
2030 /*
2031 * Range check input...
2032 */
2033
2034 if (!pc || !page_size)
2035 return (NULL);
2036
2037 if (!_cups_strncasecmp(page_size, "Custom.", 7))
2038 {
2039 /*
2040 * Custom size; size name can be one of the following:
2041 *
2042 * Custom.WIDTHxLENGTHin - Size in inches
2043 * Custom.WIDTHxLENGTHft - Size in feet
2044 * Custom.WIDTHxLENGTHcm - Size in centimeters
2045 * Custom.WIDTHxLENGTHmm - Size in millimeters
2046 * Custom.WIDTHxLENGTHm - Size in meters
2047 * Custom.WIDTHxLENGTH[pt] - Size in points
2048 */
2049
2050 double w, l; /* Width and length of page */
2051 char *ptr; /* Pointer into PageSize */
2052 struct lconv *loc; /* Locale data */
2053
2054 loc = localeconv();
2055 w = (float)_cupsStrScand(page_size + 7, &ptr, loc);
2056 if (!ptr || *ptr != 'x')
2057 return (NULL);
2058
2059 l = (float)_cupsStrScand(ptr + 1, &ptr, loc);
2060 if (!ptr)
2061 return (NULL);
2062
2063 if (!_cups_strcasecmp(ptr, "in"))
2064 {
2065 w *= 2540.0;
2066 l *= 2540.0;
2067 }
2068 else if (!_cups_strcasecmp(ptr, "ft"))
2069 {
2070 w *= 12.0 * 2540.0;
2071 l *= 12.0 * 2540.0;
2072 }
2073 else if (!_cups_strcasecmp(ptr, "mm"))
2074 {
2075 w *= 100.0;
2076 l *= 100.0;
2077 }
2078 else if (!_cups_strcasecmp(ptr, "cm"))
2079 {
2080 w *= 1000.0;
2081 l *= 1000.0;
2082 }
2083 else if (!_cups_strcasecmp(ptr, "m"))
2084 {
2085 w *= 100000.0;
2086 l *= 100000.0;
2087 }
2088 else
2089 {
2090 w *= 2540.0 / 72.0;
2091 l *= 2540.0 / 72.0;
2092 }
2093
2094 pc->custom_size.width = (int)w;
2095 pc->custom_size.length = (int)l;
2096
2097 return (&(pc->custom_size));
2098 }
2099
2100 /*
2101 * Not a custom size - look it up...
2102 */
2103
2104 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2105 if (!_cups_strcasecmp(page_size, size->map.ppd) ||
2106 !_cups_strcasecmp(page_size, size->map.pwg))
2107 return (size);
2108
2109 /*
2110 * Look up standard sizes...
2111 */
2112
2113 if ((media = _pwgMediaForPPD(page_size)) == NULL)
2114 if ((media = _pwgMediaForLegacy(page_size)) == NULL)
2115 media = _pwgMediaForPWG(page_size);
2116
2117 if (media)
2118 {
2119 pc->custom_size.width = media->width;
2120 pc->custom_size.length = media->length;
2121
2122 return (&(pc->custom_size));
2123 }
2124
2125 return (NULL);
2126 }
2127
2128
2129 /*
2130 * '_ppdCacheGetSource()' - Get the PWG media-source associated with a PPD
2131 * InputSlot.
2132 */
2133
2134 const char * /* O - PWG media-source keyword */
2135 _ppdCacheGetSource(
2136 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2137 const char *input_slot) /* I - PPD InputSlot */
2138 {
2139 int i; /* Looping var */
2140 _pwg_map_t *source; /* Current source */
2141
2142
2143 /*
2144 * Range check input...
2145 */
2146
2147 if (!pc || !input_slot)
2148 return (NULL);
2149
2150 for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++)
2151 if (!_cups_strcasecmp(input_slot, source->ppd))
2152 return (source->pwg);
2153
2154 return (NULL);
2155 }
2156
2157
2158 /*
2159 * '_ppdCacheGetType()' - Get the PWG media-type associated with a PPD
2160 * MediaType.
2161 */
2162
2163 const char * /* O - PWG media-type keyword */
2164 _ppdCacheGetType(
2165 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2166 const char *media_type) /* I - PPD MediaType */
2167 {
2168 int i; /* Looping var */
2169 _pwg_map_t *type; /* Current type */
2170
2171
2172 /*
2173 * Range check input...
2174 */
2175
2176 if (!pc || !media_type)
2177 return (NULL);
2178
2179 for (i = pc->num_types, type = pc->types; i > 0; i --, type ++)
2180 if (!_cups_strcasecmp(media_type, type->ppd))
2181 return (type->pwg);
2182
2183 return (NULL);
2184 }
2185
2186
2187 /*
2188 * '_ppdCacheWriteFile()' - Write PWG mapping data to a file.
2189 */
2190
2191 int /* O - 1 on success, 0 on failure */
2192 _ppdCacheWriteFile(
2193 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2194 const char *filename, /* I - File to write */
2195 ipp_t *attrs) /* I - Attributes to write, if any */
2196 {
2197 int i, j, k; /* Looping vars */
2198 cups_file_t *fp; /* Output file */
2199 _pwg_size_t *size; /* Current size */
2200 _pwg_map_t *map; /* Current map */
2201 _pwg_finishings_t *f; /* Current finishing option */
2202 cups_option_t *option; /* Current option */
2203 const char *value; /* Filter/pre-filter value */
2204 char newfile[1024]; /* New filename */
2205
2206
2207 /*
2208 * Range check input...
2209 */
2210
2211 if (!pc || !filename)
2212 {
2213 _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
2214 return (0);
2215 }
2216
2217 /*
2218 * Open the file and write with compression...
2219 */
2220
2221 snprintf(newfile, sizeof(newfile), "%s.N", filename);
2222 if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
2223 {
2224 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
2225 return (0);
2226 }
2227
2228 /*
2229 * Standard header...
2230 */
2231
2232 cupsFilePrintf(fp, "#CUPS-PPD-CACHE-%d\n", _PPD_CACHE_VERSION);
2233
2234 /*
2235 * Output bins...
2236 */
2237
2238 if (pc->num_bins > 0)
2239 {
2240 cupsFilePrintf(fp, "NumBins %d\n", pc->num_bins);
2241 for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
2242 cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd);
2243 }
2244
2245 /*
2246 * Media sizes...
2247 */
2248
2249 cupsFilePrintf(fp, "NumSizes %d\n", pc->num_sizes);
2250 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2251 cupsFilePrintf(fp, "Size %s %s %d %d %d %d %d %d\n", size->map.pwg,
2252 size->map.ppd, size->width, size->length, size->left,
2253 size->bottom, size->right, size->top);
2254 if (pc->custom_max_width > 0)
2255 cupsFilePrintf(fp, "CustomSize %d %d %d %d %d %d %d %d\n",
2256 pc->custom_max_width, pc->custom_max_length,
2257 pc->custom_min_width, pc->custom_min_length,
2258 pc->custom_size.left, pc->custom_size.bottom,
2259 pc->custom_size.right, pc->custom_size.top);
2260
2261 /*
2262 * Media sources...
2263 */
2264
2265 if (pc->source_option)
2266 cupsFilePrintf(fp, "SourceOption %s\n", pc->source_option);
2267
2268 if (pc->num_sources > 0)
2269 {
2270 cupsFilePrintf(fp, "NumSources %d\n", pc->num_sources);
2271 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
2272 cupsFilePrintf(fp, "Source %s %s\n", map->pwg, map->ppd);
2273 }
2274
2275 /*
2276 * Media types...
2277 */
2278
2279 if (pc->num_types > 0)
2280 {
2281 cupsFilePrintf(fp, "NumTypes %d\n", pc->num_types);
2282 for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
2283 cupsFilePrintf(fp, "Type %s %s\n", map->pwg, map->ppd);
2284 }
2285
2286 /*
2287 * Presets...
2288 */
2289
2290 for (i = _PWG_PRINT_COLOR_MODE_MONOCHROME; i < _PWG_PRINT_COLOR_MODE_MAX; i ++)
2291 for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
2292 if (pc->num_presets[i][j])
2293 {
2294 cupsFilePrintf(fp, "Preset %d %d", i, j);
2295 for (k = pc->num_presets[i][j], option = pc->presets[i][j];
2296 k > 0;
2297 k --, option ++)
2298 cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2299 cupsFilePutChar(fp, '\n');
2300 }
2301
2302 /*
2303 * Duplex/sides...
2304 */
2305
2306 if (pc->sides_option)
2307 cupsFilePrintf(fp, "SidesOption %s\n", pc->sides_option);
2308
2309 if (pc->sides_1sided)
2310 cupsFilePrintf(fp, "Sides1Sided %s\n", pc->sides_1sided);
2311
2312 if (pc->sides_2sided_long)
2313 cupsFilePrintf(fp, "Sides2SidedLong %s\n", pc->sides_2sided_long);
2314
2315 if (pc->sides_2sided_short)
2316 cupsFilePrintf(fp, "Sides2SidedShort %s\n", pc->sides_2sided_short);
2317
2318 /*
2319 * Product, cupsFilter, cupsFilter2, and cupsPreFilter...
2320 */
2321
2322 if (pc->product)
2323 cupsFilePutConf(fp, "Product", pc->product);
2324
2325 for (value = (const char *)cupsArrayFirst(pc->filters);
2326 value;
2327 value = (const char *)cupsArrayNext(pc->filters))
2328 cupsFilePutConf(fp, "Filter", value);
2329
2330 for (value = (const char *)cupsArrayFirst(pc->prefilters);
2331 value;
2332 value = (const char *)cupsArrayNext(pc->prefilters))
2333 cupsFilePutConf(fp, "PreFilter", value);
2334
2335 cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
2336
2337 /*
2338 * Finishing options...
2339 */
2340
2341 for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
2342 f;
2343 f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
2344 {
2345 cupsFilePrintf(fp, "Finishings %d", f->value);
2346 for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2347 cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2348 cupsFilePutChar(fp, '\n');
2349 }
2350
2351 /*
2352 * Max copies...
2353 */
2354
2355 cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies);
2356
2357 /*
2358 * IPP attributes, if any...
2359 */
2360
2361 if (attrs)
2362 {
2363 cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
2364
2365 attrs->state = IPP_IDLE;
2366 ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
2367 }
2368
2369 /*
2370 * Close and return...
2371 */
2372
2373 if (cupsFileClose(fp))
2374 {
2375 unlink(newfile);
2376 return (0);
2377 }
2378
2379 unlink(filename);
2380 return (!rename(newfile, filename));
2381 }
2382
2383
2384 /*
2385 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
2386 * media-source.
2387 */
2388
2389 const char * /* O - InputSlot name */
2390 _pwgInputSlotForSource(
2391 const char *media_source, /* I - PWG media-source */
2392 char *name, /* I - Name buffer */
2393 size_t namesize) /* I - Size of name buffer */
2394 {
2395 /*
2396 * Range check input...
2397 */
2398
2399 if (!media_source || !name || namesize < PPD_MAX_NAME)
2400 return (NULL);
2401
2402 if (_cups_strcasecmp(media_source, "main"))
2403 strlcpy(name, "Cassette", namesize);
2404 else if (_cups_strcasecmp(media_source, "alternate"))
2405 strlcpy(name, "Multipurpose", namesize);
2406 else if (_cups_strcasecmp(media_source, "large-capacity"))
2407 strlcpy(name, "LargeCapacity", namesize);
2408 else if (_cups_strcasecmp(media_source, "bottom"))
2409 strlcpy(name, "Lower", namesize);
2410 else if (_cups_strcasecmp(media_source, "middle"))
2411 strlcpy(name, "Middle", namesize);
2412 else if (_cups_strcasecmp(media_source, "top"))
2413 strlcpy(name, "Upper", namesize);
2414 else if (_cups_strcasecmp(media_source, "rear"))
2415 strlcpy(name, "Rear", namesize);
2416 else if (_cups_strcasecmp(media_source, "side"))
2417 strlcpy(name, "Side", namesize);
2418 else if (_cups_strcasecmp(media_source, "envelope"))
2419 strlcpy(name, "Envelope", namesize);
2420 else if (_cups_strcasecmp(media_source, "main-roll"))
2421 strlcpy(name, "Roll", namesize);
2422 else if (_cups_strcasecmp(media_source, "alternate-roll"))
2423 strlcpy(name, "Roll2", namesize);
2424 else
2425 pwg_ppdize_name(media_source, name, namesize);
2426
2427 return (name);
2428 }
2429
2430
2431 /*
2432 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
2433 * media-type.
2434 */
2435
2436 const char * /* O - MediaType name */
2437 _pwgMediaTypeForType(
2438 const char *media_type, /* I - PWG media-type */
2439 char *name, /* I - Name buffer */
2440 size_t namesize) /* I - Size of name buffer */
2441 {
2442 /*
2443 * Range check input...
2444 */
2445
2446 if (!media_type || !name || namesize < PPD_MAX_NAME)
2447 return (NULL);
2448
2449 if (_cups_strcasecmp(media_type, "auto"))
2450 strlcpy(name, "Auto", namesize);
2451 else if (_cups_strcasecmp(media_type, "cardstock"))
2452 strlcpy(name, "Cardstock", namesize);
2453 else if (_cups_strcasecmp(media_type, "envelope"))
2454 strlcpy(name, "Envelope", namesize);
2455 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
2456 strlcpy(name, "Glossy", namesize);
2457 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
2458 strlcpy(name, "HighGloss", namesize);
2459 else if (_cups_strcasecmp(media_type, "photographic-matte"))
2460 strlcpy(name, "Matte", namesize);
2461 else if (_cups_strcasecmp(media_type, "stationery"))
2462 strlcpy(name, "Plain", namesize);
2463 else if (_cups_strcasecmp(media_type, "stationery-coated"))
2464 strlcpy(name, "Coated", namesize);
2465 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
2466 strlcpy(name, "Inkjet", namesize);
2467 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
2468 strlcpy(name, "Letterhead", namesize);
2469 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
2470 strlcpy(name, "Preprinted", namesize);
2471 else if (_cups_strcasecmp(media_type, "transparency"))
2472 strlcpy(name, "Transparency", namesize);
2473 else
2474 pwg_ppdize_name(media_type, name, namesize);
2475
2476 return (name);
2477 }
2478
2479
2480 /*
2481 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
2482 */
2483
2484 const char * /* O - PageSize name */
2485 _pwgPageSizeForMedia(
2486 _pwg_media_t *media, /* I - Media */
2487 char *name, /* I - PageSize name buffer */
2488 size_t namesize) /* I - Size of name buffer */
2489 {
2490 const char *sizeptr, /* Pointer to size in PWG name */
2491 *dimptr; /* Pointer to dimensions in PWG name */
2492
2493
2494 /*
2495 * Range check input...
2496 */
2497
2498 if (!media || !name || namesize < PPD_MAX_NAME)
2499 return (NULL);
2500
2501 /*
2502 * Copy or generate a PageSize name...
2503 */
2504
2505 if (media->ppd)
2506 {
2507 /*
2508 * Use a standard Adobe name...
2509 */
2510
2511 strlcpy(name, media->ppd, namesize);
2512 }
2513 else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
2514 (sizeptr = strchr(media->pwg, '_')) == NULL ||
2515 (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
2516 (size_t)(dimptr - sizeptr) > namesize)
2517 {
2518 /*
2519 * Use a name of the form "wNNNhNNN"...
2520 */
2521
2522 snprintf(name, namesize, "w%dh%d", (int)_PWG_TOPTS(media->width),
2523 (int)_PWG_TOPTS(media->length));
2524 }
2525 else
2526 {
2527 /*
2528 * Copy the size name from class_sizename_dimensions...
2529 */
2530
2531 memcpy(name, sizeptr + 1, dimptr - sizeptr - 1);
2532 name[dimptr - sizeptr - 1] = '\0';
2533 }
2534
2535 return (name);
2536 }
2537
2538
2539 /*
2540 * 'pwg_compare_finishings()' - Compare two finishings values.
2541 */
2542
2543 static int /* O- Result of comparison */
2544 pwg_compare_finishings(
2545 _pwg_finishings_t *a, /* I - First finishings value */
2546 _pwg_finishings_t *b) /* I - Second finishings value */
2547 {
2548 return (b->value - a->value);
2549 }
2550
2551
2552 /*
2553 * 'pwg_free_finishings()' - Free a finishings value.
2554 */
2555
2556 static void
2557 pwg_free_finishings(
2558 _pwg_finishings_t *f) /* I - Finishings value */
2559 {
2560 cupsFreeOptions(f->num_options, f->options);
2561 free(f);
2562 }
2563
2564
2565 /*
2566 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
2567 */
2568
2569 static void
2570 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
2571 char *name, /* I - Name buffer */
2572 size_t namesize) /* I - Size of name buffer */
2573 {
2574 char *ptr, /* Pointer into name buffer */
2575 *end; /* End of name buffer */
2576
2577
2578 *name = toupper(*ipp++);
2579
2580 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
2581 {
2582 if (*ipp == '-' && _cups_isalpha(ipp[1]))
2583 {
2584 ipp ++;
2585 *ptr++ = toupper(*ipp++ & 255);
2586 }
2587 else
2588 *ptr++ = *ipp++;
2589 }
2590
2591 *ptr = '\0';
2592 }
2593
2594
2595 /*
2596 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
2597 */
2598
2599 static void
2600 pwg_unppdize_name(const char *ppd, /* I - PPD keyword */
2601 char *name, /* I - Name buffer */
2602 size_t namesize) /* I - Size of name buffer */
2603 {
2604 char *ptr, /* Pointer into name buffer */
2605 *end; /* End of name buffer */
2606
2607
2608 for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
2609 {
2610 if (_cups_isalnum(*ppd) || *ppd == '-')
2611 *ptr++ = tolower(*ppd & 255);
2612 else if (*ppd == '_' || *ppd == '.')
2613 *ptr++ = '-';
2614
2615 if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
2616 _cups_isupper(ppd[1]) && ptr < end)
2617 *ptr++ = '-';
2618 }
2619
2620 *ptr = '\0';
2621 }
2622
2623
2624 /*
2625 * End of "$Id$".
2626 */