Home | History | Annotate | Download | only in cups
      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 */
     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 */
    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 */
    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
   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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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 */
   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
   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 */
   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
   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
   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
   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
   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