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