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