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