1 /*
2  * PPD cache implementation for CUPS.
3  *
4  * Copyright 2010-2017 by Apple Inc.
5  *
6  * These coded instructions, statements, and computer programs are the
7  * property of Apple Inc. and are protected by Federal copyright
8  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
9  * which should have been included with this file.  If this file is
10  * missing or damaged, see the license at "http://www.cups.org/".
11  *
12  * This file is subject to the Apple OS-Developed Software exception.
13  */
14 
15 /*
16  * Include necessary headers...
17  */
18 
19 #include "cups-private.h"
20 #include "ppd-private.h"
21 #include <math.h>
22 
23 
24 /*
25  * Macro to test for two almost-equal PWG measurements.
26  */
27 
28 #define _PWG_EQUIVALENT(x, y)	(abs((x)-(y)) < 2)
29 
30 
31 /*
32  * Local functions...
33  */
34 
35 static void	pwg_add_finishing(cups_array_t *finishings, ipp_finishings_t template, const char *name, const char *value);
36 static int	pwg_compare_finishings(_pwg_finishings_t *a,
37 		                       _pwg_finishings_t *b);
38 static void	pwg_free_finishings(_pwg_finishings_t *f);
39 static void	pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
40 static void	pwg_ppdize_resolution(ipp_attribute_t *attr, int element, int *xres, int *yres, char *name, size_t namesize);
41 static void	pwg_unppdize_name(const char *ppd, char *name, size_t namesize,
42 		                  const char *dashchars);
43 
44 
45 /*
46  * '_cupsConvertOptions()' - Convert printer options to standard IPP attributes.
47  *
48  * This functions converts PPD and CUPS-specific options to their standard IPP
49  * attributes and values and adds them to the specified IPP request.
50  */
51 
52 int					/* O - New number of copies */
_cupsConvertOptions(ipp_t * request,ppd_file_t * ppd,_ppd_cache_t * pc,ipp_attribute_t * media_col_sup,ipp_attribute_t * doc_handling_sup,ipp_attribute_t * print_color_mode_sup,const char * user,const char * format,int copies,int num_options,cups_option_t * options)53 _cupsConvertOptions(
54     ipp_t           *request,		/* I - IPP request */
55     ppd_file_t      *ppd,		/* I - PPD file */
56     _ppd_cache_t    *pc,		/* I - PPD cache info */
57     ipp_attribute_t *media_col_sup,	/* I - media-col-supported values */
58     ipp_attribute_t *doc_handling_sup,	/* I - multiple-document-handling-supported values */
59     ipp_attribute_t *print_color_mode_sup,
60                                 	/* I - Printer supports print-color-mode */
61     const char    *user,		/* I - User info */
62     const char    *format,		/* I - document-format value */
63     int           copies,		/* I - Number of copies */
64     int           num_options,		/* I - Number of options */
65     cups_option_t *options)		/* I - Options */
66 {
67   int		i;			/* Looping var */
68   const char	*keyword,		/* PWG keyword */
69 		*password;		/* Password string */
70   pwg_size_t	*size;			/* PWG media size */
71   ipp_t		*media_col,		/* media-col value */
72 		*media_size;		/* media-size value */
73   const char	*media_source,		/* media-source value */
74 		*media_type,		/* media-type value */
75 		*collate_str,		/* multiple-document-handling value */
76 		*color_attr_name,	/* Supported color attribute */
77 		*mandatory;		/* Mandatory attributes */
78   int		num_finishings = 0,	/* Number of finishing values */
79 		finishings[10];		/* Finishing enum values */
80   ppd_choice_t	*choice;		/* Marked choice */
81 
82 
83  /*
84   * Send standard IPP attributes...
85   */
86 
87   if (pc->password && (password = cupsGetOption("job-password", num_options, options)) != NULL && ippGetOperation(request) != IPP_OP_VALIDATE_JOB)
88   {
89     ipp_attribute_t	*attr = NULL;	/* job-password attribute */
90 
91     if ((keyword = cupsGetOption("job-password-encryption", num_options, options)) == NULL)
92       keyword = "none";
93 
94     if (!strcmp(keyword, "none"))
95     {
96      /*
97       * Add plain-text job-password...
98       */
99 
100       attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", password, (int)strlen(password));
101     }
102     else
103     {
104      /*
105       * Add hashed job-password...
106       */
107 
108       unsigned char	hash[64];	/* Hash of password */
109       ssize_t		hashlen;	/* Length of hash */
110 
111       if ((hashlen = cupsHashData(keyword, password, strlen(password), hash, sizeof(hash))) > 0)
112         attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", hash, (int)hashlen);
113     }
114 
115     if (attr)
116       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "job-password-encryption", NULL, keyword);
117   }
118 
119   if (pc->account_id)
120   {
121     if ((keyword = cupsGetOption("job-account-id", num_options, options)) == NULL)
122       keyword = cupsGetOption("job-billing", num_options, options);
123 
124     if (keyword)
125       ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id", NULL, keyword);
126   }
127 
128   if (pc->accounting_user_id)
129   {
130     if ((keyword = cupsGetOption("job-accounting-user-id", num_options, options)) == NULL)
131       keyword = user;
132 
133     if (keyword)
134       ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-accounting-user-id", NULL, keyword);
135   }
136 
137   for (mandatory = (const char *)cupsArrayFirst(pc->mandatory); mandatory; mandatory = (const char *)cupsArrayNext(pc->mandatory))
138   {
139     if (strcmp(mandatory, "copies") &&
140 	strcmp(mandatory, "destination-uris") &&
141 	strcmp(mandatory, "finishings") &&
142 	strcmp(mandatory, "job-account-id") &&
143 	strcmp(mandatory, "job-accounting-user-id") &&
144 	strcmp(mandatory, "job-password") &&
145 	strcmp(mandatory, "job-password-encryption") &&
146 	strcmp(mandatory, "media") &&
147 	strncmp(mandatory, "media-col", 9) &&
148 	strcmp(mandatory, "multiple-document-handling") &&
149 	strcmp(mandatory, "output-bin") &&
150 	strcmp(mandatory, "print-color-mode") &&
151 	strcmp(mandatory, "print-quality") &&
152 	strcmp(mandatory, "sides") &&
153 	(keyword = cupsGetOption(mandatory, num_options, options)) != NULL)
154     {
155       _ipp_option_t *opt = _ippFindOption(mandatory);
156 				    /* Option type */
157       ipp_tag_t	value_tag = opt ? opt->value_tag : IPP_TAG_NAME;
158 				    /* Value type */
159 
160       switch (value_tag)
161       {
162 	case IPP_TAG_INTEGER :
163 	case IPP_TAG_ENUM :
164 	    ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory, atoi(keyword));
165 	    break;
166 	case IPP_TAG_BOOLEAN :
167 	    ippAddBoolean(request, IPP_TAG_JOB, mandatory, !_cups_strcasecmp(keyword, "true"));
168 	    break;
169 	case IPP_TAG_RANGE :
170 	    {
171 	      int lower, upper;	/* Range */
172 
173 	      if (sscanf(keyword, "%d-%d", &lower, &upper) != 2)
174 		lower = upper = atoi(keyword);
175 
176 	      ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper);
177 	    }
178 	    break;
179 	case IPP_TAG_STRING :
180 	    ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword, (int)strlen(keyword));
181 	    break;
182 	default :
183 	    if (!strcmp(mandatory, "print-color-mode") && !strcmp(keyword, "monochrome"))
184 	    {
185 	      if (ippContainsString(print_color_mode_sup, "auto-monochrome"))
186 		keyword = "auto-monochrome";
187 	      else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome"))
188 		keyword = "process-monochrome";
189 	    }
190 
191 	    ippAddString(request, IPP_TAG_JOB, value_tag, mandatory, NULL, keyword);
192 	    break;
193       }
194     }
195   }
196 
197   if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
198     keyword = cupsGetOption("media", num_options, options);
199 
200   media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot", num_options, options));
201   media_type   = _ppdCacheGetType(pc, cupsGetOption("MediaType", num_options, options));
202   size         = _ppdCacheGetSize(pc, keyword);
203 
204   if (size || media_source || media_type)
205   {
206    /*
207     * Add a media-col value...
208     */
209 
210     media_col = ippNew();
211 
212     if (size)
213     {
214       media_size = ippNew();
215       ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
216                     "x-dimension", size->width);
217       ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
218                     "y-dimension", size->length);
219 
220       ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
221     }
222 
223     for (i = 0; i < media_col_sup->num_values; i ++)
224     {
225       if (size && !strcmp(media_col_sup->values[i].string.text, "media-left-margin"))
226 	ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-left-margin", size->left);
227       else if (size && !strcmp(media_col_sup->values[i].string.text, "media-bottom-margin"))
228 	ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-bottom-margin", size->bottom);
229       else if (size && !strcmp(media_col_sup->values[i].string.text, "media-right-margin"))
230 	ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-right-margin", size->right);
231       else if (size && !strcmp(media_col_sup->values[i].string.text, "media-top-margin"))
232 	ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-top-margin", size->top);
233       else if (media_source && !strcmp(media_col_sup->values[i].string.text, "media-source"))
234 	ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-source", NULL, media_source);
235       else if (media_type && !strcmp(media_col_sup->values[i].string.text, "media-type"))
236 	ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-type", NULL, media_type);
237     }
238 
239     ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
240   }
241 
242   if ((keyword = cupsGetOption("output-bin", num_options, options)) == NULL)
243   {
244     if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL)
245       keyword = _ppdCacheGetBin(pc, choice->choice);
246   }
247 
248   if (keyword)
249     ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin", NULL, keyword);
250 
251   color_attr_name = print_color_mode_sup ? "print-color-mode" : "output-mode";
252 
253   if ((keyword = cupsGetOption("print-color-mode", num_options, options)) == NULL)
254   {
255     if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL)
256     {
257       if (!_cups_strcasecmp(choice->choice, "Gray"))
258 	keyword = "monochrome";
259       else
260 	keyword = "color";
261     }
262   }
263 
264   if (keyword && !strcmp(keyword, "monochrome"))
265   {
266     if (ippContainsString(print_color_mode_sup, "auto-monochrome"))
267       keyword = "auto-monochrome";
268     else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome"))
269       keyword = "process-monochrome";
270   }
271 
272   if (keyword)
273     ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, color_attr_name, NULL, keyword);
274 
275   if ((keyword = cupsGetOption("print-quality", num_options, options)) != NULL)
276     ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", atoi(keyword));
277   else if ((choice = ppdFindMarkedChoice(ppd, "cupsPrintQuality")) != NULL)
278   {
279     if (!_cups_strcasecmp(choice->choice, "draft"))
280       ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_DRAFT);
281     else if (!_cups_strcasecmp(choice->choice, "normal"))
282       ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_NORMAL);
283     else if (!_cups_strcasecmp(choice->choice, "high"))
284       ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_HIGH);
285   }
286 
287   if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
288     ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, keyword);
289   else if (pc->sides_option && (choice = ppdFindMarkedChoice(ppd, pc->sides_option)) != NULL)
290   {
291     if (!_cups_strcasecmp(choice->choice, pc->sides_1sided))
292       ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "one-sided");
293     else if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_long))
294       ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-long-edge");
295     if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_short))
296       ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-short-edge");
297   }
298 
299  /*
300   * Copies...
301   */
302 
303   if ((keyword = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
304   {
305     if (strstr(keyword, "uncollated"))
306       keyword = "false";
307     else
308       keyword = "true";
309   }
310   else if ((keyword = cupsGetOption("collate", num_options, options)) == NULL)
311     keyword = "true";
312 
313   if (format)
314   {
315     if (!_cups_strcasecmp(format, "image/gif") ||
316 	!_cups_strcasecmp(format, "image/jp2") ||
317 	!_cups_strcasecmp(format, "image/jpeg") ||
318 	!_cups_strcasecmp(format, "image/png") ||
319 	!_cups_strcasecmp(format, "image/tiff") ||
320 	!_cups_strncasecmp(format, "image/x-", 8))
321     {
322      /*
323       * Collation makes no sense for single page image formats...
324       */
325 
326       keyword = "false";
327     }
328     else if (!_cups_strncasecmp(format, "image/", 6) ||
329 	     !_cups_strcasecmp(format, "application/vnd.cups-raster"))
330     {
331      /*
332       * Multi-page image formats will have copies applied by the upstream
333       * filters...
334       */
335 
336       copies = 1;
337     }
338   }
339 
340   if (doc_handling_sup)
341   {
342     if (!_cups_strcasecmp(keyword, "true"))
343       collate_str = "separate-documents-collated-copies";
344     else
345       collate_str = "separate-documents-uncollated-copies";
346 
347     for (i = 0; i < doc_handling_sup->num_values; i ++)
348     {
349       if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
350       {
351 	ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "multiple-document-handling", NULL, collate_str);
352 	break;
353       }
354     }
355 
356     if (i >= doc_handling_sup->num_values)
357       copies = 1;
358   }
359 
360  /*
361   * Map finishing options...
362   */
363 
364   num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options, (int)(sizeof(finishings) / sizeof(finishings[0])), finishings);
365   if (num_finishings > 0)
366   {
367     ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", num_finishings, finishings);
368 
369     if (copies > 1 && (keyword = cupsGetOption("job-impressions", num_options, options)) != NULL)
370     {
371      /*
372       * Send job-pages-per-set attribute to apply finishings correctly...
373       */
374 
375       ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", atoi(keyword) / copies);
376     }
377   }
378 
379   return (copies);
380 }
381 
382 
383 /*
384  * '_ppdCacheCreateWithFile()' - Create PPD cache and mapping data from a
385  *                               written file.
386  *
387  * Use the @link _ppdCacheWriteFile@ function to write PWG mapping data to a
388  * file.
389  */
390 
391 _ppd_cache_t *				/* O  - PPD cache and mapping data */
_ppdCacheCreateWithFile(const char * filename,ipp_t ** attrs)392 _ppdCacheCreateWithFile(
393     const char *filename,		/* I  - File to read */
394     ipp_t      **attrs)			/* IO - IPP attributes, if any */
395 {
396   cups_file_t	*fp;			/* File */
397   _ppd_cache_t	*pc;			/* PWG mapping data */
398   pwg_size_t	*size;			/* Current size */
399   pwg_map_t	*map;			/* Current map */
400   _pwg_finishings_t *finishings;	/* Current finishings option */
401   int		linenum,		/* Current line number */
402 		num_bins,		/* Number of bins in file */
403 		num_sizes,		/* Number of sizes in file */
404 		num_sources,		/* Number of sources in file */
405 		num_types;		/* Number of types in file */
406   char		line[2048],		/* Current line */
407 		*value,			/* Pointer to value in line */
408 		*valueptr,		/* Pointer into value */
409 		pwg_keyword[128],	/* PWG keyword */
410 		ppd_keyword[PPD_MAX_NAME];
411 					/* PPD keyword */
412   _pwg_print_color_mode_t print_color_mode;
413 					/* Print color mode for preset */
414   _pwg_print_quality_t print_quality;	/* Print quality for preset */
415 
416 
417   DEBUG_printf(("_ppdCacheCreateWithFile(filename=\"%s\")", filename));
418 
419  /*
420   * Range check input...
421   */
422 
423   if (attrs)
424     *attrs = NULL;
425 
426   if (!filename)
427   {
428     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
429     return (NULL);
430   }
431 
432  /*
433   * Open the file...
434   */
435 
436   if ((fp = cupsFileOpen(filename, "r")) == NULL)
437   {
438     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
439     return (NULL);
440   }
441 
442  /*
443   * Read the first line and make sure it has "#CUPS-PPD-CACHE-version" in it...
444   */
445 
446   if (!cupsFileGets(fp, line, sizeof(line)))
447   {
448     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
449     DEBUG_puts("_ppdCacheCreateWithFile: Unable to read first line.");
450     cupsFileClose(fp);
451     return (NULL);
452   }
453 
454   if (strncmp(line, "#CUPS-PPD-CACHE-", 16))
455   {
456     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
457     DEBUG_printf(("_ppdCacheCreateWithFile: Wrong first line \"%s\".", line));
458     cupsFileClose(fp);
459     return (NULL);
460   }
461 
462   if (atoi(line + 16) != _PPD_CACHE_VERSION)
463   {
464     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of date PPD cache file."), 1);
465     DEBUG_printf(("_ppdCacheCreateWithFile: Cache file has version %s, "
466                   "expected %d.", line + 16, _PPD_CACHE_VERSION));
467     cupsFileClose(fp);
468     return (NULL);
469   }
470 
471  /*
472   * Allocate the mapping data structure...
473   */
474 
475   if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
476   {
477     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
478     DEBUG_puts("_ppdCacheCreateWithFile: Unable to allocate _ppd_cache_t.");
479     goto create_error;
480   }
481 
482   pc->max_copies = 9999;
483 
484  /*
485   * Read the file...
486   */
487 
488   linenum     = 0;
489   num_bins    = 0;
490   num_sizes   = 0;
491   num_sources = 0;
492   num_types   = 0;
493 
494   while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
495   {
496     DEBUG_printf(("_ppdCacheCreateWithFile: line=\"%s\", value=\"%s\", "
497                   "linenum=%d", line, value, linenum));
498 
499     if (!value)
500     {
501       DEBUG_printf(("_ppdCacheCreateWithFile: Missing value on line %d.",
502                     linenum));
503       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
504       goto create_error;
505     }
506     else if (!_cups_strcasecmp(line, "Filter"))
507     {
508       if (!pc->filters)
509         pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
510 	                            (cups_acopy_func_t)_cupsStrAlloc,
511 				    (cups_afree_func_t)_cupsStrFree);
512 
513       cupsArrayAdd(pc->filters, value);
514     }
515     else if (!_cups_strcasecmp(line, "PreFilter"))
516     {
517       if (!pc->prefilters)
518         pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
519 	                               (cups_acopy_func_t)_cupsStrAlloc,
520 				       (cups_afree_func_t)_cupsStrFree);
521 
522       cupsArrayAdd(pc->prefilters, value);
523     }
524     else if (!_cups_strcasecmp(line, "Product"))
525     {
526       pc->product = _cupsStrAlloc(value);
527     }
528     else if (!_cups_strcasecmp(line, "SingleFile"))
529     {
530       pc->single_file = !_cups_strcasecmp(value, "true");
531     }
532     else if (!_cups_strcasecmp(line, "IPP"))
533     {
534       off_t	pos = cupsFileTell(fp),	/* Position in file */
535 		length = strtol(value, NULL, 10);
536 					/* Length of IPP attributes */
537 
538       if (attrs && *attrs)
539       {
540         DEBUG_puts("_ppdCacheCreateWithFile: IPP listed multiple times.");
541 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
542 	goto create_error;
543       }
544       else if (length <= 0)
545       {
546         DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP length.");
547 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
548 	goto create_error;
549       }
550 
551       if (attrs)
552       {
553        /*
554         * Read IPP attributes into the provided variable...
555 	*/
556 
557         *attrs = ippNew();
558 
559         if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
560 		      *attrs) != IPP_STATE_DATA)
561 	{
562 	  DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
563 	  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
564 	  goto create_error;
565 	}
566       }
567       else
568       {
569        /*
570         * Skip the IPP data entirely...
571 	*/
572 
573         cupsFileSeek(fp, pos + length);
574       }
575 
576       if (cupsFileTell(fp) != (pos + length))
577       {
578         DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
579 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
580 	goto create_error;
581       }
582     }
583     else if (!_cups_strcasecmp(line, "NumBins"))
584     {
585       if (num_bins > 0)
586       {
587         DEBUG_puts("_ppdCacheCreateWithFile: NumBins listed multiple times.");
588 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
589 	goto create_error;
590       }
591 
592       if ((num_bins = atoi(value)) <= 0 || num_bins > 65536)
593       {
594         DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumBins value %d on line "
595 		      "%d.", num_sizes, linenum));
596 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
597 	goto create_error;
598       }
599 
600       if ((pc->bins = calloc((size_t)num_bins, sizeof(pwg_map_t))) == NULL)
601       {
602         DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d bins.",
603 	              num_sizes));
604 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
605 	goto create_error;
606       }
607     }
608     else if (!_cups_strcasecmp(line, "Bin"))
609     {
610       if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
611       {
612         DEBUG_printf(("_ppdCacheCreateWithFile: Bad Bin on line %d.", linenum));
613 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
614 	goto create_error;
615       }
616 
617       if (pc->num_bins >= num_bins)
618       {
619         DEBUG_printf(("_ppdCacheCreateWithFile: Too many Bin's on line %d.",
620 	              linenum));
621 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
622 	goto create_error;
623       }
624 
625       map      = pc->bins + pc->num_bins;
626       map->pwg = _cupsStrAlloc(pwg_keyword);
627       map->ppd = _cupsStrAlloc(ppd_keyword);
628 
629       pc->num_bins ++;
630     }
631     else if (!_cups_strcasecmp(line, "NumSizes"))
632     {
633       if (num_sizes > 0)
634       {
635         DEBUG_puts("_ppdCacheCreateWithFile: NumSizes listed multiple times.");
636 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
637 	goto create_error;
638       }
639 
640       if ((num_sizes = atoi(value)) < 0 || num_sizes > 65536)
641       {
642         DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSizes value %d on line "
643 	              "%d.", num_sizes, linenum));
644 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
645 	goto create_error;
646       }
647 
648       if (num_sizes > 0)
649       {
650 	if ((pc->sizes = calloc((size_t)num_sizes, sizeof(pwg_size_t))) == NULL)
651 	{
652 	  DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sizes.",
653 			num_sizes));
654 	  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
655 	  goto create_error;
656 	}
657       }
658     }
659     else if (!_cups_strcasecmp(line, "Size"))
660     {
661       if (pc->num_sizes >= num_sizes)
662       {
663         DEBUG_printf(("_ppdCacheCreateWithFile: Too many Size's on line %d.",
664 	              linenum));
665 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
666 	goto create_error;
667       }
668 
669       size = pc->sizes + pc->num_sizes;
670 
671       if (sscanf(value, "%127s%40s%d%d%d%d%d%d", pwg_keyword, ppd_keyword,
672 		 &(size->width), &(size->length), &(size->left),
673 		 &(size->bottom), &(size->right), &(size->top)) != 8)
674       {
675         DEBUG_printf(("_ppdCacheCreateWithFile: Bad Size on line %d.",
676 	              linenum));
677 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
678 	goto create_error;
679       }
680 
681       size->map.pwg = _cupsStrAlloc(pwg_keyword);
682       size->map.ppd = _cupsStrAlloc(ppd_keyword);
683 
684       pc->num_sizes ++;
685     }
686     else if (!_cups_strcasecmp(line, "CustomSize"))
687     {
688       if (pc->custom_max_width > 0)
689       {
690         DEBUG_printf(("_ppdCacheCreateWithFile: Too many CustomSize's on line "
691 	              "%d.", linenum));
692 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
693 	goto create_error;
694       }
695 
696       if (sscanf(value, "%d%d%d%d%d%d%d%d", &(pc->custom_max_width),
697                  &(pc->custom_max_length), &(pc->custom_min_width),
698 		 &(pc->custom_min_length), &(pc->custom_size.left),
699 		 &(pc->custom_size.bottom), &(pc->custom_size.right),
700 		 &(pc->custom_size.top)) != 8)
701       {
702         DEBUG_printf(("_ppdCacheCreateWithFile: Bad CustomSize on line %d.",
703 	              linenum));
704 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
705 	goto create_error;
706       }
707 
708       pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
709 		        pc->custom_max_width, pc->custom_max_length, NULL);
710       pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
711 
712       pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
713 		        pc->custom_min_width, pc->custom_min_length, NULL);
714       pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
715     }
716     else if (!_cups_strcasecmp(line, "SourceOption"))
717     {
718       pc->source_option = _cupsStrAlloc(value);
719     }
720     else if (!_cups_strcasecmp(line, "NumSources"))
721     {
722       if (num_sources > 0)
723       {
724         DEBUG_puts("_ppdCacheCreateWithFile: NumSources listed multiple "
725 	           "times.");
726 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
727 	goto create_error;
728       }
729 
730       if ((num_sources = atoi(value)) <= 0 || num_sources > 65536)
731       {
732         DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSources value %d on "
733 	              "line %d.", num_sources, linenum));
734 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
735 	goto create_error;
736       }
737 
738       if ((pc->sources = calloc((size_t)num_sources, sizeof(pwg_map_t))) == NULL)
739       {
740         DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sources.",
741 	              num_sources));
742 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
743 	goto create_error;
744       }
745     }
746     else if (!_cups_strcasecmp(line, "Source"))
747     {
748       if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
749       {
750         DEBUG_printf(("_ppdCacheCreateWithFile: Bad Source on line %d.",
751 	              linenum));
752 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
753 	goto create_error;
754       }
755 
756       if (pc->num_sources >= num_sources)
757       {
758         DEBUG_printf(("_ppdCacheCreateWithFile: Too many Source's on line %d.",
759 	              linenum));
760 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
761 	goto create_error;
762       }
763 
764       map      = pc->sources + pc->num_sources;
765       map->pwg = _cupsStrAlloc(pwg_keyword);
766       map->ppd = _cupsStrAlloc(ppd_keyword);
767 
768       pc->num_sources ++;
769     }
770     else if (!_cups_strcasecmp(line, "NumTypes"))
771     {
772       if (num_types > 0)
773       {
774         DEBUG_puts("_ppdCacheCreateWithFile: NumTypes listed multiple times.");
775 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
776 	goto create_error;
777       }
778 
779       if ((num_types = atoi(value)) <= 0 || num_types > 65536)
780       {
781         DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumTypes value %d on "
782 	              "line %d.", num_types, linenum));
783 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
784 	goto create_error;
785       }
786 
787       if ((pc->types = calloc((size_t)num_types, sizeof(pwg_map_t))) == NULL)
788       {
789         DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d types.",
790 	              num_types));
791 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
792 	goto create_error;
793       }
794     }
795     else if (!_cups_strcasecmp(line, "Type"))
796     {
797       if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
798       {
799         DEBUG_printf(("_ppdCacheCreateWithFile: Bad Type on line %d.",
800 	              linenum));
801 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
802 	goto create_error;
803       }
804 
805       if (pc->num_types >= num_types)
806       {
807         DEBUG_printf(("_ppdCacheCreateWithFile: Too many Type's on line %d.",
808 	              linenum));
809 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
810 	goto create_error;
811       }
812 
813       map      = pc->types + pc->num_types;
814       map->pwg = _cupsStrAlloc(pwg_keyword);
815       map->ppd = _cupsStrAlloc(ppd_keyword);
816 
817       pc->num_types ++;
818     }
819     else if (!_cups_strcasecmp(line, "Preset"))
820     {
821      /*
822       * Preset output-mode print-quality name=value ...
823       */
824 
825       print_color_mode = (_pwg_print_color_mode_t)strtol(value, &valueptr, 10);
826       print_quality    = (_pwg_print_quality_t)strtol(valueptr, &valueptr, 10);
827 
828       if (print_color_mode < _PWG_PRINT_COLOR_MODE_MONOCHROME ||
829           print_color_mode >= _PWG_PRINT_COLOR_MODE_MAX ||
830 	  print_quality < _PWG_PRINT_QUALITY_DRAFT ||
831 	  print_quality >= _PWG_PRINT_QUALITY_MAX ||
832 	  valueptr == value || !*valueptr)
833       {
834         DEBUG_printf(("_ppdCacheCreateWithFile: Bad Preset on line %d.",
835 	              linenum));
836 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
837 	goto create_error;
838       }
839 
840       pc->num_presets[print_color_mode][print_quality] =
841           cupsParseOptions(valueptr, 0,
842 	                   pc->presets[print_color_mode] + print_quality);
843     }
844     else if (!_cups_strcasecmp(line, "SidesOption"))
845       pc->sides_option = _cupsStrAlloc(value);
846     else if (!_cups_strcasecmp(line, "Sides1Sided"))
847       pc->sides_1sided = _cupsStrAlloc(value);
848     else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
849       pc->sides_2sided_long = _cupsStrAlloc(value);
850     else if (!_cups_strcasecmp(line, "Sides2SidedShort"))
851       pc->sides_2sided_short = _cupsStrAlloc(value);
852     else if (!_cups_strcasecmp(line, "Finishings"))
853     {
854       if (!pc->finishings)
855 	pc->finishings =
856 	    cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
857 			  NULL, NULL, 0, NULL,
858 			  (cups_afree_func_t)pwg_free_finishings);
859 
860       if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
861         goto create_error;
862 
863       finishings->value       = (ipp_finishings_t)strtol(value, &valueptr, 10);
864       finishings->num_options = cupsParseOptions(valueptr, 0,
865                                                  &(finishings->options));
866 
867       cupsArrayAdd(pc->finishings, finishings);
868     }
869     else if (!_cups_strcasecmp(line, "MaxCopies"))
870       pc->max_copies = atoi(value);
871     else if (!_cups_strcasecmp(line, "ChargeInfoURI"))
872       pc->charge_info_uri = _cupsStrAlloc(value);
873     else if (!_cups_strcasecmp(line, "JobAccountId"))
874       pc->account_id = !_cups_strcasecmp(value, "true");
875     else if (!_cups_strcasecmp(line, "JobAccountingUserId"))
876       pc->accounting_user_id = !_cups_strcasecmp(value, "true");
877     else if (!_cups_strcasecmp(line, "JobPassword"))
878       pc->password = _cupsStrAlloc(value);
879     else if (!_cups_strcasecmp(line, "Mandatory"))
880     {
881       if (pc->mandatory)
882         _cupsArrayAddStrings(pc->mandatory, value, ' ');
883       else
884         pc->mandatory = _cupsArrayNewStrings(value, ' ');
885     }
886     else if (!_cups_strcasecmp(line, "SupportFile"))
887     {
888       if (!pc->support_files)
889         pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
890                                           (cups_acopy_func_t)_cupsStrAlloc,
891                                           (cups_afree_func_t)_cupsStrFree);
892 
893       cupsArrayAdd(pc->support_files, value);
894     }
895     else
896     {
897       DEBUG_printf(("_ppdCacheCreateWithFile: Unknown %s on line %d.", line,
898 		    linenum));
899     }
900   }
901 
902   if (pc->num_sizes < num_sizes)
903   {
904     DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sizes (%d < %d).",
905                   pc->num_sizes, num_sizes));
906     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
907     goto create_error;
908   }
909 
910   if (pc->num_sources < num_sources)
911   {
912     DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sources (%d < %d).",
913                   pc->num_sources, num_sources));
914     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
915     goto create_error;
916   }
917 
918   if (pc->num_types < num_types)
919   {
920     DEBUG_printf(("_ppdCacheCreateWithFile: Not enough types (%d < %d).",
921                   pc->num_types, num_types));
922     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
923     goto create_error;
924   }
925 
926   cupsFileClose(fp);
927 
928   return (pc);
929 
930  /*
931   * If we get here the file was bad - free any data and return...
932   */
933 
934   create_error:
935 
936   cupsFileClose(fp);
937   _ppdCacheDestroy(pc);
938 
939   if (attrs)
940   {
941     ippDelete(*attrs);
942     *attrs = NULL;
943   }
944 
945   return (NULL);
946 }
947 
948 
949 /*
950  * '_ppdCacheCreateWithPPD()' - Create PWG mapping data from a PPD file.
951  */
952 
953 _ppd_cache_t *				/* O - PPD cache and mapping data */
_ppdCacheCreateWithPPD(ppd_file_t * ppd)954 _ppdCacheCreateWithPPD(ppd_file_t *ppd)	/* I - PPD file */
955 {
956   int			i, j, k;	/* Looping vars */
957   _ppd_cache_t		*pc;		/* PWG mapping data */
958   ppd_option_t		*input_slot,	/* InputSlot option */
959 			*media_type,	/* MediaType option */
960 			*output_bin,	/* OutputBin option */
961 			*color_model,	/* ColorModel option */
962 			*duplex;	/* Duplex option */
963   ppd_choice_t		*choice;	/* Current InputSlot/MediaType */
964   pwg_map_t		*map;		/* Current source/type map */
965   ppd_attr_t		*ppd_attr;	/* Current PPD preset attribute */
966   int			num_options;	/* Number of preset options and props */
967   cups_option_t		*options;	/* Preset options and properties */
968   ppd_size_t		*ppd_size;	/* Current PPD size */
969   pwg_size_t		*pwg_size;	/* Current PWG size */
970   char			pwg_keyword[3 + PPD_MAX_NAME + 1 + 12 + 1 + 12 + 3],
971 					/* PWG keyword string */
972 			ppd_name[PPD_MAX_NAME];
973 					/* Normalized PPD name */
974   const char		*pwg_name;	/* Standard PWG media name */
975   pwg_media_t		*pwg_media;	/* PWG media data */
976   _pwg_print_color_mode_t pwg_print_color_mode;
977 					/* print-color-mode index */
978   _pwg_print_quality_t	pwg_print_quality;
979 					/* print-quality index */
980   int			similar;	/* Are the old and new size similar? */
981   pwg_size_t		*old_size;	/* Current old size */
982   int			old_imageable,	/* Old imageable length in 2540ths */
983 			old_borderless,	/* Old borderless state */
984 			old_known_pwg;	/* Old PWG name is well-known */
985   int			new_width,	/* New width in 2540ths */
986 			new_length,	/* New length in 2540ths */
987 			new_left,	/* New left margin in 2540ths */
988 			new_bottom,	/* New bottom margin in 2540ths */
989 			new_right,	/* New right margin in 2540ths */
990 			new_top,	/* New top margin in 2540ths */
991 			new_imageable,	/* New imageable length in 2540ths */
992 			new_borderless,	/* New borderless state */
993 			new_known_pwg;	/* New PWG name is well-known */
994   pwg_size_t		*new_size;	/* New size to add, if any */
995   const char		*filter;	/* Current filter */
996   _pwg_finishings_t	*finishings;	/* Current finishings value */
997 
998 
999   DEBUG_printf(("_ppdCacheCreateWithPPD(ppd=%p)", ppd));
1000 
1001  /*
1002   * Range check input...
1003   */
1004 
1005   if (!ppd)
1006     return (NULL);
1007 
1008  /*
1009   * Allocate memory...
1010   */
1011 
1012   if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
1013   {
1014     DEBUG_puts("_ppdCacheCreateWithPPD: Unable to allocate _ppd_cache_t.");
1015     goto create_error;
1016   }
1017 
1018  /*
1019   * Copy and convert size data...
1020   */
1021 
1022   if (ppd->num_sizes > 0)
1023   {
1024     if ((pc->sizes = calloc((size_t)ppd->num_sizes, sizeof(pwg_size_t))) == NULL)
1025     {
1026       DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1027 		    "pwg_size_t's.", ppd->num_sizes));
1028       goto create_error;
1029     }
1030 
1031     for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes;
1032 	 i > 0;
1033 	 i --, ppd_size ++)
1034     {
1035      /*
1036       * Don't copy over custom size...
1037       */
1038 
1039       if (!_cups_strcasecmp(ppd_size->name, "Custom"))
1040 	continue;
1041 
1042      /*
1043       * Convert the PPD size name to the corresponding PWG keyword name.
1044       */
1045 
1046       if ((pwg_media = pwgMediaForPPD(ppd_size->name)) != NULL)
1047       {
1048        /*
1049 	* Standard name, do we have conflicts?
1050 	*/
1051 
1052 	for (j = 0; j < pc->num_sizes; j ++)
1053 	  if (!strcmp(pc->sizes[j].map.pwg, pwg_media->pwg))
1054 	  {
1055 	    pwg_media = NULL;
1056 	    break;
1057 	  }
1058       }
1059 
1060       if (pwg_media)
1061       {
1062        /*
1063 	* Standard name and no conflicts, use it!
1064 	*/
1065 
1066 	pwg_name      = pwg_media->pwg;
1067 	new_known_pwg = 1;
1068       }
1069       else
1070       {
1071        /*
1072 	* Not a standard name; convert it to a PWG vendor name of the form:
1073 	*
1074 	*     pp_lowerppd_WIDTHxHEIGHTuu
1075 	*/
1076 
1077 	pwg_name      = pwg_keyword;
1078 	new_known_pwg = 0;
1079 
1080 	pwg_unppdize_name(ppd_size->name, ppd_name, sizeof(ppd_name), "_.");
1081 	pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), NULL, ppd_name,
1082 			  PWG_FROM_POINTS(ppd_size->width),
1083 			  PWG_FROM_POINTS(ppd_size->length), NULL);
1084       }
1085 
1086      /*
1087       * If we have a similar paper with non-zero margins then we only want to
1088       * keep it if it has a larger imageable area length.  The NULL check is for
1089       * dimensions that are <= 0...
1090       */
1091 
1092       if ((pwg_media = _pwgMediaNearSize(PWG_FROM_POINTS(ppd_size->width),
1093 					PWG_FROM_POINTS(ppd_size->length),
1094 					0)) == NULL)
1095 	continue;
1096 
1097       new_width      = pwg_media->width;
1098       new_length     = pwg_media->length;
1099       new_left       = PWG_FROM_POINTS(ppd_size->left);
1100       new_bottom     = PWG_FROM_POINTS(ppd_size->bottom);
1101       new_right      = PWG_FROM_POINTS(ppd_size->width - ppd_size->right);
1102       new_top        = PWG_FROM_POINTS(ppd_size->length - ppd_size->top);
1103       new_imageable  = new_length - new_top - new_bottom;
1104       new_borderless = new_bottom == 0 && new_top == 0 &&
1105 		       new_left == 0 && new_right == 0;
1106 
1107       for (k = pc->num_sizes, similar = 0, old_size = pc->sizes, new_size = NULL;
1108 	   k > 0 && !similar;
1109 	   k --, old_size ++)
1110       {
1111 	old_imageable  = old_size->length - old_size->top - old_size->bottom;
1112 	old_borderless = old_size->left == 0 && old_size->bottom == 0 &&
1113 			 old_size->right == 0 && old_size->top == 0;
1114 	old_known_pwg  = strncmp(old_size->map.pwg, "oe_", 3) &&
1115 			 strncmp(old_size->map.pwg, "om_", 3);
1116 
1117 	similar = old_borderless == new_borderless &&
1118 		  _PWG_EQUIVALENT(old_size->width, new_width) &&
1119 		  _PWG_EQUIVALENT(old_size->length, new_length);
1120 
1121 	if (similar &&
1122 	    (new_known_pwg || (!old_known_pwg && new_imageable > old_imageable)))
1123 	{
1124 	 /*
1125 	  * The new paper has a larger imageable area so it could replace
1126 	  * the older paper.  Regardless of the imageable area, we always
1127 	  * prefer the size with a well-known PWG name.
1128 	  */
1129 
1130 	  new_size = old_size;
1131 	  _cupsStrFree(old_size->map.ppd);
1132 	  _cupsStrFree(old_size->map.pwg);
1133 	}
1134       }
1135 
1136       if (!similar)
1137       {
1138        /*
1139 	* The paper was unique enough to deserve its own entry so add it to the
1140 	* end.
1141 	*/
1142 
1143 	new_size = pwg_size ++;
1144 	pc->num_sizes ++;
1145       }
1146 
1147       if (new_size)
1148       {
1149        /*
1150 	* Save this size...
1151 	*/
1152 
1153 	new_size->map.ppd = _cupsStrAlloc(ppd_size->name);
1154 	new_size->map.pwg = _cupsStrAlloc(pwg_name);
1155 	new_size->width   = new_width;
1156 	new_size->length  = new_length;
1157 	new_size->left    = new_left;
1158 	new_size->bottom  = new_bottom;
1159 	new_size->right   = new_right;
1160 	new_size->top     = new_top;
1161       }
1162     }
1163   }
1164 
1165   if (ppd->variable_sizes)
1166   {
1167    /*
1168     * Generate custom size data...
1169     */
1170 
1171     pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
1172 		      PWG_FROM_POINTS(ppd->custom_max[0]),
1173 		      PWG_FROM_POINTS(ppd->custom_max[1]), NULL);
1174     pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
1175     pc->custom_max_width   = PWG_FROM_POINTS(ppd->custom_max[0]);
1176     pc->custom_max_length  = PWG_FROM_POINTS(ppd->custom_max[1]);
1177 
1178     pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
1179 		      PWG_FROM_POINTS(ppd->custom_min[0]),
1180 		      PWG_FROM_POINTS(ppd->custom_min[1]), NULL);
1181     pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
1182     pc->custom_min_width   = PWG_FROM_POINTS(ppd->custom_min[0]);
1183     pc->custom_min_length  = PWG_FROM_POINTS(ppd->custom_min[1]);
1184 
1185     pc->custom_size.left   = PWG_FROM_POINTS(ppd->custom_margins[0]);
1186     pc->custom_size.bottom = PWG_FROM_POINTS(ppd->custom_margins[1]);
1187     pc->custom_size.right  = PWG_FROM_POINTS(ppd->custom_margins[2]);
1188     pc->custom_size.top    = PWG_FROM_POINTS(ppd->custom_margins[3]);
1189   }
1190 
1191  /*
1192   * Copy and convert InputSlot data...
1193   */
1194 
1195   if ((input_slot = ppdFindOption(ppd, "InputSlot")) == NULL)
1196     input_slot = ppdFindOption(ppd, "HPPaperSource");
1197 
1198   if (input_slot)
1199   {
1200     pc->source_option = _cupsStrAlloc(input_slot->keyword);
1201 
1202     if ((pc->sources = calloc((size_t)input_slot->num_choices, sizeof(pwg_map_t))) == NULL)
1203     {
1204       DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1205                     "pwg_map_t's for InputSlot.", input_slot->num_choices));
1206       goto create_error;
1207     }
1208 
1209     pc->num_sources = input_slot->num_choices;
1210 
1211     for (i = input_slot->num_choices, choice = input_slot->choices,
1212              map = pc->sources;
1213 	 i > 0;
1214 	 i --, choice ++, map ++)
1215     {
1216       if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
1217           !_cups_strcasecmp(choice->choice, "Default"))
1218         pwg_name = "auto";
1219       else if (!_cups_strcasecmp(choice->choice, "Cassette"))
1220         pwg_name = "main";
1221       else if (!_cups_strcasecmp(choice->choice, "PhotoTray"))
1222         pwg_name = "photo";
1223       else if (!_cups_strcasecmp(choice->choice, "CDTray"))
1224         pwg_name = "disc";
1225       else if (!_cups_strncasecmp(choice->choice, "Multipurpose", 12) ||
1226                !_cups_strcasecmp(choice->choice, "MP") ||
1227                !_cups_strcasecmp(choice->choice, "MPTray"))
1228         pwg_name = "by-pass-tray";
1229       else if (!_cups_strcasecmp(choice->choice, "LargeCapacity"))
1230         pwg_name = "large-capacity";
1231       else if (!_cups_strncasecmp(choice->choice, "Lower", 5))
1232         pwg_name = "bottom";
1233       else if (!_cups_strncasecmp(choice->choice, "Middle", 6))
1234         pwg_name = "middle";
1235       else if (!_cups_strncasecmp(choice->choice, "Upper", 5))
1236         pwg_name = "top";
1237       else if (!_cups_strncasecmp(choice->choice, "Side", 4))
1238         pwg_name = "side";
1239       else if (!_cups_strcasecmp(choice->choice, "Roll"))
1240         pwg_name = "main-roll";
1241       else
1242       {
1243        /*
1244         * Convert PPD name to lowercase...
1245 	*/
1246 
1247         pwg_name = pwg_keyword;
1248 	pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1249 	                  "_");
1250       }
1251 
1252       map->pwg = _cupsStrAlloc(pwg_name);
1253       map->ppd = _cupsStrAlloc(choice->choice);
1254     }
1255   }
1256 
1257  /*
1258   * Copy and convert MediaType data...
1259   */
1260 
1261   if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
1262   {
1263     if ((pc->types = calloc((size_t)media_type->num_choices, sizeof(pwg_map_t))) == NULL)
1264     {
1265       DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1266                     "pwg_map_t's for MediaType.", media_type->num_choices));
1267       goto create_error;
1268     }
1269 
1270     pc->num_types = media_type->num_choices;
1271 
1272     for (i = media_type->num_choices, choice = media_type->choices,
1273              map = pc->types;
1274 	 i > 0;
1275 	 i --, choice ++, map ++)
1276     {
1277       if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
1278           !_cups_strcasecmp(choice->choice, "Any") ||
1279           !_cups_strcasecmp(choice->choice, "Default"))
1280         pwg_name = "auto";
1281       else if (!_cups_strncasecmp(choice->choice, "Card", 4))
1282         pwg_name = "cardstock";
1283       else if (!_cups_strncasecmp(choice->choice, "Env", 3))
1284         pwg_name = "envelope";
1285       else if (!_cups_strncasecmp(choice->choice, "Gloss", 5))
1286         pwg_name = "photographic-glossy";
1287       else if (!_cups_strcasecmp(choice->choice, "HighGloss"))
1288         pwg_name = "photographic-high-gloss";
1289       else if (!_cups_strcasecmp(choice->choice, "Matte"))
1290         pwg_name = "photographic-matte";
1291       else if (!_cups_strncasecmp(choice->choice, "Plain", 5))
1292         pwg_name = "stationery";
1293       else if (!_cups_strncasecmp(choice->choice, "Coated", 6))
1294         pwg_name = "stationery-coated";
1295       else if (!_cups_strcasecmp(choice->choice, "Inkjet"))
1296         pwg_name = "stationery-inkjet";
1297       else if (!_cups_strcasecmp(choice->choice, "Letterhead"))
1298         pwg_name = "stationery-letterhead";
1299       else if (!_cups_strncasecmp(choice->choice, "Preprint", 8))
1300         pwg_name = "stationery-preprinted";
1301       else if (!_cups_strcasecmp(choice->choice, "Recycled"))
1302         pwg_name = "stationery-recycled";
1303       else if (!_cups_strncasecmp(choice->choice, "Transparen", 10))
1304         pwg_name = "transparency";
1305       else
1306       {
1307        /*
1308         * Convert PPD name to lowercase...
1309 	*/
1310 
1311         pwg_name = pwg_keyword;
1312 	pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1313 	                  "_");
1314       }
1315 
1316       map->pwg = _cupsStrAlloc(pwg_name);
1317       map->ppd = _cupsStrAlloc(choice->choice);
1318     }
1319   }
1320 
1321  /*
1322   * Copy and convert OutputBin data...
1323   */
1324 
1325   if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
1326   {
1327     if ((pc->bins = calloc((size_t)output_bin->num_choices, sizeof(pwg_map_t))) == NULL)
1328     {
1329       DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1330                     "pwg_map_t's for OutputBin.", output_bin->num_choices));
1331       goto create_error;
1332     }
1333 
1334     pc->num_bins = output_bin->num_choices;
1335 
1336     for (i = output_bin->num_choices, choice = output_bin->choices,
1337              map = pc->bins;
1338 	 i > 0;
1339 	 i --, choice ++, map ++)
1340     {
1341       pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
1342 
1343       map->pwg = _cupsStrAlloc(pwg_keyword);
1344       map->ppd = _cupsStrAlloc(choice->choice);
1345     }
1346   }
1347 
1348   if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
1349   {
1350    /*
1351     * Copy and convert APPrinterPreset (output-mode + print-quality) data...
1352     */
1353 
1354     const char	*quality,		/* com.apple.print.preset.quality value */
1355 		*output_mode,		/* com.apple.print.preset.output-mode value */
1356 		*color_model_val,	/* ColorModel choice */
1357 		*graphicsType,		/* com.apple.print.preset.graphicsType value */
1358 		*media_front_coating;	/* com.apple.print.preset.media-front-coating value */
1359 
1360     do
1361     {
1362       num_options = _ppdParseOptions(ppd_attr->value, 0, &options,
1363                                      _PPD_PARSE_ALL);
1364 
1365       if ((quality = cupsGetOption("com.apple.print.preset.quality",
1366                                    num_options, options)) != NULL)
1367       {
1368        /*
1369         * Get the print-quality for this preset...
1370 	*/
1371 
1372 	if (!strcmp(quality, "low"))
1373 	  pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1374 	else if (!strcmp(quality, "high"))
1375 	  pwg_print_quality = _PWG_PRINT_QUALITY_HIGH;
1376 	else
1377 	  pwg_print_quality = _PWG_PRINT_QUALITY_NORMAL;
1378 
1379        /*
1380 	* Ignore graphicsType "Photo" presets that are not high quality.
1381 	*/
1382 
1383 	graphicsType = cupsGetOption("com.apple.print.preset.graphicsType",
1384 				      num_options, options);
1385 
1386 	if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH && graphicsType &&
1387 	    !strcmp(graphicsType, "Photo"))
1388 	  continue;
1389 
1390        /*
1391 	* Ignore presets for normal and draft quality where the coating
1392 	* isn't "none" or "autodetect".
1393 	*/
1394 
1395 	media_front_coating = cupsGetOption(
1396 	                          "com.apple.print.preset.media-front-coating",
1397 			          num_options, options);
1398 
1399         if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH &&
1400 	    media_front_coating &&
1401 	    strcmp(media_front_coating, "none") &&
1402 	    strcmp(media_front_coating, "autodetect"))
1403 	  continue;
1404 
1405        /*
1406         * Get the output mode for this preset...
1407 	*/
1408 
1409         output_mode     = cupsGetOption("com.apple.print.preset.output-mode",
1410 	                                num_options, options);
1411         color_model_val = cupsGetOption("ColorModel", num_options, options);
1412 
1413         if (output_mode)
1414 	{
1415 	  if (!strcmp(output_mode, "monochrome"))
1416 	    pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1417 	  else
1418 	    pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1419 	}
1420 	else if (color_model_val)
1421 	{
1422 	  if (!_cups_strcasecmp(color_model_val, "Gray"))
1423 	    pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1424 	  else
1425 	    pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1426 	}
1427 	else
1428 	  pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1429 
1430        /*
1431         * Save the options for this combination as needed...
1432 	*/
1433 
1434         if (!pc->num_presets[pwg_print_color_mode][pwg_print_quality])
1435 	  pc->num_presets[pwg_print_color_mode][pwg_print_quality] =
1436 	      _ppdParseOptions(ppd_attr->value, 0,
1437 	                       pc->presets[pwg_print_color_mode] +
1438 			           pwg_print_quality, _PPD_PARSE_OPTIONS);
1439       }
1440 
1441       cupsFreeOptions(num_options, options);
1442     }
1443     while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) != NULL);
1444   }
1445 
1446   if (!pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_DRAFT] &&
1447       !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_NORMAL] &&
1448       !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_HIGH])
1449   {
1450    /*
1451     * Try adding some common color options to create grayscale presets.  These
1452     * are listed in order of popularity...
1453     */
1454 
1455     const char	*color_option = NULL,	/* Color control option */
1456 		*gray_choice = NULL;	/* Choice to select grayscale */
1457 
1458     if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL &&
1459         ppdFindChoice(color_model, "Gray"))
1460     {
1461       color_option = "ColorModel";
1462       gray_choice  = "Gray";
1463     }
1464     else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL &&
1465              ppdFindChoice(color_model, "grayscale"))
1466     {
1467       color_option = "HPColorMode";
1468       gray_choice  = "grayscale";
1469     }
1470     else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL &&
1471              ppdFindChoice(color_model, "Mono"))
1472     {
1473       color_option = "BRMonoColor";
1474       gray_choice  = "Mono";
1475     }
1476     else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL &&
1477              ppdFindChoice(color_model, "1"))
1478     {
1479       color_option = "CNIJSGrayScale";
1480       gray_choice  = "1";
1481     }
1482     else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL &&
1483              ppdFindChoice(color_model, "True"))
1484     {
1485       color_option = "HPColorAsGray";
1486       gray_choice  = "True";
1487     }
1488 
1489     if (color_option && gray_choice)
1490     {
1491      /*
1492       * Copy and convert ColorModel (output-mode) data...
1493       */
1494 
1495       cups_option_t	*coption,	/* Color option */
1496 			  *moption;	/* Monochrome option */
1497 
1498       for (pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1499 	   pwg_print_quality < _PWG_PRINT_QUALITY_MAX;
1500 	   pwg_print_quality ++)
1501       {
1502 	if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality])
1503 	{
1504 	 /*
1505 	  * Copy the color options...
1506 	  */
1507 
1508 	  num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
1509 					[pwg_print_quality];
1510 	  options     = calloc(sizeof(cups_option_t), (size_t)num_options);
1511 
1512 	  if (options)
1513 	  {
1514 	    for (i = num_options, moption = options,
1515 		     coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
1516 					   [pwg_print_quality];
1517 		 i > 0;
1518 		 i --, moption ++, coption ++)
1519 	    {
1520 	      moption->name  = _cupsStrRetain(coption->name);
1521 	      moption->value = _cupsStrRetain(coption->value);
1522 	    }
1523 
1524 	    pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1525 		num_options;
1526 	    pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1527 		options;
1528 	  }
1529 	}
1530 	else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL)
1531 	  continue;
1532 
1533        /*
1534 	* Add the grayscale option to the preset...
1535 	*/
1536 
1537 	pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1538 	    cupsAddOption(color_option, gray_choice,
1539 			  pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
1540 					  [pwg_print_quality],
1541 			  pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME] +
1542 			      pwg_print_quality);
1543       }
1544     }
1545   }
1546 
1547  /*
1548   * Copy and convert Duplex (sides) data...
1549   */
1550 
1551   if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
1552     if ((duplex = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1553       if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
1554         if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
1555 	  duplex = ppdFindOption(ppd, "KD03Duplex");
1556 
1557   if (duplex)
1558   {
1559     pc->sides_option = _cupsStrAlloc(duplex->keyword);
1560 
1561     for (i = duplex->num_choices, choice = duplex->choices;
1562          i > 0;
1563 	 i --, choice ++)
1564     {
1565       if ((!_cups_strcasecmp(choice->choice, "None") ||
1566 	   !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
1567         pc->sides_1sided = _cupsStrAlloc(choice->choice);
1568       else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
1569 	        !_cups_strcasecmp(choice->choice, "LongEdge") ||
1570 	        !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
1571         pc->sides_2sided_long = _cupsStrAlloc(choice->choice);
1572       else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
1573 	        !_cups_strcasecmp(choice->choice, "ShortEdge") ||
1574 	        !_cups_strcasecmp(choice->choice, "Bottom")) &&
1575 	       !pc->sides_2sided_short)
1576         pc->sides_2sided_short = _cupsStrAlloc(choice->choice);
1577     }
1578   }
1579 
1580  /*
1581   * Copy filters and pre-filters...
1582   */
1583 
1584   pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
1585 			      (cups_acopy_func_t)_cupsStrAlloc,
1586 			      (cups_afree_func_t)_cupsStrFree);
1587 
1588   cupsArrayAdd(pc->filters,
1589                "application/vnd.cups-raw application/octet-stream 0 -");
1590 
1591   if ((ppd_attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL)
1592   {
1593     do
1594     {
1595       cupsArrayAdd(pc->filters, ppd_attr->value);
1596     }
1597     while ((ppd_attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
1598   }
1599   else if (ppd->num_filters > 0)
1600   {
1601     for (i = 0; i < ppd->num_filters; i ++)
1602       cupsArrayAdd(pc->filters, ppd->filters[i]);
1603   }
1604   else
1605     cupsArrayAdd(pc->filters, "application/vnd.cups-postscript 0 -");
1606 
1607  /*
1608   * See if we have a command filter...
1609   */
1610 
1611   for (filter = (const char *)cupsArrayFirst(pc->filters);
1612        filter;
1613        filter = (const char *)cupsArrayNext(pc->filters))
1614     if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
1615         _cups_isspace(filter[28]))
1616       break;
1617 
1618   if (!filter &&
1619       ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL ||
1620        _cups_strcasecmp(ppd_attr->value, "none")))
1621   {
1622    /*
1623     * No command filter and no cupsCommands keyword telling us not to use one.
1624     * See if this is a PostScript printer, and if so add a PostScript command
1625     * filter...
1626     */
1627 
1628     for (filter = (const char *)cupsArrayFirst(pc->filters);
1629 	 filter;
1630 	 filter = (const char *)cupsArrayNext(pc->filters))
1631       if (!_cups_strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
1632 	  _cups_isspace(filter[31]))
1633 	break;
1634 
1635     if (filter)
1636       cupsArrayAdd(pc->filters,
1637                    "application/vnd.cups-command application/postscript 100 "
1638                    "commandtops");
1639   }
1640 
1641   if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
1642   {
1643     pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
1644 				   (cups_acopy_func_t)_cupsStrAlloc,
1645 				   (cups_afree_func_t)_cupsStrFree);
1646 
1647     do
1648     {
1649       cupsArrayAdd(pc->prefilters, ppd_attr->value);
1650     }
1651     while ((ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) != NULL);
1652   }
1653 
1654   if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL)
1655     pc->single_file = !_cups_strcasecmp(ppd_attr->value, "true");
1656 
1657  /*
1658   * Copy the product string, if any...
1659   */
1660 
1661   if (ppd->product)
1662     pc->product = _cupsStrAlloc(ppd->product);
1663 
1664  /*
1665   * Copy finishings mapping data...
1666   */
1667 
1668   if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL)
1669   {
1670    /*
1671     * Have proper vendor mapping of IPP finishings values to PPD options...
1672     */
1673 
1674     pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
1675                                    NULL, NULL, 0, NULL,
1676                                    (cups_afree_func_t)pwg_free_finishings);
1677 
1678     do
1679     {
1680       if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
1681         goto create_error;
1682 
1683       finishings->value       = (ipp_finishings_t)atoi(ppd_attr->spec);
1684       finishings->num_options = _ppdParseOptions(ppd_attr->value, 0,
1685                                                  &(finishings->options),
1686                                                  _PPD_PARSE_OPTIONS);
1687 
1688       cupsArrayAdd(pc->finishings, finishings);
1689     }
1690     while ((ppd_attr = ppdFindNextAttr(ppd, "cupsIPPFinishings",
1691                                        NULL)) != NULL);
1692   }
1693   else
1694   {
1695    /*
1696     * No IPP mapping data, try to map common/standard PPD keywords...
1697     */
1698 
1699     ppd_option_t	*ppd_option;	/* PPD option */
1700 
1701     pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings, NULL, NULL, 0, NULL, (cups_afree_func_t)pwg_free_finishings);
1702 
1703     if ((ppd_option = ppdFindOption(ppd, "StapleLocation")) != NULL)
1704     {
1705      /*
1706       * Add staple finishings...
1707       */
1708 
1709       if (ppdFindChoice(ppd_option, "SinglePortrait"))
1710         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, "StapleLocation", "SinglePortrait");
1711       if (ppdFindChoice(ppd_option, "UpperLeft")) /* Ricoh extension */
1712         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, "StapleLocation", "UpperLeft");
1713       if (ppdFindChoice(ppd_option, "UpperRight")) /* Ricoh extension */
1714         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_RIGHT, "StapleLocation", "UpperRight");
1715       if (ppdFindChoice(ppd_option, "SingleLandscape"))
1716         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_BOTTOM_LEFT, "StapleLocation", "SingleLandscape");
1717       if (ppdFindChoice(ppd_option, "DualLandscape"))
1718         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_DUAL_LEFT, "StapleLocation", "DualLandscape");
1719     }
1720 
1721     if ((ppd_option = ppdFindOption(ppd, "RIPunch")) != NULL)
1722     {
1723      /*
1724       * Add (Ricoh) punch finishings...
1725       */
1726 
1727       if (ppdFindChoice(ppd_option, "Left2"))
1728         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_LEFT, "RIPunch", "Left2");
1729       if (ppdFindChoice(ppd_option, "Left3"))
1730         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_LEFT, "RIPunch", "Left3");
1731       if (ppdFindChoice(ppd_option, "Left4"))
1732         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_LEFT, "RIPunch", "Left4");
1733       if (ppdFindChoice(ppd_option, "Right2"))
1734         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_RIGHT, "RIPunch", "Right2");
1735       if (ppdFindChoice(ppd_option, "Right3"))
1736         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_RIGHT, "RIPunch", "Right3");
1737       if (ppdFindChoice(ppd_option, "Right4"))
1738         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_RIGHT, "RIPunch", "Right4");
1739       if (ppdFindChoice(ppd_option, "Upper2"))
1740         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_TOP, "RIPunch", "Upper2");
1741       if (ppdFindChoice(ppd_option, "Upper3"))
1742         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_TOP, "RIPunch", "Upper3");
1743       if (ppdFindChoice(ppd_option, "Upper4"))
1744         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_TOP, "RIPunch", "Upper4");
1745     }
1746 
1747     if ((ppd_option = ppdFindOption(ppd, "BindEdge")) != NULL)
1748     {
1749      /*
1750       * Add bind finishings...
1751       */
1752 
1753       if (ppdFindChoice(ppd_option, "Left"))
1754         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_LEFT, "BindEdge", "Left");
1755       if (ppdFindChoice(ppd_option, "Right"))
1756         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_RIGHT, "BindEdge", "Right");
1757       if (ppdFindChoice(ppd_option, "Top"))
1758         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_TOP, "BindEdge", "Top");
1759       if (ppdFindChoice(ppd_option, "Bottom"))
1760         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_BOTTOM, "BindEdge", "Bottom");
1761     }
1762 
1763     if ((ppd_option = ppdFindOption(ppd, "FoldType")) != NULL)
1764     {
1765      /*
1766       * Add (Adobe) fold finishings...
1767       */
1768 
1769       if (ppdFindChoice(ppd_option, "ZFold"))
1770         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_Z, "FoldType", "ZFold");
1771       if (ppdFindChoice(ppd_option, "Saddle"))
1772         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_HALF, "FoldType", "Saddle");
1773       if (ppdFindChoice(ppd_option, "DoubleGate"))
1774         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_DOUBLE_GATE, "FoldType", "DoubleGate");
1775       if (ppdFindChoice(ppd_option, "LeftGate"))
1776         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LEFT_GATE, "FoldType", "LeftGate");
1777       if (ppdFindChoice(ppd_option, "RightGate"))
1778         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_RIGHT_GATE, "FoldType", "RightGate");
1779       if (ppdFindChoice(ppd_option, "Letter"))
1780         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, "FoldType", "Letter");
1781       if (ppdFindChoice(ppd_option, "XFold"))
1782         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_POSTER, "FoldType", "XFold");
1783     }
1784 
1785     if ((ppd_option = ppdFindOption(ppd, "RIFoldType")) != NULL)
1786     {
1787      /*
1788       * Add (Ricoh) fold finishings...
1789       */
1790 
1791       if (ppdFindChoice(ppd_option, "OutsideTwoFold"))
1792         pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, "RIFoldType", "OutsideTwoFold");
1793     }
1794 
1795     if (cupsArrayCount(pc->finishings) == 0)
1796     {
1797       cupsArrayDelete(pc->finishings);
1798       pc->finishings = NULL;
1799     }
1800   }
1801 
1802  /*
1803   * Max copies...
1804   */
1805 
1806   if ((ppd_attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
1807     pc->max_copies = atoi(ppd_attr->value);
1808   else if (ppd->manual_copies)
1809     pc->max_copies = 1;
1810   else
1811     pc->max_copies = 9999;
1812 
1813  /*
1814   * cupsChargeInfoURI, cupsJobAccountId, cupsJobAccountingUserId,
1815   * cupsJobPassword, and cupsMandatory.
1816   */
1817 
1818   if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL)
1819     pc->charge_info_uri = _cupsStrAlloc(ppd_attr->value);
1820 
1821   if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountId", NULL)) != NULL)
1822     pc->account_id = !_cups_strcasecmp(ppd_attr->value, "true");
1823 
1824   if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountingUserId", NULL)) != NULL)
1825     pc->accounting_user_id = !_cups_strcasecmp(ppd_attr->value, "true");
1826 
1827   if ((ppd_attr = ppdFindAttr(ppd, "cupsJobPassword", NULL)) != NULL)
1828     pc->password = _cupsStrAlloc(ppd_attr->value);
1829 
1830   if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
1831     pc->mandatory = _cupsArrayNewStrings(ppd_attr->value, ' ');
1832 
1833  /*
1834   * Support files...
1835   */
1836 
1837   pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
1838 				    (cups_acopy_func_t)_cupsStrAlloc,
1839 				    (cups_afree_func_t)_cupsStrFree);
1840 
1841   for (ppd_attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
1842        ppd_attr;
1843        ppd_attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
1844     cupsArrayAdd(pc->support_files, ppd_attr->value);
1845 
1846   if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
1847     cupsArrayAdd(pc->support_files, ppd_attr->value);
1848 
1849  /*
1850   * Return the cache data...
1851   */
1852 
1853   return (pc);
1854 
1855  /*
1856   * If we get here we need to destroy the PWG mapping data and return NULL...
1857   */
1858 
1859   create_error:
1860 
1861   _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of memory."), 1);
1862   _ppdCacheDestroy(pc);
1863 
1864   return (NULL);
1865 }
1866 
1867 
1868 /*
1869  * '_ppdCacheDestroy()' - Free all memory used for PWG mapping data.
1870  */
1871 
1872 void
_ppdCacheDestroy(_ppd_cache_t * pc)1873 _ppdCacheDestroy(_ppd_cache_t *pc)	/* I - PPD cache and mapping data */
1874 {
1875   int		i;			/* Looping var */
1876   pwg_map_t	*map;			/* Current map */
1877   pwg_size_t	*size;			/* Current size */
1878 
1879 
1880  /*
1881   * Range check input...
1882   */
1883 
1884   if (!pc)
1885     return;
1886 
1887  /*
1888   * Free memory as needed...
1889   */
1890 
1891   if (pc->bins)
1892   {
1893     for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
1894     {
1895       _cupsStrFree(map->pwg);
1896       _cupsStrFree(map->ppd);
1897     }
1898 
1899     free(pc->bins);
1900   }
1901 
1902   if (pc->sizes)
1903   {
1904     for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1905     {
1906       _cupsStrFree(size->map.pwg);
1907       _cupsStrFree(size->map.ppd);
1908     }
1909 
1910     free(pc->sizes);
1911   }
1912 
1913   if (pc->source_option)
1914     _cupsStrFree(pc->source_option);
1915 
1916   if (pc->sources)
1917   {
1918     for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
1919     {
1920       _cupsStrFree(map->pwg);
1921       _cupsStrFree(map->ppd);
1922     }
1923 
1924     free(pc->sources);
1925   }
1926 
1927   if (pc->types)
1928   {
1929     for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
1930     {
1931       _cupsStrFree(map->pwg);
1932       _cupsStrFree(map->ppd);
1933     }
1934 
1935     free(pc->types);
1936   }
1937 
1938   if (pc->custom_max_keyword)
1939     _cupsStrFree(pc->custom_max_keyword);
1940 
1941   if (pc->custom_min_keyword)
1942     _cupsStrFree(pc->custom_min_keyword);
1943 
1944   _cupsStrFree(pc->product);
1945   cupsArrayDelete(pc->filters);
1946   cupsArrayDelete(pc->prefilters);
1947   cupsArrayDelete(pc->finishings);
1948 
1949   _cupsStrFree(pc->charge_info_uri);
1950   _cupsStrFree(pc->password);
1951 
1952   cupsArrayDelete(pc->mandatory);
1953 
1954   cupsArrayDelete(pc->support_files);
1955 
1956   free(pc);
1957 }
1958 
1959 
1960 /*
1961  * '_ppdCacheGetBin()' - Get the PWG output-bin keyword associated with a PPD
1962  *                  OutputBin.
1963  */
1964 
1965 const char *				/* O - output-bin or NULL */
_ppdCacheGetBin(_ppd_cache_t * pc,const char * output_bin)1966 _ppdCacheGetBin(
1967     _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
1968     const char   *output_bin)		/* I - PPD OutputBin string */
1969 {
1970   int	i;				/* Looping var */
1971 
1972 
1973  /*
1974   * Range check input...
1975   */
1976 
1977   if (!pc || !output_bin)
1978     return (NULL);
1979 
1980  /*
1981   * Look up the OutputBin string...
1982   */
1983 
1984 
1985   for (i = 0; i < pc->num_bins; i ++)
1986     if (!_cups_strcasecmp(output_bin, pc->bins[i].ppd))
1987       return (pc->bins[i].pwg);
1988 
1989   return (NULL);
1990 }
1991 
1992 
1993 /*
1994  * '_ppdCacheGetFinishingOptions()' - Get PPD finishing options for the given
1995  *                                    IPP finishings value(s).
1996  */
1997 
1998 int					/* O  - New number of options */
_ppdCacheGetFinishingOptions(_ppd_cache_t * pc,ipp_t * job,ipp_finishings_t value,int num_options,cups_option_t ** options)1999 _ppdCacheGetFinishingOptions(
2000     _ppd_cache_t     *pc,		/* I  - PPD cache and mapping data */
2001     ipp_t            *job,		/* I  - Job attributes or NULL */
2002     ipp_finishings_t value,		/* I  - IPP finishings value of IPP_FINISHINGS_NONE */
2003     int              num_options,	/* I  - Number of options */
2004     cups_option_t    **options)		/* IO - Options */
2005 {
2006   int			i;		/* Looping var */
2007   _pwg_finishings_t	*f,		/* PWG finishings options */
2008 			key;		/* Search key */
2009   ipp_attribute_t	*attr;		/* Finishings attribute */
2010   cups_option_t		*option;	/* Current finishings option */
2011 
2012 
2013  /*
2014   * Range check input...
2015   */
2016 
2017   if (!pc || cupsArrayCount(pc->finishings) == 0 || !options ||
2018       (!job && value == IPP_FINISHINGS_NONE))
2019     return (num_options);
2020 
2021  /*
2022   * Apply finishing options...
2023   */
2024 
2025   if (job && (attr = ippFindAttribute(job, "finishings", IPP_TAG_ENUM)) != NULL)
2026   {
2027     int	num_values = ippGetCount(attr);	/* Number of values */
2028 
2029     for (i = 0; i < num_values; i ++)
2030     {
2031       key.value = (ipp_finishings_t)ippGetInteger(attr, i);
2032 
2033       if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
2034       {
2035         int	j;			/* Another looping var */
2036 
2037         for (j = f->num_options, option = f->options; j > 0; j --, option ++)
2038           num_options = cupsAddOption(option->name, option->value,
2039                                       num_options, options);
2040       }
2041     }
2042   }
2043   else if (value != IPP_FINISHINGS_NONE)
2044   {
2045     key.value = value;
2046 
2047     if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
2048     {
2049       int	j;			/* Another looping var */
2050 
2051       for (j = f->num_options, option = f->options; j > 0; j --, option ++)
2052 	num_options = cupsAddOption(option->name, option->value,
2053 				    num_options, options);
2054     }
2055   }
2056 
2057   return (num_options);
2058 }
2059 
2060 
2061 /*
2062  * '_ppdCacheGetFinishingValues()' - Get IPP finishings value(s) from the given
2063  *                                   PPD options.
2064  */
2065 
2066 int					/* O - Number of finishings values */
_ppdCacheGetFinishingValues(_ppd_cache_t * pc,int num_options,cups_option_t * options,int max_values,int * values)2067 _ppdCacheGetFinishingValues(
2068     _ppd_cache_t  *pc,			/* I - PPD cache and mapping data */
2069     int           num_options,		/* I - Number of options */
2070     cups_option_t *options,		/* I - Options */
2071     int           max_values,		/* I - Maximum number of finishings values */
2072     int           *values)		/* O - Finishings values */
2073 {
2074   int			i,		/* Looping var */
2075 			num_values = 0;	/* Number of values */
2076   _pwg_finishings_t	*f;		/* Current finishings option */
2077   cups_option_t		*option;	/* Current option */
2078   const char		*val;		/* Value for option */
2079 
2080 
2081  /*
2082   * Range check input...
2083   */
2084 
2085   DEBUG_printf(("_ppdCacheGetFinishingValues(pc=%p, num_options=%d, options=%p, max_values=%d, values=%p)", pc, num_options, options, max_values, values));
2086 
2087   if (!pc || !pc->finishings || num_options < 1 || max_values < 1 || !values)
2088   {
2089     DEBUG_puts("_ppdCacheGetFinishingValues: Bad arguments, returning 0.");
2090     return (0);
2091   }
2092 
2093  /*
2094   * Go through the finishings options and see what is set...
2095   */
2096 
2097   for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
2098        f;
2099        f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
2100   {
2101     DEBUG_printf(("_ppdCacheGetFinishingValues: Checking %d (%s)", f->value, ippEnumString("finishings", f->value)));
2102 
2103     for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2104     {
2105       DEBUG_printf(("_ppdCacheGetFinishingValues: %s=%s?", option->name, option->value));
2106 
2107       if ((val = cupsGetOption(option->name, num_options, options)) == NULL ||
2108           _cups_strcasecmp(option->value, val))
2109       {
2110         DEBUG_puts("_ppdCacheGetFinishingValues: NO");
2111         break;
2112       }
2113     }
2114 
2115     if (i == 0)
2116     {
2117       DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d.", f->value));
2118 
2119       values[num_values ++] = f->value;
2120 
2121       if (num_values >= max_values)
2122         break;
2123     }
2124   }
2125 
2126   DEBUG_printf(("_ppdCacheGetFinishingValues: Returning %d.", num_values));
2127 
2128   return (num_values);
2129 }
2130 
2131 
2132 /*
2133  * '_ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job
2134  *                        attributes or a keyword string.
2135  */
2136 
2137 const char *				/* O - PPD InputSlot or NULL */
_ppdCacheGetInputSlot(_ppd_cache_t * pc,ipp_t * job,const char * keyword)2138 _ppdCacheGetInputSlot(
2139     _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2140     ipp_t        *job,			/* I - Job attributes or NULL */
2141     const char   *keyword)		/* I - Keyword string or NULL */
2142 {
2143  /*
2144   * Range check input...
2145   */
2146 
2147   if (!pc || pc->num_sources == 0 || (!job && !keyword))
2148     return (NULL);
2149 
2150   if (job && !keyword)
2151   {
2152    /*
2153     * Lookup the media-col attribute and any media-source found there...
2154     */
2155 
2156     ipp_attribute_t	*media_col,	/* media-col attribute */
2157 			*media_source;	/* media-source attribute */
2158     pwg_size_t		size;		/* Dimensional size */
2159     int			margins_set;	/* Were the margins set? */
2160 
2161     media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2162     if (media_col &&
2163         (media_source = ippFindAttribute(ippGetCollection(media_col, 0),
2164                                          "media-source",
2165 	                                 IPP_TAG_KEYWORD)) != NULL)
2166     {
2167      /*
2168       * Use the media-source value from media-col...
2169       */
2170 
2171       keyword = ippGetString(media_source, 0, NULL);
2172     }
2173     else if (pwgInitSize(&size, job, &margins_set))
2174     {
2175      /*
2176       * For media <= 5x7, look for a photo tray...
2177       */
2178 
2179       if (size.width <= (5 * 2540) && size.length <= (7 * 2540))
2180         keyword = "photo";
2181     }
2182   }
2183 
2184   if (keyword)
2185   {
2186     int	i;				/* Looping var */
2187 
2188     for (i = 0; i < pc->num_sources; i ++)
2189       if (!_cups_strcasecmp(keyword, pc->sources[i].pwg))
2190         return (pc->sources[i].ppd);
2191   }
2192 
2193   return (NULL);
2194 }
2195 
2196 
2197 /*
2198  * '_ppdCacheGetMediaType()' - Get the PPD MediaType associated with the job
2199  *                        attributes or a keyword string.
2200  */
2201 
2202 const char *				/* O - PPD MediaType or NULL */
_ppdCacheGetMediaType(_ppd_cache_t * pc,ipp_t * job,const char * keyword)2203 _ppdCacheGetMediaType(
2204     _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2205     ipp_t        *job,			/* I - Job attributes or NULL */
2206     const char   *keyword)		/* I - Keyword string or NULL */
2207 {
2208  /*
2209   * Range check input...
2210   */
2211 
2212   if (!pc || pc->num_types == 0 || (!job && !keyword))
2213     return (NULL);
2214 
2215   if (job && !keyword)
2216   {
2217    /*
2218     * Lookup the media-col attribute and any media-source found there...
2219     */
2220 
2221     ipp_attribute_t	*media_col,	/* media-col attribute */
2222 			*media_type;	/* media-type attribute */
2223 
2224     media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2225     if (media_col)
2226     {
2227       if ((media_type = ippFindAttribute(media_col->values[0].collection,
2228                                          "media-type",
2229 	                                 IPP_TAG_KEYWORD)) == NULL)
2230 	media_type = ippFindAttribute(media_col->values[0].collection,
2231 				      "media-type", IPP_TAG_NAME);
2232 
2233       if (media_type)
2234 	keyword = media_type->values[0].string.text;
2235     }
2236   }
2237 
2238   if (keyword)
2239   {
2240     int	i;				/* Looping var */
2241 
2242     for (i = 0; i < pc->num_types; i ++)
2243       if (!_cups_strcasecmp(keyword, pc->types[i].pwg))
2244         return (pc->types[i].ppd);
2245   }
2246 
2247   return (NULL);
2248 }
2249 
2250 
2251 /*
2252  * '_ppdCacheGetOutputBin()' - Get the PPD OutputBin associated with the keyword
2253  *                        string.
2254  */
2255 
2256 const char *				/* O - PPD OutputBin or NULL */
_ppdCacheGetOutputBin(_ppd_cache_t * pc,const char * output_bin)2257 _ppdCacheGetOutputBin(
2258     _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2259     const char   *output_bin)		/* I - Keyword string */
2260 {
2261   int	i;				/* Looping var */
2262 
2263 
2264  /*
2265   * Range check input...
2266   */
2267 
2268   if (!pc || !output_bin)
2269     return (NULL);
2270 
2271  /*
2272   * Look up the OutputBin string...
2273   */
2274 
2275 
2276   for (i = 0; i < pc->num_bins; i ++)
2277     if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg))
2278       return (pc->bins[i].ppd);
2279 
2280   return (NULL);
2281 }
2282 
2283 
2284 /*
2285  * '_ppdCacheGetPageSize()' - Get the PPD PageSize associated with the job
2286  *                       attributes or a keyword string.
2287  */
2288 
2289 const char *				/* O - PPD PageSize or NULL */
_ppdCacheGetPageSize(_ppd_cache_t * pc,ipp_t * job,const char * keyword,int * exact)2290 _ppdCacheGetPageSize(
2291     _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2292     ipp_t        *job,			/* I - Job attributes or NULL */
2293     const char   *keyword,		/* I - Keyword string or NULL */
2294     int          *exact)		/* O - 1 if exact match, 0 otherwise */
2295 {
2296   int		i;			/* Looping var */
2297   pwg_size_t	*size,			/* Current size */
2298 		*closest,		/* Closest size */
2299 		jobsize;		/* Size data from job */
2300   int		margins_set,		/* Were the margins set? */
2301 		dwidth,			/* Difference in width */
2302 		dlength,		/* Difference in length */
2303 		dleft,			/* Difference in left margins */
2304 		dright,			/* Difference in right margins */
2305 		dbottom,		/* Difference in bottom margins */
2306 		dtop,			/* Difference in top margins */
2307 		dmin,			/* Minimum difference */
2308 		dclosest;		/* Closest difference */
2309   const char	*ppd_name;		/* PPD media name */
2310 
2311 
2312   DEBUG_printf(("_ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)",
2313 	        pc, job, keyword, exact));
2314 
2315  /*
2316   * Range check input...
2317   */
2318 
2319   if (!pc || (!job && !keyword))
2320     return (NULL);
2321 
2322   if (exact)
2323     *exact = 0;
2324 
2325   ppd_name = keyword;
2326 
2327   if (job)
2328   {
2329    /*
2330     * Try getting the PPD media name from the job attributes...
2331     */
2332 
2333     ipp_attribute_t	*attr;		/* Job attribute */
2334 
2335     if ((attr = ippFindAttribute(job, "PageSize", IPP_TAG_ZERO)) == NULL)
2336       if ((attr = ippFindAttribute(job, "PageRegion", IPP_TAG_ZERO)) == NULL)
2337         attr = ippFindAttribute(job, "media", IPP_TAG_ZERO);
2338 
2339 #ifdef DEBUG
2340     if (attr)
2341       DEBUG_printf(("1_ppdCacheGetPageSize: Found attribute %s (%s)",
2342                     attr->name, ippTagString(attr->value_tag)));
2343     else
2344       DEBUG_puts("1_ppdCacheGetPageSize: Did not find media attribute.");
2345 #endif /* DEBUG */
2346 
2347     if (attr && (attr->value_tag == IPP_TAG_NAME ||
2348                  attr->value_tag == IPP_TAG_KEYWORD))
2349       ppd_name = attr->values[0].string.text;
2350   }
2351 
2352   DEBUG_printf(("1_ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name));
2353 
2354   if (ppd_name)
2355   {
2356    /*
2357     * Try looking up the named PPD size first...
2358     */
2359 
2360     for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2361     {
2362       DEBUG_printf(("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]",
2363                     (int)(size - pc->sizes), size->map.pwg, size->map.ppd));
2364 
2365       if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
2366           !_cups_strcasecmp(ppd_name, size->map.pwg))
2367       {
2368 	if (exact)
2369 	  *exact = 1;
2370 
2371         DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", ppd_name));
2372 
2373         return (size->map.ppd);
2374       }
2375     }
2376   }
2377 
2378   if (job && !keyword)
2379   {
2380    /*
2381     * Get the size using media-col or media, with the preference being
2382     * media-col.
2383     */
2384 
2385     if (!pwgInitSize(&jobsize, job, &margins_set))
2386       return (NULL);
2387   }
2388   else
2389   {
2390    /*
2391     * Get the size using a media keyword...
2392     */
2393 
2394     pwg_media_t	*media;		/* Media definition */
2395 
2396 
2397     if ((media = pwgMediaForPWG(keyword)) == NULL)
2398       if ((media = pwgMediaForLegacy(keyword)) == NULL)
2399         if ((media = pwgMediaForPPD(keyword)) == NULL)
2400 	  return (NULL);
2401 
2402     jobsize.width  = media->width;
2403     jobsize.length = media->length;
2404     margins_set    = 0;
2405   }
2406 
2407  /*
2408   * Now that we have the dimensions and possibly the margins, look at the
2409   * available sizes and find the match...
2410   */
2411 
2412   closest  = NULL;
2413   dclosest = 999999999;
2414 
2415   if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
2416       _cups_strncasecmp(ppd_name, "custom_", 7))
2417   {
2418     for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2419     {
2420      /*
2421       * Adobe uses a size matching algorithm with an epsilon of 5 points, which
2422       * is just about 176/2540ths...
2423       */
2424 
2425       dwidth  = size->width - jobsize.width;
2426       dlength = size->length - jobsize.length;
2427 
2428       if (dwidth <= -176 || dwidth >= 176 || dlength <= -176 || dlength >= 176)
2429 	continue;
2430 
2431       if (margins_set)
2432       {
2433        /*
2434 	* Use a tighter epsilon of 1 point (35/2540ths) for margins...
2435 	*/
2436 
2437 	dleft   = size->left - jobsize.left;
2438 	dright  = size->right - jobsize.right;
2439 	dtop    = size->top - jobsize.top;
2440 	dbottom = size->bottom - jobsize.bottom;
2441 
2442 	if (dleft <= -35 || dleft >= 35 || dright <= -35 || dright >= 35 ||
2443 	    dtop <= -35 || dtop >= 35 || dbottom <= -35 || dbottom >= 35)
2444 	{
2445 	  dleft   = dleft < 0 ? -dleft : dleft;
2446 	  dright  = dright < 0 ? -dright : dright;
2447 	  dbottom = dbottom < 0 ? -dbottom : dbottom;
2448 	  dtop    = dtop < 0 ? -dtop : dtop;
2449 	  dmin    = dleft + dright + dbottom + dtop;
2450 
2451 	  if (dmin < dclosest)
2452 	  {
2453 	    dclosest = dmin;
2454 	    closest  = size;
2455 	  }
2456 
2457 	  continue;
2458 	}
2459       }
2460 
2461       if (exact)
2462 	*exact = 1;
2463 
2464       DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd));
2465 
2466       return (size->map.ppd);
2467     }
2468   }
2469 
2470   if (closest)
2471   {
2472     DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (closest)",
2473                   closest->map.ppd));
2474 
2475     return (closest->map.ppd);
2476   }
2477 
2478  /*
2479   * If we get here we need to check for custom page size support...
2480   */
2481 
2482   if (jobsize.width >= pc->custom_min_width &&
2483       jobsize.width <= pc->custom_max_width &&
2484       jobsize.length >= pc->custom_min_length &&
2485       jobsize.length <= pc->custom_max_length)
2486   {
2487    /*
2488     * In range, format as Custom.WWWWxLLLL (points).
2489     */
2490 
2491     snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d",
2492              (int)PWG_TO_POINTS(jobsize.width), (int)PWG_TO_POINTS(jobsize.length));
2493 
2494     if (margins_set && exact)
2495     {
2496       dleft   = pc->custom_size.left - jobsize.left;
2497       dright  = pc->custom_size.right - jobsize.right;
2498       dtop    = pc->custom_size.top - jobsize.top;
2499       dbottom = pc->custom_size.bottom - jobsize.bottom;
2500 
2501       if (dleft > -35 && dleft < 35 && dright > -35 && dright < 35 &&
2502           dtop > -35 && dtop < 35 && dbottom > -35 && dbottom < 35)
2503 	*exact = 1;
2504     }
2505     else if (exact)
2506       *exact = 1;
2507 
2508     DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (custom)",
2509                   pc->custom_ppd_size));
2510 
2511     return (pc->custom_ppd_size);
2512   }
2513 
2514  /*
2515   * No custom page size support or the size is out of range - return NULL.
2516   */
2517 
2518   DEBUG_puts("1_ppdCacheGetPageSize: Returning NULL");
2519 
2520   return (NULL);
2521 }
2522 
2523 
2524 /*
2525  * '_ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize.
2526  */
2527 
2528 pwg_size_t *				/* O - PWG size or NULL */
_ppdCacheGetSize(_ppd_cache_t * pc,const char * page_size)2529 _ppdCacheGetSize(
2530     _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2531     const char   *page_size)		/* I - PPD PageSize */
2532 {
2533   int		i;			/* Looping var */
2534   pwg_media_t	*media;			/* Media */
2535   pwg_size_t	*size;			/* Current size */
2536 
2537 
2538  /*
2539   * Range check input...
2540   */
2541 
2542   if (!pc || !page_size)
2543     return (NULL);
2544 
2545   if (!_cups_strncasecmp(page_size, "Custom.", 7))
2546   {
2547    /*
2548     * Custom size; size name can be one of the following:
2549     *
2550     *    Custom.WIDTHxLENGTHin    - Size in inches
2551     *    Custom.WIDTHxLENGTHft    - Size in feet
2552     *    Custom.WIDTHxLENGTHcm    - Size in centimeters
2553     *    Custom.WIDTHxLENGTHmm    - Size in millimeters
2554     *    Custom.WIDTHxLENGTHm     - Size in meters
2555     *    Custom.WIDTHxLENGTH[pt]  - Size in points
2556     */
2557 
2558     double		w, l;		/* Width and length of page */
2559     char		*ptr;		/* Pointer into PageSize */
2560     struct lconv	*loc;		/* Locale data */
2561 
2562     loc = localeconv();
2563     w   = (float)_cupsStrScand(page_size + 7, &ptr, loc);
2564     if (!ptr || *ptr != 'x')
2565       return (NULL);
2566 
2567     l = (float)_cupsStrScand(ptr + 1, &ptr, loc);
2568     if (!ptr)
2569       return (NULL);
2570 
2571     if (!_cups_strcasecmp(ptr, "in"))
2572     {
2573       w *= 2540.0;
2574       l *= 2540.0;
2575     }
2576     else if (!_cups_strcasecmp(ptr, "ft"))
2577     {
2578       w *= 12.0 * 2540.0;
2579       l *= 12.0 * 2540.0;
2580     }
2581     else if (!_cups_strcasecmp(ptr, "mm"))
2582     {
2583       w *= 100.0;
2584       l *= 100.0;
2585     }
2586     else if (!_cups_strcasecmp(ptr, "cm"))
2587     {
2588       w *= 1000.0;
2589       l *= 1000.0;
2590     }
2591     else if (!_cups_strcasecmp(ptr, "m"))
2592     {
2593       w *= 100000.0;
2594       l *= 100000.0;
2595     }
2596     else
2597     {
2598       w *= 2540.0 / 72.0;
2599       l *= 2540.0 / 72.0;
2600     }
2601 
2602     pc->custom_size.width  = (int)w;
2603     pc->custom_size.length = (int)l;
2604 
2605     return (&(pc->custom_size));
2606   }
2607 
2608  /*
2609   * Not a custom size - look it up...
2610   */
2611 
2612   for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2613     if (!_cups_strcasecmp(page_size, size->map.ppd) ||
2614         !_cups_strcasecmp(page_size, size->map.pwg))
2615       return (size);
2616 
2617  /*
2618   * Look up standard sizes...
2619   */
2620 
2621   if ((media = pwgMediaForPPD(page_size)) == NULL)
2622     if ((media = pwgMediaForLegacy(page_size)) == NULL)
2623       media = pwgMediaForPWG(page_size);
2624 
2625   if (media)
2626   {
2627     pc->custom_size.width  = media->width;
2628     pc->custom_size.length = media->length;
2629 
2630     return (&(pc->custom_size));
2631   }
2632 
2633   return (NULL);
2634 }
2635 
2636 
2637 /*
2638  * '_ppdCacheGetSource()' - Get the PWG media-source associated with a PPD
2639  *                          InputSlot.
2640  */
2641 
2642 const char *				/* O - PWG media-source keyword */
_ppdCacheGetSource(_ppd_cache_t * pc,const char * input_slot)2643 _ppdCacheGetSource(
2644     _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2645     const char   *input_slot)		/* I - PPD InputSlot */
2646 {
2647   int		i;			/* Looping var */
2648   pwg_map_t	*source;		/* Current source */
2649 
2650 
2651  /*
2652   * Range check input...
2653   */
2654 
2655   if (!pc || !input_slot)
2656     return (NULL);
2657 
2658   for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++)
2659     if (!_cups_strcasecmp(input_slot, source->ppd))
2660       return (source->pwg);
2661 
2662   return (NULL);
2663 }
2664 
2665 
2666 /*
2667  * '_ppdCacheGetType()' - Get the PWG media-type associated with a PPD
2668  *                        MediaType.
2669  */
2670 
2671 const char *				/* O - PWG media-type keyword */
_ppdCacheGetType(_ppd_cache_t * pc,const char * media_type)2672 _ppdCacheGetType(
2673     _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2674     const char   *media_type)		/* I - PPD MediaType */
2675 {
2676   int		i;			/* Looping var */
2677   pwg_map_t	*type;			/* Current type */
2678 
2679 
2680  /*
2681   * Range check input...
2682   */
2683 
2684   if (!pc || !media_type)
2685     return (NULL);
2686 
2687   for (i = pc->num_types, type = pc->types; i > 0; i --, type ++)
2688     if (!_cups_strcasecmp(media_type, type->ppd))
2689       return (type->pwg);
2690 
2691   return (NULL);
2692 }
2693 
2694 
2695 /*
2696  * '_ppdCacheWriteFile()' - Write PWG mapping data to a file.
2697  */
2698 
2699 int					/* O - 1 on success, 0 on failure */
_ppdCacheWriteFile(_ppd_cache_t * pc,const char * filename,ipp_t * attrs)2700 _ppdCacheWriteFile(
2701     _ppd_cache_t *pc,			/* I - PPD cache and mapping data */
2702     const char   *filename,		/* I - File to write */
2703     ipp_t        *attrs)		/* I - Attributes to write, if any */
2704 {
2705   int			i, j, k;	/* Looping vars */
2706   cups_file_t		*fp;		/* Output file */
2707   pwg_size_t		*size;		/* Current size */
2708   pwg_map_t		*map;		/* Current map */
2709   _pwg_finishings_t	*f;		/* Current finishing option */
2710   cups_option_t		*option;	/* Current option */
2711   const char		*value;		/* Filter/pre-filter value */
2712   char			newfile[1024];	/* New filename */
2713 
2714 
2715  /*
2716   * Range check input...
2717   */
2718 
2719   if (!pc || !filename)
2720   {
2721     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
2722     return (0);
2723   }
2724 
2725  /*
2726   * Open the file and write with compression...
2727   */
2728 
2729   snprintf(newfile, sizeof(newfile), "%s.N", filename);
2730   if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
2731   {
2732     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
2733     return (0);
2734   }
2735 
2736  /*
2737   * Standard header...
2738   */
2739 
2740   cupsFilePrintf(fp, "#CUPS-PPD-CACHE-%d\n", _PPD_CACHE_VERSION);
2741 
2742  /*
2743   * Output bins...
2744   */
2745 
2746   if (pc->num_bins > 0)
2747   {
2748     cupsFilePrintf(fp, "NumBins %d\n", pc->num_bins);
2749     for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
2750       cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd);
2751   }
2752 
2753  /*
2754   * Media sizes...
2755   */
2756 
2757   cupsFilePrintf(fp, "NumSizes %d\n", pc->num_sizes);
2758   for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2759     cupsFilePrintf(fp, "Size %s %s %d %d %d %d %d %d\n", size->map.pwg,
2760 		   size->map.ppd, size->width, size->length, size->left,
2761 		   size->bottom, size->right, size->top);
2762   if (pc->custom_max_width > 0)
2763     cupsFilePrintf(fp, "CustomSize %d %d %d %d %d %d %d %d\n",
2764                    pc->custom_max_width, pc->custom_max_length,
2765 		   pc->custom_min_width, pc->custom_min_length,
2766 		   pc->custom_size.left, pc->custom_size.bottom,
2767 		   pc->custom_size.right, pc->custom_size.top);
2768 
2769  /*
2770   * Media sources...
2771   */
2772 
2773   if (pc->source_option)
2774     cupsFilePrintf(fp, "SourceOption %s\n", pc->source_option);
2775 
2776   if (pc->num_sources > 0)
2777   {
2778     cupsFilePrintf(fp, "NumSources %d\n", pc->num_sources);
2779     for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
2780       cupsFilePrintf(fp, "Source %s %s\n", map->pwg, map->ppd);
2781   }
2782 
2783  /*
2784   * Media types...
2785   */
2786 
2787   if (pc->num_types > 0)
2788   {
2789     cupsFilePrintf(fp, "NumTypes %d\n", pc->num_types);
2790     for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
2791       cupsFilePrintf(fp, "Type %s %s\n", map->pwg, map->ppd);
2792   }
2793 
2794  /*
2795   * Presets...
2796   */
2797 
2798   for (i = _PWG_PRINT_COLOR_MODE_MONOCHROME; i < _PWG_PRINT_COLOR_MODE_MAX; i ++)
2799     for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
2800       if (pc->num_presets[i][j])
2801       {
2802 	cupsFilePrintf(fp, "Preset %d %d", i, j);
2803 	for (k = pc->num_presets[i][j], option = pc->presets[i][j];
2804 	     k > 0;
2805 	     k --, option ++)
2806 	  cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2807 	cupsFilePutChar(fp, '\n');
2808       }
2809 
2810  /*
2811   * Duplex/sides...
2812   */
2813 
2814   if (pc->sides_option)
2815     cupsFilePrintf(fp, "SidesOption %s\n", pc->sides_option);
2816 
2817   if (pc->sides_1sided)
2818     cupsFilePrintf(fp, "Sides1Sided %s\n", pc->sides_1sided);
2819 
2820   if (pc->sides_2sided_long)
2821     cupsFilePrintf(fp, "Sides2SidedLong %s\n", pc->sides_2sided_long);
2822 
2823   if (pc->sides_2sided_short)
2824     cupsFilePrintf(fp, "Sides2SidedShort %s\n", pc->sides_2sided_short);
2825 
2826  /*
2827   * Product, cupsFilter, cupsFilter2, and cupsPreFilter...
2828   */
2829 
2830   if (pc->product)
2831     cupsFilePutConf(fp, "Product", pc->product);
2832 
2833   for (value = (const char *)cupsArrayFirst(pc->filters);
2834        value;
2835        value = (const char *)cupsArrayNext(pc->filters))
2836     cupsFilePutConf(fp, "Filter", value);
2837 
2838   for (value = (const char *)cupsArrayFirst(pc->prefilters);
2839        value;
2840        value = (const char *)cupsArrayNext(pc->prefilters))
2841     cupsFilePutConf(fp, "PreFilter", value);
2842 
2843   cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
2844 
2845  /*
2846   * Finishing options...
2847   */
2848 
2849   for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
2850        f;
2851        f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
2852   {
2853     cupsFilePrintf(fp, "Finishings %d", f->value);
2854     for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2855       cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2856     cupsFilePutChar(fp, '\n');
2857   }
2858 
2859  /*
2860   * Max copies...
2861   */
2862 
2863   cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies);
2864 
2865  /*
2866   * Accounting/quota/PIN/managed printing values...
2867   */
2868 
2869   if (pc->charge_info_uri)
2870     cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri);
2871 
2872   cupsFilePrintf(fp, "AccountId %s\n", pc->account_id ? "true" : "false");
2873   cupsFilePrintf(fp, "AccountingUserId %s\n",
2874                  pc->accounting_user_id ? "true" : "false");
2875 
2876   if (pc->password)
2877     cupsFilePutConf(fp, "Password", pc->password);
2878 
2879   for (value = (char *)cupsArrayFirst(pc->mandatory);
2880        value;
2881        value = (char *)cupsArrayNext(pc->mandatory))
2882     cupsFilePutConf(fp, "Mandatory", value);
2883 
2884  /*
2885   * Support files...
2886   */
2887 
2888   for (value = (char *)cupsArrayFirst(pc->support_files);
2889        value;
2890        value = (char *)cupsArrayNext(pc->support_files))
2891     cupsFilePutConf(fp, "SupportFile", value);
2892 
2893  /*
2894   * IPP attributes, if any...
2895   */
2896 
2897   if (attrs)
2898   {
2899     cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
2900 
2901     attrs->state = IPP_STATE_IDLE;
2902     ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
2903   }
2904 
2905  /*
2906   * Close and return...
2907   */
2908 
2909   if (cupsFileClose(fp))
2910   {
2911     unlink(newfile);
2912     return (0);
2913   }
2914 
2915   unlink(filename);
2916   return (!rename(newfile, filename));
2917 }
2918 
2919 
2920 /*
2921  * '_ppdCreateFromIPP()' - Create a PPD file describing the capabilities
2922  *                         of an IPP printer.
2923  */
2924 
2925 char *					/* O - PPD filename or @code NULL@ on error */
_ppdCreateFromIPP(char * buffer,size_t bufsize,ipp_t * response)2926 _ppdCreateFromIPP(char   *buffer,	/* I - Filename buffer */
2927                   size_t bufsize,	/* I - Size of filename buffer */
2928 		  ipp_t  *response)	/* I - Get-Printer-Attributes response */
2929 {
2930   cups_file_t		*fp;		/* PPD file */
2931   cups_array_t		*sizes;		/* Media sizes we've added */
2932   ipp_attribute_t	*attr,		/* xxx-supported */
2933 			*defattr,	/* xxx-default */
2934                         *quality,	/* print-quality-supported */
2935 			*x_dim, *y_dim;	/* Media dimensions */
2936   ipp_t			*media_size;	/* Media size collection */
2937   char			make[256],	/* Make and model */
2938 			*model,		/* Model name */
2939 			ppdname[PPD_MAX_NAME];
2940 		    			/* PPD keyword */
2941   int			i, j,		/* Looping vars */
2942 			count,		/* Number of values */
2943 			bottom,		/* Largest bottom margin */
2944 			left,		/* Largest left margin */
2945 			right,		/* Largest right margin */
2946 			top,		/* Largest top margin */
2947 			is_apple = 0,	/* Does the printer support Apple raster? */
2948 			is_pdf = 0,	/* Does the printer support PDF? */
2949 			is_pwg = 0;	/* Does the printer support PWG Raster? */
2950   pwg_media_t		*pwg;		/* PWG media size */
2951   int			xres, yres;	/* Resolution values */
2952   cups_lang_t		*lang = cupsLangDefault();
2953 					/* Localization info */
2954   struct lconv		*loc = localeconv();
2955 					/* Locale data */
2956   static const char * const finishings[][2] =
2957   {					/* Finishings strings */
2958     { "bale", _("Bale") },
2959     { "bind", _("Bind") },
2960     { "bind-bottom", _("Bind (Reverse Landscape)") },
2961     { "bind-left", _("Bind (Portrait)") },
2962     { "bind-right", _("Bind (Reverse Portrait)") },
2963     { "bind-top", _("Bind (Landscape)") },
2964     { "booklet-maker", _("Booklet Maker") },
2965     { "coat", _("Coat") },
2966     { "cover", _("Cover") },
2967     { "edge-stitch", _("Staple Edge") },
2968     { "edge-stitch-bottom", _("Staple Edge (Reverse Landscape)") },
2969     { "edge-stitch-left", _("Staple Edge (Portrait)") },
2970     { "edge-stitch-right", _("Staple Edge (Reverse Portrait)") },
2971     { "edge-stitch-top", _("Staple Edge (Landscape)") },
2972     { "fold", _("Fold") },
2973     { "fold-accordian", _("Accordian Fold") },
2974     { "fold-double-gate", _("Double Gate Fold") },
2975     { "fold-engineering-z", _("Engineering Z Fold") },
2976     { "fold-gate", _("Gate Fold") },
2977     { "fold-half", _("Half Fold") },
2978     { "fold-half-z", _("Half Z Fold") },
2979     { "fold-left-gate", _("Left Gate Fold") },
2980     { "fold-letter", _("Letter Fold") },
2981     { "fold-parallel", _("Parallel Fold") },
2982     { "fold-poster", _("Poster Fold") },
2983     { "fold-right-gate", _("Right Gate Fold") },
2984     { "fold-z", _("Z Fold") },
2985     { "jog-offset", _("Jog") },
2986     { "laminate", _("Laminate") },
2987     { "punch", _("Punch") },
2988     { "punch-bottom-left", _("Single Punch (Reverse Landscape)") },
2989     { "punch-bottom-right", _("Single Punch (Reverse Portrait)") },
2990     { "punch-double-bottom", _("2-Hole Punch (Reverse Portrait)") },
2991     { "punch-double-left", _("2-Hole Punch (Reverse Landscape)") },
2992     { "punch-double-right", _("2-Hole Punch (Landscape)") },
2993     { "punch-double-top", _("2-Hole Punch (Portrait)") },
2994     { "punch-quad-bottom", _("4-Hole Punch (Reverse Landscape)") },
2995     { "punch-quad-left", _("4-Hole Punch (Portrait)") },
2996     { "punch-quad-right", _("4-Hole Punch (Reverse Portrait)") },
2997     { "punch-quad-top", _("4-Hole Punch (Landscape)") },
2998     { "punch-top-left", _("Single Punch (Portrait)") },
2999     { "punch-top-right", _("Single Punch (Landscape)") },
3000     { "punch-triple-bottom", _("3-Hole Punch (Reverse Landscape)") },
3001     { "punch-triple-left", _("3-Hole Punch (Portrait)") },
3002     { "punch-triple-right", _("3-Hole Punch (Reverse Portrait)") },
3003     { "punch-triple-top", _("3-Hole Punch (Landscape)") },
3004     { "punch-multiple-bottom", _("Multi-Hole Punch (Reverse Landscape)") },
3005     { "punch-multiple-left", _("Multi-Hole Punch (Portrait)") },
3006     { "punch-multiple-right", _("Multi-Hole Punch (Reverse Portrait)") },
3007     { "punch-multiple-top", _("Multi-Hole Punch (Landscape)") },
3008     { "saddle-stitch", _("Saddle Stitch") },
3009     { "staple", _("Staple") },
3010     { "staple-bottom-left", _("Single Staple (Reverse Landscape)") },
3011     { "staple-bottom-right", _("Single Staple (Reverse Portrait)") },
3012     { "staple-dual-bottom", _("Double Staple (Reverse Landscape)") },
3013     { "staple-dual-left", _("Double Staple (Portrait)") },
3014     { "staple-dual-right", _("Double Staple (Reverse Portrait)") },
3015     { "staple-dual-top", _("Double Staple (Landscape)") },
3016     { "staple-top-left", _("Single Staple (Portrait)") },
3017     { "staple-top-right", _("Single Staple (Landscape)") },
3018     { "staple-triple-bottom", _("Triple Staple (Reverse Landscape)") },
3019     { "staple-triple-left", _("Triple Staple (Portrait)") },
3020     { "staple-triple-right", _("Triple Staple (Reverse Portrait)") },
3021     { "staple-triple-top", _("Triple Staple (Landscape)") },
3022     { "trim", _("Cut Media") }
3023   };
3024 
3025 
3026  /*
3027   * Range check input...
3028   */
3029 
3030   if (buffer)
3031     *buffer = '\0';
3032 
3033   if (!buffer || bufsize < 1)
3034   {
3035     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
3036     return (NULL);
3037   }
3038 
3039   if (!response)
3040   {
3041     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No IPP attributes."), 1);
3042     return (NULL);
3043   }
3044 
3045  /*
3046   * Open a temporary file for the PPD...
3047   */
3048 
3049   if ((fp = cupsTempFile2(buffer, (int)bufsize)) == NULL)
3050   {
3051     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
3052     return (NULL);
3053   }
3054 
3055  /*
3056   * Standard stuff for PPD file...
3057   */
3058 
3059   cupsFilePuts(fp, "*PPD-Adobe: \"4.3\"\n");
3060   cupsFilePuts(fp, "*FormatVersion: \"4.3\"\n");
3061   cupsFilePrintf(fp, "*FileVersion: \"%d.%d\"\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
3062   cupsFilePuts(fp, "*LanguageVersion: English\n");
3063   cupsFilePuts(fp, "*LanguageEncoding: ISOLatin1\n");
3064   cupsFilePuts(fp, "*PSVersion: \"(3010.000) 0\"\n");
3065   cupsFilePuts(fp, "*LanguageLevel: \"3\"\n");
3066   cupsFilePuts(fp, "*FileSystem: False\n");
3067   cupsFilePuts(fp, "*PCFileName: \"ippeve.ppd\"\n");
3068 
3069   if ((attr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT)) != NULL)
3070     strlcpy(make, ippGetString(attr, 0, NULL), sizeof(make));
3071   else
3072     strlcpy(make, "Unknown Printer", sizeof(make));
3073 
3074   if (!_cups_strncasecmp(make, "Hewlett Packard ", 16) ||
3075       !_cups_strncasecmp(make, "Hewlett-Packard ", 16))
3076   {
3077     model = make + 16;
3078     strlcpy(make, "HP", sizeof(make));
3079   }
3080   else if ((model = strchr(make, ' ')) != NULL)
3081     *model++ = '\0';
3082   else
3083     model = make;
3084 
3085   cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make);
3086   cupsFilePrintf(fp, "*ModelName: \"%s\"\n", model);
3087   cupsFilePrintf(fp, "*Product: \"(%s)\"\n", model);
3088   cupsFilePrintf(fp, "*NickName: \"%s\"\n", model);
3089   cupsFilePrintf(fp, "*ShortNickName: \"%s\"\n", model);
3090 
3091   if ((attr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN)) != NULL && ippGetBoolean(attr, 0))
3092     cupsFilePuts(fp, "*ColorDevice: True\n");
3093   else
3094     cupsFilePuts(fp, "*ColorDevice: False\n");
3095 
3096   cupsFilePrintf(fp, "*cupsVersion: %d.%d\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
3097   cupsFilePuts(fp, "*cupsSNMPSupplies: False\n");
3098   cupsFilePuts(fp, "*cupsLanguages: \"en\"\n");
3099 
3100  /*
3101   * Filters...
3102   */
3103 
3104   if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL)
3105   {
3106     is_apple = ippContainsString(attr, "image/urf");
3107     is_pdf   = ippContainsString(attr, "application/pdf");
3108     is_pwg   = ippContainsString(attr, "image/pwg-raster");
3109 
3110     for (i = 0, count = ippGetCount(attr); i < count; i ++)
3111     {
3112       const char *format = ippGetString(attr, i, NULL);
3113 					/* PDL */
3114 
3115      /*
3116       * Write cupsFilter2 lines for supported formats...
3117       */
3118 
3119       if (!_cups_strcasecmp(format, "application/pdf"))
3120         cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 10 -\"\n");
3121       else if (!_cups_strcasecmp(format, "image/jpeg") || !_cups_strcasecmp(format, "image/png"))
3122         cupsFilePrintf(fp, "*cupsFilter2: \"%s %s 0 -\"\n", format, format);
3123       else if (!_cups_strcasecmp(format, "image/pwg-raster") || !_cups_strcasecmp(format, "image/urf"))
3124         cupsFilePrintf(fp, "*cupsFilter2: \"%s %s 100 -\"\n", format, format);
3125     }
3126   }
3127 
3128   if (!is_apple && !is_pdf && !is_pwg)
3129     goto bad_ppd;
3130 
3131  /*
3132   * PageSize/PageRegion/ImageableArea/PaperDimension
3133   */
3134 
3135   if ((attr = ippFindAttribute(response, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != NULL)
3136   {
3137     for (i = 1, bottom = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3138       if (ippGetInteger(attr, i) > bottom)
3139         bottom = ippGetInteger(attr, i);
3140   }
3141   else
3142     bottom = 1270;
3143 
3144   if ((attr = ippFindAttribute(response, "media-left-margin-supported", IPP_TAG_INTEGER)) != NULL)
3145   {
3146     for (i = 1, left = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3147       if (ippGetInteger(attr, i) > left)
3148         left = ippGetInteger(attr, i);
3149   }
3150   else
3151     left = 635;
3152 
3153   if ((attr = ippFindAttribute(response, "media-right-margin-supported", IPP_TAG_INTEGER)) != NULL)
3154   {
3155     for (i = 1, right = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3156       if (ippGetInteger(attr, i) > right)
3157         right = ippGetInteger(attr, i);
3158   }
3159   else
3160     right = 635;
3161 
3162   if ((attr = ippFindAttribute(response, "media-top-margin-supported", IPP_TAG_INTEGER)) != NULL)
3163   {
3164     for (i = 1, top = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3165       if (ippGetInteger(attr, i) > top)
3166         top = ippGetInteger(attr, i);
3167   }
3168   else
3169     top = 1270;
3170 
3171   if ((defattr = ippFindAttribute(response, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3172   {
3173     if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3174     {
3175       media_size = ippGetCollection(attr, 0);
3176       x_dim      = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3177       y_dim      = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3178 
3179       if (x_dim && y_dim && (pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL)
3180 	strlcpy(ppdname, pwg->ppd, sizeof(ppdname));
3181       else
3182 	strlcpy(ppdname, "Unknown", sizeof(ppdname));
3183     }
3184     else
3185       strlcpy(ppdname, "Unknown", sizeof(ppdname));
3186   }
3187   else if ((pwg = pwgMediaForPWG(ippGetString(ippFindAttribute(response, "media-default", IPP_TAG_ZERO), 0, NULL))) != NULL)
3188     strlcpy(ppdname, pwg->ppd, sizeof(ppdname));
3189   else
3190     strlcpy(ppdname, "Unknown", sizeof(ppdname));
3191 
3192   if ((attr = ippFindAttribute(response, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) == NULL)
3193     attr = ippFindAttribute(response, "media-supported", IPP_TAG_ZERO);
3194   if (attr)
3195   {
3196     cupsFilePrintf(fp, "*OpenUI *PageSize: PickOne\n"
3197 		       "*OrderDependency: 10 AnySetup *PageSize\n"
3198                        "*DefaultPageSize: %s\n", ppdname);
3199 
3200     sizes = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
3201 
3202     for (i = 0, count = ippGetCount(attr); i < count; i ++)
3203     {
3204       if (ippGetValueTag(attr) == IPP_TAG_BEGIN_COLLECTION)
3205       {
3206 	media_size = ippGetCollection(attr, i);
3207 	x_dim      = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3208 	y_dim      = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3209 
3210 	pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3211       }
3212       else
3213         pwg = pwgMediaForPWG(ippGetString(attr, i, NULL));
3214 
3215       if (pwg)
3216       {
3217         char	twidth[256],		/* Width string */
3218 		tlength[256];		/* Length string */
3219 
3220         if (cupsArrayFind(sizes, (void *)pwg->ppd))
3221         {
3222           cupsFilePrintf(fp, "*%% warning: Duplicate size '%s' reported by printer.\n", pwg->ppd);
3223           continue;
3224         }
3225 
3226         cupsArrayAdd(sizes, (void *)pwg->ppd);
3227 
3228         _cupsStrFormatd(twidth, twidth + sizeof(twidth), pwg->width * 72.0 / 2540.0, loc);
3229         _cupsStrFormatd(tlength, tlength + sizeof(tlength), pwg->length * 72.0 / 2540.0, loc);
3230 
3231         cupsFilePrintf(fp, "*PageSize %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", pwg->ppd, twidth, tlength);
3232       }
3233     }
3234     cupsFilePuts(fp, "*CloseUI: *PageSize\n");
3235 
3236     cupsArrayDelete(sizes);
3237     sizes = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
3238 
3239     cupsFilePrintf(fp, "*OpenUI *PageRegion: PickOne\n"
3240                        "*OrderDependency: 10 AnySetup *PageRegion\n"
3241                        "*DefaultPageRegion: %s\n", ppdname);
3242     for (i = 0, count = ippGetCount(attr); i < count; i ++)
3243     {
3244       if (ippGetValueTag(attr) == IPP_TAG_BEGIN_COLLECTION)
3245       {
3246 	media_size = ippGetCollection(attr, i);
3247 	x_dim      = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3248 	y_dim      = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3249 
3250 	pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3251       }
3252       else
3253         pwg = pwgMediaForPWG(ippGetString(attr, i, NULL));
3254 
3255       if (pwg)
3256       {
3257         char	twidth[256],		/* Width string */
3258 		tlength[256];		/* Length string */
3259 
3260         if (cupsArrayFind(sizes, (void *)pwg->ppd))
3261           continue;
3262 
3263         cupsArrayAdd(sizes, (void *)pwg->ppd);
3264 
3265         _cupsStrFormatd(twidth, twidth + sizeof(twidth), pwg->width * 72.0 / 2540.0, loc);
3266         _cupsStrFormatd(tlength, tlength + sizeof(tlength), pwg->length * 72.0 / 2540.0, loc);
3267 
3268         cupsFilePrintf(fp, "*PageRegion %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", pwg->ppd, twidth, tlength);
3269       }
3270     }
3271     cupsFilePuts(fp, "*CloseUI: *PageRegion\n");
3272 
3273     cupsArrayDelete(sizes);
3274     sizes = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
3275 
3276     cupsFilePrintf(fp, "*DefaultImageableArea: %s\n"
3277 		       "*DefaultPaperDimension: %s\n", ppdname, ppdname);
3278     for (i = 0, count = ippGetCount(attr); i < count; i ++)
3279     {
3280       if (ippGetValueTag(attr) == IPP_TAG_BEGIN_COLLECTION)
3281       {
3282 	media_size = ippGetCollection(attr, i);
3283 	x_dim      = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3284 	y_dim      = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3285 
3286 	pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3287       }
3288       else
3289         pwg = pwgMediaForPWG(ippGetString(attr, i, NULL));
3290 
3291       if (pwg)
3292       {
3293         char	tleft[256],		/* Left string */
3294 		tbottom[256],		/* Bottom string */
3295 		tright[256],		/* Right string */
3296 		ttop[256],		/* Top string */
3297 		twidth[256],		/* Width string */
3298 		tlength[256];		/* Length string */
3299 
3300         if (cupsArrayFind(sizes, (void *)pwg->ppd))
3301           continue;
3302 
3303         cupsArrayAdd(sizes, (void *)pwg->ppd);
3304 
3305         _cupsStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc);
3306         _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), bottom * 72.0 / 2540.0, loc);
3307         _cupsStrFormatd(tright, tright + sizeof(tright), (pwg->width - right) * 72.0 / 2540.0, loc);
3308         _cupsStrFormatd(ttop, ttop + sizeof(ttop), (pwg->length - top) * 72.0 / 2540.0, loc);
3309         _cupsStrFormatd(twidth, twidth + sizeof(twidth), pwg->width * 72.0 / 2540.0, loc);
3310         _cupsStrFormatd(tlength, tlength + sizeof(tlength), pwg->length * 72.0 / 2540.0, loc);
3311 
3312         cupsFilePrintf(fp, "*ImageableArea %s: \"%s %s %s %s\"\n", pwg->ppd, tleft, tbottom, tright, ttop);
3313         cupsFilePrintf(fp, "*PaperDimension %s: \"%s %s\"\n", pwg->ppd, twidth, tlength);
3314       }
3315     }
3316 
3317     cupsArrayDelete(sizes);
3318   }
3319   else
3320     goto bad_ppd;
3321 
3322  /*
3323   * InputSlot...
3324   */
3325 
3326   if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source", IPP_TAG_ZERO)) != NULL)
3327     pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3328   else
3329     strlcpy(ppdname, "Unknown", sizeof(ppdname));
3330 
3331   if ((attr = ippFindAttribute(response, "media-source-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3332   {
3333     static const char * const sources[][2] =
3334     {					/* "media-source" strings */
3335       { "Auto", _("Automatic") },
3336       { "Main", _("Main") },
3337       { "Alternate", _("Alternate") },
3338       { "LargeCapacity", _("Large Capacity") },
3339       { "Manual", _("Manual") },
3340       { "Envelope", _("Envelope") },
3341       { "Disc", _("Disc") },
3342       { "Photo", _("Photo") },
3343       { "Hagaki", _("Hagaki") },
3344       { "MainRoll", _("Main Roll") },
3345       { "AlternateRoll", _("Alternate Roll") },
3346       { "Top", _("Top") },
3347       { "Middle", _("Middle") },
3348       { "Bottom", _("Bottom") },
3349       { "Side", _("Side") },
3350       { "Left", _("Left") },
3351       { "Right", _("Right") },
3352       { "Center", _("Center") },
3353       { "Rear", _("Rear") },
3354       { "ByPassTray", _("Multipurpose") },
3355       { "Tray1", _("Tray 1") },
3356       { "Tray2", _("Tray 2") },
3357       { "Tray3", _("Tray 3") },
3358       { "Tray4", _("Tray 4") },
3359       { "Tray5", _("Tray 5") },
3360       { "Tray6", _("Tray 6") },
3361       { "Tray7", _("Tray 7") },
3362       { "Tray8", _("Tray 8") },
3363       { "Tray9", _("Tray 9") },
3364       { "Tray10", _("Tray 10") },
3365       { "Tray11", _("Tray 11") },
3366       { "Tray12", _("Tray 12") },
3367       { "Tray13", _("Tray 13") },
3368       { "Tray14", _("Tray 14") },
3369       { "Tray15", _("Tray 15") },
3370       { "Tray16", _("Tray 16") },
3371       { "Tray17", _("Tray 17") },
3372       { "Tray18", _("Tray 18") },
3373       { "Tray19", _("Tray 19") },
3374       { "Tray20", _("Tray 20") },
3375       { "Roll1", _("Roll 1") },
3376       { "Roll2", _("Roll 2") },
3377       { "Roll3", _("Roll 3") },
3378       { "Roll4", _("Roll 4") },
3379       { "Roll5", _("Roll 5") },
3380       { "Roll6", _("Roll 6") },
3381       { "Roll7", _("Roll 7") },
3382       { "Roll8", _("Roll 8") },
3383       { "Roll9", _("Roll 9") },
3384       { "Roll10", _("Roll 10") }
3385     };
3386 
3387     cupsFilePrintf(fp, "*OpenUI *InputSlot: PickOne\n"
3388                        "*OrderDependency: 10 AnySetup *InputSlot\n"
3389                        "*DefaultInputSlot: %s\n", ppdname);
3390     for (i = 0, count = ippGetCount(attr); i < count; i ++)
3391     {
3392       pwg_ppdize_name(ippGetString(attr, i, NULL), ppdname, sizeof(ppdname));
3393 
3394       for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++)
3395         if (!strcmp(sources[j][0], ppdname))
3396 	{
3397 	  cupsFilePrintf(fp, "*InputSlot %s/%s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, _cupsLangString(lang, sources[j][1]), j);
3398 	  break;
3399 	}
3400     }
3401     cupsFilePuts(fp, "*CloseUI: *InputSlot\n");
3402   }
3403 
3404  /*
3405   * MediaType...
3406   */
3407 
3408   if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type", IPP_TAG_ZERO)) != NULL)
3409     pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3410   else
3411     strlcpy(ppdname, "Unknown", sizeof(ppdname));
3412 
3413   if ((attr = ippFindAttribute(response, "media-type-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3414   {
3415     static const char * const media_types[][2] =
3416     {					/* "media-type" strings */
3417       { "aluminum", _("Aluminum") },
3418       { "auto", _("Automatic") },
3419       { "back-print-film", _("Back Print Film") },
3420       { "cardboard", _("Cardboard") },
3421       { "cardstock", _("Cardstock") },
3422       { "cd", _("CD") },
3423       { "com.hp.advanced-photo", _("Advanced Photo Paper") }, /* HP */
3424       { "com.hp.brochure-glossy", _("Glossy Brochure Paper") }, /* HP */
3425       { "com.hp.brochure-matte", _("Matte Brochure Paper") }, /* HP */
3426       { "com.hp.cover-matte", _("Matte Cover Paper") }, /* HP */
3427       { "com.hp.ecosmart-lite", _("Office Recycled Paper") }, /* HP */
3428       { "com.hp.everyday-glossy", _("Everyday Glossy Photo Paper") }, /* HP */
3429       { "com.hp.everyday-matte", _("Everyday Matte Paper") }, /* HP */
3430       { "com.hp.extra-heavy", _("Extra Heavyweight Paper") }, /* HP */
3431       { "com.hp.intermediate", _("Multipurpose Paper") }, /* HP */
3432       { "com.hp.mid-weight", _("Mid-Weight Paper") }, /* HP */
3433       { "com.hp.premium-inkjet", _("Premium Inkjet Paper") }, /* HP */
3434       { "com.hp.premium-photo", _("Premium Photo Glossy Paper") }, /* HP */
3435       { "com.hp.premium-presentation-matte", _("Premium Presentation Matte Paper") }, /* HP */
3436       { "continuous", _("Continuous") },
3437       { "continuous-long", _("Continuous Long") },
3438       { "continuous-short", _("Continuous Short") },
3439       { "disc", _("Optical Disc") },
3440       { "disc-glossy", _("Glossy Optical Disc") },
3441       { "disc-high-gloss", _("High Gloss Optical Disc") },
3442       { "disc-matte", _("Matte Optical Disc") },
3443       { "disc-satin", _("Satin Optical Disc") },
3444       { "disc-semi-gloss", _("Semi-Gloss Optical Disc") },
3445       { "double-wall", _("Double Wall Cardboard") },
3446       { "dry-film", _("Dry Film") },
3447       { "dvd", _("DVD") },
3448       { "embossing-foil", _("Embossing Foil") },
3449       { "end-board", _("End Board") },
3450       { "envelope", _("Envelope") },
3451       { "envelope-archival", _("Archival Envelope") },
3452       { "envelope-bond", _("Bond Envelope") },
3453       { "envelope-coated", _("Coated Envelope") },
3454       { "envelope-cotton", _("Cotton Envelope") },
3455       { "envelope-fine", _("Fine Envelope") },
3456       { "envelope-heavyweight", _("Heavyweight Envelope") },
3457       { "envelope-inkjet", _("Inkjet Envelope") },
3458       { "envelope-lightweight", _("Lightweight Envelope") },
3459       { "envelope-plain", _("Plain Envelope") },
3460       { "envelope-preprinted", _("Preprinted Envelope") },
3461       { "envelope-window", _("Windowed Envelope") },
3462       { "fabric", _("Fabric") },
3463       { "fabric-archival", _("Archival Fabric") },
3464       { "fabric-glossy", _("Glossy Fabric") },
3465       { "fabric-high-gloss", _("High Gloss Fabric") },
3466       { "fabric-matte", _("Matte Fabric") },
3467       { "fabric-semi-gloss", _("Semi-Gloss Fabric") },
3468       { "fabric-waterproof", _("Waterproof Fabric") },
3469       { "film", _("Film") },
3470       { "flexo-base", _("Flexo Base") },
3471       { "flexo-photo-polymer", _("Flexo Photo Polymer") },
3472       { "flute", _("Flute") },
3473       { "foil", _("Foil") },
3474       { "full-cut-tabs", _("Full Cut Tabs") },
3475       { "glass", _("Glass") },
3476       { "glass-colored", _("Glass Colored") },
3477       { "glass-opaque", _("Glass Opaque") },
3478       { "glass-surfaced", _("Glass Surfaced") },
3479       { "glass-textured", _("Glass Textured") },
3480       { "gravure-cylinder", _("Gravure Cylinder") },
3481       { "image-setter-paper", _("Image Setter Paper") },
3482       { "imaging-cylinder", _("Imaging Cylinder") },
3483       { "jp.co.canon_photo-paper-plus-glossy-ii", _("Photo Paper Plus Glossy II") }, /* Canon */
3484       { "jp.co.canon_photo-paper-pro-platinum", _("Photo Paper Pro Platinum") }, /* Canon */
3485       { "jp.co.canon-photo-paper-plus-glossy-ii", _("Photo Paper Plus Glossy II") }, /* Canon */
3486       { "jp.co.canon-photo-paper-pro-platinum", _("Photo Paper Pro Platinum") }, /* Canon */
3487       { "labels", _("Labels") },
3488       { "labels-colored", _("Colored Labels") },
3489       { "labels-glossy", _("Glossy Labels") },
3490       { "labels-high-gloss", _("High Gloss Labels") },
3491       { "labels-inkjet", _("Inkjet Labels") },
3492       { "labels-matte", _("Matte Labels") },
3493       { "labels-permanent", _("Permanent Labels") },
3494       { "labels-satin", _("Satin Labels") },
3495       { "labels-security", _("Security Labels") },
3496       { "labels-semi-gloss", _("Semi-Gloss Labels") },
3497       { "laminating-foil", _("Laminating Foil") },
3498       { "letterhead", _("Letterhead") },
3499       { "metal", _("Metal") },
3500       { "metal-glossy", _("Metal Glossy") },
3501       { "metal-high-gloss", _("Metal High Gloss") },
3502       { "metal-matte", _("Metal Matte") },
3503       { "metal-satin", _("Metal Satin") },
3504       { "metal-semi-gloss", _("Metal Semi Gloss") },
3505       { "mounting-tape", _("Mounting Tape") },
3506       { "multi-layer", _("Multi Layer") },
3507       { "multi-part-form", _("Multi Part Form") },
3508       { "other", _("Other") },
3509       { "paper", _("Paper") },
3510       { "photo", _("Photo Paper") }, /* HP mis-spelling */
3511       { "photographic", _("Photo Paper") },
3512       { "photographic-archival", _("Archival Photo Paper") },
3513       { "photographic-film", _("Photo Film") },
3514       { "photographic-glossy", _("Glossy Photo Paper") },
3515       { "photographic-high-gloss", _("High Gloss Photo Paper") },
3516       { "photographic-matte", _("Matte Photo Paper") },
3517       { "photographic-satin", _("Satin Photo Paper") },
3518       { "photographic-semi-gloss", _("Semi-Gloss Photo Paper") },
3519       { "plastic", _("Plastic") },
3520       { "plastic-archival", _("Plastic Archival") },
3521       { "plastic-colored", _("Plastic Colored") },
3522       { "plastic-glossy", _("Plastic Glossy") },
3523       { "plastic-high-gloss", _("Plastic High Gloss") },
3524       { "plastic-matte", _("Plastic Matte") },
3525       { "plastic-satin", _("Plastic Satin") },
3526       { "plastic-semi-gloss", _("Plastic Semi Gloss") },
3527       { "plate", _("Plate") },
3528       { "polyester", _("Polyester") },
3529       { "pre-cut-tabs", _("Pre Cut Tabs") },
3530       { "roll", _("Roll") },
3531       { "screen", _("Screen") },
3532       { "screen-paged", _("Screen Paged") },
3533       { "self-adhesive", _("Self Adhesive") },
3534       { "self-adhesive-film", _("Self Adhesive Film") },
3535       { "shrink-foil", _("Shrink Foil") },
3536       { "single-face", _("Single Face") },
3537       { "single-wall", _("Single Wall Cardboard") },
3538       { "sleeve", _("Sleeve") },
3539       { "stationery", _("Plain Paper") },
3540       { "stationery-archival", _("Archival Paper") },
3541       { "stationery-coated", _("Coated Paper") },
3542       { "stationery-cotton", _("Cotton Paper") },
3543       { "stationery-fine", _("Vellum Paper") },
3544       { "stationery-heavyweight", _("Heavyweight Paper") },
3545       { "stationery-heavyweight-coated", _("Heavyweight Coated Paper") },
3546       { "stationery-inkjet", _("Inkjet Paper") },
3547       { "stationery-letterhead", _("Letterhead") },
3548       { "stationery-lightweight", _("Lightweight Paper") },
3549       { "stationery-preprinted", _("Preprinted Paper") },
3550       { "stationery-prepunched", _("Punched Paper") },
3551       { "tab-stock", _("Tab Stock") },
3552       { "tractor", _("Tractor") },
3553       { "transfer", _("Transfer") },
3554       { "transparency", _("Transparency") },
3555       { "triple-wall", _("Triple Wall Cardboard") },
3556       { "wet-film", _("Wet Film") }
3557     };
3558 
3559     cupsFilePrintf(fp, "*OpenUI *MediaType: PickOne\n"
3560                        "*OrderDependency: 10 AnySetup *MediaType\n"
3561                        "*DefaultMediaType: %s\n", ppdname);
3562     for (i = 0; i < count; i ++)
3563     {
3564       const char *keyword = ippGetString(attr, i, NULL);
3565 
3566       pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3567 
3568       for (j = 0; j < (int)(sizeof(media_types) / sizeof(media_types[0])); j ++)
3569         if (!strcmp(keyword, media_types[j][0]))
3570           break;
3571 
3572       if (j < (int)(sizeof(media_types) / sizeof(media_types[0])))
3573         cupsFilePrintf(fp, "*MediaType %s/%s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, _cupsLangString(lang, media_types[j][1]), ppdname);
3574       else
3575         cupsFilePrintf(fp, "*MediaType %s/%s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, keyword, ppdname);
3576     }
3577     cupsFilePuts(fp, "*CloseUI: *MediaType\n");
3578   }
3579 
3580  /*
3581   * ColorModel...
3582   */
3583 
3584   if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
3585     if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) == NULL)
3586       if ((attr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) == NULL)
3587         attr = ippFindAttribute(response, "output-mode-supported", IPP_TAG_KEYWORD);
3588 
3589   if (attr)
3590   {
3591     const char *default_color = NULL;	/* Default */
3592 
3593     for (i = 0, count = ippGetCount(attr); i < count; i ++)
3594     {
3595       const char *keyword = ippGetString(attr, i, NULL);
3596 					/* Keyword for color/bit depth */
3597 
3598       if (!strcmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
3599       {
3600         if (!default_color)
3601 	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3602 			     "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
3603 
3604         cupsFilePrintf(fp, "*ColorModel FastGray/%s: \"<</cupsColorSpace 3/cupsBitsPerColor 1/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Fast Grayscale")));
3605 
3606         if (!default_color)
3607 	  default_color = "FastGray";
3608       }
3609       else if (!strcmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
3610       {
3611         if (!default_color)
3612 	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3613 			     "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
3614 
3615         cupsFilePrintf(fp, "*ColorModel Gray/%s: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Grayscale")));
3616 
3617         if (!default_color || !strcmp(default_color, "FastGray"))
3618 	  default_color = "Gray";
3619       }
3620       else if (!strcmp(keyword, "srgb_8") || !strcmp(keyword, "SRGB24") || !strcmp(keyword, "color"))
3621       {
3622         if (!default_color)
3623 	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3624 			     "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
3625 
3626         cupsFilePrintf(fp, "*ColorModel RGB/%s: \"<</cupsColorSpace 19/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Color")));
3627 
3628 	default_color = "RGB";
3629       }
3630       else if (!strcmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48"))
3631       {
3632         if (!default_color)
3633 	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3634 			     "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
3635 
3636         cupsFilePrintf(fp, "*ColorModel AdobeRGB/%s: \"<</cupsColorSpace 20/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Deep Color")));
3637 
3638         if (!default_color)
3639 	  default_color = "AdobeRGB";
3640       }
3641     }
3642 
3643     if (default_color)
3644     {
3645       cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color);
3646       cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
3647     }
3648   }
3649 
3650  /*
3651   * Duplex...
3652   */
3653 
3654   if ((attr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL && ippContainsString(attr, "two-sided-long-edge"))
3655   {
3656     cupsFilePrintf(fp, "*OpenUI *Duplex/%s: PickOne\n"
3657 		       "*OrderDependency: 10 AnySetup *Duplex\n"
3658 		       "*DefaultDuplex: None\n"
3659 		       "*Duplex None/%s: \"<</Duplex false>>setpagedevice\"\n"
3660 		       "*Duplex DuplexNoTumble/%s: \"<</Duplex true/Tumble false>>setpagedevice\"\n"
3661 		       "*Duplex DuplexTumble/%s: \"<</Duplex true/Tumble true>>setpagedevice\"\n"
3662 		       "*CloseUI: *Duplex\n", _cupsLangString(lang, _("2-Sided Printing")), _cupsLangString(lang, _("Off (1-Sided)")), _cupsLangString(lang, _("Long-Edge (Portrait)")), _cupsLangString(lang, _("Short-Edge (Landscape)")));
3663 
3664     if ((attr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
3665     {
3666       const char *keyword = ippGetString(attr, 0, NULL);
3667 					/* Keyword value */
3668 
3669       if (!strcmp(keyword, "flipped"))
3670         cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3671       else if (!strcmp(keyword, "manual-tumble"))
3672         cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3673       else if (!strcmp(keyword, "normal"))
3674         cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3675       else
3676         cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3677     }
3678     else if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
3679     {
3680       for (i = 0, count = ippGetCount(attr); i < count; i ++)
3681       {
3682 	const char *dm = ippGetString(attr, i, NULL);
3683 					  /* DM value */
3684 
3685 	if (!_cups_strcasecmp(dm, "DM1"))
3686 	{
3687 	  cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3688 	  break;
3689 	}
3690 	else if (!_cups_strcasecmp(dm, "DM2"))
3691 	{
3692 	  cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3693 	  break;
3694 	}
3695 	else if (!_cups_strcasecmp(dm, "DM3"))
3696 	{
3697 	  cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3698 	  break;
3699 	}
3700 	else if (!_cups_strcasecmp(dm, "DM4"))
3701 	{
3702 	  cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3703 	  break;
3704 	}
3705       }
3706     }
3707   }
3708 
3709  /*
3710   * Output bin...
3711   */
3712 
3713   if ((attr = ippFindAttribute(response, "output-bin-default", IPP_TAG_ZERO)) != NULL)
3714     pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3715   else
3716     strlcpy(ppdname, "Unknown", sizeof(ppdname));
3717 
3718   if ((attr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3719   {
3720     static const char * const output_bins[][2] =
3721     {					/* "output-bin" strings */
3722       { "auto", _("Automatic") },
3723       { "bottom", _("Bottom Tray") },
3724       { "center", _("Center Tray") },
3725       { "face-down", _("Face Down") },
3726       { "face-up", _("Face Up") },
3727       { "large-capacity", _("Large Capacity Tray") },
3728       { "left", _("Left Tray") },
3729       { "mailbox-1", _("Mailbox 1") },
3730       { "mailbox-2", _("Mailbox 2") },
3731       { "mailbox-3", _("Mailbox 3") },
3732       { "mailbox-4", _("Mailbox 4") },
3733       { "mailbox-5", _("Mailbox 5") },
3734       { "mailbox-6", _("Mailbox 6") },
3735       { "mailbox-7", _("Mailbox 7") },
3736       { "mailbox-8", _("Mailbox 8") },
3737       { "mailbox-9", _("Mailbox 9") },
3738       { "mailbox-10", _("Mailbox 10") },
3739       { "middle", _("Middle") },
3740       { "my-mailbox", _("My Mailbox") },
3741       { "rear", _("Rear Tray") },
3742       { "right", _("Right Tray") },
3743       { "side", _("Side Tray") },
3744       { "stacker-1", _("Stacker 1") },
3745       { "stacker-2", _("Stacker 2") },
3746       { "stacker-3", _("Stacker 3") },
3747       { "stacker-4", _("Stacker 4") },
3748       { "stacker-5", _("Stacker 5") },
3749       { "stacker-6", _("Stacker 6") },
3750       { "stacker-7", _("Stacker 7") },
3751       { "stacker-8", _("Stacker 8") },
3752       { "stacker-9", _("Stacker 9") },
3753       { "stacker-10", _("Stacker 10") },
3754       { "top", _("Top Tray") },
3755       { "tray-1", _("Tray 1") },
3756       { "tray-2", _("Tray 2") },
3757       { "tray-3", _("Tray 3") },
3758       { "tray-4", _("Tray 4") },
3759       { "tray-5", _("Tray 5") },
3760       { "tray-6", _("Tray 6") },
3761       { "tray-7", _("Tray 7") },
3762       { "tray-8", _("Tray 8") },
3763       { "tray-9", _("Tray 9") },
3764       { "tray-10", _("Tray 10") }
3765     };
3766 
3767     cupsFilePrintf(fp, "*OpenUI *OutputBin: PickOne\n"
3768                        "*OrderDependency: 10 AnySetup *OutputBin\n"
3769                        "*DefaultOutputBin: %s\n", ppdname);
3770     for (i = 0; i < (int)(sizeof(output_bins) / sizeof(output_bins[0])); i ++)
3771     {
3772       if (!ippContainsString(attr, output_bins[i][0]))
3773         continue;
3774 
3775       pwg_ppdize_name(output_bins[i][0], ppdname, sizeof(ppdname));
3776 
3777       cupsFilePrintf(fp, "*OutputBin %s/%s: \"\"\n", ppdname, _cupsLangString(lang, output_bins[i][1]));
3778     }
3779     cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
3780   }
3781 
3782  /*
3783   * Finishing options...
3784   *
3785   * Eventually need to re-add support for finishings-col-database, however
3786   * it is difficult to map arbitrary finishing-template values to PPD options
3787   * and have the right constraints apply (e.g. stapling vs. folding vs.
3788   * punching, etc.)
3789   */
3790 
3791   if ((attr = ippFindAttribute(response, "finishings-supported", IPP_TAG_ENUM)) != NULL)
3792   {
3793     const char		*name;		/* String name */
3794     int			value;		/* Enum value */
3795     cups_array_t	*names;		/* Names we've added */
3796 
3797     count = ippGetCount(attr);
3798     names = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
3799 
3800    /*
3801     * Staple/Bind/Stitch
3802     */
3803 
3804     for (i = 0; i < count; i ++)
3805     {
3806       value = ippGetInteger(attr, i);
3807       name  = ippEnumString("finishings", value);
3808 
3809       if (!strncmp(name, "staple-", 7) || !strncmp(name, "bind-", 5) || !strncmp(name, "edge-stitch-", 12) || !strcmp(name, "saddle-stitch"))
3810         break;
3811     }
3812 
3813     if (i < count)
3814     {
3815       cupsFilePrintf(fp, "*OpenUI *StapleLocation/%s: PickOne\n", _cupsLangString(lang, _("Staple")));
3816       cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
3817       cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
3818       cupsFilePrintf(fp, "*StapleLocation None/%s: \"\"\n", _cupsLangString(lang, _("None")));
3819 
3820       for (; i < count; i ++)
3821       {
3822         value = ippGetInteger(attr, i);
3823         name  = ippEnumString("finishings", value);
3824 
3825         if (strncmp(name, "staple-", 7) && strncmp(name, "bind-", 5) && strncmp(name, "edge-stitch-", 12) && strcmp(name, "saddle-stitch"))
3826           continue;
3827 
3828         if (cupsArrayFind(names, (char *)name))
3829           continue;			/* Already did this finishing template */
3830 
3831         cupsArrayAdd(names, (char *)name);
3832 
3833         for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0])); j ++)
3834         {
3835           if (!strcmp(finishings[j][0], name))
3836           {
3837             cupsFilePrintf(fp, "*StapleLocation %s/%s: \"\"\n", name, _cupsLangString(lang, finishings[j][1]));
3838             cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, name, name);
3839             break;
3840           }
3841         }
3842       }
3843 
3844       cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
3845     }
3846 
3847    /*
3848     * Fold
3849     */
3850 
3851     for (i = 0; i < count; i ++)
3852     {
3853       value = ippGetInteger(attr, i);
3854       name  = ippEnumString("finishings", value);
3855 
3856       if (!strncmp(name, "fold-", 5))
3857         break;
3858     }
3859 
3860     if (i < count)
3861     {
3862       cupsFilePrintf(fp, "*OpenUI *FoldType/%s: PickOne\n", _cupsLangString(lang, _("Fold")));
3863       cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
3864       cupsFilePuts(fp, "*DefaultFoldType: None\n");
3865       cupsFilePrintf(fp, "*FoldType None/%s: \"\"\n", _cupsLangString(lang, _("None")));
3866 
3867       for (; i < count; i ++)
3868       {
3869         value = ippGetInteger(attr, i);
3870         name  = ippEnumString("finishings", value);
3871 
3872         if (strncmp(name, "fold-", 5))
3873           continue;
3874 
3875         if (cupsArrayFind(names, (char *)name))
3876           continue;			/* Already did this finishing template */
3877 
3878         cupsArrayAdd(names, (char *)name);
3879 
3880         for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0])); j ++)
3881         {
3882           if (!strcmp(finishings[j][0], name))
3883           {
3884             cupsFilePrintf(fp, "*FoldType %s/%s: \"\"\n", name, _cupsLangString(lang, finishings[j][1]));
3885             cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, name, name);
3886             break;
3887           }
3888         }
3889       }
3890 
3891       cupsFilePuts(fp, "*CloseUI: *FoldType\n");
3892     }
3893 
3894    /*
3895     * Punch
3896     */
3897 
3898     for (i = 0; i < count; i ++)
3899     {
3900       value = ippGetInteger(attr, i);
3901       name  = ippEnumString("finishings", value);
3902 
3903       if (!strncmp(name, "punch-", 6))
3904         break;
3905     }
3906 
3907     if (i < count)
3908     {
3909       cupsFilePrintf(fp, "*OpenUI *PunchMedia/%s: PickOne\n", _cupsLangString(lang, _("Punch")));
3910       cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
3911       cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
3912       cupsFilePrintf(fp, "*PunchMedia None/%s: \"\"\n", _cupsLangString(lang, _("None")));
3913 
3914       for (i = 0; i < count; i ++)
3915       {
3916         value = ippGetInteger(attr, i);
3917         name  = ippEnumString("finishings", value);
3918 
3919         if (strncmp(name, "punch-", 6))
3920           continue;
3921 
3922         if (cupsArrayFind(names, (char *)name))
3923           continue;			/* Already did this finishing template */
3924 
3925         cupsArrayAdd(names, (char *)name);
3926 
3927         for (j = 0; j < (int)(sizeof(finishings) / sizeof(finishings[0])); j ++)
3928         {
3929           if (!strcmp(finishings[j][0], name))
3930           {
3931             cupsFilePrintf(fp, "*PunchMedia %s/%s: \"\"\n", name, _cupsLangString(lang, finishings[j][1]));
3932             cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, name, name);
3933             break;
3934           }
3935         }
3936       }
3937 
3938       cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
3939     }
3940 
3941    /*
3942     * Booklet
3943     */
3944 
3945     if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER))
3946     {
3947       cupsFilePrintf(fp, "*OpenUI *Booklet/%s: Boolean\n", _cupsLangString(lang, _("Booklet")));
3948       cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
3949       cupsFilePuts(fp, "*DefaultBooklet: False\n");
3950       cupsFilePuts(fp, "*Booklet False: \"\"\n");
3951       cupsFilePuts(fp, "*Booklet True: \"\"\n");
3952       cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n", IPP_FINISHINGS_BOOKLET_MAKER);
3953       cupsFilePuts(fp, "*CloseUI: *Booklet\n");
3954     }
3955 
3956     cupsArrayDelete(names);
3957   }
3958 
3959  /*
3960   * cupsPrintQuality and DefaultResolution...
3961   */
3962 
3963   quality = ippFindAttribute(response, "print-quality-supported", IPP_TAG_ENUM);
3964 
3965   if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
3966   {
3967     count = ippGetCount(attr);
3968 
3969     pwg_ppdize_resolution(attr, count / 2, &xres, &yres, ppdname, sizeof(ppdname));
3970     cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
3971 
3972     cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
3973 		       "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
3974 		       "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
3975     if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
3976     {
3977       pwg_ppdize_resolution(attr, 0, &xres, &yres, NULL, 0);
3978       cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), xres, yres);
3979     }
3980     pwg_ppdize_resolution(attr, count / 2, &xres, &yres, NULL, 0);
3981     cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), xres, yres);
3982     if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
3983     {
3984       if (count > 1)
3985         pwg_ppdize_resolution(attr, count - 1, &xres, &yres, NULL, 0);
3986       else
3987         pwg_ppdize_resolution(attr, 0, &xres, &yres, NULL, 0);
3988       cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), xres, yres);
3989     }
3990 
3991     cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
3992   }
3993   else if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
3994   {
3995     int lowdpi = 0, hidpi = 0;		/* Lower and higher resolution */
3996 
3997     for (i = 0, count = ippGetCount(attr); i < count; i ++)
3998     {
3999       const char *rs = ippGetString(attr, i, NULL);
4000 					/* RS value */
4001 
4002       if (_cups_strncasecmp(rs, "RS", 2))
4003         continue;
4004 
4005       lowdpi = atoi(rs + 2);
4006       if ((rs = strrchr(rs, '-')) != NULL)
4007         hidpi = atoi(rs + 1);
4008       else
4009         hidpi = lowdpi;
4010       break;
4011     }
4012 
4013     if (lowdpi == 0)
4014     {
4015      /*
4016       * Invalid "urf-supported" value...
4017       */
4018 
4019       goto bad_ppd;
4020     }
4021     else
4022     {
4023      /*
4024       * Generate print qualities based on low and high DPIs...
4025       */
4026 
4027       cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi);
4028 
4029       cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
4030 			 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4031 			 "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
4032       if ((lowdpi & 1) == 0)
4033 	cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), lowdpi, lowdpi / 2);
4034       else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4035 	cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), lowdpi, lowdpi);
4036       cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), lowdpi, lowdpi);
4037       if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4038 	cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), hidpi, hidpi);
4039       cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4040     }
4041   }
4042   else if (is_apple || is_pwg)
4043     goto bad_ppd;
4044   else
4045   {
4046     if ((attr = ippFindAttribute(response, "printer-resolution-default", IPP_TAG_RESOLUTION)) != NULL)
4047     {
4048       pwg_ppdize_resolution(attr, 0, &xres, &yres, ppdname, sizeof(ppdname));
4049     }
4050     else
4051     {
4052       xres = yres = 300;
4053       strlcpy(ppdname, "300dpi", sizeof(ppdname));
4054     }
4055 
4056     cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4057 
4058     cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
4059                        "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4060                        "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
4061     if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4062       cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), xres, yres);
4063     cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), xres, yres);
4064     if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
4065       cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), xres, yres);
4066     cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4067   }
4068 
4069  /*
4070   * Close up and return...
4071   */
4072 
4073   cupsFileClose(fp);
4074 
4075   return (buffer);
4076 
4077  /*
4078   * If we get here then there was a problem creating the PPD...
4079   */
4080 
4081   bad_ppd:
4082 
4083   cupsFileClose(fp);
4084   unlink(buffer);
4085   *buffer = '\0';
4086 
4087   _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Printer does not support required IPP attributes or document formats."), 1);
4088 
4089   return (NULL);
4090 }
4091 
4092 
4093 /*
4094  * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
4095  *                              media-source.
4096  */
4097 
4098 const char *				/* O - InputSlot name */
_pwgInputSlotForSource(const char * media_source,char * name,size_t namesize)4099 _pwgInputSlotForSource(
4100     const char *media_source,		/* I - PWG media-source */
4101     char       *name,			/* I - Name buffer */
4102     size_t     namesize)		/* I - Size of name buffer */
4103 {
4104  /*
4105   * Range check input...
4106   */
4107 
4108   if (!media_source || !name || namesize < PPD_MAX_NAME)
4109     return (NULL);
4110 
4111   if (_cups_strcasecmp(media_source, "main"))
4112     strlcpy(name, "Cassette", namesize);
4113   else if (_cups_strcasecmp(media_source, "alternate"))
4114     strlcpy(name, "Multipurpose", namesize);
4115   else if (_cups_strcasecmp(media_source, "large-capacity"))
4116     strlcpy(name, "LargeCapacity", namesize);
4117   else if (_cups_strcasecmp(media_source, "bottom"))
4118     strlcpy(name, "Lower", namesize);
4119   else if (_cups_strcasecmp(media_source, "middle"))
4120     strlcpy(name, "Middle", namesize);
4121   else if (_cups_strcasecmp(media_source, "top"))
4122     strlcpy(name, "Upper", namesize);
4123   else if (_cups_strcasecmp(media_source, "rear"))
4124     strlcpy(name, "Rear", namesize);
4125   else if (_cups_strcasecmp(media_source, "side"))
4126     strlcpy(name, "Side", namesize);
4127   else if (_cups_strcasecmp(media_source, "envelope"))
4128     strlcpy(name, "Envelope", namesize);
4129   else if (_cups_strcasecmp(media_source, "main-roll"))
4130     strlcpy(name, "Roll", namesize);
4131   else if (_cups_strcasecmp(media_source, "alternate-roll"))
4132     strlcpy(name, "Roll2", namesize);
4133   else
4134     pwg_ppdize_name(media_source, name, namesize);
4135 
4136   return (name);
4137 }
4138 
4139 
4140 /*
4141  * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
4142  *                            media-type.
4143  */
4144 
4145 const char *				/* O - MediaType name */
_pwgMediaTypeForType(const char * media_type,char * name,size_t namesize)4146 _pwgMediaTypeForType(
4147     const char *media_type,		/* I - PWG media-type */
4148     char       *name,			/* I - Name buffer */
4149     size_t     namesize)		/* I - Size of name buffer */
4150 {
4151  /*
4152   * Range check input...
4153   */
4154 
4155   if (!media_type || !name || namesize < PPD_MAX_NAME)
4156     return (NULL);
4157 
4158   if (_cups_strcasecmp(media_type, "auto"))
4159     strlcpy(name, "Auto", namesize);
4160   else if (_cups_strcasecmp(media_type, "cardstock"))
4161     strlcpy(name, "Cardstock", namesize);
4162   else if (_cups_strcasecmp(media_type, "envelope"))
4163     strlcpy(name, "Envelope", namesize);
4164   else if (_cups_strcasecmp(media_type, "photographic-glossy"))
4165     strlcpy(name, "Glossy", namesize);
4166   else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
4167     strlcpy(name, "HighGloss", namesize);
4168   else if (_cups_strcasecmp(media_type, "photographic-matte"))
4169     strlcpy(name, "Matte", namesize);
4170   else if (_cups_strcasecmp(media_type, "stationery"))
4171     strlcpy(name, "Plain", namesize);
4172   else if (_cups_strcasecmp(media_type, "stationery-coated"))
4173     strlcpy(name, "Coated", namesize);
4174   else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
4175     strlcpy(name, "Inkjet", namesize);
4176   else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
4177     strlcpy(name, "Letterhead", namesize);
4178   else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
4179     strlcpy(name, "Preprinted", namesize);
4180   else if (_cups_strcasecmp(media_type, "transparency"))
4181     strlcpy(name, "Transparency", namesize);
4182   else
4183     pwg_ppdize_name(media_type, name, namesize);
4184 
4185   return (name);
4186 }
4187 
4188 
4189 /*
4190  * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
4191  */
4192 
4193 const char *				/* O - PageSize name */
_pwgPageSizeForMedia(pwg_media_t * media,char * name,size_t namesize)4194 _pwgPageSizeForMedia(
4195     pwg_media_t *media,		/* I - Media */
4196     char         *name,			/* I - PageSize name buffer */
4197     size_t       namesize)		/* I - Size of name buffer */
4198 {
4199   const char	*sizeptr,		/* Pointer to size in PWG name */
4200 		*dimptr;		/* Pointer to dimensions in PWG name */
4201 
4202 
4203  /*
4204   * Range check input...
4205   */
4206 
4207   if (!media || !name || namesize < PPD_MAX_NAME)
4208     return (NULL);
4209 
4210  /*
4211   * Copy or generate a PageSize name...
4212   */
4213 
4214   if (media->ppd)
4215   {
4216    /*
4217     * Use a standard Adobe name...
4218     */
4219 
4220     strlcpy(name, media->ppd, namesize);
4221   }
4222   else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
4223            (sizeptr = strchr(media->pwg, '_')) == NULL ||
4224 	   (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
4225 	   (size_t)(dimptr - sizeptr) > namesize)
4226   {
4227    /*
4228     * Use a name of the form "wNNNhNNN"...
4229     */
4230 
4231     snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
4232              (int)PWG_TO_POINTS(media->length));
4233   }
4234   else
4235   {
4236    /*
4237     * Copy the size name from class_sizename_dimensions...
4238     */
4239 
4240     memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
4241     name[dimptr - sizeptr - 1] = '\0';
4242   }
4243 
4244   return (name);
4245 }
4246 
4247 
4248 /*
4249  * 'pwg_add_finishing()' - Add a finishings value.
4250  */
4251 
4252 static void
pwg_add_finishing(cups_array_t * finishings,ipp_finishings_t template,const char * name,const char * value)4253 pwg_add_finishing(
4254     cups_array_t     *finishings,	/* I - Finishings array */
4255     ipp_finishings_t template,		/* I - Finishing template */
4256     const char       *name,		/* I - PPD option */
4257     const char       *value)		/* I - PPD choice */
4258 {
4259   _pwg_finishings_t	*f;		/* New finishings value */
4260 
4261 
4262   if ((f = (_pwg_finishings_t *)calloc(1, sizeof(_pwg_finishings_t))) != NULL)
4263   {
4264     f->value       = template;
4265     f->num_options = cupsAddOption(name, value, 0, &f->options);
4266 
4267     cupsArrayAdd(finishings, f);
4268   }
4269 }
4270 
4271 
4272 /*
4273  * 'pwg_compare_finishings()' - Compare two finishings values.
4274  */
4275 
4276 static int				/* O - Result of comparison */
pwg_compare_finishings(_pwg_finishings_t * a,_pwg_finishings_t * b)4277 pwg_compare_finishings(
4278     _pwg_finishings_t *a,		/* I - First finishings value */
4279     _pwg_finishings_t *b)		/* I - Second finishings value */
4280 {
4281   return ((int)b->value - (int)a->value);
4282 }
4283 
4284 
4285 /*
4286  * 'pwg_free_finishings()' - Free a finishings value.
4287  */
4288 
4289 static void
pwg_free_finishings(_pwg_finishings_t * f)4290 pwg_free_finishings(
4291     _pwg_finishings_t *f)		/* I - Finishings value */
4292 {
4293   cupsFreeOptions(f->num_options, f->options);
4294   free(f);
4295 }
4296 
4297 
4298 /*
4299  * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
4300  */
4301 
4302 static void
pwg_ppdize_name(const char * ipp,char * name,size_t namesize)4303 pwg_ppdize_name(const char *ipp,	/* I - IPP keyword */
4304                 char       *name,	/* I - Name buffer */
4305 		size_t     namesize)	/* I - Size of name buffer */
4306 {
4307   char	*ptr,				/* Pointer into name buffer */
4308 	*end;				/* End of name buffer */
4309 
4310 
4311   if (!ipp)
4312   {
4313     *name = '\0';
4314     return;
4315   }
4316 
4317   *name = (char)toupper(*ipp++);
4318 
4319   for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
4320   {
4321     if (*ipp == '-' && _cups_isalnum(ipp[1]))
4322     {
4323       ipp ++;
4324       *ptr++ = (char)toupper(*ipp++ & 255);
4325     }
4326     else
4327       *ptr++ = *ipp++;
4328   }
4329 
4330   *ptr = '\0';
4331 }
4332 
4333 
4334 /*
4335  * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values.
4336  */
4337 
4338 static void
pwg_ppdize_resolution(ipp_attribute_t * attr,int element,int * xres,int * yres,char * name,size_t namesize)4339 pwg_ppdize_resolution(
4340     ipp_attribute_t *attr,		/* I - Attribute to convert */
4341     int             element,		/* I - Element to convert */
4342     int             *xres,		/* O - X resolution in DPI */
4343     int             *yres,		/* O - Y resolution in DPI */
4344     char            *name,		/* I - Name buffer */
4345     size_t          namesize)		/* I - Size of name buffer */
4346 {
4347   ipp_res_t units;			/* Units for resolution */
4348 
4349 
4350   *xres = ippGetResolution(attr, element, yres, &units);
4351 
4352   if (units == IPP_RES_PER_CM)
4353   {
4354     *xres = (int)(*xres * 2.54);
4355     *yres = (int)(*yres * 2.54);
4356   }
4357 
4358   if (name && namesize > 4)
4359   {
4360     if (*xres == *yres)
4361       snprintf(name, namesize, "%ddpi", *xres);
4362     else
4363       snprintf(name, namesize, "%dx%ddpi", *xres, *yres);
4364   }
4365 }
4366 
4367 
4368 /*
4369  * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
4370  */
4371 
4372 static void
pwg_unppdize_name(const char * ppd,char * name,size_t namesize,const char * dashchars)4373 pwg_unppdize_name(const char *ppd,	/* I - PPD keyword */
4374 		  char       *name,	/* I - Name buffer */
4375                   size_t     namesize,	/* I - Size of name buffer */
4376                   const char *dashchars)/* I - Characters to be replaced by dashes */
4377 {
4378   char	*ptr,				/* Pointer into name buffer */
4379 	*end;				/* End of name buffer */
4380 
4381 
4382   if (_cups_islower(*ppd))
4383   {
4384    /*
4385     * Already lowercase name, use as-is?
4386     */
4387 
4388     const char *ppdptr;			/* Pointer into PPD keyword */
4389 
4390     for (ppdptr = ppd + 1; *ppdptr; ppdptr ++)
4391       if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr))
4392         break;
4393 
4394     if (!*ppdptr)
4395     {
4396       strlcpy(name, ppd, namesize);
4397       return;
4398     }
4399   }
4400 
4401   for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
4402   {
4403     if (_cups_isalnum(*ppd) || *ppd == '-')
4404       *ptr++ = (char)tolower(*ppd & 255);
4405     else if (strchr(dashchars, *ppd))
4406       *ptr++ = '-';
4407     else
4408       *ptr++ = *ppd;
4409 
4410     if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
4411 	_cups_isupper(ppd[1]) && ptr < end)
4412       *ptr++ = '-';
4413     else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
4414       *ptr++ = '-';
4415   }
4416 
4417   *ptr = '\0';
4418 }
4419