]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
PPD option preset auto-generation for common job IPP attributes
authorTill Kamppeter <till.kamppeter@gmail.com>
Wed, 1 Sep 2021 11:21:53 +0000 (13:21 +0200)
committerTill Kamppeter <till.kamppeter@gmail.com>
Wed, 1 Sep 2021 11:21:53 +0000 (13:21 +0200)
To make the job IPP attributes "print-color-mode", "print-quality",
and "print-content-optimize" actually doing "the right thing" for most
printers we auto-generate PPD option presets now.

CUPS already had the functionality to apply a preset of one or more
PPD option settings for each of the 6 combinations of
"print-color-mode" and "print-quality" settings for longer time but
this was never actually being made use of as it required manually
defining the presets via "APPrinterPreset" PPD attributes. This no one
wants to do with the ~10000 PPDs which come with a typical Linux
distribution nowadays. Even a user with his few print queues does not
want to do it, know how to do it, or know that it is even possible.

This commit makes this fully automatic on every PPD where the presets
are not already manually defined via "APPrinterPreset" attributes.

When creating the PPD cache for a queue's PPD file and the PPD is
without manual presets, it calls the new function
_ppdCacheAssignPresets() which analyses for each PPD option the names
of the choices to find out which choices switch to manochrome or to
color, lower or raise print quality, or improve text/graphics/photo
printing and which are the moset suitable of them. It also analyses
the PostScript/PJL code of the choices to find out whether it affects
the printing resolution.

With this it selects which options get into the the presets and with
which choices.

In addition to presets for the "print-color-mode"/"print-quality"
combos presets for "print-content-optimize" are introduced.

PPD Option settings added by preset are logged on job execution.

CHANGES.md
cups/ppd-cache.c
cups/ppd-private.h
scheduler/job.c

index a2b393a1d459b479441d74d4c8810f2c1d02dfb8..e7d5b2fba75661c9c255859bea4938bdbc031c3c 100644 (file)
@@ -5,6 +5,9 @@ CUPS v2.4rc1 (Pending)
 ----------------------
 
 - Added support for CUPS running in a Snapcraft snap.
+- Added PPD option preset auto-generation: For all 6 combinations of
+  print-color-mode and print-quality and also for the 5 settings of
+  print-content-optimize presets are auto-generated.
 - Added support for AirPrint and Mopria clients (Issue #105)
 - Added configure support for specifying systemd dependencies in the CUPS
   service file (Issue #144)
index d10e825cb677dcfdf04d13020ba5aa2d4c249c0a..2d65109198d2f9b961d310193b04a035579ee0a5 100644 (file)
@@ -466,6 +466,8 @@ _ppdCacheCreateWithFile(
   _pwg_print_color_mode_t print_color_mode;
                                        /* Print color mode for preset */
   _pwg_print_quality_t print_quality;  /* Print quality for preset */
+  _pwg_print_content_optimize_t print_content_optimize;
+                                        /* Content optimize for preset */
 
 
   DEBUG_printf(("_ppdCacheCreateWithFile(filename=\"%s\")", filename));
@@ -891,6 +893,28 @@ _ppdCacheCreateWithFile(
           cupsParseOptions(valueptr, 0,
                           pc->presets[print_color_mode] + print_quality);
     }
+    else if (!_cups_strcasecmp(line, "OptimizePreset"))
+    {
+     /*
+      * Preset print_content_optimize name=value ...
+      */
+
+      print_content_optimize = (_pwg_print_content_optimize_t)strtol(value, &valueptr, 10);
+
+      if (print_content_optimize < _PWG_PRINT_CONTENT_OPTIMIZE_AUTO ||
+          print_content_optimize >= _PWG_PRINT_CONTENT_OPTIMIZE_MAX ||
+         valueptr == value || !*valueptr)
+      {
+        DEBUG_printf(("ppdCacheCreateWithFile: Bad Optimize Preset on line %d.",
+                     linenum));
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
+       goto create_error;
+      }
+
+      pc->num_optimize_presets[print_content_optimize] =
+          cupsParseOptions(valueptr, 0,
+                          pc->optimize_presets + print_content_optimize);
+    }
     else if (!_cups_strcasecmp(line, "SidesOption"))
       pc->sides_option = strdup(value);
     else if (!_cups_strcasecmp(line, "Sides1Sided"))
@@ -1018,6 +1042,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
                        *ppd_option;    /* Other PPD option */
   ppd_choice_t         *choice;        /* Current InputSlot/MediaType */
   pwg_map_t            *map;           /* Current source/type map */
+  int                   preset_added = 0; /* Preset definition found in PPD? */
   ppd_attr_t           *ppd_attr;      /* Current PPD preset attribute */
   int                  num_options;    /* Number of preset options and props */
   cups_option_t                *options;       /* Preset options and properties */
@@ -1427,6 +1452,10 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)  /* I - PPD file */
 
   if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
   {
+   /*
+    * "Classic" Mac OS approach
+    */
+
    /*
     * Copy and convert APPrinterPreset (output-mode + print-quality) data...
     */
@@ -1527,114 +1556,133 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)       /* I - PPD file */
              _ppdParseOptions(ppd_attr->value, 0,
                               pc->presets[pwg_print_color_mode] +
                                   pwg_print_quality, _PPD_PARSE_OPTIONS);
+       preset_added = 1;
       }
 
       cupsFreeOptions(num_options, options);
     }
     while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) != NULL);
-  }
 
-  if (!pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_DRAFT] &&
-      !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_NORMAL] &&
-      !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_HIGH])
-  {
-   /*
-    * Try adding some common color options to create grayscale presets.  These
-    * are listed in order of popularity...
-    */
-
-    const char *color_option = NULL,   /* Color control option */
-               *gray_choice = NULL;    /* Choice to select grayscale */
-
-    if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL &&
-        ppdFindChoice(color_model, "Gray"))
-    {
-      color_option = "ColorModel";
-      gray_choice  = "Gray";
-    }
-    else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL &&
-             ppdFindChoice(color_model, "grayscale"))
-    {
-      color_option = "HPColorMode";
-      gray_choice  = "grayscale";
-    }
-    else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL &&
-             ppdFindChoice(color_model, "Mono"))
-    {
-      color_option = "BRMonoColor";
-      gray_choice  = "Mono";
-    }
-    else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL &&
-             ppdFindChoice(color_model, "1"))
-    {
-      color_option = "CNIJSGrayScale";
-      gray_choice  = "1";
-    }
-    else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL &&
-             ppdFindChoice(color_model, "True"))
-    {
-      color_option = "HPColorAsGray";
-      gray_choice  = "True";
-    }
-
-    if (color_option && gray_choice)
+    if (preset_added &&
+       !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_DRAFT] &&
+       !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_NORMAL] &&
+       !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_HIGH])
     {
      /*
-      * Copy and convert ColorModel (output-mode) data...
+      * Try adding some common color options to create grayscale presets. These
+      * are listed in order of popularity...
       */
 
-      cups_option_t    *coption,       /* Color option */
-                         *moption;     /* Monochrome option */
+      const char       *color_option = NULL,   /* Color control option */
+                       *gray_choice = NULL;    /* Choice to select grayscale */
 
-      for (pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
-          pwg_print_quality < _PWG_PRINT_QUALITY_MAX;
-          pwg_print_quality ++)
+      if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL &&
+         ppdFindChoice(color_model, "Gray"))
       {
-       if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality])
-       {
-        /*
-         * Copy the color options...
-         */
+       color_option = "ColorModel";
+       gray_choice  = "Gray";
+      }
+      else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL &&
+              ppdFindChoice(color_model, "grayscale"))
+      {
+       color_option = "HPColorMode";
+       gray_choice  = "grayscale";
+      }
+      else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL &&
+              ppdFindChoice(color_model, "Mono"))
+      {
+       color_option = "BRMonoColor";
+       gray_choice  = "Mono";
+      }
+      else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL &&
+              ppdFindChoice(color_model, "1"))
+      {
+       color_option = "CNIJSGrayScale";
+       gray_choice  = "1";
+      }
+      else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL &&
+              ppdFindChoice(color_model, "True"))
+      {
+       color_option = "HPColorAsGray";
+       gray_choice  = "True";
+      }
 
