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