]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/ppd-cache.c
Update ipp documentation to reflect the behavior of configuring WiFi on IPP USB printers.
[thirdparty/cups.git] / cups / ppd-cache.c
index bae64e059e67b0673277d8d6386eaa1b18501984..091f39f3cc19e9baec273fae349390146a0be001 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * PPD cache implementation for CUPS.
  *
- * Copyright © 2010-2018 by Apple Inc.
+ * Copyright © 2010-2019 by Apple Inc.
  *
  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
  * information.
@@ -13,6 +13,7 @@
 
 #include "cups-private.h"
 #include "ppd-private.h"
+#include "debug-internal.h"
 #include <math.h>
 
 
@@ -77,8 +78,11 @@ _cupsConvertOptions(
   int          num_finishings = 0,     /* Number of finishing values */
                finishings[10];         /* Finishing enum values */
   ppd_choice_t *choice;                /* Marked choice */
-  int           finishings_copies = copies;
+  int           finishings_copies = copies,
                                         /* Number of copies for finishings */
+                job_pages = 0,         /* job-pages value */
+               number_up = 1;          /* number-up value */
+  const char   *value;                 /* Option value */
 
 
  /*
@@ -291,11 +295,11 @@ _cupsConvertOptions(
     ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, keyword);
   else if (pc->sides_option && (choice = ppdFindMarkedChoice(ppd, pc->sides_option)) != NULL)
   {
-    if (!_cups_strcasecmp(choice->choice, pc->sides_1sided))
+    if (pc->sides_1sided && !_cups_strcasecmp(choice->choice, pc->sides_1sided))
       ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "one-sided");
-    else if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_long))
+    else if (pc->sides_2sided_long && !_cups_strcasecmp(choice->choice, pc->sides_2sided_long))
       ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-long-edge");
-    if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_short))
+    else if (pc->sides_2sided_short && !_cups_strcasecmp(choice->choice, pc->sides_2sided_short))
       ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-short-edge");
   }
 
@@ -364,6 +368,28 @@ _cupsConvertOptions(
   * Map finishing options...
   */
 
+  if (copies != finishings_copies)
+  {
+    // Figure out the proper job-pages-per-set value...
+    if ((value = cupsGetOption("job-pages", num_options, options)) == NULL)
+      value = cupsGetOption("com.apple.print.PrintSettings.PMTotalBeginPages..n.", num_options, options);
+
+    if (value)
+      job_pages = atoi(value);
+
+    // Adjust for number-up
+    if ((value = cupsGetOption("number-up", num_options, options)) != NULL)
+      number_up = atoi(value);
+
+    job_pages = (job_pages + number_up - 1) / number_up;
+
+    // When duplex printing, raster data will include an extra (blank) page to
+    // make the total number of pages even.  Make sure this is reflected in the
+    // page count...
+    if ((job_pages & 1) && (keyword = cupsGetOption("sides", num_options, options)) != NULL && strcmp(keyword, "one-sided"))
+      job_pages ++;
+  }
+
   if ((finishing_template = cupsGetOption("cupsFinishingTemplate", num_options, options)) == NULL)
     finishing_template = cupsGetOption("finishing-template", num_options, options);
 
@@ -375,13 +401,13 @@ _cupsConvertOptions(
     ippAddCollection(request, IPP_TAG_JOB, "finishings-col", fin_col);
     ippDelete(fin_col);
 
-    if (copies != finishings_copies && (keyword = cupsGetOption("job-impressions", num_options, options)) != NULL)
+    if (copies != finishings_copies && job_pages > 0)
     {
      /*
       * Send job-pages-per-set attribute to apply finishings correctly...
       */
 
-      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", atoi(keyword) / finishings_copies);
+      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", job_pages);
     }
   }
   else
@@ -391,13 +417,13 @@ _cupsConvertOptions(
     {
       ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", num_finishings, finishings);
 
-      if (copies != finishings_copies && (keyword = cupsGetOption("job-impressions", num_options, options)) != NULL)
+      if (copies != finishings_copies && job_pages > 0)
       {
        /*
        * Send job-pages-per-set attribute to apply finishings correctly...
        */
 
-       ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", atoi(keyword) / finishings_copies);
+       ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", job_pages);
       }
     }
   }
@@ -532,24 +558,20 @@ _ppdCacheCreateWithFile(
     else if (!_cups_strcasecmp(line, "Filter"))
     {
       if (!pc->filters)
-        pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
-                                   (cups_acopy_func_t)_cupsStrAlloc,
-                                   (cups_afree_func_t)_cupsStrFree);
+        pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
 
       cupsArrayAdd(pc->filters, value);
     }
     else if (!_cups_strcasecmp(line, "PreFilter"))
     {
       if (!pc->prefilters)
-        pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
-                                      (cups_acopy_func_t)_cupsStrAlloc,
-                                      (cups_afree_func_t)_cupsStrFree);
+        pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
 
       cupsArrayAdd(pc->prefilters, value);
     }
     else if (!_cups_strcasecmp(line, "Product"))
     {
-      pc->product = _cupsStrAlloc(value);
+      pc->product = strdup(value);
     }
     else if (!_cups_strcasecmp(line, "SingleFile"))
     {
@@ -649,8 +671,8 @@ _ppdCacheCreateWithFile(
       }
 
       map      = pc->bins + pc->num_bins;
-      map->pwg = _cupsStrAlloc(pwg_keyword);
-      map->ppd = _cupsStrAlloc(ppd_keyword);
+      map->pwg = strdup(pwg_keyword);
+      map->ppd = strdup(ppd_keyword);
 
       pc->num_bins ++;
     }