-         num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
-                                       [pwg_print_quality];
-         options     = calloc(sizeof(cups_option_t), (size_t)num_options);
+      if (color_option && gray_choice)
+      {
+       /*
+       * Copy and convert ColorModel (output-mode) data...
+       */
 
-         if (options)
+       cups_option_t   *coption,       /* Color option */
+                       *moption;       /* Monochrome option */
+
+       for (pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
+            pwg_print_quality < _PWG_PRINT_QUALITY_MAX;
+            pwg_print_quality ++)
+        {
+         if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality])
          {
-           for (i = num_options, moption = options,
-                    coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
-                                          [pwg_print_quality];
-                i > 0;
-                i --, moption ++, coption ++)
-           {
-             moption->name  = _cupsStrRetain(coption->name);
-             moption->value = _cupsStrRetain(coption->value);
-           }
+          /*
+           * Copy the color options...
+           */
 
-           pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
+           num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                                        [pwg_print_quality];
+           options     = calloc(sizeof(cups_option_t), (size_t)num_options);
+
+           if (options)
+           {
+             for (i = num_options, moption = options,
+                      coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                                           [pwg_print_quality];
+                  i > 0;
+                  i --, moption ++, coption ++)
+             {
+               moption->name  = _cupsStrRetain(coption->name);
+               moption->value = _cupsStrRetain(coption->value);
+             }
+
+             pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
                num_options;
-           pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
+             pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
                options;
+           }
          }
-       }
-       else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL)
-         continue;
-
-       /*
-       * Add the grayscale option to the preset...
-       */
-
-       pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
-           cupsAddOption(color_option, gray_choice,
-                         pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
-                                         [pwg_print_quality],
-                         pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME] +
+         else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL)
+           continue;
+
+         /*
+          * Add the grayscale option to the preset...
+          */
+
+         pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
+             cupsAddOption(color_option, gray_choice,
+                           pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+                                          [pwg_print_quality],
+                           pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME] +
                              pwg_print_quality);
+       }
       }
     }
   }
 
+  if (!preset_added)
+  {
+   /*
+    * Auto-association of PPD file option settings with the IPP job attributes
+    * print-color-mode, print-quality, and print-content-optimize
+    *
+    * This is used to retro-fit PPD files and classic CUPS drivers into
+    * Printer Applications, which are IPP printers for the clients and so
+    * should get controlled by standard IPP attributes as far as possible
+    *
+    * Note that settings assigned to print-content-optimize are only used
+    * when printing with "high" print-quality
+    */
+
+    _ppdCacheAssignPresets(ppd, pc);
+  }
+
  /*
   * Copy and convert Duplex (sides) data...
   */
@@ -1967,6 +2015,984 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
 }
 
 
