]> 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 30287bb293a1899c761cdfc15d7df7e6ce756c9b..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.
@@ -78,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 */
 
 
  /*
@@ -365,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);
 
@@ -376,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
@@ -392,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);
       }
     }
   }
@@ -1075,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?
@@ -1393,7 +1418,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
       * 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);
     }
   }
@@ -2173,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 ++)
     {
@@ -2188,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;
@@ -2957,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;
@@ -3156,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...
   */
@@ -3188,7 +3223,7 @@ _ppdCreateFromIPP(char   *buffer, /* I - Filename buffer */
 
     pattern[maxlen] = '\0';
 
-    cupsFilePrintf(fp, "*cupsPassword: \"%s\"\n", pattern);
+    cupsFilePrintf(fp, "*cupsJobPassword: \"%s\"\n", pattern);
   }
 
  /*
@@ -3218,7 +3253,7 @@ _ppdCreateFromIPP(char   *buffer, /* I - Filename buffer */
         cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 10 -\"\n");
     }
     else
-      cupsFilePuts(fp, "*cupsManualCopies: true\n");
+      cupsFilePuts(fp, "*cupsManualCopies: True\n");
     if (is_apple)
       cupsFilePuts(fp, "*cupsFilter2: \"image/urf image/urf 100 -\"\n");
     if (is_pwg)
@@ -3610,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",
@@ -3668,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;
        }
     }
@@ -3734,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);
@@ -3780,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"))
       {
@@ -3914,7 +3968,7 @@ _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 */
@@ -3973,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);
@@ -3994,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");
@@ -4021,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");
@@ -4038,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");
@@ -4058,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))
@@ -4071,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");
@@ -4088,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");
@@ -4108,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))
@@ -4121,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");
@@ -4147,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);
   }
 
@@ -4174,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);
@@ -4942,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))
@@ -4953,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)
@@ -4965,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';
 }