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