@@ -704,8 +726,8 @@ _ppdCacheCreateWithFile(
        goto create_error;
       }
 
-      size->map.pwg = _cupsStrAlloc(pwg_keyword);
-      size->map.ppd = _cupsStrAlloc(ppd_keyword);
+      size->map.pwg = strdup(pwg_keyword);
+      size->map.ppd = strdup(ppd_keyword);
 
       pc->num_sizes ++;
     }
@@ -733,15 +755,15 @@ _ppdCacheCreateWithFile(
 
       pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
                        pc->custom_max_width, pc->custom_max_length, NULL);
-      pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
+      pc->custom_max_keyword = strdup(pwg_keyword);
 
       pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
                        pc->custom_min_width, pc->custom_min_length, NULL);
-      pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
+      pc->custom_min_keyword = strdup(pwg_keyword);
     }
     else if (!_cups_strcasecmp(line, "SourceOption"))
     {
-      pc->source_option = _cupsStrAlloc(value);
+      pc->source_option = strdup(value);
     }
     else if (!_cups_strcasecmp(line, "NumSources"))
     {
@@ -788,8 +810,8 @@ _ppdCacheCreateWithFile(
       }
 
       map      = pc->sources + pc->num_sources;
-      map->pwg = _cupsStrAlloc(pwg_keyword);
-      map->ppd = _cupsStrAlloc(ppd_keyword);
+      map->pwg = strdup(pwg_keyword);
+      map->ppd = strdup(ppd_keyword);
 
       pc->num_sources ++;
     }
@@ -837,8 +859,8 @@ _ppdCacheCreateWithFile(
       }
 
       map      = pc->types + pc->num_types;
-      map->pwg = _cupsStrAlloc(pwg_keyword);
-      map->ppd = _cupsStrAlloc(ppd_keyword);
+      map->pwg = strdup(pwg_keyword);
+      map->ppd = strdup(ppd_keyword);
 
       pc->num_types ++;
     }
@@ -868,13 +890,13 @@ _ppdCacheCreateWithFile(
                           pc->presets[print_color_mode] + print_quality);
     }
     else if (!_cups_strcasecmp(line, "SidesOption"))
-      pc->sides_option = _cupsStrAlloc(value);
+      pc->sides_option = strdup(value);
     else if (!_cups_strcasecmp(line, "Sides1Sided"))
-      pc->sides_1sided = _cupsStrAlloc(value);
+      pc->sides_1sided = strdup(value);
     else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
-      pc->sides_2sided_long = _cupsStrAlloc(value);
+      pc->sides_2sided_long = strdup(value);
     else if (!_cups_strcasecmp(line, "Sides2SidedShort"))
-      pc->sides_2sided_short = _cupsStrAlloc(value);
+      pc->sides_2sided_short = strdup(value);
     else if (!_cups_strcasecmp(line, "Finishings"))
     {
       if (!pc->finishings)
@@ -895,20 +917,20 @@ _ppdCacheCreateWithFile(
     else if (!_cups_strcasecmp(line, "FinishingTemplate"))
     {
       if (!pc->templates)
-        pc->templates = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsStrAlloc, (cups_afree_func_t)_cupsStrFree);
+        pc->templates = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
 
       cupsArrayAdd(pc->templates, value);
     }
     else if (!_cups_strcasecmp(line, "MaxCopies"))
       pc->max_copies = atoi(value);
     else if (!_cups_strcasecmp(line, "ChargeInfoURI"))
-      pc->charge_info_uri = _cupsStrAlloc(value);
+      pc->charge_info_uri = strdup(value);
     else if (!_cups_strcasecmp(line, "JobAccountId"))
       pc->account_id = !_cups_strcasecmp(value, "true");
     else if (!_cups_strcasecmp(line, "JobAccountingUserId"))
       pc->accounting_user_id = !_cups_strcasecmp(value, "true");
     else if (!_cups_strcasecmp(line, "JobPassword"))
-      pc->password = _cupsStrAlloc(value);
+      pc->password = strdup(value);
     else if (!_cups_strcasecmp(line, "Mandatory"))
     {
       if (pc->mandatory)
@@ -919,9 +941,7 @@ _ppdCacheCreateWithFile(
     else if (!_cups_strcasecmp(line, "SupportFile"))
     {
       if (!pc->support_files)
-        pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
-                                          (cups_acopy_func_t)_cupsStrAlloc,
-                                          (cups_afree_func_t)_cupsStrFree);
+        pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
 
       cupsArrayAdd(pc->support_files, value);
     }
@@ -1080,7 +1100,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
       * Convert the PPD size name to the corresponding PWG keyword name.
       */
 
-      if ((pwg_media = pwgMediaForPPD(ppd_size->name)) != NULL)
+      if ((pwg_media = pwgMediaForSize(PWG_FROM_POINTS(ppd_size->width), PWG_FROM_POINTS(ppd_size->length))) != NULL)
       {
        /*
        * Standard name, do we have conflicts?
@@ -1165,8 +1185,8 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
          */
 
          new_size = old_size;
-         _cupsStrFree(old_size->map.ppd);
-         _cupsStrFree(old_size->map.pwg);
+         free(old_size->map.ppd);
+         free(old_size->map.pwg);
        }
       }
 
@@ -1187,8 +1207,8 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
        * Save this size...
        */
 
-       new_size->map.ppd = _cupsStrAlloc(ppd_size->name);
-       new_size->map.pwg = _cupsStrAlloc(pwg_name);
+       new_size->map.ppd = strdup(ppd_size->name);
+       new_size->map.pwg = strdup(pwg_name);
        new_size->width   = new_width;
        new_size->length  = new_length;
        new_size->left    = new_left;
@@ -1208,14 +1228,14 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
     pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
                      PWG_FROM_POINTS(ppd->custom_max[0]),
                      PWG_FROM_POINTS(ppd->custom_max[1]), NULL);