+/*
+ * '_ppdCacheAssignPresets()' - Go through all the options and choices in
+ *                              the PPD to find out which influence
+ *                              color/bw, print quality, and content
+ *                              optimizations to assign them to the prsets
+ *                              so that jobs can easily be controlled with
+ *                              standard IPP attributes
+ */
+
+void
+_ppdCacheAssignPresets(ppd_file_t *ppd,
+                      _ppd_cache_t *pc)
+{
+  /* properties and scores for each choice of the option under evaluation */
+  typedef struct choice_properties_s
+  {
+    int sets_mono,     /* Does this choice switch to monochrome printing? */
+        sets_color,    /*                     ... to color printing? */
+        sets_draft,    /*                     ... to draft/lower quality? */
+        sets_normal,   /*                     ... to standard/normal quality? */
+        sets_high,     /*                     ... to high/better quality? */
+        for_photo,     /* Does this choice improve photo printing? */
+        for_graphics,  /*                      ... graphics printing? */
+        for_text,      /*                      ... text printing? */
+        for_tg,        /*                      ... text & graphics printing? */
+        is_default;    /* Is this choice the PPD default? */
+    unsigned int res_x,/* Does this choice set resolution (0 if not)? */
+                 res_y;
+  } choice_properties_t;
+  int                    i, j, k, l;
+  unsigned int           m;                /* Ratio for lowering or improving
+                                             resolution */
+  int                    pass;             /* Passes to go through to find best
+                                             choice */
+  ppd_group_t            *group;           /* PPD option group */
+  ppd_option_t          *option;          /* PPD option */
+  int                    is_color;         /* Is this PPD for a color printer */
+  unsigned int           base_res_x = 0,   /* Base resolution of the pPD file */
+                         base_res_y = 0;
+  cups_page_header2_t    header,           /* CUPS Raster header to investigate
+                                             embedded code in PPD */
+                         optheader;        /* CUPS Raster header to investigate
+                                             embedded code in one PPD option */
+  int                    preferred_bits;   /* for _cupsRasterExecPS() function
+                                             call */
+  ppd_attr_t             *ppd_attr;        /* PPD attribute */
+  int                    res_factor = 1;   /* Weights of the scores for the */
+  int                    name_factor = 10; /* print quality */
+  int                    color_factor = 1000;
+
+  /* Do we have a color printer ? */
+  is_color = (ppd->color_device ? 1 : 0);
+
+  /* what is the base/default resolution for this PPD? */
+  ppdMarkDefaults(ppd);
+  cupsRasterInterpretPPD(&header, ppd, 0, NULL, NULL);
+  if (header.HWResolution[0] != 100 || header.HWResolution[1] != 100)
+  {
+    base_res_x = header.HWResolution[0];
+    base_res_y = header.HWResolution[1];
+  }
+  else if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL)
+  {
+    /* Use the PPD-defined default resolution... */
+    if (sscanf(ppd_attr->value, "%dx%d", &base_res_x, &base_res_y) == 1)
+      base_res_y = base_res_x;
+  }
+
+  /* Go through all options of the PPD file */
+  for (i = ppd->num_groups, group = ppd->groups;
+       i > 0;
+       i --, group ++)
+  {
+    /* Skip the "Installable Options" group */
+    if (strncasecmp(group->name, "Installable", 11) == 0)
+      continue;
+
+    for (j = group->num_options, option = group->options;
+         j > 0;
+         j --, option ++)
+    {
+      int sets_color_mode = 0,         /* Scores for current choice */
+         sets_quality = 0,
+         sets_optimization = 0;
+      int best_mono_draft = 0,         /* Best score for each preset for this
+                                         option */
+          best_mono_normal = 0,
+          best_mono_high = 0,
+          best_color_draft = 0,
+          best_color_normal = 0,
+          best_color_high = 0,
+          best_photo = 0,
+          best_graphics = 0,
+          best_text = 0,
+         best_tg = 0;
+      int default_ch = -1,             /* Index of default choice */
+         best_mono_draft_ch = -1,     /* Index of choice with best score */
+          best_mono_normal_ch = -1,
+          best_mono_high_ch = -1,
+          best_color_draft_ch = -1,
+          best_color_normal_ch = -1,
+          best_color_high_ch = -1,
+          best_photo_ch = -1,
+          best_graphics_ch = -1,
+          best_text_ch = -1,
+         best_tg_ch = -1;
+      cups_array_t *choice_properties; /* Array of properties of all choices
+                                         of this option */
+      choice_properties_t *properties; /* Properties of current choice */
+      char *o,                         /* Name of current option */
+          *c,                         /* Name of current choice */
+          *p;                         /* Pointer into string */
+      int  score;                      /* Temp variable for score
+                                         calculations */
+
+      o = option->keyword;
+
+      /* Skip options which do not change color mode and quality or
+         generally do not make sense in presets */
+      if (strcasecmp(o, "PageSize") == 0 ||
+         strcasecmp(o, "PageRegion") == 0 ||
+         strcasecmp(o, "InputSlot") == 0 ||
+         strcasecmp(o, "MediaSource") == 0 ||
+         strcasecmp(o, "MediaType") == 0 ||
+         strcasecmp(o, "OutputBin") == 0 ||
+         strcasecmp(o, "Duplex") == 0 ||
+         strcasecmp(o, "JCLDuplex") == 0 ||
+         strcasecmp(o, "EFDuplex") == 0 ||
+         strcasecmp(o, "EFDuplexing") == 0 ||
+         strcasecmp(o, "ARDuplex") == 0 ||
+         strcasecmp(o, "KD03Duplex") == 0 ||
+         strcasecmp(o, "Collate") == 0)
+       continue;
+
+      /* Set members options of composite options in Foomatic to stay
+         controlled by the composite option */
+
+      /* Composite options in Foomatic are options which set a number
+        of other options, so each choice of them is the same as a
+        preset in CUPS.  In addition, some PPDs in Foomatic have a
+        composite option named "PrintoutMode" with 6 choices, exactly
+        the 6 of the grid of CUPS presets, color/mono in draft,
+        mediaum, and high quality. The composite options are created
+        by hand, so they surely do for what they are intended for and
+        so they are safer as this preset auto-generation
+        algorithm. Therefore we only let the composite option be set
+        in our presets and set the member options to leave the
+        control at the composite option */
+
+      if (strstr(ppd->nickname, "Foomatic") &&
+         !strncmp(option->choices[0].choice, "From", 4) &&
+         ppdFindOption(ppd, option->choices[0].choice + 4))
+      {
+       for (k = 0; k < 2; k ++)
+         for (l = 0; l < 3; l ++)
+           if (cupsGetOption(option->choices[0].choice + 4,
+                             pc->num_presets[k][l], pc->presets[k][l]))
+             pc->num_presets[k][l] =
+               cupsAddOption(o, option->choices[0].choice,
+                             pc->num_presets[k][l], &(pc->presets[k][l]));
+       for (k = 0; k < 5; k ++)
+         if (cupsGetOption(option->choices[0].choice + 4,
+                           pc->num_optimize_presets[k],
+                           pc->optimize_presets[k]))
+           pc->num_optimize_presets[k] =
+             cupsAddOption(o, option->choices[0].choice,
+                           pc->num_optimize_presets[k],
+                           &(pc->optimize_presets[k]));
+       continue;
+      }
+
+      /* Array for properties of the choices */
+      choice_properties = cupsArrayNew(NULL, NULL);
+
+      /*
+       * Gather the data for each choice
+       */
+
+      for (k = 0; k < option->num_choices; k ++)
+      {
+       properties =
+         (choice_properties_t *)calloc(1, sizeof(choice_properties_t));
+
+       c = option->choices[k].choice;
+
+       /* Is this the default choice? (preferred for "normal" quality,
+          used for color if no choice name suggests being color */
+       if (strcmp(c, option->defchoice) == 0)
+       {
+         properties->is_default = 1;
+         default_ch = k;
+       }
+
+       /*
+       * Color/Gray - print-color-mode
+       */
+
+       /* If we have a color device, check whether this option sets mono or
+          color printing */
+       if (is_color)
+       {
+         if (strcasecmp(o, "CNIJSGrayScale") == 0)
+         {
+           if (strcasecmp(c, "1") == 0)
+             properties->sets_mono = 2;
+           else
+             properties->sets_color = 1;
+         }
+         else if (strcasecmp(o, "HPColorAsGray") == 0) /* HP PostScript */
+         {
+           if (strcasecmp(c, "True") == 0)
+             properties->sets_mono = 2;
+           else
+             properties->sets_color = 1;
+         }
+         else if (strcasecmp(o, "ColorModel") == 0 ||
+                  strcasecmp(o, "ColorMode") == 0 ||
+                  strcasecmp(o, "OutputMode") == 0 ||
+                  strcasecmp(o, "PrintoutMode") == 0 ||
+                  strcasecmp(o, "ARCMode") == 0 || /* Sharp */
+                  strcasestr(o, "ColorMode") ||
+                  strcasecmp(o, "ColorResType") == 0 || /* Toshiba */
+                  strcasestr(o, "MonoColor")) /* Brother */
+         {
+           /* Monochrome/grayscale printing */
+           if (strcasestr(c, "Mono") ||
+               strcasecmp(c, "Black") == 0 ||
+               ((p = strcasestr(c, "Black")) && strcasestr(p, "White")) ||
+               (strncasecmp(c, "BW", 2) == 0 && !isalpha(c[2])))
+             properties->sets_mono = 2;
+           else if (strcasestr(c, "Gray") ||
+                    strcasestr(c, "Grey") ||
+                    strcasecmp(c, "BlackOnly") == 0) /* Lexmark */
+             properties->sets_mono = 3;
+
+           /* Color printing */
+           if (((p = strcasestr(c, "CMY")) && !strcasestr(p, "Gray")) ||
+               strcasecmp(c, "ColorOnly") == 0 || /* Lexmark */
+               ((p = strcasestr(c, "Adobe")) && strcasestr(p, "RGB")))
+             properties->sets_color = 2;
+           else if (strcasestr(c, "sRGB"))
+             properties->sets_color = 4;
+           else if (strcasestr(c, "RGB") ||
+                    strcasestr(c, "Color"))
+             properties->sets_color = 3;
+         }
+
+         /* This option actually sets color mode */
+         if (properties->sets_mono || properties->sets_color)
+           sets_color_mode = 1;
+       }
+
+       /*
+       * Output Quality - print-quality
+       */
+
+       /* check whether this option affects print quality or content
+          optimization */
+
+       /* Determine influence of the options and choices on the print
+          quality by their names */
+
+       /* Vendor-specific option and choice names */
+       if (strcasecmp(o, "ARCPPriority") == 0) /* Sharp */
+       {
+         if (strcasecmp(c, "Quality") == 0)
+           properties->sets_high = 10;
+         else if (strcasecmp(c, "Speed") == 0)
+           properties->sets_draft = 10;
+       }
+       else if (strcasecmp(o, "BRJpeg") == 0) /* Brother */
+       {
+         if (strcasecmp(c, "QualityPrior") == 0)
+           properties->sets_high = 10;
+         else if (strcasecmp(c, "SpeedPrior") == 0)
+           properties->sets_draft = 10;
+       }
+       else if (strcasecmp(o, "RIPrintMode") == 0) /* Ricoh & OEM */
+       {
+         if (strcasecmp(c, "1rhit") == 0)
+           properties->sets_high = 7;
+         else if (strcasecmp(c, "6rhit") == 0)
+           properties->sets_high = 10;
+         else if (strcasecmp(c, "3rhit") == 0 ||
+                  strcasecmp(c, "4rhit") == 0 ||
+                  strcasecmp(c, "5rhit") == 0)
+           properties->sets_draft = 10;
+         else if (strcasecmp(c, "0rhit") == 0)
+           properties->sets_normal = 10;
+       }
+       else if (strcasecmp(o, "EconoMode") == 0 || /* Foomatic */
+                strcasecmp(o, "EconoFast") == 0)   /* Foomatic (HP PPA) */
+       {
+         if (strcasecmp(c, "Off") == 0 ||
+             strcasecmp(c, "False") == 0)
+           properties->sets_high = 1;
+         else if (strcasecmp(c, "On") == 0 ||
+                  strcasecmp(c, "True") == 0 ||
+                  strcasecmp(c, "Low") == 0)
+           properties->sets_draft = 10;
+         else if (strcasecmp(c, "High") == 0)
+           properties->sets_draft = 11;
+       }
+       else if (strcasestr(o, "ColorPrecision")) /* Gutenprint */
+       {
+         if (strcasecmp(c, "best") == 0)
+           properties->sets_high = 10;
+       }
+       /* Generic boolean options which enhance quality if true */
+       else if (((p = strcasestr(o, "slow")) && strcasestr(p, "dry")) ||
+                ((p = strcasestr(o, "color")) && strcasestr(p, "enhance")) ||
+                ((p = strcasestr(o, "resolution")) &&
+                 !strcasestr(p, "enhance")) ||
+                strcasecmp(o, "RET") == 0 ||
+                ((p = strcasestr(o, "uni")) && strcasestr(p, "direction")))
+       {
+         if (strcasecmp(c, "True") == 0 ||
+             strcasecmp(c, "On") == 0 ||
+             strcasecmp(c, "Yes") == 0 ||
+             strcasecmp(c, "1") == 0 ||
+             strcasecmp(c, "Medium") == 0) /* Resolution Enhancement/RET (HP)*/
+           properties->sets_high = 3;
+         else if (strcasecmp(c, "False") == 0 ||
+                  strcasecmp(c, "Off") == 0 ||
+                  strcasecmp(c, "No") == 0 ||
+                  strcasecmp(c, "0") == 0)
+           properties->sets_draft = 3;
+       }
+       /* Generic boolean options which reduce quality if true */
+       else if (strcasestr(o, "draft") ||
+                strcasestr(o, "economy") ||
+                ((p = strcasestr(o, "eco")) && strcasestr(p, "mode")) ||
+                ((p = strcasestr(o, "toner")) && strcasestr(p, "sav")) ||
+                ((p = strcasestr(o, "bi")) && strcasestr(p, "direction")) ||
+                strcasecmp(o, "EcoBlack") == 0 || /* Foomatic (Alps) */
+                strcasecmp(o, "bidi") == 0 ||
+                strcasecmp(o, "bi-di") == 0)
+       {
+         if (strcasecmp(c, "True") == 0 ||
+             strcasecmp(c, "On") == 0 ||
+             strcasecmp(c, "Yes") == 0 ||
+             strcasecmp(c, "1") == 0 ||
+             strcasecmp(c, "Medium") == 0) /* EconomyMode (Brother) */
+           properties->sets_draft = 3;
+         else if (strcasecmp(c, "False") == 0 ||
+                  strcasecmp(c, "Off") == 0 ||
+                  strcasecmp(c, "No") == 0 ||
+                  strcasecmp(c, "0") == 0)
+           properties->sets_high = 3;
+       }
+       /* Generic enumerated choice option and choice names */
+       else if (strcasecmp(o, "ColorModel") == 0 ||
+                strcasecmp(o, "ColorMode") == 0 ||
+                strcasecmp(o, "OutputMode") == 0 || /* HPLIP hpcups */
+                strcasecmp(o, "PrintoutMode") == 0 || /* Foomatic */
+                strcasecmp(o, "PrintQuality") == 0 ||
+                strcasecmp(o, "PrintMode") == 0 ||
+                strcasestr(o, "ColorMode") ||
+                strcasecmp(o, "ColorResType") == 0 || /* Toshiba */
+                strcasestr(o, "MonoColor") || /* Brother */
+                strcasestr(o, "Quality") ||
+                strcasestr(o, "Precision") || /* ex. stpColorPrecision
+                                                 in Gutenprint */
+                strcasestr(o, "PrintingDirection")) /* Gutenprint */
+       {
+         /* High quality */
+         if (strcasecmp(c, "Quality") == 0 ||
+             strcasecmp(c, "5") == 0)
+           properties->sets_high = 1;
+         else if (strcasestr(c, "Photo") ||
+                  strcasestr(c, "Enhance") ||
+                  strcasestr(c, "slow") ||
+                  strncasecmp(c, "ImageREt", 8) == 0 || /* HPLIP */
+                  ((p = strcasestr(c, "low")) && strcasestr(p, "speed")))
+           properties->sets_high = 2;
+         else if (strcasestr(c, "fine") ||
+                  strcasestr(c, "deep") ||
+                  ((p = strcasestr(c, "high")) && !strcasestr(p, "speed")) ||
+                  strcasestr(c, "HQ") ||
+                  strcasecmp(c, "ImageREt1200") == 0 || /* HPLIP */
+                  strcasecmp(c, "Enhanced") == 0)
+           properties->sets_high = 3;
+         else if (strcasestr(c, "best") ||
+                  strcasecmp(c, "high") == 0 ||
+                  strcasecmp(c, "fine") == 0 ||
+                  strcasecmp(c, "HQ") == 0 ||
+                  strcasecmp(c, "CMYGray") == 0 || /* HPLIP */
+                  strcasecmp(c, "ImageREt2400") == 0 || /* HPLIP */
+                  strcasestr(c, "unidir"))
+           properties->sets_high = 4;
+         else if (strcasecmp(c, "best") == 0 ||
+                  strcasecmp(c, "monolowdetail") == 0) /* Toshiba */
+           properties->sets_high = 5;
+
+         /* Low/Draft quality */
+         if (strcasecmp(c, "monolowdetail") == 0 || /* Toshiba */
+             strcasecmp(c, "3") == 0)
+           properties->sets_draft = 1;
+         else if (((p = strcasestr(c, "fast")) && strcasestr(p, "draft")) ||
+                  ((p = strcasestr(c, "high")) && strcasestr(p, "speed")) ||
+                  (strcasestr(c, "speed") && !strcasestr(c, "low")))
+           properties->sets_draft = 2;
+         else if (strcasestr(c, "quick") ||
+                  (strcasestr(c, "fast") &&
+                   !(strncasecmp(c, "FastRes", 7) == 0 && isdigit(*(c + 7)))))
+           /* HPLIP has FastRes600, FastRes1200, ... which are not draft */
+           properties->sets_draft = 3;
+         else if (strcasecmp(c, "quick") == 0 ||
+                  strcasecmp(c, "fast") == 0 ||
+                  strcasestr(c, "draft") ||
+                  (strcasestr(c, "low") && !strcasestr(c, "slow")) ||
+                  strcasestr(c, "coarse"))
+           properties->sets_draft = 4;
+         else if (strcasecmp(c, "draft") == 0 ||
+                  strcasecmp(c, "low") == 0 ||
+                  strcasecmp(c, "coarse") == 0 ||
+                  strcasestr(c, "bidir"))
+           properties->sets_draft = 5;
+
+         /* Use high or low quality but not the extremes */
+         if (strcasestr(c, "ultra") ||
+             strcasestr(c, "very") ||
+             strcasestr(c, "super"))
+         {
+           if (properties->sets_high > 1)
+             properties->sets_high --;
+           if (properties->sets_draft > 1)
+             properties->sets_draft --;
+         }
+
+         /* Normal quality */
+         if (strcasestr(c, "automatic") ||
+             strcasecmp(c, "none") == 0 ||
+             strcasecmp(c, "4") == 0 ||
+             strcasecmp(c, "FastRes1200") == 0) /* HPLIP */
+           properties->sets_normal = 1;
+         else if (strcasestr(c, "normal") ||
+                  strcasestr(c, "standard") ||
+                  strcasestr(c, "default") ||
+                  strcasecmp(c, "FastRes600") == 0) /* HPLIP */
+           properties->sets_normal = 2;
+         else if (strcasecmp(c, "normal") == 0 ||
+                  strcasecmp(c, "standard") == 0 ||
+                  strcasecmp(c, "default") == 0)
+           properties->sets_normal = 4;
+       }
+
+       /* Apply the weight factor for option/choice-name-related scores */
+       properties->sets_high *= name_factor;
+       properties->sets_draft *= name_factor;
+       properties->sets_normal *= name_factor;
+
+       /* Determine influence of the options and choices on the print
+          quality by how they change the output resolution compared to
+          the base/default resolution */
+       if (base_res_x && base_res_y)
+       {
+         /* First, analyse the code snippet (PostScript, PJL) assigned
+            to each choice of the option whether it sets resolution */
+         if (option->choices[k].code && option->choices[k].code[0])
+         {
+           /* Assume code to be PostScript (also used for CUPS Raster) */
+           preferred_bits = 0;
+           optheader = header;
+           if (_cupsRasterExecPS(&optheader, &preferred_bits,
+                                 option->choices[k].code) == 0)
+           {
+             properties->res_x = optheader.HWResolution[0];
+             properties->res_y = optheader.HWResolution[1];
+           }
+           else
+             properties->res_x = properties->res_y = 0; /* invalid */
+           if (properties->res_x == 0 || properties->res_y == 0)
+           {
+             /* Now try PJL */
+             if ((p = strstr(option->choices[k].code, "SET")) &&
+                 isspace(*(p + 3)) && (p = strstr(p + 4, "RESOLUTION=")))
+             {
+               p += 11;
+               if (sscanf(p, "%dX%d",
+                          &(properties->res_x), &(properties->res_y)) == 1)
+                   properties->res_y = properties->res_x;
+             }
+           }
+           if (properties->res_x == 100 && properties->res_y == 100)
+             properties->res_x = properties->res_y = 0; /* Code does not
+                                                           set resolution */
+         }
+         else
+           properties->res_x = properties->res_y = 0; /* invalid */
+
+         /* Then parse the choice name whether it contains a
+            resolution value (Must have "dpi", as otherwise can be
+            something else, like a page size */
+         if ((properties->res_x == 0 || properties->res_y == 0) &&
+             (p = strcasestr(c, "dpi")) != NULL)
+         {
+           if (p > c)
+           {
+             p --;
+             while (p > c && isspace(*p))
+               p --;
+             if (p > c && isdigit(*p))
+             {
+               char x;
+               while (p > c && isdigit(*p))
+                 p --;
+               if (p > c && (*p == 'x' || *p == 'X'))
+                 p --;
+               while (p > c && isdigit(*p))
+                 p --;
+               while (!isdigit(*p))
+                 p ++;
+               if (sscanf(p, "%d%c%d",
+                          &(properties->res_x), &x, &(properties->res_y)) == 2)
+                   properties->res_y = properties->res_x;
+             }
+           }
+         }
+
+         if (properties->res_x != 0 && properties->res_y != 0)
+         {
+           /* Choice suggests to set the resolution */
+           /* Raising resolution compared to default? */
+           m = (properties->res_x * properties->res_y) /
+               (base_res_x * base_res_y);
+           /* No or small change -> Normal quality */
+           if (m == 1)
+             properties->sets_normal += res_factor * 4;
+           else if (m > 1 && m < 2)
+             properties->sets_normal += res_factor * 2;
+           /* At least double the pixels -> High quality */
+           else if (m == 2)
+             properties->sets_high += res_factor * 3;
+           else if (m > 2 && m <= 8)
+             properties->sets_high += res_factor * 4;
+           else if (m > 8 && m <= 32)
+             properties->sets_high += res_factor * 2;
+           else if (m > 32)
+             properties->sets_high += res_factor * 1;
+           else if (m < 1)
+           {
+             /* Reducing resolution compared to default? */
+             m = (base_res_x * base_res_y) /
+                 (properties->res_x * properties->res_y);
+             /* No or small change -> Normal quality */
+             if (m == 1)
+               properties->sets_normal += res_factor * 1;
+             else if (m > 1 && m < 2)
+               properties->sets_normal += res_factor * 1;
+             /* At most half the pixels -> Draft quality */
+             else if (m == 2)
+               properties->sets_draft += res_factor * 3;
+             else if (m > 2 && m < 8)
+               properties->sets_draft += res_factor * 4;
+             else if (m >= 8 && m < 32)
+               properties->sets_draft += res_factor * 2;
+             else if (m >= 32)
+               properties->sets_draft += res_factor * 1;
+           }
+         }
+       }
+
+       /* This option actually sets print quality */
+       if (properties->sets_draft || properties->sets_high)
+         sets_quality = 1;
+
+       /* Add the properties of this choice */
+       cupsArrayAdd(choice_properties, properties);
+      }
+
+     /*
+      * Find the best choice for each field of the color/quality preset
+      * grid
+      */
+
+      for (pass = 0; pass < 2; pass ++)
+      {
+       for (k = 0; k < option->num_choices; k ++)
+        {
+         properties = cupsArrayIndex(choice_properties, k);
+
+         /* presets[0][0]: Mono/Draft */
+         if (best_mono_draft >= 0 &&
+             !properties->sets_color &&
+             (!properties->sets_high || pass > 0))
+         {
+           score = color_factor * properties->sets_mono +
+             properties->sets_draft;
+           if (score > best_mono_draft)
+           {
+             best_mono_draft = score;
+             best_mono_draft_ch = k;
+           }
+         }
+
+         /* presets[0][1]: Mono/Normal */
+         if (best_mono_normal >= 0 &&
+             !properties->sets_color &&
+             (!properties->sets_draft || pass > 1) &&
+             (!properties->sets_high  || pass > 0))
+         {
+           score = color_factor * properties->sets_mono +
+             properties->sets_normal;
+           if (score > best_mono_normal)
+           {
+             best_mono_normal = score;
+             best_mono_normal_ch = k;
+           }
+         }
+
+         /* presets[0][2]: Mono/High */
+         if (best_mono_high >= 0 &&
+             !properties->sets_color &&
+             (!properties->sets_draft || pass > 0))
+         {
+           score = color_factor * properties->sets_mono +
+             properties->sets_high;
+           if (score > best_mono_high)
+           {
+             best_mono_high = score;
+             best_mono_high_ch = k;
+           }
+         }
+
+         /* presets[1][0]: Color/Draft */
+         if (best_color_draft >= 0 &&
+             !properties->sets_mono &&
+             (!properties->sets_high || pass > 0))
+         {
+           score = color_factor * properties->sets_color +
+             properties->sets_draft;
+           if (score > best_color_draft)
+           {
+             best_color_draft = score;
+             best_color_draft_ch = k;
+           }
+         }
+
+         /* presets[1][1]: Color/Normal */
+         if (best_color_normal >= 0 &&
+             !properties->sets_mono &&
+             (!properties->sets_draft || pass > 1) &&
+             (!properties->sets_high  || pass > 0))
+         {
+           score = color_factor * properties->sets_color +
+             properties->sets_normal;
+           if (score > best_color_normal)
+           {
+             best_color_normal = score;
+             best_color_normal_ch = k;
+           }
+         }
+
+         /* presets[1][2]: Color/High */
+         if (best_color_high >= 0 &&
+             !properties->sets_mono &&
+             (!properties->sets_draft || pass > 0))
+         {
+           score = color_factor * properties->sets_color +
+             properties->sets_high;
+           if (score > best_color_high)
+           {
+             best_color_high = score;
+             best_color_high_ch = k;
+           }
+         }
+       }
+       /* Block next passes for the presets where we are done */
+       if (best_mono_draft_ch >= 0)
+         best_mono_draft = -1;
+       if (best_mono_normal_ch >= 0)
+         best_mono_normal = -1;
+       if (best_mono_high_ch >= 0)
+         best_mono_high = -1;
+       if (best_color_draft_ch >= 0)
+         best_color_draft = -1;
+       if (best_color_normal_ch >= 0)
+         best_color_normal = -1;
+       if (best_color_high_ch >= 0)
+         best_color_high = -1;
+      }
+
+     /*
+      * Content Optimization - print-content-optimize
+      */
+
+      for (k = 0; k < option->num_choices; k ++)
+      {
+       properties = cupsArrayIndex(choice_properties, k);
+       c = option->choices[k].choice;
+
+       /* Vendor-specific options */
+       if (strcasecmp(o, "ARCOType") == 0) /* Sharp */
+       {
+         if (strcasecmp(c, "COTDrawing") == 0)
+         {
+           properties->for_text = 3;
+           properties->for_graphics = 2;
+           properties->for_tg = 2;
+         }
+         else if (strcasecmp(c, "COTGraphics") == 0)
+         {
+           properties->for_graphics = 3;
+           properties->for_tg = 3;
+         }
+         else if (strcasecmp(c, "COTPhoto") == 0)
+           properties->for_photo = 3;
+       }
+       else if (strcasecmp(o, "HPRGBEmulation") == 0) /* HP */
+       {
+         if (strcasecmp(c, "DefaultSRGB") == 0)
+           properties->for_text = 3;
+         else if (strcasecmp(c, "VividSRGB") == 0)
+         {
+           properties->for_graphics = 3;
+           properties->for_tg = 3;
+         }
+         else if (strcasecmp(c, "PhotoSRGB") == 0)
+           properties->for_photo = 3;
+       }
+       else
+       /* Generic choice names */
+       {
+         if (strcasestr(c, "photo"))
+           properties->for_photo = 6;
+         else if (strcasecmp(c, "photo") == 0)
+           properties->for_photo = 7;
+
+         if (strcasestr(c, "graphic"))
+           properties->for_graphics = 6;
+         else if (strcasecmp(c, "graphic") == 0 ||
+                  strcasecmp(c, "graphics") == 0)
+           properties->for_graphics = 7;
+
+         if (strcasestr(c, "text"))
+         {
+           if (strcasestr(c, "graphic"))
+             properties->for_tg = 7;
+           else
+             properties->for_text = 6;
+         }
+         else if (strcasecmp(c, "text") == 0)
+           properties->for_text = 7;
+
+         if (strcasestr(c, "presentation"))
+         {
+           properties->for_text = 4;
+           properties->for_graphics = 4;
+           properties->for_tg = 4;
+         }
+         else if (strcasecmp(c, "presentation") == 0)
+         {
+           properties->for_text = 5;
+           properties->for_graphics = 5;
+           properties->for_tg = 5;
+         }
+
+         if (strcasestr(c, "lineart"))
+         {
+           properties->for_graphics = 2;
+           properties->for_tg = 2;
+         }
+         else if (strcasecmp(c, "lineart") == 0)
+         {
+           properties->for_graphics = 3;
+           properties->for_tg = 3;
+         }
+
+         if (strcasestr(c, "drawing"))
+         {
+           properties->for_graphics = 4;
+           properties->for_tg = 4;
+         }
+         else if (strcasecmp(c, "drawing") == 0)
+         {
+           properties->for_graphics = 5;
+           properties->for_tg = 5;
+         }
+
+         if (strcasestr(c, "natural"))
+           properties->for_photo = 2;
+         else if (strcasecmp(c, "natural") == 0)
+           properties->for_photo = 3;
+
+         if (strcasestr(c, "vivid"))
+         {
+           properties->for_text = 2;
+           properties->for_graphics = 2;
+           properties->for_tg = 2;
+         }
+         else if (strcasecmp(c, "vivid") == 0)
+         {
+           properties->for_text = 3;
+           properties->for_graphics = 3;
+           properties->for_tg = 3;
+         }
+       }
+
+       /* We apply these optimizations only in high quality mode
+          therefore we prefer settings for high quality */
+       if (properties->sets_high && !properties->sets_draft)
+       {
+         if (properties->for_photo)
+           properties->for_photo += 10;
+         if (properties->for_graphics)
+           properties->for_graphics += 10;
+         if (properties->for_text)
+           properties->for_text += 10;
+         if (properties->for_tg)
+           properties->for_tg += 10;
+       }
+
+       /*
+       * Find the best choice for each field of the content optimize presets
+       */
+
+       /* Find best choice for each task */
+       /* optimize_presets[1]: Photo */
+       if (properties->for_photo > best_photo)
+       {
+         best_photo = properties->for_photo;
+         best_photo_ch = k;
+       }
+       /* optimize_presets[2]: Graphics */
+       if (properties->for_graphics > best_graphics)
+       {
+         best_graphics = properties->for_graphics;
+         best_graphics_ch = k;
+       }
+       /* optimize_presets[3]: Text */
+       if (properties->for_text > best_text)
+       {
+         best_text = properties->for_text;
+         best_text_ch = k;
+       }
+       /* optimize_presets[4]: Text and Graphics */
+       if (properties->for_tg > best_tg)
+       {
+         best_tg = properties->for_tg;
+         best_tg_ch = k;
+       }
+
+       /* This option actually does content optimization */
+       if (properties->for_text || properties->for_graphics ||
+           properties->for_tg || properties->for_photo)
+         sets_optimization = 1;
+      }
+
+     /*
+      * Fill in the presets
+      */
+
+      if (sets_color_mode || sets_quality)
+      {
+       /* presets[0][0]: Mono/Draft */
+       if (best_mono_draft_ch < 0)
+         best_mono_draft_ch = default_ch;
+       if (best_mono_draft_ch >= 0)
+         pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+                        [_PWG_PRINT_QUALITY_DRAFT] =
+           cupsAddOption(o, option->choices[best_mono_draft_ch].choice,
+                         pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+                                        [_PWG_PRINT_QUALITY_DRAFT],
+                         &(pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+                                      [_PWG_PRINT_QUALITY_DRAFT]));
+
+       /* presets[0][1]: Mono/Normal */
+       if (best_mono_normal_ch < 0)
+         best_mono_normal_ch = default_ch;
+       if (best_mono_normal_ch >= 0)
+         pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+                        [_PWG_PRINT_QUALITY_NORMAL] =
+           cupsAddOption(o, option->choices[best_mono_normal_ch].choice,
+                         pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+                                        [_PWG_PRINT_QUALITY_NORMAL],
+                         &(pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+                                      [_PWG_PRINT_QUALITY_NORMAL]));
+
+       /* presets[0][2]: Mono/High */
+       if (best_mono_high_ch < 0)
+         best_mono_high_ch = default_ch;
+       if (best_mono_high_ch >= 0)
+         pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+                        [_PWG_PRINT_QUALITY_HIGH] =
+           cupsAddOption(o, option->choices[best_mono_high_ch].choice,
+                         pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+                                        [_PWG_PRINT_QUALITY_HIGH],
+                         &(pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
+                                      [_PWG_PRINT_QUALITY_HIGH]));
+
+       /* presets[1][0]: Color/Draft */
+       if (best_color_draft_ch < 0)
+         best_color_draft_ch = default_ch;
+       if (best_color_draft_ch >= 0)
+         pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                        [_PWG_PRINT_QUALITY_DRAFT] =
+           cupsAddOption(o, option->choices[best_color_draft_ch].choice,
+                         pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                                        [_PWG_PRINT_QUALITY_DRAFT],
+                         &(pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                                      [_PWG_PRINT_QUALITY_DRAFT]));
+
+       /* presets[1][1]: Color/Normal */
+       if (best_color_normal_ch < 0)
+         best_color_normal_ch = default_ch;
+       if (best_color_normal_ch >= 0)
+         pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                        [_PWG_PRINT_QUALITY_NORMAL] =
+           cupsAddOption(o, option->choices[best_color_normal_ch].choice,
+                         pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                                        [_PWG_PRINT_QUALITY_NORMAL],
+                         &(pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                                      [_PWG_PRINT_QUALITY_NORMAL]));
+
+       /* presets[1][2]: Color/High */
+       if (best_color_high_ch < 0)
+         best_color_high_ch = default_ch;
+       if (best_color_high_ch >= 0)
+         pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                        [_PWG_PRINT_QUALITY_HIGH] =
+           cupsAddOption(o, option->choices[best_color_high_ch].choice,
+                         pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                                        [_PWG_PRINT_QUALITY_HIGH],
+                         &(pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
+                                      [_PWG_PRINT_QUALITY_HIGH]));
+
+      }
+
+      if (sets_optimization)
+      {
+
+       /* optimize_presets[1]: Photo */
+       if (best_photo_ch >= 0)
+         pc->num_optimize_presets[_PWG_PRINT_CONTENT_OPTIMIZE_PHOTO] =
+           cupsAddOption
+             (o, option->choices[best_photo_ch].choice,
+              pc->num_optimize_presets[_PWG_PRINT_CONTENT_OPTIMIZE_PHOTO],
+              &(pc->optimize_presets[_PWG_PRINT_CONTENT_OPTIMIZE_PHOTO]));
+
+       /* optimize_presets[2]: Graphics */
+       if (best_graphics_ch >= 0)
+         pc->num_optimize_presets[_PWG_PRINT_CONTENT_OPTIMIZE_GRAPHICS] =
+           cupsAddOption
+             (o, option->choices[best_graphics_ch].choice,
+              pc->num_optimize_presets
+                [_PWG_PRINT_CONTENT_OPTIMIZE_GRAPHICS],
+              &(pc->optimize_presets
+                [_PWG_PRINT_CONTENT_OPTIMIZE_GRAPHICS]));
+
+       /* optimize_presets[1]: Text */
+       if (best_text_ch >= 0)
+         pc->num_optimize_presets[_PWG_PRINT_CONTENT_OPTIMIZE_TEXT] =
+           cupsAddOption
+             (o, option->choices[best_text_ch].choice,
+              pc->num_optimize_presets[_PWG_PRINT_CONTENT_OPTIMIZE_TEXT],
+              &(pc->optimize_presets[_PWG_PRINT_CONTENT_OPTIMIZE_TEXT]));
+
+       /* optimize_presets[1]: Text and Graphics */
+       if (best_tg_ch >= 0)
+         pc->num_optimize_presets
+           [_PWG_PRINT_CONTENT_OPTIMIZE_TEXT_AND_GRAPHICS] =
+           cupsAddOption
+             (o, option->choices[best_tg_ch].choice,
+              pc->num_optimize_presets
+                [_PWG_PRINT_CONTENT_OPTIMIZE_TEXT_AND_GRAPHICS],
+              &(pc->optimize_presets
+                  [_PWG_PRINT_CONTENT_OPTIMIZE_TEXT_AND_GRAPHICS]));
+
+      }
+
+      for (k = 0; k < option->num_choices; k ++)
+       free(cupsArrayIndex(choice_properties, k));
+      cupsArrayDelete(choice_properties);
+    }
+  }
+}
+
 /*
  * '_ppdCacheDestroy()' - Free all memory used for PWG mapping data.
  */
