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