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