@@ -1974,7 +3000,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
 void
 _ppdCacheDestroy(_ppd_cache_t *pc)     /* I - PPD cache and mapping data */
 {
-  int          i;                      /* Looping var */
+  int          i, j;                   /* Looping vars */
   pwg_map_t    *map;                   /* Current map */
   pwg_size_t   *size;                  /* Current size */
 
@@ -2053,6 +3079,15 @@ _ppdCacheDestroy(_ppd_cache_t *pc)       /* I - PPD cache and mapping data */
 
   cupsArrayDelete(pc->strings);
 
+  for (i = _PWG_PRINT_COLOR_MODE_MONOCHROME; i < _PWG_PRINT_COLOR_MODE_MAX; i ++)
+    for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
+      if (pc->num_presets[i][j])
+       cupsFreeOptions(pc->num_presets[i][j], pc->presets[i][j]);
+
+  for (i = _PWG_PRINT_CONTENT_OPTIMIZE_AUTO; i < _PWG_PRINT_CONTENT_OPTIMIZE_MAX; i ++)
+    if (pc->num_optimize_presets[i])
+      cupsFreeOptions(pc->num_optimize_presets[i], pc->optimize_presets[i]);
+
   free(pc);
 }
 
@@ -2921,6 +3956,21 @@ _ppdCacheWriteFile(
        cupsFilePutChar(fp, '\n');
       }
 
