1/*
2 * "$Id: ppd-cache.c 4276 2013-04-09 20:18:49Z msweet $"
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
68static int	pwg_compare_finishings(_pwg_finishings_t *a,
69		                       _pwg_finishings_t *b);
70static void	pwg_free_finishings(_pwg_finishings_t *f);
71static void	pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
72static 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
1455void
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
1548const 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
1581int					/* 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
1649int					/* 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  if (!pc || !pc->finishings || num_options < 1 || max_values < 1 || !values)
1669    return (0);
1670
1671 /*
1672  * Go through the finishings options and see what is set...
1673  */
1674
1675  for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
1676       f;
1677       f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
1678  {
1679    for (i = f->num_options, option = f->options; i > 0; i --, option ++)
1680      if ((val = cupsGetOption(option->name, num_options, options)) == NULL ||
1681          _cups_strcasecmp(option->value, val))
1682        break;
1683
1684    if (i == 0)
1685    {
1686      values[num_values ++] = f->value;
1687
1688      if (num_values >= max_values)
1689        break;
1690    }
1691  }
1692
1693  return (num_values);
1694}
1695
1696
1697/*
1698 * '_ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job
1699 *                        attributes or a keyword string.
1700 */
1701
1702const char *				/* O - PPD InputSlot or NULL */
1703_ppdCacheGetInputSlot(
1704    _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
1705    ipp_t        *job,			/* I - Job attributes or NULL */
1706    const char   *keyword)		/* I - Keyword string or NULL */
1707{
1708 /*
1709  * Range check input...
1710  */
1711
1712  if (!pc || pc->num_sources == 0 || (!job && !keyword))
1713    return (NULL);
1714
1715  if (job && !keyword)
1716  {
1717   /*
1718    * Lookup the media-col attribute and any media-source found there...
1719    */
1720
1721    ipp_attribute_t	*media_col,	/* media-col attribute */
1722			*media_source;	/* media-source attribute */
1723    pwg_size_t		size;		/* Dimensional size */
1724    int			margins_set;	/* Were the margins set? */
1725
1726    media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
1727    if (media_col &&
1728        (media_source = ippFindAttribute(ippGetCollection(media_col, 0),
1729                                         "media-source",
1730	                                 IPP_TAG_KEYWORD)) != NULL)
1731    {
1732     /*
1733      * Use the media-source value from media-col...
1734      */
1735
1736      keyword = ippGetString(media_source, 0, NULL);
1737    }
1738    else if (pwgInitSize(&size, job, &margins_set))
1739    {
1740     /*
1741      * For media <= 5x7, look for a photo tray...
1742      */
1743
1744      if (size.width <= (5 * 2540) && size.length <= (7 * 2540))
1745        keyword = "photo";
1746    }
1747  }
1748
1749  if (keyword)
1750  {
1751    int	i;				/* Looping var */
1752
1753    for (i = 0; i < pc->num_sources; i ++)
1754      if (!_cups_strcasecmp(keyword, pc->sources[i].pwg))
1755        return (pc->sources[i].ppd);
1756  }
1757
1758  return (NULL);
1759}
1760
1761
1762/*
1763 * '_ppdCacheGetMediaType()' - Get the PPD MediaType associated with the job
1764 *                        attributes or a keyword string.
1765 */
1766
1767const char *				/* O - PPD MediaType or NULL */
1768_ppdCacheGetMediaType(
1769    _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
1770    ipp_t        *job,			/* I - Job attributes or NULL */
1771    const char   *keyword)		/* I - Keyword string or NULL */
1772{
1773 /*
1774  * Range check input...
1775  */
1776
1777  if (!pc || pc->num_types == 0 || (!job && !keyword))
1778    return (NULL);
1779
1780  if (job && !keyword)
1781  {
1782   /*
1783    * Lookup the media-col attribute and any media-source found there...
1784    */
1785
1786    ipp_attribute_t	*media_col,	/* media-col attribute */
1787			*media_type;	/* media-type attribute */
1788
1789    media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
1790    if (media_col)
1791    {
1792      if ((media_type = ippFindAttribute(media_col->values[0].collection,
1793                                         "media-type",
1794	                                 IPP_TAG_KEYWORD)) == NULL)
1795	media_type = ippFindAttribute(media_col->values[0].collection,
1796				      "media-type", IPP_TAG_NAME);
1797
1798      if (media_type)
1799	keyword = media_type->values[0].string.text;
1800    }
1801  }
1802
1803  if (keyword)
1804  {
1805    int	i;				/* Looping var */
1806
1807    for (i = 0; i < pc->num_types; i ++)
1808      if (!_cups_strcasecmp(keyword, pc->types[i].pwg))
1809        return (pc->types[i].ppd);
1810  }
1811
1812  return (NULL);
1813}
1814
1815
1816/*
1817 * '_ppdCacheGetOutputBin()' - Get the PPD OutputBin associated with the keyword
1818 *                        string.
1819 */
1820
1821const char *				/* O - PPD OutputBin or NULL */
1822_ppdCacheGetOutputBin(
1823    _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
1824    const char   *output_bin)		/* I - Keyword string */
1825{
1826  int	i;				/* Looping var */
1827
1828
1829 /*
1830  * Range check input...
1831  */
1832
1833  if (!pc || !output_bin)
1834    return (NULL);
1835
1836 /*
1837  * Look up the OutputBin string...
1838  */
1839
1840
1841  for (i = 0; i < pc->num_bins; i ++)
1842    if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg))
1843      return (pc->bins[i].ppd);
1844
1845  return (NULL);
1846}
1847
1848
1849/*
1850 * '_ppdCacheGetPageSize()' - Get the PPD PageSize associated with the job
1851 *                       attributes or a keyword string.
1852 */
1853
1854const char *				/* O - PPD PageSize or NULL */
1855_ppdCacheGetPageSize(
1856    _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
1857    ipp_t        *job,			/* I - Job attributes or NULL */
1858    const char   *keyword,		/* I - Keyword string or NULL */
1859    int          *exact)		/* O - 1 if exact match, 0 otherwise */
1860{
1861  int		i;			/* Looping var */
1862  pwg_size_t	*size,			/* Current size */
1863		*closest,		/* Closest size */
1864		jobsize;		/* Size data from job */
1865  int		margins_set,		/* Were the margins set? */
1866		dwidth,			/* Difference in width */
1867		dlength,		/* Difference in length */
1868		dleft,			/* Difference in left margins */
1869		dright,			/* Difference in right margins */
1870		dbottom,		/* Difference in bottom margins */
1871		dtop,			/* Difference in top margins */
1872		dmin,			/* Minimum difference */
1873		dclosest;		/* Closest difference */
1874  const char	*ppd_name;		/* PPD media name */
1875
1876
1877  DEBUG_printf(("_ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)",
1878	        pc, job, keyword, exact));
1879
1880 /*
1881  * Range check input...
1882  */
1883
1884  if (!pc || (!job && !keyword))
1885    return (NULL);
1886
1887  if (exact)
1888    *exact = 0;
1889
1890  ppd_name = keyword;
1891
1892  if (job)
1893  {
1894   /*
1895    * Try getting the PPD media name from the job attributes...
1896    */
1897
1898    ipp_attribute_t	*attr;		/* Job attribute */
1899
1900    if ((attr = ippFindAttribute(job, "PageSize", IPP_TAG_ZERO)) == NULL)
1901      if ((attr = ippFindAttribute(job, "PageRegion", IPP_TAG_ZERO)) == NULL)
1902        attr = ippFindAttribute(job, "media", IPP_TAG_ZERO);
1903
1904#ifdef DEBUG
1905    if (attr)
1906      DEBUG_printf(("1_ppdCacheGetPageSize: Found attribute %s (%s)",
1907                    attr->name, ippTagString(attr->value_tag)));
1908    else
1909      DEBUG_puts("1_ppdCacheGetPageSize: Did not find media attribute.");
1910#endif /* DEBUG */
1911
1912    if (attr && (attr->value_tag == IPP_TAG_NAME ||
1913                 attr->value_tag == IPP_TAG_KEYWORD))
1914      ppd_name = attr->values[0].string.text;
1915  }
1916
1917  DEBUG_printf(("1_ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name));
1918
1919  if (ppd_name)
1920  {
1921   /*
1922    * Try looking up the named PPD size first...
1923    */
1924
1925    for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1926    {
1927      DEBUG_printf(("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]",
1928                    (int)(size - pc->sizes), size->map.pwg, size->map.ppd));
1929
1930      if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
1931          !_cups_strcasecmp(ppd_name, size->map.pwg))
1932      {
1933	if (exact)
1934	  *exact = 1;
1935
1936        DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", ppd_name));
1937
1938        return (size->map.ppd);
1939      }
1940    }
1941  }
1942
1943  if (job && !keyword)
1944  {
1945   /*
1946    * Get the size using media-col or media, with the preference being
1947    * media-col.
1948    */
1949
1950    if (!pwgInitSize(&jobsize, job, &margins_set))
1951      return (NULL);
1952  }
1953  else
1954  {
1955   /*
1956    * Get the size using a media keyword...
1957    */
1958
1959    pwg_media_t	*media;		/* Media definition */
1960
1961
1962    if ((media = pwgMediaForPWG(keyword)) == NULL)
1963      if ((media = pwgMediaForLegacy(keyword)) == NULL)
1964        if ((media = pwgMediaForPPD(keyword)) == NULL)
1965	  return (NULL);
1966
1967    jobsize.width  = media->width;
1968    jobsize.length = media->length;
1969    margins_set    = 0;
1970  }
1971
1972 /*
1973  * Now that we have the dimensions and possibly the margins, look at the
1974  * available sizes and find the match...
1975  */
1976
1977  closest  = NULL;
1978  dclosest = 999999999;
1979
1980  if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
1981      _cups_strncasecmp(ppd_name, "custom_", 7))
1982  {
1983    for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1984    {
1985     /*
1986      * Adobe uses a size matching algorithm with an epsilon of 5 points, which
1987      * is just about 176/2540ths...
1988      */
1989
1990      dwidth  = size->width - jobsize.width;
1991      dlength = size->length - jobsize.length;
1992
1993      if (dwidth <= -176 || dwidth >= 176 || dlength <= -176 || dlength >= 176)
1994	continue;
1995
1996      if (margins_set)
1997      {
1998       /*
1999	* Use a tighter epsilon of 1 point (35/2540ths) for margins...
2000	*/
2001
2002	dleft   = size->left - jobsize.left;
2003	dright  = size->right - jobsize.right;
2004	dtop    = size->top - jobsize.top;
2005	dbottom = size->bottom - jobsize.bottom;
2006
2007	if (dleft <= -35 || dleft >= 35 || dright <= -35 || dright >= 35 ||
2008	    dtop <= -35 || dtop >= 35 || dbottom <= -35 || dbottom >= 35)
2009	{
2010	  dleft   = dleft < 0 ? -dleft : dleft;
2011	  dright  = dright < 0 ? -dright : dright;
2012	  dbottom = dbottom < 0 ? -dbottom : dbottom;
2013	  dtop    = dtop < 0 ? -dtop : dtop;
2014	  dmin    = dleft + dright + dbottom + dtop;
2015
2016	  if (dmin < dclosest)
2017	  {
2018	    dclosest = dmin;
2019	    closest  = size;
2020	  }
2021
2022	  continue;
2023	}
2024      }
2025
2026      if (exact)
2027	*exact = 1;
2028
2029      DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd));
2030
2031      return (size->map.ppd);
2032    }
2033  }
2034
2035  if (closest)
2036  {
2037    DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (closest)",
2038                  closest->map.ppd));
2039
2040    return (closest->map.ppd);
2041  }
2042
2043 /*
2044  * If we get here we need to check for custom page size support...
2045  */
2046
2047  if (jobsize.width >= pc->custom_min_width &&
2048      jobsize.width <= pc->custom_max_width &&
2049      jobsize.length >= pc->custom_min_length &&
2050      jobsize.length <= pc->custom_max_length)
2051  {
2052   /*
2053    * In range, format as Custom.WWWWxLLLL (points).
2054    */
2055
2056    snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d",
2057             (int)PWG_TO_POINTS(jobsize.width), (int)PWG_TO_POINTS(jobsize.length));
2058
2059    if (margins_set && exact)
2060    {
2061      dleft   = pc->custom_size.left - jobsize.left;
2062      dright  = pc->custom_size.right - jobsize.right;
2063      dtop    = pc->custom_size.top - jobsize.top;
2064      dbottom = pc->custom_size.bottom - jobsize.bottom;
2065
2066      if (dleft > -35 && dleft < 35 && dright > -35 && dright < 35 &&
2067          dtop > -35 && dtop < 35 && dbottom > -35 && dbottom < 35)
2068	*exact = 1;
2069    }
2070    else if (exact)
2071      *exact = 1;
2072
2073    DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (custom)",
2074                  pc->custom_ppd_size));
2075
2076    return (pc->custom_ppd_size);
2077  }
2078
2079 /*
2080  * No custom page size support or the size is out of range - return NULL.
2081  */
2082
2083  DEBUG_puts("1_ppdCacheGetPageSize: Returning NULL");
2084
2085  return (NULL);
2086}
2087
2088
2089/*
2090 * '_ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize.
2091 */
2092
2093pwg_size_t *				/* O - PWG size or NULL */
2094_ppdCacheGetSize(
2095    _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2096    const char   *page_size)		/* I - PPD PageSize */
2097{
2098  int		i;			/* Looping var */
2099  pwg_media_t	*media;			/* Media */
2100  pwg_size_t	*size;			/* Current size */
2101
2102
2103 /*
2104  * Range check input...
2105  */
2106
2107  if (!pc || !page_size)
2108    return (NULL);
2109
2110  if (!_cups_strncasecmp(page_size, "Custom.", 7))
2111  {
2112   /*
2113    * Custom size; size name can be one of the following:
2114    *
2115    *    Custom.WIDTHxLENGTHin    - Size in inches
2116    *    Custom.WIDTHxLENGTHft    - Size in feet
2117    *    Custom.WIDTHxLENGTHcm    - Size in centimeters
2118    *    Custom.WIDTHxLENGTHmm    - Size in millimeters
2119    *    Custom.WIDTHxLENGTHm     - Size in meters
2120    *    Custom.WIDTHxLENGTH[pt]  - Size in points
2121    */
2122
2123    double		w, l;		/* Width and length of page */
2124    char		*ptr;		/* Pointer into PageSize */
2125    struct lconv	*loc;		/* Locale data */
2126
2127    loc = localeconv();
2128    w   = (float)_cupsStrScand(page_size + 7, &ptr, loc);
2129    if (!ptr || *ptr != 'x')
2130      return (NULL);
2131
2132    l = (float)_cupsStrScand(ptr + 1, &ptr, loc);
2133    if (!ptr)
2134      return (NULL);
2135
2136    if (!_cups_strcasecmp(ptr, "in"))
2137    {
2138      w *= 2540.0;
2139      l *= 2540.0;
2140    }
2141    else if (!_cups_strcasecmp(ptr, "ft"))
2142    {
2143      w *= 12.0 * 2540.0;
2144      l *= 12.0 * 2540.0;
2145    }
2146    else if (!_cups_strcasecmp(ptr, "mm"))
2147    {
2148      w *= 100.0;
2149      l *= 100.0;
2150    }
2151    else if (!_cups_strcasecmp(ptr, "cm"))
2152    {
2153      w *= 1000.0;
2154      l *= 1000.0;
2155    }
2156    else if (!_cups_strcasecmp(ptr, "m"))
2157    {
2158      w *= 100000.0;
2159      l *= 100000.0;
2160    }
2161    else
2162    {
2163      w *= 2540.0 / 72.0;
2164      l *= 2540.0 / 72.0;
2165    }
2166
2167    pc->custom_size.width  = (int)w;
2168    pc->custom_size.length = (int)l;
2169
2170    return (&(pc->custom_size));
2171  }
2172
2173 /*
2174  * Not a custom size - look it up...
2175  */
2176
2177  for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2178    if (!_cups_strcasecmp(page_size, size->map.ppd) ||
2179        !_cups_strcasecmp(page_size, size->map.pwg))
2180      return (size);
2181
2182 /*
2183  * Look up standard sizes...
2184  */
2185
2186  if ((media = pwgMediaForPPD(page_size)) == NULL)
2187    if ((media = pwgMediaForLegacy(page_size)) == NULL)
2188      media = pwgMediaForPWG(page_size);
2189
2190  if (media)
2191  {
2192    pc->custom_size.width  = media->width;
2193    pc->custom_size.length = media->length;
2194
2195    return (&(pc->custom_size));
2196  }
2197
2198  return (NULL);
2199}
2200
2201
2202/*
2203 * '_ppdCacheGetSource()' - Get the PWG media-source associated with a PPD
2204 *                          InputSlot.
2205 */
2206
2207const char *				/* O - PWG media-source keyword */
2208_ppdCacheGetSource(
2209    _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2210    const char   *input_slot)		/* I - PPD InputSlot */
2211{
2212  int		i;			/* Looping var */
2213  pwg_map_t	*source;		/* Current source */
2214
2215
2216 /*
2217  * Range check input...
2218  */
2219
2220  if (!pc || !input_slot)
2221    return (NULL);
2222
2223  for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++)
2224    if (!_cups_strcasecmp(input_slot, source->ppd))
2225      return (source->pwg);
2226
2227  return (NULL);
2228}
2229
2230
2231/*
2232 * '_ppdCacheGetType()' - Get the PWG media-type associated with a PPD
2233 *                        MediaType.
2234 */
2235
2236const char *				/* O - PWG media-type keyword */
2237_ppdCacheGetType(
2238    _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2239    const char   *media_type)		/* I - PPD MediaType */
2240{
2241  int		i;			/* Looping var */
2242  pwg_map_t	*type;			/* Current type */
2243
2244
2245 /*
2246  * Range check input...
2247  */
2248
2249  if (!pc || !media_type)
2250    return (NULL);
2251
2252  for (i = pc->num_types, type = pc->types; i > 0; i --, type ++)
2253    if (!_cups_strcasecmp(media_type, type->ppd))
2254      return (type->pwg);
2255
2256  return (NULL);
2257}
2258
2259
2260/*
2261 * '_ppdCacheWriteFile()' - Write PWG mapping data to a file.
2262 */
2263
2264int					/* O - 1 on success, 0 on failure */
2265_ppdCacheWriteFile(
2266    _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2267    const char   *filename,		/* I - File to write */
2268    ipp_t        *attrs)		/* I - Attributes to write, if any */
2269{
2270  int			i, j, k;	/* Looping vars */
2271  cups_file_t		*fp;		/* Output file */
2272  pwg_size_t		*size;		/* Current size */
2273  pwg_map_t		*map;		/* Current map */
2274  _pwg_finishings_t	*f;		/* Current finishing option */
2275  cups_option_t		*option;	/* Current option */
2276  const char		*value;		/* Filter/pre-filter value */
2277  char			newfile[1024];	/* New filename */
2278
2279
2280 /*
2281  * Range check input...
2282  */
2283
2284  if (!pc || !filename)
2285  {
2286    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
2287    return (0);
2288  }
2289
2290 /*
2291  * Open the file and write with compression...
2292  */
2293
2294  snprintf(newfile, sizeof(newfile), "%s.N", filename);
2295  if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
2296  {
2297    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
2298    return (0);
2299  }
2300
2301 /*
2302  * Standard header...
2303  */
2304
2305  cupsFilePrintf(fp, "#CUPS-PPD-CACHE-%d\n", _PPD_CACHE_VERSION);
2306
2307 /*
2308  * Output bins...
2309  */
2310
2311  if (pc->num_bins > 0)
2312  {
2313    cupsFilePrintf(fp, "NumBins %d\n", pc->num_bins);
2314    for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
2315      cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd);
2316  }
2317
2318 /*
2319  * Media sizes...
2320  */
2321
2322  cupsFilePrintf(fp, "NumSizes %d\n", pc->num_sizes);
2323  for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2324    cupsFilePrintf(fp, "Size %s %s %d %d %d %d %d %d\n", size->map.pwg,
2325		   size->map.ppd, size->width, size->length, size->left,
2326		   size->bottom, size->right, size->top);
2327  if (pc->custom_max_width > 0)
2328    cupsFilePrintf(fp, "CustomSize %d %d %d %d %d %d %d %d\n",
2329                   pc->custom_max_width, pc->custom_max_length,
2330		   pc->custom_min_width, pc->custom_min_length,
2331		   pc->custom_size.left, pc->custom_size.bottom,
2332		   pc->custom_size.right, pc->custom_size.top);
2333
2334 /*
2335  * Media sources...
2336  */
2337
2338  if (pc->source_option)
2339    cupsFilePrintf(fp, "SourceOption %s\n", pc->source_option);
2340
2341  if (pc->num_sources > 0)
2342  {
2343    cupsFilePrintf(fp, "NumSources %d\n", pc->num_sources);
2344    for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
2345      cupsFilePrintf(fp, "Source %s %s\n", map->pwg, map->ppd);
2346  }
2347
2348 /*
2349  * Media types...
2350  */
2351
2352  if (pc->num_types > 0)
2353  {
2354    cupsFilePrintf(fp, "NumTypes %d\n", pc->num_types);
2355    for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
2356      cupsFilePrintf(fp, "Type %s %s\n", map->pwg, map->ppd);
2357  }
2358
2359 /*
2360  * Presets...
2361  */
2362
2363  for (i = _PWG_PRINT_COLOR_MODE_MONOCHROME; i < _PWG_PRINT_COLOR_MODE_MAX; i ++)
2364    for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
2365      if (pc->num_presets[i][j])
2366      {
2367	cupsFilePrintf(fp, "Preset %d %d", i, j);
2368	for (k = pc->num_presets[i][j], option = pc->presets[i][j];
2369	     k > 0;
2370	     k --, option ++)
2371	  cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2372	cupsFilePutChar(fp, '\n');
2373      }
2374
2375 /*
2376  * Duplex/sides...
2377  */
2378
2379  if (pc->sides_option)
2380    cupsFilePrintf(fp, "SidesOption %s\n", pc->sides_option);
2381
2382  if (pc->sides_1sided)
2383    cupsFilePrintf(fp, "Sides1Sided %s\n", pc->sides_1sided);
2384
2385  if (pc->sides_2sided_long)
2386    cupsFilePrintf(fp, "Sides2SidedLong %s\n", pc->sides_2sided_long);
2387
2388  if (pc->sides_2sided_short)
2389    cupsFilePrintf(fp, "Sides2SidedShort %s\n", pc->sides_2sided_short);
2390
2391 /*
2392  * Product, cupsFilter, cupsFilter2, and cupsPreFilter...
2393  */
2394
2395  if (pc->product)
2396    cupsFilePutConf(fp, "Product", pc->product);
2397
2398  for (value = (const char *)cupsArrayFirst(pc->filters);
2399       value;
2400       value = (const char *)cupsArrayNext(pc->filters))
2401    cupsFilePutConf(fp, "Filter", value);
2402
2403  for (value = (const char *)cupsArrayFirst(pc->prefilters);
2404       value;
2405       value = (const char *)cupsArrayNext(pc->prefilters))
2406    cupsFilePutConf(fp, "PreFilter", value);
2407
2408  cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
2409
2410 /*
2411  * Finishing options...
2412  */
2413
2414  for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
2415       f;
2416       f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
2417  {
2418    cupsFilePrintf(fp, "Finishings %d", f->value);
2419    for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2420      cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2421    cupsFilePutChar(fp, '\n');
2422  }
2423
2424 /*
2425  * Max copies...
2426  */
2427
2428  cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies);
2429
2430 /*
2431  * Accounting/quota/PIN/managed printing values...
2432  */
2433
2434  if (pc->charge_info_uri)
2435    cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri);
2436
2437  cupsFilePrintf(fp, "AccountId %s\n", pc->account_id ? "true" : "false");
2438  cupsFilePrintf(fp, "AccountingUserId %s\n",
2439                 pc->accounting_user_id ? "true" : "false");
2440
2441  if (pc->password)
2442    cupsFilePutConf(fp, "Password", pc->password);
2443
2444  for (value = (char *)cupsArrayFirst(pc->mandatory);
2445       value;
2446       value = (char *)cupsArrayNext(pc->mandatory))
2447    cupsFilePutConf(fp, "Mandatory", value);
2448
2449 /*
2450  * Support files...
2451  */
2452
2453  for (value = (char *)cupsArrayFirst(pc->support_files);
2454       value;
2455       value = (char *)cupsArrayNext(pc->support_files))
2456    cupsFilePutConf(fp, "SupportFile", value);
2457
2458 /*
2459  * IPP attributes, if any...
2460  */
2461
2462  if (attrs)
2463  {
2464    cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
2465
2466    attrs->state = IPP_STATE_IDLE;
2467    ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
2468  }
2469
2470 /*
2471  * Close and return...
2472  */
2473
2474  if (cupsFileClose(fp))
2475  {
2476    unlink(newfile);
2477    return (0);
2478  }
2479
2480  unlink(filename);
2481  return (!rename(newfile, filename));
2482}
2483
2484
2485/*
2486 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
2487 *                              media-source.
2488 */
2489
2490const char *				/* O - InputSlot name */
2491_pwgInputSlotForSource(
2492    const char *media_source,		/* I - PWG media-source */
2493    char       *name,			/* I - Name buffer */
2494    size_t     namesize)		/* I - Size of name buffer */
2495{
2496 /*
2497  * Range check input...
2498  */
2499
2500  if (!media_source || !name || namesize < PPD_MAX_NAME)
2501    return (NULL);
2502
2503  if (_cups_strcasecmp(media_source, "main"))
2504    strlcpy(name, "Cassette", namesize);
2505  else if (_cups_strcasecmp(media_source, "alternate"))
2506    strlcpy(name, "Multipurpose", namesize);
2507  else if (_cups_strcasecmp(media_source, "large-capacity"))
2508    strlcpy(name, "LargeCapacity", namesize);
2509  else if (_cups_strcasecmp(media_source, "bottom"))
2510    strlcpy(name, "Lower", namesize);
2511  else if (_cups_strcasecmp(media_source, "middle"))
2512    strlcpy(name, "Middle", namesize);
2513  else if (_cups_strcasecmp(media_source, "top"))
2514    strlcpy(name, "Upper", namesize);
2515  else if (_cups_strcasecmp(media_source, "rear"))
2516    strlcpy(name, "Rear", namesize);
2517  else if (_cups_strcasecmp(media_source, "side"))
2518    strlcpy(name, "Side", namesize);
2519  else if (_cups_strcasecmp(media_source, "envelope"))
2520    strlcpy(name, "Envelope", namesize);
2521  else if (_cups_strcasecmp(media_source, "main-roll"))
2522    strlcpy(name, "Roll", namesize);
2523  else if (_cups_strcasecmp(media_source, "alternate-roll"))
2524    strlcpy(name, "Roll2", namesize);
2525  else
2526    pwg_ppdize_name(media_source, name, namesize);
2527
2528  return (name);
2529}
2530
2531
2532/*
2533 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
2534 *                            media-type.
2535 */
2536
2537const char *				/* O - MediaType name */
2538_pwgMediaTypeForType(
2539    const char *media_type,		/* I - PWG media-type */
2540    char       *name,			/* I - Name buffer */
2541    size_t     namesize)		/* I - Size of name buffer */
2542{
2543 /*
2544  * Range check input...
2545  */
2546
2547  if (!media_type || !name || namesize < PPD_MAX_NAME)
2548    return (NULL);
2549
2550  if (_cups_strcasecmp(media_type, "auto"))
2551    strlcpy(name, "Auto", namesize);
2552  else if (_cups_strcasecmp(media_type, "cardstock"))
2553    strlcpy(name, "Cardstock", namesize);
2554  else if (_cups_strcasecmp(media_type, "envelope"))
2555    strlcpy(name, "Envelope", namesize);
2556  else if (_cups_strcasecmp(media_type, "photographic-glossy"))
2557    strlcpy(name, "Glossy", namesize);
2558  else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
2559    strlcpy(name, "HighGloss", namesize);
2560  else if (_cups_strcasecmp(media_type, "photographic-matte"))
2561    strlcpy(name, "Matte", namesize);
2562  else if (_cups_strcasecmp(media_type, "stationery"))
2563    strlcpy(name, "Plain", namesize);
2564  else if (_cups_strcasecmp(media_type, "stationery-coated"))
2565    strlcpy(name, "Coated", namesize);
2566  else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
2567    strlcpy(name, "Inkjet", namesize);
2568  else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
2569    strlcpy(name, "Letterhead", namesize);
2570  else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
2571    strlcpy(name, "Preprinted", namesize);
2572  else if (_cups_strcasecmp(media_type, "transparency"))
2573    strlcpy(name, "Transparency", namesize);
2574  else
2575    pwg_ppdize_name(media_type, name, namesize);
2576
2577  return (name);
2578}
2579
2580
2581/*
2582 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
2583 */
2584
2585const char *				/* O - PageSize name */
2586_pwgPageSizeForMedia(
2587    pwg_media_t *media,		/* I - Media */
2588    char         *name,			/* I - PageSize name buffer */
2589    size_t       namesize)		/* I - Size of name buffer */
2590{
2591  const char	*sizeptr,		/* Pointer to size in PWG name */
2592		*dimptr;		/* Pointer to dimensions in PWG name */
2593
2594
2595 /*
2596  * Range check input...
2597  */
2598
2599  if (!media || !name || namesize < PPD_MAX_NAME)
2600    return (NULL);
2601
2602 /*
2603  * Copy or generate a PageSize name...
2604  */
2605
2606  if (media->ppd)
2607  {
2608   /*
2609    * Use a standard Adobe name...
2610    */
2611
2612    strlcpy(name, media->ppd, namesize);
2613  }
2614  else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
2615           (sizeptr = strchr(media->pwg, '_')) == NULL ||
2616	   (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
2617	   (size_t)(dimptr - sizeptr) > namesize)
2618  {
2619   /*
2620    * Use a name of the form "wNNNhNNN"...
2621    */
2622
2623    snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
2624             (int)PWG_TO_POINTS(media->length));
2625  }
2626  else
2627  {
2628   /*
2629    * Copy the size name from class_sizename_dimensions...
2630    */
2631
2632    memcpy(name, sizeptr + 1, dimptr - sizeptr - 1);
2633    name[dimptr - sizeptr - 1] = '\0';
2634  }
2635
2636  return (name);
2637}
2638
2639
2640/*
2641 * 'pwg_compare_finishings()' - Compare two finishings values.
2642 */
2643
2644static int				/* O- Result of comparison */
2645pwg_compare_finishings(
2646    _pwg_finishings_t *a,		/* I - First finishings value */
2647    _pwg_finishings_t *b)		/* I - Second finishings value */
2648{
2649  return (b->value - a->value);
2650}
2651
2652
2653/*
2654 * 'pwg_free_finishings()' - Free a finishings value.
2655 */
2656
2657static void
2658pwg_free_finishings(
2659    _pwg_finishings_t *f)		/* I - Finishings value */
2660{
2661  cupsFreeOptions(f->num_options, f->options);
2662  free(f);
2663}
2664
2665
2666/*
2667 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
2668 */
2669
2670static void
2671pwg_ppdize_name(const char *ipp,	/* I - IPP keyword */
2672                char       *name,	/* I - Name buffer */
2673		size_t     namesize)	/* I - Size of name buffer */
2674{
2675  char	*ptr,				/* Pointer into name buffer */
2676	*end;				/* End of name buffer */
2677
2678
2679  *name = toupper(*ipp++);
2680
2681  for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
2682  {
2683    if (*ipp == '-' && _cups_isalpha(ipp[1]))
2684    {
2685      ipp ++;
2686      *ptr++ = toupper(*ipp++ & 255);
2687    }
2688    else
2689      *ptr++ = *ipp++;
2690  }
2691
2692  *ptr = '\0';
2693}
2694
2695
2696/*
2697 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
2698 */
2699
2700static void
2701pwg_unppdize_name(const char *ppd,	/* I - PPD keyword */
2702		  char       *name,	/* I - Name buffer */
2703                  size_t     namesize,	/* I - Size of name buffer */
2704                  const char *dashchars)/* I - Characters to be replaced by dashes */
2705{
2706  char	*ptr,				/* Pointer into name buffer */
2707	*end;				/* End of name buffer */
2708
2709
2710  for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
2711  {
2712    if (_cups_isalnum(*ppd) || *ppd == '-')
2713      *ptr++ = tolower(*ppd & 255);
2714    else if (strchr(dashchars, *ppd))
2715      *ptr++ = '-';
2716    else
2717      *ptr++ = *ppd;
2718
2719    if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
2720	_cups_isupper(ppd[1]) && ptr < end)
2721      *ptr++ = '-';
2722  }
2723
2724  *ptr = '\0';
2725}
2726
2727
2728/*
2729 * End of "$Id: ppd-cache.c 4276 2013-04-09 20:18:49Z msweet $".
2730 */
2731