-    pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
+    pc->custom_max_keyword = strdup(pwg_keyword);
     pc->custom_max_width   = PWG_FROM_POINTS(ppd->custom_max[0]);
     pc->custom_max_length  = PWG_FROM_POINTS(ppd->custom_max[1]);
 
     pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
                      PWG_FROM_POINTS(ppd->custom_min[0]),
                      PWG_FROM_POINTS(ppd->custom_min[1]), NULL);
-    pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
+    pc->custom_min_keyword = strdup(pwg_keyword);
     pc->custom_min_width   = PWG_FROM_POINTS(ppd->custom_min[0]);
     pc->custom_min_length  = PWG_FROM_POINTS(ppd->custom_min[1]);
 
@@ -1234,7 +1254,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
 
   if (input_slot)
   {
-    pc->source_option = _cupsStrAlloc(input_slot->keyword);
+    pc->source_option = strdup(input_slot->keyword);
 
     if ((pc->sources = calloc((size_t)input_slot->num_choices, sizeof(pwg_map_t))) == NULL)
     {
@@ -1286,8 +1306,8 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
                          "_");
       }
 
-      map->pwg = _cupsStrAlloc(pwg_name);
-      map->ppd = _cupsStrAlloc(choice->choice);
+      map->pwg = strdup(pwg_name);
+      map->ppd = strdup(choice->choice);
 
      /*
       * Add localized text for PWG keyword to message catalog...
@@ -1357,8 +1377,8 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
                          "_");
       }
 
-      map->pwg = _cupsStrAlloc(pwg_name);
-      map->ppd = _cupsStrAlloc(choice->choice);
+      map->pwg = strdup(pwg_name);
+      map->ppd = strdup(choice->choice);
 
      /*
       * Add localized text for PWG keyword to message catalog...
@@ -1391,14 +1411,14 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
     {
       pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
 
-      map->pwg = _cupsStrAlloc(pwg_keyword);
-      map->ppd = _cupsStrAlloc(choice->choice);
+      map->pwg = strdup(pwg_keyword);
+      map->ppd = strdup(choice->choice);
 
      /*
       * Add localized text for PWG keyword to message catalog...
       */
 
-      snprintf(msg_id, sizeof(msg_id), "output-bin.%s", pwg_name);
+      snprintf(msg_id, sizeof(msg_id), "output-bin.%s", pwg_keyword);
       pwg_add_message(pc->strings, msg_id, choice->text);
     }
   }
@@ -1625,7 +1645,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
 
   if (duplex)
   {
-    pc->sides_option = _cupsStrAlloc(duplex->keyword);
+    pc->sides_option = strdup(duplex->keyword);
 
     for (i = duplex->num_choices, choice = duplex->choices;
          i > 0;
@@ -1633,16 +1653,16 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
     {
       if ((!_cups_strcasecmp(choice->choice, "None") ||
           !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
-        pc->sides_1sided = _cupsStrAlloc(choice->choice);
+        pc->sides_1sided = strdup(choice->choice);
       else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
                !_cups_strcasecmp(choice->choice, "LongEdge") ||
                !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
-        pc->sides_2sided_long = _cupsStrAlloc(choice->choice);
+        pc->sides_2sided_long = strdup(choice->choice);
       else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
                !_cups_strcasecmp(choice->choice, "ShortEdge") ||
                !_cups_strcasecmp(choice->choice, "Bottom")) &&
               !pc->sides_2sided_short)
-        pc->sides_2sided_short = _cupsStrAlloc(choice->choice);
+        pc->sides_2sided_short = strdup(choice->choice);
     }
   }
 
@@ -1650,9 +1670,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
   * Copy filters and pre-filters...
   */
 
-  pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
-                             (cups_acopy_func_t)_cupsStrAlloc,
-                             (cups_afree_func_t)_cupsStrFree);
+  pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
 
   cupsArrayAdd(pc->filters,
                "application/vnd.cups-raw application/octet-stream 0 -");
@@ -1709,9 +1727,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
 
   if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
   {
-    pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
-                                  (cups_acopy_func_t)_cupsStrAlloc,
-                                  (cups_afree_func_t)_cupsStrFree);
+    pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
 
     do
     {
@@ -1728,7 +1744,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
   */
 
   if (ppd->product)
-    pc->product = _cupsStrAlloc(ppd->product);
+    pc->product = strdup(ppd->product);
 
  /*
   * Copy finishings mapping data...
@@ -1868,7 +1884,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
 
   if ((ppd_option = ppdFindOption(ppd, "cupsFinishingTemplate")) != NULL)
   {
-    pc->templates = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsStrAlloc, (cups_afree_func_t)_cupsStrFree);
+    pc->templates = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
 
     for (choice = ppd_option->choices, i = ppd_option->num_choices; i > 0; choice ++, i --)
     {
@@ -1900,7 +1916,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
   */
 
   if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL)
-    pc->charge_info_uri = _cupsStrAlloc(ppd_attr->value);
+    pc->charge_info_uri = strdup(ppd_attr->value);
 
   if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountId", NULL)) != NULL)
     pc->account_id = !_cups_strcasecmp(ppd_attr->value, "true");
