]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd-cache.c
Merge changes from CUPS 1.6svn-r9939.
[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 (!_cups_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 (!_cups_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 (!_cups_strcasecmp(line, "Product"))
211 {
212 pc->product = _cupsStrAlloc(value);
213 }
214 else if (!_cups_strcasecmp(line, "SingleFile"))
215 {
216 pc->single_file = !_cups_strcasecmp(value, "true");
217 }
218 else if (!_cups_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 (!_cups_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 (!_cups_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 (!_cups_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 (!_cups_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 (!_cups_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 (!_cups_strcasecmp(line, "SourceOption"))
400 {
401 pc->source_option = _cupsStrAlloc(value);
402 }
403 else if (!_cups_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 (!_cups_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 (!_cups_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 (!_cups_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 (!_cups_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 (!_cups_strcasecmp(line, "SidesOption"))
528 pc->sides_option = _cupsStrAlloc(value);
529 else if (!_cups_strcasecmp(line, "Sides1Sided"))
530 pc->sides_1sided = _cupsStrAlloc(value);
531 else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
532 pc->sides_2sided_long = _cupsStrAlloc(value);
533 else if (!_cups_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 (!_cups_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 (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
854 !_cups_strcasecmp(choice->choice, "Default"))
855 pwg_name = "auto";
856 else if (!_cups_strcasecmp(choice->choice, "Cassette"))
857 pwg_name = "main";
858 else if (!_cups_strcasecmp(choice->choice, "PhotoTray"))
859 pwg_name = "photo";
860 else if (!_cups_strcasecmp(choice->choice, "CDTray"))
861 pwg_name = "disc";
862 else if (!_cups_strncasecmp(choice->choice, "Multipurpose", 12) ||
863 !_cups_strcasecmp(choice->choice, "MP") ||
864 !_cups_strcasecmp(choice->choice, "MPTray"))
865 pwg_name = "alternate";
866 else if (!_cups_strcasecmp(choice->choice, "LargeCapacity"))
867 pwg_name = "large-capacity";
868 else if (!_cups_strncasecmp(choice->choice, "Lower", 5))
869 pwg_name = "bottom";
870 else if (!_cups_strncasecmp(choice->choice, "Middle", 6))
871 pwg_name = "middle";
872 else if (!_cups_strncasecmp(choice->choice, "Upper", 5))
873 pwg_name = "top";
874 else if (!_cups_strncasecmp(choice->choice, "Side", 4))
875 pwg_name = "side";
876 else if (!_cups_strcasecmp(choice->choice, "Roll"))
877 pwg_name = "main-roll";
878 else
879 {
880 /*
881 * Convert PPD name to lowercase...
882 */
883
884 pwg_name = pwg_keyword;
885 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword));
886 }
887
888 map->pwg = _cupsStrAlloc(pwg_name);
889 map->ppd = _cupsStrAlloc(choice->choice);
890 }
891 }
892
893 /*
894 * Copy and convert MediaType data...
895 */
896
897 if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
898 {
899 if ((pc->types = calloc(media_type->num_choices,
900 sizeof(_pwg_map_t))) == NULL)
901 {
902 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
903 "_pwg_map_t's for MediaType.", media_type->num_choices));
904 goto create_error;
905 }
906
907 pc->num_types = media_type->num_choices;
908
909 for (i = media_type->num_choices, choice = media_type->choices,
910 map = pc->types;
911 i > 0;
912 i --, choice ++, map ++)
913 {
914 if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
915 !_cups_strcasecmp(choice->choice, "Any") ||
916 !_cups_strcasecmp(choice->choice, "Default"))
917 pwg_name = "auto";
918 else if (!_cups_strncasecmp(choice->choice, "Card", 4))
919 pwg_name = "cardstock";
920 else if (!_cups_strncasecmp(choice->choice, "Env", 3))
921 pwg_name = "envelope";
922 else if (!_cups_strncasecmp(choice->choice, "Gloss", 5))
923 pwg_name = "photographic-glossy";
924 else if (!_cups_strcasecmp(choice->choice, "HighGloss"))
925 pwg_name = "photographic-high-gloss";
926 else if (!_cups_strcasecmp(choice->choice, "Matte"))
927 pwg_name = "photographic-matte";
928 else if (!_cups_strncasecmp(choice->choice, "Plain", 5))
929 pwg_name = "stationery";
930 else if (!_cups_strncasecmp(choice->choice, "Coated", 6))
931 pwg_name = "stationery-coated";
932 else if (!_cups_strcasecmp(choice->choice, "Inkjet"))
933 pwg_name = "stationery-inkjet";
934 else if (!_cups_strcasecmp(choice->choice, "Letterhead"))
935 pwg_name = "stationery-letterhead";
936 else if (!_cups_strncasecmp(choice->choice, "Preprint", 8))
937 pwg_name = "stationery-preprinted";
938 else if (!_cups_strcasecmp(choice->choice, "Recycled"))
939 pwg_name = "stationery-recycled";
940 else if (!_cups_strncasecmp(choice->choice, "Transparen", 10))
941 pwg_name = "transparency";
942 else
943 {
944 /*
945 * Convert PPD name to lowercase...
946 */
947
948 pwg_name = pwg_keyword;
949 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword));
950 }
951
952 map->pwg = _cupsStrAlloc(pwg_name);
953 map->ppd = _cupsStrAlloc(choice->choice);
954 }
955 }
956
957 /*
958 * Copy and convert OutputBin data...
959 */
960
961 if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
962 {
963 if ((pc->bins = calloc(output_bin->num_choices,
964 sizeof(_pwg_map_t))) == NULL)
965 {
966 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
967 "_pwg_map_t's for OutputBin.", output_bin->num_choices));
968 goto create_error;
969 }
970
971 pc->num_bins = output_bin->num_choices;
972
973 for (i = output_bin->num_choices, choice = output_bin->choices,
974 map = pc->bins;
975 i > 0;
976 i --, choice ++, map ++)
977 {
978 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword));
979
980 map->pwg = _cupsStrAlloc(pwg_keyword);
981 map->ppd = _cupsStrAlloc(choice->choice);
982 }
983 }
984
985 if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
986 {
987 /*
988 * Copy and convert APPrinterPreset (output-mode + print-quality) data...
989 */
990
991 const char *quality, /* com.apple.print.preset.quality value */
992 *output_mode, /* com.apple.print.preset.output-mode value */
993 *color_model_val, /* ColorModel choice */
994 *graphicsType, /* com.apple.print.preset.graphicsType value */
995 *media_front_coating; /* com.apple.print.preset.media-front-coating value */
996
997 do
998 {
999 num_options = _ppdParseOptions(ppd_attr->value, 0, &options,
1000 _PPD_PARSE_ALL);
1001
1002 if ((quality = cupsGetOption("com.apple.print.preset.quality",
1003 num_options, options)) != NULL)
1004 {
1005 /*
1006 * Get the print-quality for this preset...
1007 */
1008
1009 if (!strcmp(quality, "low"))
1010 pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1011 else if (!strcmp(quality, "high"))
1012 pwg_print_quality = _PWG_PRINT_QUALITY_HIGH;
1013 else
1014 pwg_print_quality = _PWG_PRINT_QUALITY_NORMAL;
1015
1016 /*
1017 * Ignore graphicsType "Photo" presets that are not high quality.
1018 */
1019
1020 graphicsType = cupsGetOption("com.apple.print.preset.graphicsType",
1021 num_options, options);
1022
1023 if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH && graphicsType &&
1024 !strcmp(graphicsType, "Photo"))
1025 continue;
1026
1027 /*
1028 * Ignore presets for normal and draft quality where the coating
1029 * isn't "none" or "autodetect".
1030 */
1031
1032 media_front_coating = cupsGetOption(
1033 "com.apple.print.preset.media-front-coating",
1034 num_options, options);
1035
1036 if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH &&
1037 media_front_coating &&
1038 strcmp(media_front_coating, "none") &&
1039 strcmp(media_front_coating, "autodetect"))
1040 continue;
1041
1042 /*
1043 * Get the output mode for this preset...
1044 */
1045
1046 output_mode = cupsGetOption("com.apple.print.preset.output-mode",
1047 num_options, options);
1048 color_model_val = cupsGetOption("ColorModel", num_options, options);
1049
1050 if (output_mode)
1051 {
1052 if (!strcmp(output_mode, "monochrome"))
1053 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1054 else
1055 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1056 }
1057 else if (color_model_val)
1058 {
1059 if (!_cups_strcasecmp(color_model_val, "Gray"))
1060 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1061 else
1062 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1063 }
1064 else
1065 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1066
1067 /*
1068 * Save the options for this combination as needed...
1069 */
1070
1071 if (!pc->num_presets[pwg_print_color_mode][pwg_print_quality])
1072 pc->num_presets[pwg_print_color_mode][pwg_print_quality] =
1073 _ppdParseOptions(ppd_attr->value, 0,
1074 pc->presets[pwg_print_color_mode] +
1075 pwg_print_quality, _PPD_PARSE_OPTIONS);
1076 }
1077
1078 cupsFreeOptions(num_options, options);
1079 }
1080 while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) != NULL);
1081 }
1082
1083 if (!pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_DRAFT] &&
1084 !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_NORMAL] &&
1085 !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_HIGH])
1086 {
1087 /*
1088 * Try adding some common color options to create grayscale presets. These
1089 * are listed in order of popularity...
1090 */
1091
1092 const char *color_option = NULL, /* Color control option */
1093 *gray_choice = NULL; /* Choice to select grayscale */
1094
1095 if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL &&
1096 ppdFindChoice(color_model, "Gray"))
1097 {
1098 color_option = "ColorModel";
1099 gray_choice = "Gray";
1100 }
1101 else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL &&
1102 ppdFindChoice(color_model, "grayscale"))
1103 {
1104 color_option = "HPColorMode";
1105 gray_choice = "grayscale";
1106 }
1107 else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL &&
1108 ppdFindChoice(color_model, "Mono"))
1109 {
1110 color_option = "BRMonoColor";
1111 gray_choice = "Mono";
1112 }
1113 else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL &&
1114 ppdFindChoice(color_model, "1"))
1115 {
1116 color_option = "CNIJSGrayScale";
1117 gray_choice = "1";
1118 }
1119 else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL &&
1120 ppdFindChoice(color_model, "True"))
1121 {
1122 color_option = "HPColorAsGray";
1123 gray_choice = "True";
1124 }
1125
1126 if (color_option && gray_choice)
1127 {
1128 /*
1129 * Copy and convert ColorModel (output-mode) data...
1130 */
1131
1132 cups_option_t *coption, /* Color option */
1133 *moption; /* Monochrome option */
1134
1135 for (pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1136 pwg_print_quality < _PWG_PRINT_QUALITY_MAX;
1137 pwg_print_quality ++)
1138 {
1139 if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality])
1140 {
1141 /*
1142 * Copy the color options...
1143 */
1144
1145 num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
1146 [pwg_print_quality];
1147 options = calloc(sizeof(cups_option_t), num_options);
1148
1149 if (options)
1150 {
1151 for (i = num_options, moption = options,
1152 coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
1153 [pwg_print_quality];
1154 i > 0;
1155 i --, moption ++, coption ++)
1156 {
1157 moption->name = _cupsStrRetain(coption->name);
1158 moption->value = _cupsStrRetain(coption->value);
1159 }
1160
1161 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1162 num_options;
1163 pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1164 options;
1165 }
1166 }
1167 else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL)
1168 continue;
1169
1170 /*
1171 * Add the grayscale option to the preset...
1172 */
1173
1174 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1175 cupsAddOption(color_option, gray_choice,
1176 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
1177 [pwg_print_quality],
1178 pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME] +
1179 pwg_print_quality);
1180 }
1181 }
1182 }
1183
1184 /*
1185 * Copy and convert Duplex (sides) data...
1186 */
1187
1188 if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
1189 if ((duplex = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1190 if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
1191 if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
1192 duplex = ppdFindOption(ppd, "KD03Duplex");
1193
1194 if (duplex)
1195 {
1196 pc->sides_option = _cupsStrAlloc(duplex->keyword);
1197
1198 for (i = duplex->num_choices, choice = duplex->choices;
1199 i > 0;
1200 i --, choice ++)
1201 {
1202 if ((!_cups_strcasecmp(choice->choice, "None") ||
1203 !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
1204 pc->sides_1sided = _cupsStrAlloc(choice->choice);
1205 else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
1206 !_cups_strcasecmp(choice->choice, "LongEdge") ||
1207 !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
1208 pc->sides_2sided_long = _cupsStrAlloc(choice->choice);
1209 else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
1210 !_cups_strcasecmp(choice->choice, "ShortEdge") ||
1211 !_cups_strcasecmp(choice->choice, "Bottom")) &&
1212 !pc->sides_2sided_short)
1213 pc->sides_2sided_short = _cupsStrAlloc(choice->choice);
1214 }
1215 }
1216
1217 /*
1218 * Copy filters and pre-filters...
1219 */
1220
1221 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
1222 (cups_acopy_func_t)_cupsStrAlloc,
1223 (cups_afree_func_t)_cupsStrFree);
1224
1225 cupsArrayAdd(pc->filters,
1226 "application/vnd.cups-raw application/octet-stream 0 -");
1227
1228 if ((ppd_attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL)
1229 {
1230 do
1231 {
1232 cupsArrayAdd(pc->filters, ppd_attr->value);
1233 }
1234 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
1235 }
1236 else if (ppd->num_filters > 0)
1237 {
1238 for (i = 0; i < ppd->num_filters; i ++)
1239 cupsArrayAdd(pc->filters, ppd->filters[i]);
1240 }
1241 else
1242 cupsArrayAdd(pc->filters, "application/vnd.cups-postscript 0 -");
1243
1244 /*
1245 * See if we have a command filter...
1246 */
1247
1248 for (filter = (const char *)cupsArrayFirst(pc->filters);
1249 filter;
1250 filter = (const char *)cupsArrayNext(pc->filters))
1251 if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
1252 _cups_isspace(filter[28]))
1253 break;
1254
1255 if (!filter &&
1256 ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL ||
1257 _cups_strcasecmp(ppd_attr->value, "none")))
1258 {
1259 /*
1260 * No command filter and no cupsCommands keyword telling us not to use one.
1261 * See if this is a PostScript printer, and if so add a PostScript command
1262 * filter...
1263 */
1264
1265 for (filter = (const char *)cupsArrayFirst(pc->filters);
1266 filter;
1267 filter = (const char *)cupsArrayNext(pc->filters))
1268 if (!_cups_strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
1269 _cups_isspace(filter[31]))
1270 break;
1271
1272 if (filter)
1273 cupsArrayAdd(pc->filters,
1274 "application/vnd.cups-command application/postscript 0 -");
1275 }
1276
1277 if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
1278 {
1279 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
1280 (cups_acopy_func_t)_cupsStrAlloc,
1281 (cups_afree_func_t)_cupsStrFree);
1282
1283 do
1284 {
1285 cupsArrayAdd(pc->prefilters, ppd_attr->value);
1286 }
1287 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) != NULL);
1288 }
1289
1290 if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL)
1291 pc->single_file = !_cups_strcasecmp(ppd_attr->value, "true");
1292
1293 /*
1294 * Copy the product string, if any...
1295 */
1296
1297 if (ppd->product)
1298 pc->product = _cupsStrAlloc(ppd->product);
1299
1300 /*
1301 * Return the cache data...
1302 */
1303
1304 return (pc);
1305
1306 /*
1307 * If we get here we need to destroy the PWG mapping data and return NULL...
1308 */
1309
1310 create_error:
1311
1312 _cupsSetError(IPP_INTERNAL_ERROR, _("Out of memory."), 1);
1313 _ppdCacheDestroy(pc);
1314
1315 return (NULL);
1316 }
1317
1318
1319 /*
1320 * '_ppdCacheDestroy()' - Free all memory used for PWG mapping data.
1321 */
1322
1323 void
1324 _ppdCacheDestroy(_ppd_cache_t *pc) /* I - PPD cache and mapping data */
1325 {
1326 int i; /* Looping var */
1327 _pwg_map_t *map; /* Current map */
1328 _pwg_size_t *size; /* Current size */
1329
1330
1331 /*
1332 * Range check input...
1333 */
1334
1335 if (!pc)
1336 return;
1337
1338 /*
1339 * Free memory as needed...
1340 */
1341
1342 if (pc->bins)
1343 {
1344 for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
1345 {
1346 _cupsStrFree(map->pwg);
1347 _cupsStrFree(map->ppd);
1348 }
1349
1350 free(pc->bins);
1351 }
1352
1353 if (pc->sizes)
1354 {
1355 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1356 {
1357 _cupsStrFree(size->map.pwg);
1358 _cupsStrFree(size->map.ppd);
1359 }
1360
1361 free(pc->sizes);
1362 }
1363
1364 if (pc->source_option)
1365 _cupsStrFree(pc->source_option);
1366
1367 if (pc->sources)
1368 {
1369 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
1370 {
1371 _cupsStrFree(map->pwg);
1372 _cupsStrFree(map->ppd);
1373 }
1374
1375 free(pc->sources);
1376 }
1377
1378 if (pc->types)
1379 {
1380 for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
1381 {
1382 _cupsStrFree(map->pwg);
1383 _cupsStrFree(map->ppd);
1384 }
1385
1386 free(pc->types);
1387 }
1388
1389 if (pc->custom_max_keyword)
1390 _cupsStrFree(pc->custom_max_keyword);
1391
1392 if (pc->custom_min_keyword)
1393 _cupsStrFree(pc->custom_min_keyword);
1394
1395 _cupsStrFree(pc->product);
1396 cupsArrayDelete(pc->filters);
1397 cupsArrayDelete(pc->prefilters);
1398
1399 free(pc);
1400 }
1401
1402
1403 /*
1404 * '_ppdCacheGetBin()' - Get the PWG output-bin keyword associated with a PPD
1405 * OutputBin.
1406 */
1407
1408 const char * /* O - output-bin or NULL */
1409 _ppdCacheGetBin(
1410 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1411 const char *output_bin) /* I - PPD OutputBin string */
1412 {
1413 int i; /* Looping var */
1414
1415
1416 /*
1417 * Range check input...
1418 */
1419
1420 if (!pc || !output_bin)
1421 return (NULL);
1422
1423 /*
1424 * Look up the OutputBin string...
1425 */
1426
1427
1428 for (i = 0; i < pc->num_bins; i ++)
1429 if (!_cups_strcasecmp(output_bin, pc->bins[i].ppd))
1430 return (pc->bins[i].pwg);
1431
1432 return (NULL);
1433 }
1434
1435
1436 /*
1437 * '_ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job
1438 * attributes or a keyword string.
1439 */
1440
1441 const char * /* O - PPD InputSlot or NULL */
1442 _ppdCacheGetInputSlot(
1443 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1444 ipp_t *job, /* I - Job attributes or NULL */
1445 const char *keyword) /* I - Keyword string or NULL */
1446 {
1447 /*
1448 * Range check input...
1449 */
1450
1451 if (!pc || pc->num_sources == 0 || (!job && !keyword))
1452 return (NULL);
1453
1454 if (job && !keyword)
1455 {
1456 /*
1457 * Lookup the media-col attribute and any media-source found there...
1458 */
1459
1460 ipp_attribute_t *media_col, /* media-col attribute */
1461 *media_source; /* media-source attribute */
1462 _pwg_size_t size; /* Dimensional size */
1463 int margins_set; /* Were the margins set? */
1464
1465 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
1466 if (media_col &&
1467 (media_source = ippFindAttribute(media_col->values[0].collection,
1468 "media-source",
1469 IPP_TAG_KEYWORD)) != NULL)
1470 {
1471 /*
1472 * Use the media-source value from media-col...
1473 */
1474
1475 keyword = media_source->values[0].string.text;
1476 }
1477 else if (_pwgInitSize(&size, job, &margins_set))
1478 {
1479 /*
1480 * For media <= 5x7, look for a photo tray...
1481 */
1482
1483 if (size.width <= (5 * 2540) && size.length <= (7 * 2540))
1484 keyword = "photo";
1485 }
1486 }
1487
1488 if (keyword)
1489 {
1490 int i; /* Looping var */
1491
1492 for (i = 0; i < pc->num_sources; i ++)
1493 if (!_cups_strcasecmp(keyword, pc->sources[i].pwg))
1494 return (pc->sources[i].ppd);
1495 }
1496
1497 return (NULL);
1498 }
1499
1500
1501 /*
1502 * '_ppdCacheGetMediaType()' - Get the PPD MediaType associated with the job
1503 * attributes or a keyword string.
1504 */
1505
1506 const char * /* O - PPD MediaType or NULL */
1507 _ppdCacheGetMediaType(
1508 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1509 ipp_t *job, /* I - Job attributes or NULL */
1510 const char *keyword) /* I - Keyword string or NULL */
1511 {
1512 /*
1513 * Range check input...
1514 */
1515
1516 if (!pc || pc->num_types == 0 || (!job && !keyword))
1517 return (NULL);
1518
1519 if (job && !keyword)
1520 {
1521 /*
1522 * Lookup the media-col attribute and any media-source found there...
1523 */
1524
1525 ipp_attribute_t *media_col, /* media-col attribute */
1526 *media_type; /* media-type attribute */
1527
1528 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
1529 if (media_col)
1530 {
1531 if ((media_type = ippFindAttribute(media_col->values[0].collection,
1532 "media-type",
1533 IPP_TAG_KEYWORD)) == NULL)
1534 media_type = ippFindAttribute(media_col->values[0].collection,
1535 "media-type", IPP_TAG_NAME);
1536
1537 if (media_type)
1538 keyword = media_type->values[0].string.text;
1539 }
1540 }
1541
1542 if (keyword)
1543 {
1544 int i; /* Looping var */
1545
1546 for (i = 0; i < pc->num_types; i ++)
1547 if (!_cups_strcasecmp(keyword, pc->types[i].pwg))
1548 return (pc->types[i].ppd);
1549 }
1550
1551 return (NULL);
1552 }
1553
1554
1555 /*
1556 * '_ppdCacheGetOutputBin()' - Get the PPD OutputBin associated with the keyword
1557 * string.
1558 */
1559
1560 const char * /* O - PPD OutputBin or NULL */
1561 _ppdCacheGetOutputBin(
1562 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1563 const char *output_bin) /* I - Keyword string */
1564 {
1565 int i; /* Looping var */
1566
1567
1568 /*
1569 * Range check input...
1570 */
1571
1572 if (!pc || !output_bin)
1573 return (NULL);
1574
1575 /*
1576 * Look up the OutputBin string...
1577 */
1578
1579
1580 for (i = 0; i < pc->num_bins; i ++)
1581 if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg))
1582 return (pc->bins[i].ppd);
1583
1584 return (NULL);
1585 }
1586
1587
1588 /*
1589 * '_ppdCacheGetPageSize()' - Get the PPD PageSize associated with the job
1590 * attributes or a keyword string.
1591 */
1592
1593 const char * /* O - PPD PageSize or NULL */
1594 _ppdCacheGetPageSize(
1595 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1596 ipp_t *job, /* I - Job attributes or NULL */
1597 const char *keyword, /* I - Keyword string or NULL */
1598 int *exact) /* O - 1 if exact match, 0 otherwise */
1599 {
1600 int i; /* Looping var */
1601 _pwg_size_t *size, /* Current size */
1602 *closest, /* Closest size */
1603 jobsize; /* Size data from job */
1604 int margins_set, /* Were the margins set? */
1605 dwidth, /* Difference in width */
1606 dlength, /* Difference in length */
1607 dleft, /* Difference in left margins */
1608 dright, /* Difference in right margins */
1609 dbottom, /* Difference in bottom margins */
1610 dtop, /* Difference in top margins */
1611 dmin, /* Minimum difference */
1612 dclosest; /* Closest difference */
1613 const char *ppd_name; /* PPD media name */
1614
1615
1616 DEBUG_printf(("_ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)",
1617 pc, job, keyword, exact));
1618
1619 /*
1620 * Range check input...
1621 */
1622
1623 if (!pc || (!job && !keyword))
1624 return (NULL);
1625
1626 if (exact)
1627 *exact = 0;
1628
1629 ppd_name = keyword;
1630
1631 if (job)
1632 {
1633 /*
1634 * Try getting the PPD media name from the job attributes...
1635 */
1636
1637 ipp_attribute_t *attr; /* Job attribute */
1638
1639 if ((attr = ippFindAttribute(job, "PageSize", IPP_TAG_ZERO)) == NULL)
1640 if ((attr = ippFindAttribute(job, "PageRegion", IPP_TAG_ZERO)) == NULL)
1641 attr = ippFindAttribute(job, "media", IPP_TAG_ZERO);
1642
1643 #ifdef DEBUG
1644 if (attr)
1645 DEBUG_printf(("1_ppdCacheGetPageSize: Found attribute %s (%s)",
1646 attr->name, ippTagString(attr->value_tag)));
1647 else
1648 DEBUG_puts("1_ppdCacheGetPageSize: Did not find media attribute.");
1649 #endif /* DEBUG */
1650
1651 if (attr && (attr->value_tag == IPP_TAG_NAME ||
1652 attr->value_tag == IPP_TAG_KEYWORD))
1653 ppd_name = attr->values[0].string.text;
1654 }
1655
1656 DEBUG_printf(("1_ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name));
1657
1658 if (ppd_name)
1659 {
1660 /*
1661 * Try looking up the named PPD size first...
1662 */
1663
1664 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1665 {
1666 DEBUG_printf(("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]",
1667 (int)(size - pc->sizes), size->map.pwg, size->map.ppd));
1668
1669 if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
1670 !_cups_strcasecmp(ppd_name, size->map.pwg))
1671 {
1672 if (exact)
1673 *exact = 1;
1674
1675 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", ppd_name));
1676
1677 return (size->map.ppd);
1678 }
1679 }
1680 }
1681
1682 if (job && !keyword)
1683 {
1684 /*
1685 * Get the size using media-col or media, with the preference being
1686 * media-col.
1687 */
1688
1689 if (!_pwgInitSize(&jobsize, job, &margins_set))
1690 return (NULL);
1691 }
1692 else
1693 {
1694 /*
1695 * Get the size using a media keyword...
1696 */
1697
1698 _pwg_media_t *media; /* Media definition */
1699
1700
1701 if ((media = _pwgMediaForPWG(keyword)) == NULL)
1702 if ((media = _pwgMediaForLegacy(keyword)) == NULL)
1703 if ((media = _pwgMediaForPPD(keyword)) == NULL)
1704 return (NULL);
1705
1706 jobsize.width = media->width;
1707 jobsize.length = media->length;
1708 margins_set = 0;
1709 }
1710
1711 /*
1712 * Now that we have the dimensions and possibly the margins, look at the
1713 * available sizes and find the match...
1714 */
1715
1716 closest = NULL;
1717 dclosest = 999999999;
1718
1719 if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
1720 _cups_strncasecmp(ppd_name, "custom_", 7))
1721 {
1722 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1723 {
1724 /*
1725 * Adobe uses a size matching algorithm with an epsilon of 5 points, which
1726 * is just about 176/2540ths...
1727 */
1728
1729 dwidth = size->width - jobsize.width;
1730 dlength = size->length - jobsize.length;
1731
1732 if (dwidth <= -176 || dwidth >= 176 || dlength <= -176 || dlength >= 176)
1733 continue;
1734
1735 if (margins_set)
1736 {
1737 /*
1738 * Use a tighter epsilon of 1 point (35/2540ths) for margins...
1739 */
1740
1741 dleft = size->left - jobsize.left;
1742 dright = size->right - jobsize.right;
1743 dtop = size->top - jobsize.top;
1744 dbottom = size->bottom - jobsize.bottom;
1745
1746 if (dleft <= -35 || dleft >= 35 || dright <= -35 || dright >= 35 ||
1747 dtop <= -35 || dtop >= 35 || dbottom <= -35 || dbottom >= 35)
1748 {
1749 dleft = dleft < 0 ? -dleft : dleft;
1750 dright = dright < 0 ? -dright : dright;
1751 dbottom = dbottom < 0 ? -dbottom : dbottom;
1752 dtop = dtop < 0 ? -dtop : dtop;
1753 dmin = dleft + dright + dbottom + dtop;
1754
1755 if (dmin < dclosest)
1756 {
1757 dclosest = dmin;
1758 closest = size;
1759 }
1760
1761 continue;
1762 }
1763 }
1764
1765 if (exact)
1766 *exact = 1;
1767
1768 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd));
1769
1770 return (size->map.ppd);
1771 }
1772 }
1773
1774 if (closest)
1775 {
1776 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (closest)",
1777 closest->map.ppd));
1778
1779 return (closest->map.ppd);
1780 }
1781
1782 /*
1783 * If we get here we need to check for custom page size support...
1784 */
1785
1786 if (jobsize.width >= pc->custom_min_width &&
1787 jobsize.width <= pc->custom_max_width &&
1788 jobsize.length >= pc->custom_min_length &&
1789 jobsize.length <= pc->custom_max_length)
1790 {
1791 /*
1792 * In range, format as Custom.WWWWxLLLL (points).
1793 */
1794
1795 snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d",
1796 (int)_PWG_TOPTS(jobsize.width), (int)_PWG_TOPTS(jobsize.length));
1797
1798 if (margins_set && exact)
1799 {
1800 dleft = pc->custom_size.left - jobsize.left;
1801 dright = pc->custom_size.right - jobsize.right;
1802 dtop = pc->custom_size.top - jobsize.top;
1803 dbottom = pc->custom_size.bottom - jobsize.bottom;
1804
1805 if (dleft > -35 && dleft < 35 && dright > -35 && dright < 35 &&
1806 dtop > -35 && dtop < 35 && dbottom > -35 && dbottom < 35)
1807 *exact = 1;
1808 }
1809 else if (exact)
1810 *exact = 1;
1811
1812 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (custom)",
1813 pc->custom_ppd_size));
1814
1815 return (pc->custom_ppd_size);
1816 }
1817
1818 /*
1819 * No custom page size support or the size is out of range - return NULL.
1820 */
1821
1822 DEBUG_puts("1_ppdCacheGetPageSize: Returning NULL");
1823
1824 return (NULL);
1825 }
1826
1827
1828 /*
1829 * '_ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize.
1830 */
1831
1832 _pwg_size_t * /* O - PWG size or NULL */
1833 _ppdCacheGetSize(
1834 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1835 const char *page_size) /* I - PPD PageSize */
1836 {
1837 int i;
1838 _pwg_size_t *size; /* Current size */
1839
1840
1841 /*
1842 * Range check input...
1843 */
1844
1845 if (!pc || !page_size)
1846 return (NULL);
1847
1848 if (!_cups_strncasecmp(page_size, "Custom.", 7))
1849 {
1850 /*
1851 * Custom size; size name can be one of the following:
1852 *
1853 * Custom.WIDTHxLENGTHin - Size in inches
1854 * Custom.WIDTHxLENGTHft - Size in feet
1855 * Custom.WIDTHxLENGTHcm - Size in centimeters
1856 * Custom.WIDTHxLENGTHmm - Size in millimeters
1857 * Custom.WIDTHxLENGTHm - Size in meters
1858 * Custom.WIDTHxLENGTH[pt] - Size in points
1859 */
1860
1861 double w, l; /* Width and length of page */
1862 char *ptr; /* Pointer into PageSize */
1863 struct lconv *loc; /* Locale data */
1864
1865 loc = localeconv();
1866 w = (float)_cupsStrScand(page_size + 7, &ptr, loc);
1867 if (!ptr || *ptr != 'x')
1868 return (NULL);
1869
1870 l = (float)_cupsStrScand(ptr + 1, &ptr, loc);
1871 if (!ptr)
1872 return (NULL);
1873
1874 if (!_cups_strcasecmp(ptr, "in"))
1875 {
1876 w *= 2540.0;
1877 l *= 2540.0;
1878 }
1879 else if (!_cups_strcasecmp(ptr, "ft"))
1880 {
1881 w *= 12.0 * 2540.0;
1882 l *= 12.0 * 2540.0;
1883 }
1884 else if (!_cups_strcasecmp(ptr, "mm"))
1885 {
1886 w *= 100.0;
1887 l *= 100.0;
1888 }
1889 else if (!_cups_strcasecmp(ptr, "cm"))
1890 {
1891 w *= 1000.0;
1892 l *= 1000.0;
1893 }
1894 else if (!_cups_strcasecmp(ptr, "m"))
1895 {
1896 w *= 100000.0;
1897 l *= 100000.0;
1898 }
1899 else
1900 {
1901 w *= 2540.0 / 72.0;
1902 l *= 2540.0 / 72.0;
1903 }
1904
1905 pc->custom_size.width = (int)w;
1906 pc->custom_size.length = (int)l;
1907
1908 return (&(pc->custom_size));
1909 }
1910
1911 /*
1912 * Not a custom size - look it up...
1913 */
1914
1915 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1916 if (!_cups_strcasecmp(page_size, size->map.ppd))
1917 return (size);
1918
1919 return (NULL);
1920 }
1921
1922
1923 /*
1924 * '_ppdCacheGetSource()' - Get the PWG media-source associated with a PPD
1925 * InputSlot.
1926 */
1927
1928 const char * /* O - PWG media-source keyword */
1929 _ppdCacheGetSource(
1930 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1931 const char *input_slot) /* I - PPD InputSlot */
1932 {
1933 int i; /* Looping var */
1934 _pwg_map_t *source; /* Current source */
1935
1936
1937 /*
1938 * Range check input...
1939 */
1940
1941 if (!pc || !input_slot)
1942 return (NULL);
1943
1944 for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++)
1945 if (!_cups_strcasecmp(input_slot, source->ppd))
1946 return (source->pwg);
1947
1948 return (NULL);
1949 }
1950
1951
1952 /*
1953 * '_ppdCacheGetType()' - Get the PWG media-type associated with a PPD
1954 * MediaType.
1955 */
1956
1957 const char * /* O - PWG media-type keyword */
1958 _ppdCacheGetType(
1959 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1960 const char *media_type) /* I - PPD MediaType */
1961 {
1962 int i; /* Looping var */
1963 _pwg_map_t *type; /* Current type */
1964
1965
1966 /*
1967 * Range check input...
1968 */
1969
1970 if (!pc || !media_type)
1971 return (NULL);
1972
1973 for (i = pc->num_types, type = pc->types; i > 0; i --, type ++)
1974 if (!_cups_strcasecmp(media_type, type->ppd))
1975 return (type->pwg);
1976
1977 return (NULL);
1978 }
1979
1980
1981 /*
1982 * '_ppdCacheWriteFile()' - Write PWG mapping data to a file.
1983 */
1984
1985 int /* O - 1 on success, 0 on failure */
1986 _ppdCacheWriteFile(
1987 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1988 const char *filename, /* I - File to write */
1989 ipp_t *attrs) /* I - Attributes to write, if any */
1990 {
1991 int i, j, k; /* Looping vars */
1992 cups_file_t *fp; /* Output file */
1993 _pwg_size_t *size; /* Current size */
1994 _pwg_map_t *map; /* Current map */
1995 cups_option_t *option; /* Current option */
1996 const char *value; /* Filter/pre-filter value */
1997 char newfile[1024]; /* New filename */
1998
1999
2000 /*
2001 * Range check input...
2002 */
2003
2004 if (!pc || !filename)
2005 {
2006 _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
2007 return (0);
2008 }
2009
2010 /*
2011 * Open the file and write with compression...
2012 */
2013
2014 snprintf(newfile, sizeof(newfile), "%s.N", filename);
2015 if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
2016 {
2017 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
2018 return (0);
2019 }
2020
2021 /*
2022 * Standard header...
2023 */
2024
2025 cupsFilePrintf(fp, "#CUPS-PPD-CACHE-%d\n", _PPD_CACHE_VERSION);
2026
2027 /*
2028 * Output bins...
2029 */
2030
2031 if (pc->num_bins > 0)
2032 {
2033 cupsFilePrintf(fp, "NumBins %d\n", pc->num_bins);
2034 for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
2035 cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd);
2036 }
2037
2038 /*
2039 * Media sizes...
2040 */
2041
2042 cupsFilePrintf(fp, "NumSizes %d\n", pc->num_sizes);
2043 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2044 cupsFilePrintf(fp, "Size %s %s %d %d %d %d %d %d\n", size->map.pwg,
2045 size->map.ppd, size->width, size->length, size->left,
2046 size->bottom, size->right, size->top);
2047 if (pc->custom_max_width > 0)
2048 cupsFilePrintf(fp, "CustomSize %d %d %d %d %d %d %d %d\n",
2049 pc->custom_max_width, pc->custom_max_length,
2050 pc->custom_min_width, pc->custom_min_length,
2051 pc->custom_size.left, pc->custom_size.bottom,
2052 pc->custom_size.right, pc->custom_size.top);
2053
2054 /*
2055 * Media sources...
2056 */
2057
2058 if (pc->source_option)
2059 cupsFilePrintf(fp, "SourceOption %s\n", pc->source_option);
2060
2061 if (pc->num_sources > 0)
2062 {
2063 cupsFilePrintf(fp, "NumSources %d\n", pc->num_sources);
2064 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
2065 cupsFilePrintf(fp, "Source %s %s\n", map->pwg, map->ppd);
2066 }
2067
2068 /*
2069 * Media types...
2070 */
2071
2072 if (pc->num_types > 0)
2073 {
2074 cupsFilePrintf(fp, "NumTypes %d\n", pc->num_types);
2075 for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
2076 cupsFilePrintf(fp, "Type %s %s\n", map->pwg, map->ppd);
2077 }
2078
2079 /*
2080 * Presets...
2081 */
2082
2083 for (i = _PWG_PRINT_COLOR_MODE_MONOCHROME; i < _PWG_PRINT_COLOR_MODE_MAX; i ++)
2084 for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
2085 if (pc->num_presets[i][j])
2086 {
2087 cupsFilePrintf(fp, "Preset %d %d", i, j);
2088 for (k = pc->num_presets[i][j], option = pc->presets[i][j];
2089 k > 0;
2090 k --, option ++)
2091 cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2092 cupsFilePutChar(fp, '\n');
2093 }
2094
2095 /*
2096 * Duplex/sides...
2097 */
2098
2099 if (pc->sides_option)
2100 cupsFilePrintf(fp, "SidesOption %s\n", pc->sides_option);
2101
2102 if (pc->sides_1sided)
2103 cupsFilePrintf(fp, "Sides1Sided %s\n", pc->sides_1sided);
2104
2105 if (pc->sides_2sided_long)
2106 cupsFilePrintf(fp, "Sides2SidedLong %s\n", pc->sides_2sided_long);
2107
2108 if (pc->sides_2sided_short)
2109 cupsFilePrintf(fp, "Sides2SidedShort %s\n", pc->sides_2sided_short);
2110
2111 /*
2112 * Product, cupsFilter, cupsFilter2, and cupsPreFilter...
2113 */
2114
2115 if (pc->product)
2116 cupsFilePutConf(fp, "Product", pc->product);
2117
2118 for (value = (const char *)cupsArrayFirst(pc->filters);
2119 value;
2120 value = (const char *)cupsArrayNext(pc->filters))
2121 cupsFilePutConf(fp, "Filter", value);
2122
2123 for (value = (const char *)cupsArrayFirst(pc->prefilters);
2124 value;
2125 value = (const char *)cupsArrayNext(pc->prefilters))
2126 cupsFilePutConf(fp, "PreFilter", value);
2127
2128 cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
2129
2130 /*
2131 * IPP attributes, if any...
2132 */
2133
2134 if (attrs)
2135 {
2136 cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
2137
2138 attrs->state = IPP_IDLE;
2139 ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
2140 }
2141
2142 /*
2143 * Close and return...
2144 */
2145
2146 if (cupsFileClose(fp))
2147 {
2148 unlink(newfile);
2149 return (0);
2150 }
2151
2152 unlink(filename);
2153 return (!rename(newfile, filename));
2154 }
2155
2156
2157 /*
2158 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
2159 * media-source.
2160 */
2161
2162 const char * /* O - InputSlot name */
2163 _pwgInputSlotForSource(
2164 const char *media_source, /* I - PWG media-source */
2165 char *name, /* I - Name buffer */
2166 size_t namesize) /* I - Size of name buffer */
2167 {
2168 /*
2169 * Range check input...
2170 */
2171
2172 if (!media_source || !name || namesize < PPD_MAX_NAME)
2173 return (NULL);
2174
2175 if (_cups_strcasecmp(media_source, "main"))
2176 strlcpy(name, "Cassette", namesize);
2177 else if (_cups_strcasecmp(media_source, "alternate"))
2178 strlcpy(name, "Multipurpose", namesize);
2179 else if (_cups_strcasecmp(media_source, "large-capacity"))
2180 strlcpy(name, "LargeCapacity", namesize);
2181 else if (_cups_strcasecmp(media_source, "bottom"))
2182 strlcpy(name, "Lower", namesize);
2183 else if (_cups_strcasecmp(media_source, "middle"))
2184 strlcpy(name, "Middle", namesize);
2185 else if (_cups_strcasecmp(media_source, "top"))
2186 strlcpy(name, "Upper", namesize);
2187 else if (_cups_strcasecmp(media_source, "rear"))
2188 strlcpy(name, "Rear", namesize);
2189 else if (_cups_strcasecmp(media_source, "side"))
2190 strlcpy(name, "Side", namesize);
2191 else if (_cups_strcasecmp(media_source, "envelope"))
2192 strlcpy(name, "Envelope", namesize);
2193 else if (_cups_strcasecmp(media_source, "main-roll"))
2194 strlcpy(name, "Roll", namesize);
2195 else if (_cups_strcasecmp(media_source, "alternate-roll"))
2196 strlcpy(name, "Roll2", namesize);
2197 else
2198 pwg_ppdize_name(media_source, name, namesize);
2199
2200 return (name);
2201 }
2202
2203
2204 /*
2205 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
2206 * media-type.
2207 */
2208
2209 const char * /* O - MediaType name */
2210 _pwgMediaTypeForType(
2211 const char *media_type, /* I - PWG media-type */
2212 char *name, /* I - Name buffer */
2213 size_t namesize) /* I - Size of name buffer */
2214 {
2215 /*
2216 * Range check input...
2217 */
2218
2219 if (!media_type || !name || namesize < PPD_MAX_NAME)
2220 return (NULL);
2221
2222 if (_cups_strcasecmp(media_type, "auto"))
2223 strlcpy(name, "Auto", namesize);
2224 else if (_cups_strcasecmp(media_type, "cardstock"))
2225 strlcpy(name, "Cardstock", namesize);
2226 else if (_cups_strcasecmp(media_type, "envelope"))
2227 strlcpy(name, "Envelope", namesize);
2228 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
2229 strlcpy(name, "Glossy", namesize);
2230 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
2231 strlcpy(name, "HighGloss", namesize);
2232 else if (_cups_strcasecmp(media_type, "photographic-matte"))
2233 strlcpy(name, "Matte", namesize);
2234 else if (_cups_strcasecmp(media_type, "stationery"))
2235 strlcpy(name, "Plain", namesize);
2236 else if (_cups_strcasecmp(media_type, "stationery-coated"))
2237 strlcpy(name, "Coated", namesize);
2238 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
2239 strlcpy(name, "Inkjet", namesize);
2240 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
2241 strlcpy(name, "Letterhead", namesize);
2242 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
2243 strlcpy(name, "Preprinted", namesize);
2244 else if (_cups_strcasecmp(media_type, "transparency"))
2245 strlcpy(name, "Transparency", namesize);
2246 else
2247 pwg_ppdize_name(media_type, name, namesize);
2248
2249 return (name);
2250 }
2251
2252
2253 /*
2254 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
2255 */
2256
2257 const char * /* O - PageSize name */
2258 _pwgPageSizeForMedia(
2259 _pwg_media_t *media, /* I - Media */
2260 char *name, /* I - PageSize name buffer */
2261 size_t namesize) /* I - Size of name buffer */
2262 {
2263 const char *sizeptr, /* Pointer to size in PWG name */
2264 *dimptr; /* Pointer to dimensions in PWG name */
2265
2266
2267 /*
2268 * Range check input...
2269 */
2270
2271 if (!media || !name || namesize < PPD_MAX_NAME)
2272 return (NULL);
2273
2274 /*
2275 * Copy or generate a PageSize name...
2276 */
2277
2278 if (media->ppd)
2279 {
2280 /*
2281 * Use a standard Adobe name...
2282 */
2283
2284 strlcpy(name, media->ppd, namesize);
2285 }
2286 else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
2287 (sizeptr = strchr(media->pwg, '_')) == NULL ||
2288 (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
2289 (size_t)(dimptr - sizeptr) > namesize)
2290 {
2291 /*
2292 * Use a name of the form "wNNNhNNN"...
2293 */
2294
2295 snprintf(name, namesize, "w%dh%d", (int)_PWG_TOPTS(media->width),
2296 (int)_PWG_TOPTS(media->length));
2297 }
2298 else
2299 {
2300 /*
2301 * Copy the size name from class_sizename_dimensions...
2302 */
2303
2304 memcpy(name, sizeptr + 1, dimptr - sizeptr - 1);
2305 name[dimptr - sizeptr - 1] = '\0';
2306 }
2307
2308 return (name);
2309 }
2310
2311
2312 /*
2313 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
2314 */
2315
2316 static void
2317 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
2318 char *name, /* I - Name buffer */
2319 size_t namesize) /* I - Size of name buffer */
2320 {
2321 char *ptr, /* Pointer into name buffer */
2322 *end; /* End of name buffer */
2323
2324
2325 *name = toupper(*ipp++);
2326
2327 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
2328 {
2329 if (*ipp == '-' && _cups_isalpha(ipp[1]))
2330 {
2331 ipp ++;
2332 *ptr++ = toupper(*ipp++ & 255);
2333 }
2334 else
2335 *ptr++ = *ipp++;
2336 }
2337
2338 *ptr = '\0';
2339 }
2340
2341
2342 /*
2343 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
2344 */
2345
2346 static void
2347 pwg_unppdize_name(const char *ppd, /* I - PPD keyword */
2348 char *name, /* I - Name buffer */
2349 size_t namesize) /* I - Size of name buffer */
2350 {
2351 char *ptr, /* Pointer into name buffer */
2352 *end; /* End of name buffer */
2353
2354
2355 for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
2356 {
2357 if (_cups_isalnum(*ppd) || *ppd == '-')
2358 *ptr++ = tolower(*ppd & 255);
2359 else if (*ppd == '_' || *ppd == '.')
2360 *ptr++ = '-';
2361
2362 if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
2363 _cups_isupper(ppd[1]) && ptr < end)
2364 *ptr++ = '-';
2365 }
2366
2367 *ptr = '\0';
2368 }
2369
2370
2371 /*
2372 * End of "$Id$".
2373 */