+ /*
+  * Optimization Presets...
+  */
+
+  for (i = _PWG_PRINT_CONTENT_OPTIMIZE_AUTO; i < _PWG_PRINT_CONTENT_OPTIMIZE_MAX; i ++)
+    if (pc->num_optimize_presets[i])
+    {
+      cupsFilePrintf(fp, "OptimizePreset %d", i);
+      for (k = pc->num_optimize_presets[i], option = pc->optimize_presets[i];
+          k > 0;
+          k --, option ++)
+       cupsFilePrintf(fp, " %s=%s", option->name, option->value);
+      cupsFilePutChar(fp, '\n');
+    }
+
  /*
   * Duplex/sides...
   */
index 3e3040f1a01e5ebbd9d5ee2f3d4b530a725bc44e..65153a0e26e25e1d1565b8c52c528caaa5841578 100644 (file)
@@ -36,7 +36,7 @@ extern "C" {
  * Constants...
  */
 
-#  define _PPD_CACHE_VERSION   10      /* Version number in cache file */
+#  define _PPD_CACHE_VERSION   11      /* Version number in cache file */
 
 
 /*
@@ -101,6 +101,16 @@ typedef enum _pwg_print_quality_e  /**** PWG print-quality values ****/
   _PWG_PRINT_QUALITY_MAX
 } _pwg_print_quality_t;
 
