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