--- a/cups/ppd-cache.c +++ b/cups/ppd-cache.c @@ -504,6 +504,8 @@ _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)); @@ -929,6 +931,28 @@ 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")) @@ -1056,6 +1080,7 @@ *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 */ @@ -1512,6 +1537,10 @@ if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL) { /* + * "Classic" Mac OS approach + */ + + /* * Copy and convert APPrinterPreset (output-mode + print-quality) data... */ @@ -1611,114 +1640,133 @@ _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"; + } + + if (color_option && gray_choice) + { + /* + * Copy and convert ColorModel (output-mode) data... + */ + + 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]) + { + /* + * Copy the color options... + */ + + num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR] + [pwg_print_quality]; + options = calloc(sizeof(cups_option_t), (size_t)num_options); - num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR] - [pwg_print_quality]; - options = calloc((size_t)num_options, sizeof(cups_option_t)); - - if (options) - { - for (i = num_options, moption = options, - coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR] - [pwg_print_quality]; - i > 0; - i --, moption ++, coption ++) + if (options) { - moption->name = _cupsStrRetain(coption->name); - moption->value = _cupsStrRetain(coption->value); - } + 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] = + 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... - */ + else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL) + continue; - 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] + + /* + * 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... */ @@ -2052,13 +2100,996 @@ /* + * '_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 (_cups_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 (_cups_strcasecmp(o, "PageSize") == 0 || + _cups_strcasecmp(o, "PageRegion") == 0 || + _cups_strcasecmp(o, "InputSlot") == 0 || + _cups_strcasecmp(o, "MediaSource") == 0 || + _cups_strcasecmp(o, "MediaType") == 0 || + _cups_strcasecmp(o, "OutputBin") == 0 || + _cups_strcasecmp(o, "Duplex") == 0 || + _cups_strcasecmp(o, "JCLDuplex") == 0 || + _cups_strcasecmp(o, "EFDuplex") == 0 || + _cups_strcasecmp(o, "EFDuplexing") == 0 || + _cups_strcasecmp(o, "ARDuplex") == 0 || + _cups_strcasecmp(o, "KD03Duplex") == 0 || + _cups_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 (_cups_strcasecmp(o, "CNIJSGrayScale") == 0) + { + if (_cups_strcasecmp(c, "1") == 0) + properties->sets_mono = 2; + else + properties->sets_color = 1; + } + else if (_cups_strcasecmp(o, "HPColorAsGray") == 0 || /* HP PostScript */ + _cups_strcasecmp(o, "HPPJLColorAsGray") == 0) /* HP PostScript */ + { + if (_cups_strcasecmp(c, "True") == 0 || + _cups_strcasecmp(c, "yes") == 0) + properties->sets_mono = 2; + else + properties->sets_color = 1; + } + else if (_cups_strcasecmp(o, "ColorModel") == 0 || + _cups_strcasecmp(o, "ColorMode") == 0 || + _cups_strcasecmp(o, "OutputMode") == 0 || + _cups_strcasecmp(o, "PrintoutMode") == 0 || + _cups_strcasecmp(o, "ARCMode") == 0 || /* Sharp */ + _cups_strcasestr(o, "ColorMode") || + _cups_strcasecmp(o, "ColorResType") == 0 || /* Toshiba */ + _cups_strcasestr(o, "MonoColor")) /* Brother */ + { + /* Monochrome/grayscale printing */ + if (_cups_strcasestr(c, "Mono") || + _cups_strcasecmp(c, "Black") == 0 || + ((p = _cups_strcasestr(c, "Black")) && _cups_strcasestr(p, "White")) || + (_cups_strncasecmp(c, "BW", 2) == 0 && !isalpha(c[2]))) + properties->sets_mono = 2; + else if (_cups_strcasestr(c, "Gray") || + _cups_strcasestr(c, "Grey") || + _cups_strcasecmp(c, "BlackOnly") == 0) /* Lexmark */ + properties->sets_mono = 3; + + /* Color printing */ + if (((p = _cups_strcasestr(c, "CMY")) && !_cups_strcasestr(p, "Gray")) || + _cups_strcasecmp(c, "ColorOnly") == 0 || /* Lexmark */ + ((p = _cups_strcasestr(c, "Adobe")) && _cups_strcasestr(p, "RGB"))) + properties->sets_color = 2; + else if (_cups_strcasestr(c, "sRGB")) + properties->sets_color = 4; + else if (_cups_strcasestr(c, "RGB") || + _cups_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 (_cups_strcasecmp(o, "ARCPPriority") == 0) /* Sharp */ + { + if (_cups_strcasecmp(c, "Quality") == 0) + properties->sets_high = 10; + else if (_cups_strcasecmp(c, "Speed") == 0) + properties->sets_draft = 10; + } + else if (_cups_strcasecmp(o, "BRJpeg") == 0) /* Brother */ + { + if (_cups_strcasecmp(c, "QualityPrior") == 0) + properties->sets_high = 10; + else if (_cups_strcasecmp(c, "SpeedPrior") == 0) + properties->sets_draft = 10; + } + else if (_cups_strcasecmp(o, "RIPrintMode") == 0) /* Ricoh & OEM */ + { + if (_cups_strcasecmp(c, "1rhit") == 0) + properties->sets_high = 7; + else if (_cups_strcasecmp(c, "6rhit") == 0) + properties->sets_high = 10; + else if (_cups_strcasecmp(c, "3rhit") == 0 || + _cups_strcasecmp(c, "4rhit") == 0 || + _cups_strcasecmp(c, "5rhit") == 0) + properties->sets_draft = 10; + else if (_cups_strcasecmp(c, "0rhit") == 0) + properties->sets_normal = 10; + } + else if (_cups_strcasecmp(o, "EconoMode") == 0 || /* Foomatic */ + _cups_strcasecmp(o, "EconoFast") == 0) /* Foomatic (HP PPA) */ + { + if (_cups_strcasecmp(c, "Off") == 0 || + _cups_strcasecmp(c, "False") == 0) + properties->sets_high = 1; + else if (_cups_strcasecmp(c, "On") == 0 || + _cups_strcasecmp(c, "True") == 0 || + _cups_strcasecmp(c, "Low") == 0) + properties->sets_draft = 10; + else if (_cups_strcasecmp(c, "High") == 0) + properties->sets_draft = 11; + } + else if (_cups_strcasestr(o, "ColorPrecision")) /* Gutenprint */ + { + if (_cups_strcasecmp(c, "best") == 0) + properties->sets_high = 10; + } + /* Generic boolean options which enhance quality if true */ + else if (((p = _cups_strcasestr(o, "slow")) && _cups_strcasestr(p, "dry")) || + ((p = _cups_strcasestr(o, "color")) && _cups_strcasestr(p, "enhance")) || + ((p = _cups_strcasestr(o, "resolution")) && + !_cups_strcasestr(p, "enhance")) || + _cups_strcasecmp(o, "RET") == 0 || + _cups_strcasecmp(o, "Smoothing") == 0 || /* HPLIP */ + ((p = _cups_strcasestr(o, "uni")) && _cups_strcasestr(p, "direction"))) + { + if (_cups_strcasecmp(c, "True") == 0 || + _cups_strcasecmp(c, "On") == 0 || + _cups_strcasecmp(c, "Yes") == 0 || + _cups_strcasecmp(c, "1") == 0 || + _cups_strcasecmp(c, "Medium") == 0) /* Resolution Enhancement/RET (HP)*/ + properties->sets_high = 3; + else if (_cups_strcasecmp(c, "False") == 0 || + _cups_strcasecmp(c, "Off") == 0 || + _cups_strcasecmp(c, "No") == 0 || + _cups_strcasecmp(c, "0") == 0) + properties->sets_draft = 3; + } + /* Generic boolean options which reduce quality if true */ + else if (_cups_strcasestr(o, "draft") || + _cups_strcasestr(o, "economy") || + ((p = _cups_strcasestr(o, "eco")) && _cups_strcasestr(p, "mode")) || + ((p = _cups_strcasestr(o, "toner")) && _cups_strcasestr(p, "sav")) || + ((p = _cups_strcasestr(o, "bi")) && _cups_strcasestr(p, "direction")) || + _cups_strcasecmp(o, "EcoBlack") == 0 || /* Foomatic (Alps) */ + _cups_strcasecmp(o, "bidi") == 0 || + _cups_strcasecmp(o, "bi-di") == 0) + { + if (_cups_strcasecmp(c, "True") == 0 || + _cups_strcasecmp(c, "On") == 0 || + _cups_strcasecmp(c, "Yes") == 0 || + _cups_strcasecmp(c, "1") == 0 || + _cups_strcasecmp(c, "Medium") == 0) /* EconomyMode (Brother) */ + properties->sets_draft = 3; + else if (_cups_strcasecmp(c, "False") == 0 || + _cups_strcasecmp(c, "Off") == 0 || + _cups_strcasecmp(c, "No") == 0 || + _cups_strcasecmp(c, "0") == 0) + properties->sets_high = 3; + } + /* Generic enumerated choice option and choice names */ + else if (_cups_strcasecmp(o, "ColorModel") == 0 || + _cups_strcasecmp(o, "ColorMode") == 0 || + _cups_strcasecmp(o, "OutputMode") == 0 || /* HPLIP hpcups */ + _cups_strcasecmp(o, "PrintoutMode") == 0 || /* Foomatic */ + _cups_strcasecmp(o, "PrintQuality") == 0 || + _cups_strcasecmp(o, "PrintMode") == 0 || + _cups_strcasestr(o, "ColorMode") || + _cups_strcasestr(o, "HalfTone") || /* HPLIP */ + _cups_strcasecmp(o, "ColorResType") == 0 || /* Toshiba */ + _cups_strcasestr(o, "MonoColor") || /* Brother */ + _cups_strcasestr(o, "Quality") || + _cups_strcasestr(o, "Resolution") || + _cups_strcasestr(o, "Precision") || /* ex. stpColorPrecision + in Gutenprint */ + _cups_strcasestr(o, "PrintingDirection")) /* Gutenprint */ + { + /* High quality */ + if (_cups_strcasecmp(c, "Quality") == 0 || + _cups_strcasecmp(c, "5") == 0) + properties->sets_high = 1; + else if (_cups_strcasestr(c, "Photo") || + _cups_strcasestr(c, "Enhance") || + _cups_strcasestr(c, "slow") || + _cups_strncasecmp(c, "ProRes", 6) == 0 || /* HPLIP */ + _cups_strncasecmp(c, "ImageREt", 8) == 0 || /* HPLIP */ + ((p = _cups_strcasestr(c, "low")) && _cups_strcasestr(p, "speed"))) + properties->sets_high = 2; + else if (_cups_strcasestr(c, "fine") || + _cups_strcasestr(c, "deep") || + ((p = _cups_strcasestr(c, "high")) && !_cups_strcasestr(p, "speed")) || + _cups_strcasestr(c, "HQ") || + _cups_strcasecmp(c, "ProRes600") == 0 || /* HPLIP */ + _cups_strcasecmp(c, "ImageREt1200") == 0 || /* HPLIP */ + _cups_strcasecmp(c, "Enhanced") == 0) + properties->sets_high = 3; + else if (_cups_strcasestr(c, "best") || + _cups_strcasecmp(c, "high") == 0 || + _cups_strcasecmp(c, "fine") == 0 || + _cups_strcasecmp(c, "HQ") == 0 || + _cups_strcasecmp(c, "CMYGray") == 0 || /* HPLIP */ + _cups_strcasecmp(c, "ProRes1200") == 0 || /* HPLIP */ + _cups_strcasecmp(c, "ImageREt2400") == 0 || /* HPLIP */ + _cups_strcasestr(c, "unidir")) + properties->sets_high = 4; + else if (_cups_strcasecmp(c, "best") == 0 || + _cups_strcasecmp(c, "ProRes2400") == 0 || /* HPLIP */ + _cups_strcasecmp(c, "monolowdetail") == 0) /* Toshiba */ + properties->sets_high = 5; + + /* Low/Draft quality */ + if (_cups_strcasecmp(c, "monolowdetail") == 0 || /* Toshiba */ + _cups_strcasecmp(c, "3") == 0) + properties->sets_draft = 1; + else if (((p = _cups_strcasestr(c, "fast")) && _cups_strcasestr(p, "draft")) || + ((p = _cups_strcasestr(c, "high")) && _cups_strcasestr(p, "speed")) || + (_cups_strcasestr(c, "speed") && !_cups_strcasestr(c, "low"))) + properties->sets_draft = 2; + else if (_cups_strcasestr(c, "quick") || + (_cups_strcasestr(c, "fast") && + !(_cups_strncasecmp(c, "FastRes", 7) == 0 && isdigit(*(c + 7))))) + /* HPLIP has FastRes600, FastRes1200, ... which are not draft */ + properties->sets_draft = 3; + else if (_cups_strcasecmp(c, "quick") == 0 || + _cups_strcasecmp(c, "fast") == 0 || + _cups_strcasestr(c, "draft") || + (_cups_strcasestr(c, "low") && !_cups_strcasestr(c, "slow")) || + _cups_strcasestr(c, "coarse")) + properties->sets_draft = 4; + else if (_cups_strcasecmp(c, "draft") == 0 || + _cups_strcasecmp(c, "low") == 0 || + _cups_strcasecmp(c, "coarse") == 0 || + _cups_strcasestr(c, "bidir")) + properties->sets_draft = 5; + + /* Use high or low quality but not the extremes */ + if (_cups_strcasestr(c, "ultra") || + _cups_strcasestr(c, "very") || + _cups_strcasestr(c, "super")) + { + if (properties->sets_high > 1) + properties->sets_high --; + if (properties->sets_draft > 1) + properties->sets_draft --; + } + + /* Normal quality */ + if (_cups_strcasestr(c, "automatic") || + _cups_strcasecmp(c, "none") == 0 || + _cups_strcasecmp(c, "4") == 0 || + _cups_strcasecmp(c, "FastRes1200") == 0) /* HPLIP */ + properties->sets_normal = 1; + else if (_cups_strcasestr(c, "normal") || + _cups_strcasestr(c, "standard") || + _cups_strcasestr(c, "default") || + _cups_strcasecmp(c, "FastRes600") == 0) /* HPLIP */ + properties->sets_normal = 2; + else if (_cups_strcasecmp(c, "normal") == 0 || + _cups_strcasecmp(c, "standard") == 0 || + _cups_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 = _cups_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; + /* 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; + /* 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 < 3; 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 (_cups_strcasecmp(o, "ARCOType") == 0) /* Sharp */ + { + if (_cups_strcasecmp(c, "COTDrawing") == 0) + { + properties->for_text = 3; + properties->for_graphics = 2; + properties->for_tg = 2; + } + else if (_cups_strcasecmp(c, "COTGraphics") == 0) + { + properties->for_graphics = 3; + properties->for_tg = 3; + } + else if (_cups_strcasecmp(c, "COTPhoto") == 0) + properties->for_photo = 3; + } + else if (_cups_strcasecmp(o, "HPRGBEmulation") == 0) /* HP */ + { + if (_cups_strcasecmp(c, "DefaultSRGB") == 0) + properties->for_text = 3; + else if (_cups_strcasecmp(c, "VividSRGB") == 0) + { + properties->for_graphics = 3; + properties->for_tg = 3; + } + else if (_cups_strcasecmp(c, "PhotoSRGB") == 0) + properties->for_photo = 3; + } + else + /* Generic choice names */ + { + if (_cups_strcasestr(c, "photo")) + properties->for_photo = 6; + else if (_cups_strcasecmp(c, "photo") == 0) + properties->for_photo = 7; + + if (_cups_strcasestr(c, "graphic")) + properties->for_graphics = 6; + else if (_cups_strcasecmp(c, "graphic") == 0 || + _cups_strcasecmp(c, "graphics") == 0) + properties->for_graphics = 7; + + if (_cups_strcasestr(c, "text")) + { + if (_cups_strcasestr(c, "graphic")) + properties->for_tg = 7; + else + properties->for_text = 6; + } + else if (_cups_strcasecmp(c, "text") == 0) + properties->for_text = 7; + + if (_cups_strcasestr(c, "presentation")) + { + properties->for_text = 4; + properties->for_graphics = 4; + properties->for_tg = 4; + } + else if (_cups_strcasecmp(c, "presentation") == 0) + { + properties->for_text = 5; + properties->for_graphics = 5; + properties->for_tg = 5; + } + + if (_cups_strcasestr(c, "lineart")) + { + properties->for_graphics = 2; + properties->for_tg = 2; + } + else if (_cups_strcasecmp(c, "lineart") == 0) + { + properties->for_graphics = 3; + properties->for_tg = 3; + } + + if (_cups_strcasestr(c, "drawing")) + { + properties->for_graphics = 4; + properties->for_tg = 4; + } + else if (_cups_strcasecmp(c, "drawing") == 0) + { + properties->for_graphics = 5; + properties->for_tg = 5; + } + + if (_cups_strcasestr(c, "natural")) + properties->for_photo = 2; + else if (_cups_strcasecmp(c, "natural") == 0) + properties->for_photo = 3; + + if (_cups_strcasestr(c, "vivid")) + { + properties->for_text = 2; + properties->for_graphics = 2; + properties->for_tg = 2; + } + else if (_cups_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. */ 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 */ @@ -2137,6 +3168,15 @@ 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); } @@ -3025,6 +4065,21 @@ } /* + * 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... */ --- a/cups/ppd-private.h +++ b/cups/ppd-private.h @@ -36,7 +36,7 @@ * Constants... */ -# define _PPD_CACHE_VERSION 11 /* Version number in cache file */ +# define _PPD_CACHE_VERSION 12 /* Version number in cache file */ /* @@ -101,6 +101,16 @@ _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 @@ /* 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 *_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... --- a/cups/string-private.h +++ b/cups/string-private.h @@ -150,6 +150,8 @@ extern int _cups_strncasecmp(const char *, const char *, size_t n) _CUPS_PRIVATE; +extern char *_cups_strcasestr(const char *, const char *) _CUPS_PRIVATE; + # ifndef HAVE_STRLCAT extern size_t _cups_strlcat(char *, const char *, size_t) _CUPS_PRIVATE; # define strlcat _cups_strlcat --- a/cups/string.c +++ b/cups/string.c @@ -671,6 +671,36 @@ return (-1); } +/* + * '_cups_strcasestr()' - Do a case-insensitive search for a sub-string. + */ + +char * /* O - Pointer to found sub-string or + NULL if not found */ +_cups_strcasestr(const char *haystack, /* I - String in which to searh */ + const char *needle) /* I - Sub-string */ +{ + char cn, /* Character in needle */ + ch; /* Character in haystack */ + size_t len; /* Length of needle */ + + if ((cn = *needle++) != 0) + { + cn = _cups_tolower(cn); + len = strlen(needle); + do + { + do + { + if ((ch = *haystack++) == 0) + return (NULL); + } while (_cups_tolower(ch) != cn); + } while (_cups_strncasecmp(haystack, needle, len) != 0); + haystack --; + } + return ((char *)haystack); +} + #ifndef HAVE_STRLCAT /* --- a/scheduler/job.c +++ b/scheduler/job.c @@ -3653,9 +3653,13 @@ 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 */ @@ -3677,7 +3681,7 @@ 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... @@ -3739,6 +3743,12 @@ } } + 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) { /* @@ -3753,7 +3763,72 @@ { 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); }