+typedef enum _pwg_print_content_optimize_e /** PWG print-content-optimize **/
+{
+  _PWG_PRINT_CONTENT_OPTIMIZE_AUTO = 0, /* print-content-optimize=auto */
+  _PWG_PRINT_CONTENT_OPTIMIZE_PHOTO,   /* print-content-optimize=photo */
+  _PWG_PRINT_CONTENT_OPTIMIZE_GRAPHICS, /* print-content-optimize=graphics */
+  _PWG_PRINT_CONTENT_OPTIMIZE_TEXT,    /* print-content-optimize=text */
+  _PWG_PRINT_CONTENT_OPTIMIZE_TEXT_AND_GRAPHICS, /* ...=text-and-graphics */
+  _PWG_PRINT_CONTENT_OPTIMIZE_MAX
+} _pwg_print_content_optimize_t;
+
 typedef struct _pwg_finishings_s       /**** PWG finishings mapping data ****/
 {
   ipp_finishings_t     value;          /* finishings value */
@@ -131,6 +141,11 @@ struct _ppd_cache_s                        /**** PPD cache and PWG conversion data ****/
                                        /* Number of print-color-mode/print-quality options */
   cups_option_t        *presets[_PWG_PRINT_COLOR_MODE_MAX][_PWG_PRINT_QUALITY_MAX];
                                        /* print-color-mode/print-quality options */
+  int          num_optimize_presets[_PWG_PRINT_CONTENT_OPTIMIZE_MAX];
+                                       /* Number of print-content-optimize
+                                          options */
+  cups_option_t        *optimize_presets[_PWG_PRINT_CONTENT_OPTIMIZE_MAX];
+                                       /* print-content-optimize options */
   char         *sides_option,          /* PPD option for sides */
                *sides_1sided,          /* Choice for one-sided */
                *sides_2sided_long,     /* Choice for two-sided-long-edge */
@@ -214,6 +229,8 @@ extern const char   *_pwgMediaTypeForType(const char *media_type,
 extern const char      *_pwgPageSizeForMedia(pwg_media_t *media,
                                              char *name, size_t namesize) _CUPS_PRIVATE;
 
+extern void             _ppdCacheAssignPresets(ppd_file_t *ppd, _ppd_cache_t *pc) _CUPS_PRIVATE;
+
 
 /*
  * C++ magic...
index b448acda5fec7dc35673b727f9991cd5c08d21d0..615ef98f4c0b1a459596efc1f56777c580bda8d2 100644 (file)
@@ -3636,9 +3636,13 @@ get_options(cupsd_job_t *job,            /* I - Job */
   cups_option_t                *pwgppds,       /* PWG->PPD options */
                        *pwgppd,        /* Current PWG->PPD option */
                        *preset;        /* Current preset option */
-  int                  print_color_mode,
+  int                  print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR,
                                        /* Output mode (if any) */
-                       print_quality;  /* Print quality (if any) */
+                       print_quality = _PWG_PRINT_QUALITY_NORMAL,
+                                        /* Print quality (if any) */
+                       print_content_optimize =
+                          _PWG_PRINT_CONTENT_OPTIMIZE_AUTO;
+                                        /* Print content type (if any)*/
   const char           *ppd;           /* PPD option choice */
   int                  exact;          /* Did we get an exact match? */
   static char          *options = NULL;/* Full list of options */
@@ -3660,7 +3664,7 @@ get_options(cupsd_job_t *job,             /* I - Job */
   if (pc &&
       !ippFindAttribute(job->attrs, "com.apple.print.DocumentTicket.PMSpoolFormat", IPP_TAG_ZERO) &&
       !ippFindAttribute(job->attrs, "APPrinterPreset", IPP_TAG_ZERO) &&
-      (ippFindAttribute(job->attrs, "print-color-mode", IPP_TAG_ZERO) || ippFindAttribute(job->attrs, "print-quality", IPP_TAG_ZERO) || ippFindAttribute(job->attrs, "cupsPrintQuality", IPP_TAG_ZERO)))
+      (ippFindAttribute(job->attrs, "print-color-mode", IPP_TAG_ZERO) || ippFindAttribute(job->attrs, "print-quality", IPP_TAG_ZERO) || ippFindAttribute(job->attrs, "print-content-optimize", IPP_TAG_ZERO) || ippFindAttribute(job->attrs, "cupsPrintQuality", IPP_TAG_ZERO)))
   {
    /*
     * Map print-color-mode and print-quality to a preset...
@@ -3722,6 +3726,12 @@ get_options(cupsd_job_t *job,            /* I - Job */
       }
     }
 
+    cupsdLogJob(job, CUPSD_LOG_DEBUG,
+               "print-color-mode=%s, print-quality=%s",
+               print_color_mode == _PWG_PRINT_COLOR_MODE_MONOCHROME ?
+               "gray" : "color",
+               print_quality == _PWG_PRINT_QUALITY_DRAFT ? "draft" :
+               (print_quality == _PWG_PRINT_QUALITY_HIGH ? "high" : "normal"));
     if (pc->num_presets[print_color_mode][print_quality] > 0)
     {
      /*
@@ -3736,7 +3746,72 @@ get_options(cupsd_job_t *job,            /* I - Job */
       {
         if (!ippFindAttribute(job->attrs, preset->name, IPP_TAG_ZERO))
         {
-          cupsdLogJob(job, CUPSD_LOG_DEBUG2, "Adding preset option %s=%s", preset->name, preset->value);
+          cupsdLogJob(job, CUPSD_LOG_DEBUG, "Adding preset option %s=%s", preset->name, preset->value);
+
+         num_pwgppds = cupsAddOption(preset->name, preset->value, num_pwgppds, &pwgppds);
+        }
+      }
+    }
+  }
+
+  if (pc &&
+      ippFindAttribute(job->attrs, "print-content-optimize", IPP_TAG_ZERO))
+  {
+   /*
+    * Map print-content-optimize to a preset...
+    */
+
+    if ((attr = ippFindAttribute(job->attrs, "print-content-optimize",
+                                IPP_TAG_KEYWORD)) != NULL)
+    {
+      if (!strcmp(attr->values[0].string.text, "auto"))
+       print_content_optimize = _PWG_PRINT_CONTENT_OPTIMIZE_AUTO;
+      else if (!strcmp(attr->values[0].string.text, "photo"))
+       print_content_optimize = _PWG_PRINT_CONTENT_OPTIMIZE_PHOTO;
+      else if (!strcmp(attr->values[0].string.text, "graphics") ||
+              !strcmp(attr->values[0].string.text, "graphic"))
+       print_content_optimize = _PWG_PRINT_CONTENT_OPTIMIZE_GRAPHICS;
+      else if (!strcmp(attr->values[0].string.text, "text"))
+       print_content_optimize = _PWG_PRINT_CONTENT_OPTIMIZE_TEXT;
+      else if (!strcmp(attr->values[0].string.text, "text-and-graphics") ||
+              !strcmp(attr->values[0].string.text, "text-and-graphic"))
+       print_content_optimize = _PWG_PRINT_CONTENT_OPTIMIZE_TEXT_AND_GRAPHICS;
+      else
+       print_content_optimize = _PWG_PRINT_CONTENT_OPTIMIZE_AUTO;
+    }
+    else
+      print_content_optimize = _PWG_PRINT_CONTENT_OPTIMIZE_AUTO;
+
+  cupsdLogJob(job, CUPSD_LOG_DEBUG,
+             "print-content-optimize=%s",
+              (print_content_optimize == _PWG_PRINT_CONTENT_OPTIMIZE_AUTO ?
+              "automatic" :
+               (print_content_optimize == _PWG_PRINT_CONTENT_OPTIMIZE_PHOTO ?
+               "photo" :
+                (print_content_optimize == _PWG_PRINT_CONTENT_OPTIMIZE_GRAPHICS ?
+                "graphics" :
+                 (print_content_optimize == _PWG_PRINT_CONTENT_OPTIMIZE_TEXT ?
+                 "text" :
+                  "text and graphics")))));
+    if (pc->num_optimize_presets[print_content_optimize] > 0)
+    {
+     /*
+      * Copy the preset options as long as the corresponding names are not
+      * already defined in the IPP request and also if it does not change
+      * the print quality preset (as long as we do not print in high quality)
+      * ...
+      */
+
+      for (i = pc->num_optimize_presets[print_content_optimize],
+              preset = pc->optimize_presets[print_content_optimize];
+          i > 0;
+          i --, preset ++)
+      {
+        if (!ippFindAttribute(job->attrs, preset->name, IPP_TAG_ZERO) &&
+           (print_quality == _PWG_PRINT_QUALITY_HIGH ||
+            cupsGetOption(preset->name, num_pwgppds, pwgppds) == NULL))
+        {
+          cupsdLogJob(job, CUPSD_LOG_DEBUG, "Adding content optimization preset option %s=%s", preset->name, preset->value);
 
          num_pwgppds = cupsAddOption(preset->name, preset->value, num_pwgppds, &pwgppds);
         }