@@ -1909,7 +1925,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
     pc->accounting_user_id = !_cups_strcasecmp(ppd_attr->value, "true");
 
   if ((ppd_attr = ppdFindAttr(ppd, "cupsJobPassword", NULL)) != NULL)
-    pc->password = _cupsStrAlloc(ppd_attr->value);
+    pc->password = strdup(ppd_attr->value);
 
   if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
     pc->mandatory = _cupsArrayNewStrings(ppd_attr->value, ' ');
@@ -1918,9 +1934,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
   * Support files...
   */
 
-  pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
-                                   (cups_acopy_func_t)_cupsStrAlloc,
-                                   (cups_afree_func_t)_cupsStrFree);
+  pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
 
   for (ppd_attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
        ppd_attr;
@@ -1976,8 +1990,8 @@ _ppdCacheDestroy(_ppd_cache_t *pc)        /* I - PPD cache and mapping data */
   {
     for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
     {
-      _cupsStrFree(map->pwg);
-      _cupsStrFree(map->ppd);
+      free(map->pwg);
+      free(map->ppd);
     }
 
     free(pc->bins);
@@ -1987,22 +2001,21 @@ _ppdCacheDestroy(_ppd_cache_t *pc)      /* I - PPD cache and mapping data */
   {
     for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
     {
-      _cupsStrFree(size->map.pwg);
-      _cupsStrFree(size->map.ppd);
+      free(size->map.pwg);
+      free(size->map.ppd);
     }
 
     free(pc->sizes);
   }
 
-  if (pc->source_option)
-    _cupsStrFree(pc->source_option);
+  free(pc->source_option);
 
   if (pc->sources)
   {
     for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
     {
-      _cupsStrFree(map->pwg);
-      _cupsStrFree(map->ppd);
+      free(map->pwg);
+      free(map->ppd);
     }
 
     free(pc->sources);
@@ -2012,26 +2025,23 @@ _ppdCacheDestroy(_ppd_cache_t *pc)      /* I - PPD cache and mapping data */
   {
     for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
     {
-      _cupsStrFree(map->pwg);
-      _cupsStrFree(map->ppd);
+      free(map->pwg);
+      free(map->ppd);
     }
 
     free(pc->types);
   }
 
-  if (pc->custom_max_keyword)
-    _cupsStrFree(pc->custom_max_keyword);
-
-  if (pc->custom_min_keyword)
-    _cupsStrFree(pc->custom_min_keyword);
+  free(pc->custom_max_keyword);
+  free(pc->custom_min_keyword);
 
-  _cupsStrFree(pc->product);
+  free(pc->product);
   cupsArrayDelete(pc->filters);
   cupsArrayDelete(pc->prefilters);
   cupsArrayDelete(pc->finishings);
 
-  _cupsStrFree(pc->charge_info_uri);
-  _cupsStrFree(pc->password);
+  free(pc->charge_info_uri);
+  free(pc->password);
 
   cupsArrayDelete(pc->mandatory);
 
@@ -2188,7 +2198,7 @@ _ppdCacheGetFinishingValues(
        f;
        f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
   {
-    DEBUG_printf(("_ppdCacheGetFinishingValues: Checking %d (%s)", f->value, ippEnumString("finishings", f->value)));
+    DEBUG_printf(("_ppdCacheGetFinishingValues: Checking %d (%s)", (int)f->value, ippEnumString("finishings", (int)f->value)));
 
     for (i = f->num_options, option = f->options; i > 0; i --, option ++)
     {
@@ -2203,9 +2213,9 @@ _ppdCacheGetFinishingValues(
 
     if (i == 0)
     {
-      DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d (%s)", f->value, ippEnumString("finishings", f->value)));
+      DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d (%s)", (int)f->value, ippEnumString("finishings", (int)f->value)));
 
-      values[num_values ++] = f->value;
+      values[num_values ++] = (int)f->value;
 
       if (num_values >= max_values)
         break;
@@ -2972,12 +2982,12 @@ _ppdCacheWriteFile(
   if (pc->charge_info_uri)
     cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri);
 
-  cupsFilePrintf(fp, "AccountId %s\n", pc->account_id ? "true" : "false");
-  cupsFilePrintf(fp, "AccountingUserId %s\n",
+  cupsFilePrintf(fp, "JobAccountId %s\n", pc->account_id ? "true" : "false");
+  cupsFilePrintf(fp, "JobAccountingUserId %s\n",
                  pc->accounting_user_id ? "true" : "false");
 
   if (pc->password)
-    cupsFilePutConf(fp, "Password", pc->password);
+    cupsFilePutConf(fp, "JobPassword", pc->password);
 
   for (value = (char *)cupsArrayFirst(pc->mandatory);
        value;
@@ -3171,6 +3181,16 @@ _ppdCreateFromIPP(char   *buffer,        /* I - Filename buffer */
       httpClose(http);
   }
 
+ /*
+  * Accounting...
+  */
+
+  if (ippGetBoolean(ippFindAttribute(response, "job-account-id-supported", IPP_TAG_BOOLEAN), 0))
+    cupsFilePuts(fp, "*cupsJobAccountId: True\n");
+
+  if (ippGetBoolean(ippFindAttribute(response, "job-accounting-user-id-supported", IPP_TAG_BOOLEAN), 0))
+    cupsFilePuts(fp, "*cupsJobAccountingUserId: True\n");
+
  /*
   * Password/PIN printing...
   */
@@ -3184,26 +3204,26 @@ _ppdCreateFromIPP(char   *buffer,       /* I - Filename buffer */
                                        /* Type of password */
 
     if (maxlen > (int)(sizeof(pattern) - 1))
-      maxlen = sizeof(pattern) - 1;
+      maxlen = (int)sizeof(pattern) - 1;
 
     if (!repertoire || !strcmp(repertoire, "iana_us-ascii_digits"))
-      memset(pattern, '1', maxlen);
+      memset(pattern, '1', (size_t)maxlen);
     else if (!strcmp(repertoire, "iana_us-ascii_letters"))
-      memset(pattern, 'A', maxlen);
+      memset(pattern, 'A', (size_t)maxlen);
     else if (!strcmp(repertoire, "iana_us-ascii_complex"))
-      memset(pattern, 'C', maxlen);
+      memset(pattern, 'C', (size_t)maxlen);
     else if (!strcmp(repertoire, "iana_us-ascii_any"))
-      memset(pattern, '.', maxlen);
+      memset(pattern, '.', (size_t)maxlen);
     else if (!strcmp(repertoire, "iana_utf-8_digits"))
-      memset(pattern, 'N', maxlen);
+      memset(pattern, 'N', (size_t)maxlen);
     else if (!strcmp(repertoire, "iana_utf-8_letters"))
-      memset(pattern, 'U', maxlen);
+      memset(pattern, 'U', (size_t)maxlen);
     else
-      memset(pattern, '*', maxlen);
+      memset(pattern, '*', (size_t)maxlen);
 
     pattern[maxlen] = '\0';
 
-    cupsFilePrintf(fp, "*cupsPassword: \"%s\"\n", pattern);
+    cupsFilePrintf(fp, "*cupsJobPassword: \"%s\"\n", pattern);
   }
 
  /*
@@ -3232,6 +3252,8 @@ _ppdCreateFromIPP(char   *buffer, /* I - Filename buffer */
       else
         cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 10 -\"\n");
     }
+    else
+      cupsFilePuts(fp, "*cupsManualCopies: True\n");
     if (is_apple)
       cupsFilePuts(fp, "*cupsFilter2: \"image/urf image/urf 100 -\"\n");
     if (is_pwg)
@@ -3623,10 +3645,12 @@ _ppdCreateFromIPP(char   *buffer,       /* I - Filename buffer */
   if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source", IPP_TAG_ZERO)) != NULL)
     pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
   else
-    strlcpy(ppdname, "Unknown", sizeof(ppdname));
+    ppdname[0] = '\0';
 
   if ((attr = ippFindAttribute(response, "media-source-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
   {
+    int have_default = ppdname[0] != '\0';
+                                       /* Do we have a default InputSlot? */
     static const char * const sources[] =
     {                                  /* Standard "media-source" strings */
       "auto",
@@ -3681,21 +3705,31 @@ _ppdCreateFromIPP(char   *buffer,       /* I - Filename buffer */
       "roll-10"
     };
 
-    cupsFilePrintf(fp, "*OpenUI *InputSlot: PickOne\n"
-                       "*OrderDependency: 10 AnySetup *InputSlot\n"
-                       "*DefaultInputSlot: %s\n", ppdname);
-    for (i = 0, count = ippGetCount(attr); i < count; i ++)
+    cupsFilePuts(fp, "*OpenUI *InputSlot: PickOne\n"
+                     "*OrderDependency: 10 AnySetup *InputSlot\n");
+    if (have_default)
+      cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
+
+    for (i = 0; i < count; i ++)
     {
       keyword = ippGetString(attr, i, NULL);
 
       pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
 
+      if (i == 0 && !have_default)
+       cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
+
       for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++)
         if (!strcmp(sources[j], keyword))
        {
          snprintf(msgid, sizeof(msgid), "media-source.%s", keyword);
+
+         if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
+           if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
+             msgstr = keyword;
+
          cupsFilePrintf(fp, "*InputSlot %s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, j);
-         cupsFilePrintf(fp, "*%s.InputSlot %s/%s: \"\"\n", lang->language, ppdname, _cupsLangString(lang, msgid));
+         cupsFilePrintf(fp, "*%s.InputSlot %s/%s: \"\"\n", lang->language, ppdname, msgstr);
          break;
        }
     }
@@ -3747,6 +3781,8 @@ _ppdCreateFromIPP(char   *buffer, /* I - Filename buffer */
     int wrote_color = 0;
     const char *default_color = NULL;  /* Default */
 
+    cupsFilePrintf(fp, "*%% ColorModel from %s\n", ippGetName(attr));
+
     for (i = 0, count = ippGetCount(attr); i < count; i ++)
     {
       keyword = ippGetString(attr, i, NULL);
@@ -3793,6 +3829,11 @@ _ppdCreateFromIPP(char   *buffer,        /* I - Filename buffer */
        PRINTF_COLOROPTION("RGB", _("Color"), CUPS_CSPACE_SRGB, 8)
 
        default_color = "RGB";
+
+        // Apparently some printers only advertise color support, so make sure
+        // we also do grayscale for these printers...
+       if (!ippContainsString(attr, "sgray_8") && !ippContainsString(attr, "black_1") && !ippContainsString(attr, "black_8") && !ippContainsString(attr, "W8") && !ippContainsString(attr, "W8-16"))
+         PRINTF_COLOROPTION("Gray", _("GrayScale"), CUPS_CSPACE_SW, 8)
       }
       else if (!strcasecmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48") || !strcmp(keyword, "ADOBERGB24-48"))
       {
@@ -3927,11 +3968,23 @@ _ppdCreateFromIPP(char   *buffer,       /* I - Filename buffer */
   else
     strlcpy(ppdname, "Unknown", sizeof(ppdname));
 
-  if ((attr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
+  if ((attr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 0)
   {
+    ipp_attribute_t    *trays = ippFindAttribute(response, "printer-output-tray", IPP_TAG_STRING);
+                                       /* printer-output-tray attribute, if any */
+    const char         *tray_ptr;      /* printer-output-tray value */
+    int                        tray_len;       /* Len of printer-output-tray value */
+    char               tray[IPP_MAX_OCTETSTRING];
+                                       /* printer-output-tray string value */
+
     cupsFilePrintf(fp, "*OpenUI *OutputBin: PickOne\n"
                        "*OrderDependency: 10 AnySetup *OutputBin\n"
                        "*DefaultOutputBin: %s\n", ppdname);
+    if (!strcmp(ppdname, "FaceUp"))
+      cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n");
+    else
+      cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n");
+
     for (i = 0; i < count; i ++)
     {
       keyword = ippGetString(attr, i, NULL);
@@ -3945,6 +3998,24 @@ _ppdCreateFromIPP(char   *buffer,        /* I - Filename buffer */
 
       cupsFilePrintf(fp, "*OutputBin %s: \"\"\n", ppdname);
       cupsFilePrintf(fp, "*%s.OutputBin %s/%s: \"\"\n", lang->language, ppdname, msgstr);
+
+      if ((tray_ptr = ippGetOctetString(trays, i, &tray_len)) != NULL)
+      {
+        if (tray_len >= (int)sizeof(tray))
+          tray_len = (int)sizeof(tray) - 1;
+
+        memcpy(tray, tray_ptr, (size_t)tray_len);
+        tray[tray_len] = '\0';
+
+        if (strstr(tray, "stackingorder=lastToFirst;"))
+          cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
+        else
+          cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
+      }
+      else if (!strcmp(ppdname, "FaceUp"))
+       cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
+      else
+       cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
     }
     cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
   }
@@ -3956,7 +4027,25 @@ _ppdCreateFromIPP(char   *buffer,        /* I - Filename buffer */
   if ((attr = ippFindAttribute(response, "finishings-supported", IPP_TAG_ENUM)) != NULL)
   {
     int                        value;          /* Enum value */
+    const char         *ppd_keyword;   /* PPD keyword for enum */
     cups_array_t       *names;         /* Names we've added */
+    static const char * const base_keywords[] =
+    {                                  /* Base STD 92 keywords */
+      NULL,                            /* none */
+      "SingleAuto",                    /* staple */
+      "SingleAuto",                    /* punch */
+      NULL,                            /* cover */
+      "BindAuto",                      /* bind */
+      "SaddleStitch",                  /* saddle-stitch */
+      "EdgeStitchAuto",                        /* edge-stitch */
+      "Auto",                          /* fold */
+      NULL,                            /* trim */
+      NULL,                            /* bale */
+      NULL,                            /* booklet-maker */
+      NULL,                            /* jog-offset */
+      NULL,                            /* coat */
+      NULL                             /* laminate */
+    };
 
     count       = ippGetCount(attr);
     names       = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
@@ -3977,6 +4066,33 @@ _ppdCreateFromIPP(char   *buffer,        /* I - Filename buffer */
 
     if (i < count)
     {
+      static const char * const staple_keywords[] =
+      {                                        /* StapleLocation keywords */
+       "SinglePortrait",
+       "SingleRevLandscape",
+       "SingleLandscape",
+       "SingleRevPortrait",
+       "EdgeStitchPortrait",
+       "EdgeStitchLandscape",
+       "EdgeStitchRevPortrait",
+       "EdgeStitchRevLandscape",
+       "DualPortrait",
+       "DualLandscape",
+       "DualRevPortrait",
+       "DualRevLandscape",
+       "TriplePortrait",
+       "TripleLandscape",
+       "TripleRevPortrait",
+       "TripleRevLandscape"
+      };
+      static const char * const bind_keywords[] =
+      {                                        /* StapleLocation binding keywords */
+       "BindPortrait",
+       "BindLandscape",
+       "BindRevPortrait",
+       "BindRevLandscape"
+      };
+
       cupsArrayAdd(fin_options, "*StapleLocation");
 
       cupsFilePuts(fp, "*OpenUI *StapleLocation: PickOne\n");
@@ -4004,9 +4120,21 @@ _ppdCreateFromIPP(char   *buffer,        /* I - Filename buffer */
          if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
            msgstr = keyword;
 
-       cupsFilePrintf(fp, "*StapleLocation %s: \"\"\n", keyword);
-       cupsFilePrintf(fp, "*%s.StapleLocation %s/%s: \"\"\n", lang->language, keyword, msgstr);
-       cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, keyword, keyword);
+        if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
+          ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
+        else if (value >= IPP_FINISHINGS_STAPLE_TOP_LEFT && value <= IPP_FINISHINGS_STAPLE_TRIPLE_BOTTOM)
+          ppd_keyword = staple_keywords[value - IPP_FINISHINGS_STAPLE_TOP_LEFT];
+        else if (value >= IPP_FINISHINGS_BIND_LEFT && value <= IPP_FINISHINGS_BIND_BOTTOM)
+          ppd_keyword = bind_keywords[value - IPP_FINISHINGS_BIND_LEFT];
+        else
+          ppd_keyword = NULL;
+
+        if (!ppd_keyword)
+          continue;
+
+       cupsFilePrintf(fp, "*StapleLocation %s: \"\"\n", ppd_keyword);
+       cupsFilePrintf(fp, "*%s.StapleLocation %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
+       cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, keyword, ppd_keyword);
       }
 
       cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
@@ -4021,12 +4149,28 @@ _ppdCreateFromIPP(char   *buffer,       /* I - Filename buffer */
       value   = ippGetInteger(attr, i);
       keyword = ippEnumString("finishings", value);
 
-      if (!strncmp(keyword, "fold-", 5))
+      if (!strncmp(keyword, "cups-fold-", 10) || !strcmp(keyword, "fold") || !strncmp(keyword, "fold-", 5))
         break;
     }
 
     if (i < count)
     {
+      static const char * const fold_keywords[] =
+      {                                        /* FoldType keywords */
+       "Accordion",
+       "DoubleGate",
+       "Gate",
+       "Half",
+       "HalfZ",
+       "LeftGate",
+       "Letter",
+       "Parallel",
+       "XFold",
+       "RightGate",
+       "ZFold",
+       "EngineeringZ"
+      };
+
       cupsArrayAdd(fin_options, "*FoldType");
 
       cupsFilePuts(fp, "*OpenUI *FoldType: PickOne\n");
@@ -4041,7 +4185,9 @@ _ppdCreateFromIPP(char   *buffer, /* I - Filename buffer */
         value   = ippGetInteger(attr, i);
         keyword = ippEnumString("finishings", value);
 
-        if (strncmp(keyword, "fold-", 5))
+        if (!strncmp(keyword, "cups-fold-", 10))
+          keyword += 5;
+        else if (strcmp(keyword, "fold") && strncmp(keyword, "fold-", 5))
           continue;
 
         if (cupsArrayFind(names, (char *)keyword))
@@ -4054,9 +4200,21 @@ _ppdCreateFromIPP(char   *buffer,        /* I - Filename buffer */
          if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
            msgstr = keyword;
 
-       cupsFilePrintf(fp, "*FoldType %s: \"\"\n", keyword);
-       cupsFilePrintf(fp, "*%s.FoldType %s/%s: \"\"\n", lang->language, keyword, msgstr);
-       cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, keyword, keyword);
+        if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
+          ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
+        else if (value >= IPP_FINISHINGS_FOLD_ACCORDION && value <= IPP_FINISHINGS_FOLD_ENGINEERING_Z)
+          ppd_keyword = fold_keywords[value - IPP_FINISHINGS_FOLD_ACCORDION];
+        else if (value >= IPP_FINISHINGS_CUPS_FOLD_ACCORDION && value <= IPP_FINISHINGS_CUPS_FOLD_Z)
+          ppd_keyword = fold_keywords[value - IPP_FINISHINGS_CUPS_FOLD_ACCORDION];
+        else
+          ppd_keyword = NULL;
+
+        if (!ppd_keyword)
+          continue;
+
+       cupsFilePrintf(fp, "*FoldType %s: \"\"\n", ppd_keyword);
+       cupsFilePrintf(fp, "*%s.FoldType %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
+       cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, keyword, ppd_keyword);
       }
 
       cupsFilePuts(fp, "*CloseUI: *FoldType\n");
@@ -4071,12 +4229,36 @@ _ppdCreateFromIPP(char   *buffer,       /* I - Filename buffer */
       value   = ippGetInteger(attr, i);
       keyword = ippEnumString("finishings", value);
 
-      if (!strncmp(keyword, "punch-", 6))
+      if (!strncmp(keyword, "cups-punch-", 11) || !strncmp(keyword, "punch-", 6))
         break;
     }
 
     if (i < count)
     {
+      static const char * const punch_keywords[] =
+      {                                        /* PunchMedia keywords */
+       "SinglePortrait",
+       "SingleRevLandscape",
+       "SingleLandscape",
+       "SingleRevPortrait",
+       "DualPortrait",
+       "DualLandscape",
+       "DualRevPortrait",
+       "DualRevLandscape",
+       "TriplePortrait",
+       "TripleLandscape",
+       "TripleRevPortrait",
+       "TripleRevLandscape",
+       "QuadPortrait",
+       "QuadLandscape",
+       "QuadRevPortrait",
+       "QuadRevLandscape",
+       "MultiplePortrait",
+       "MultipleLandscape",
+       "MultipleRevPortrait",
+       "MultipleRevLandscape"
+      };
+
       cupsArrayAdd(fin_options, "*PunchMedia");
 
       cupsFilePuts(fp, "*OpenUI *PunchMedia: PickOne\n");
@@ -4091,7 +4273,9 @@ _ppdCreateFromIPP(char   *buffer, /* I - Filename buffer */
         value   = ippGetInteger(attr, i);
         keyword = ippEnumString("finishings", value);
 
-        if (strncmp(keyword, "punch-", 6))
+        if (!strncmp(keyword, "cups-punch-", 11))
+          keyword += 5;
+        else if (strncmp(keyword, "punch-", 6))
           continue;
 
         if (cupsArrayFind(names, (char *)keyword))
@@ -4104,9 +4288,21 @@ _ppdCreateFromIPP(char   *buffer,        /* I - Filename buffer */
          if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
            msgstr = keyword;
 
-       cupsFilePrintf(fp, "*PunchMedia %s: \"\"\n", keyword);
-       cupsFilePrintf(fp, "*%s.PunchMedia %s/%s: \"\"\n", lang->language, keyword, msgstr);
-       cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, keyword, keyword);
+        if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
+          ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
+        else if (value >= IPP_FINISHINGS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_PUNCH_MULTIPLE_BOTTOM)
+          ppd_keyword = punch_keywords[value - IPP_FINISHINGS_PUNCH_TOP_LEFT];
+        else if (value >= IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM)
+          ppd_keyword = punch_keywords[value - IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT];
+        else
+          ppd_keyword = NULL;
+
+        if (!ppd_keyword)
+          continue;
+
+       cupsFilePrintf(fp, "*PunchMedia %s: \"\"\n", ppd_keyword);
+       cupsFilePrintf(fp, "*%s.PunchMedia %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
+       cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, keyword, ppd_keyword);
       }
 
       cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
@@ -4130,6 +4326,69 @@ _ppdCreateFromIPP(char   *buffer,        /* I - Filename buffer */
       cupsFilePuts(fp, "*CloseUI: *Booklet\n");
     }
 
+   /*
+    * CutMedia
+    */
+
+    for (i = 0; i < count; i ++)
+    {
+      value   = ippGetInteger(attr, i);
+      keyword = ippEnumString("finishings", value);
+
+      if (!strcmp(keyword, "trim") || !strncmp(keyword, "trim-", 5))
+        break;
+    }
+
+    if (i < count)
+    {
+      static const char * const trim_keywords[] =
+      {                                /* CutMedia keywords */
+        "EndOfPage",
+        "EndOfDoc",
+        "EndOfSet",
+        "EndOfJob"
+      };
+
+      cupsArrayAdd(fin_options, "*CutMedia");
+
+      cupsFilePuts(fp, "*OpenUI *CutMedia: PickOne\n");
+      cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *CutMedia\n");
+      cupsFilePrintf(fp, "*%s.Translation CutMedia/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Cut")));
+      cupsFilePuts(fp, "*DefaultCutMedia: None\n");
+      cupsFilePuts(fp, "*CutMedia None: \"\"\n");
+      cupsFilePrintf(fp, "*%s.CutMedia None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
+
+      for (i = 0; i < count; i ++)
+      {
+        value   = ippGetInteger(attr, i);
+        keyword = ippEnumString("finishings", value);
+
+       if (strcmp(keyword, "trim") && strncmp(keyword, "trim-", 5))
+          continue;
+
+        if (cupsArrayFind(names, (char *)keyword))
+          continue;                    /* Already did this finishing template */
+
+        cupsArrayAdd(names, (char *)keyword);
+
+       snprintf(msgid, sizeof(msgid), "finishings.%d", value);
+       if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
+         if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
+           msgstr = keyword;
+
+        if (value == IPP_FINISHINGS_TRIM)
+          ppd_keyword = "Auto";
+       else
+         ppd_keyword = trim_keywords[value - IPP_FINISHINGS_TRIM_AFTER_PAGES];
+
+       cupsFilePrintf(fp, "*CutMedia %s: \"\"\n", ppd_keyword);
+       cupsFilePrintf(fp, "*%s.CutMedia %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr);
+       cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*CutMedia %s\"\n", value, keyword, ppd_keyword);
+      }
+
+      cupsFilePuts(fp, "*CloseUI: *CutMedia\n");
+    }
+
     cupsArrayDelete(names);
   }
 
@@ -4157,7 +4416,7 @@ _ppdCreateFromIPP(char   *buffer, /* I - Filename buffer */
       if (!keyword || cupsArrayFind(templates, (void *)keyword))
         continue;
 
-      if (strncmp(keyword, "fold-", 5) && (strstr(keyword, "-bottom") || strstr(keyword, "-left") || strstr(keyword, "-right") || strstr(keyword, "-top")))
+      if (!strcmp(keyword, "none"))
         continue;
 
       cupsArrayAdd(templates, (void *)keyword);
@@ -4925,6 +5184,8 @@ pwg_unppdize_name(const char *ppd,        /* I - PPD keyword */
 {
   char *ptr,                           /* Pointer into name buffer */
        *end;                           /* End of name buffer */
+  int   nodash = 1;                     /* Next char in IPP name cannot be a
+                                           dash (first char or after a dash) */
 
 
   if (_cups_islower(*ppd))
@@ -4936,7 +5197,9 @@ pwg_unppdize_name(const char *ppd,        /* I - PPD keyword */
     const char *ppdptr;                        /* Pointer into PPD keyword */
 
     for (ppdptr = ppd + 1; *ppdptr; ppdptr ++)
-      if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr))
+      if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr) ||
+         (*ppdptr == '-' && *(ppdptr - 1) == '-') ||
+         (*ppdptr == '-' && *(ppdptr + 1) == '\0'))
         break;
 
     if (!*ppdptr)
@@ -4948,19 +5211,44 @@ pwg_unppdize_name(const char *ppd,      /* I - PPD keyword */
 
   for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
   {
-    if (_cups_isalnum(*ppd) || *ppd == '-')
+    if (_cups_isalnum(*ppd))
+    {
       *ptr++ = (char)tolower(*ppd & 255);
-    else if (strchr(dashchars, *ppd))
-      *ptr++ = '-';
+      nodash = 0;
+    }
+    else if (*ppd == '-' || strchr(dashchars, *ppd))
+    {
+      if (nodash == 0)
+      {
+       *ptr++ = '-';
+       nodash = 1;
+      }
+    }
     else
+    {
       *ptr++ = *ppd;
+      nodash = 0;
+    }
 
-    if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
-       _cups_isupper(ppd[1]) && ptr < end)
-      *ptr++ = '-';
-    else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
-      *ptr++ = '-';
+    if (nodash == 0)
+    {
+      if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
+         _cups_isupper(ppd[1]) && ptr < end)
+      {
+       *ptr++ = '-';
+       nodash = 1;
+      }
+      else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
+      {
+       *ptr++ = '-';
+       nodash = 1;
+      }
+    }
   }
 
+  /* Remove trailing dashes */
+  while (ptr > name && *(ptr - 1) == '-')
+    ptr --;
+
   *ptr = '\0';
 }