X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=cups%2Fppd-cache.c;h=5965e382bb80b2108b7c9ebbf56451daa4af53fb;hb=HEAD;hp=ddccfb93febba1c4bb0f2784a8e6e2fc97a33cf2;hpb=79d7d84abfe27c9c8b7e622e0fda0d42244387dd;p=thirdparty%2Fcups.git diff --git a/cups/ppd-cache.c b/cups/ppd-cache.c index ddccfb93f..091f39f3c 100644 --- a/cups/ppd-cache.c +++ b/cups/ppd-cache.c @@ -1,17 +1,10 @@ /* - * "$Id$" - * * PPD cache implementation for CUPS. * - * Copyright 2010-2015 by Apple Inc. - * - * These coded instructions, statements, and computer programs are the - * property of Apple Inc. and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "LICENSE.txt" - * which should have been included with this file. If this file is - * file is missing or damaged, see the license at "http://www.cups.org/". + * Copyright © 2010-2019 by Apple Inc. * - * This file is subject to the Apple OS-Developed Software exception. + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* @@ -19,6 +12,8 @@ */ #include "cups-private.h" +#include "ppd-private.h" +#include "debug-internal.h" #include @@ -33,14 +28,410 @@ * Local functions... */ -static int pwg_compare_finishings(_pwg_finishings_t *a, - _pwg_finishings_t *b); +static int cups_get_url(http_t **http, const char *url, char *name, size_t namesize); +static void pwg_add_finishing(cups_array_t *finishings, ipp_finishings_t template, const char *name, const char *value); +static void pwg_add_message(cups_array_t *a, const char *msg, const char *str); +static int pwg_compare_finishings(_pwg_finishings_t *a, _pwg_finishings_t *b); +static int pwg_compare_sizes(cups_size_t *a, cups_size_t *b); +static cups_size_t *pwg_copy_size(cups_size_t *size); static void pwg_free_finishings(_pwg_finishings_t *f); static void pwg_ppdize_name(const char *ipp, char *name, size_t namesize); +static void pwg_ppdize_resolution(ipp_attribute_t *attr, int element, int *xres, int *yres, char *name, size_t namesize); static void pwg_unppdize_name(const char *ppd, char *name, size_t namesize, const char *dashchars); +/* + * '_cupsConvertOptions()' - Convert printer options to standard IPP attributes. + * + * This functions converts PPD and CUPS-specific options to their standard IPP + * attributes and values and adds them to the specified IPP request. + */ + +int /* O - New number of copies */ +_cupsConvertOptions( + ipp_t *request, /* I - IPP request */ + ppd_file_t *ppd, /* I - PPD file */ + _ppd_cache_t *pc, /* I - PPD cache info */ + ipp_attribute_t *media_col_sup, /* I - media-col-supported values */ + ipp_attribute_t *doc_handling_sup, /* I - multiple-document-handling-supported values */ + ipp_attribute_t *print_color_mode_sup, + /* I - Printer supports print-color-mode */ + const char *user, /* I - User info */ + const char *format, /* I - document-format value */ + int copies, /* I - Number of copies */ + int num_options, /* I - Number of options */ + cups_option_t *options) /* I - Options */ +{ + int i; /* Looping var */ + const char *keyword, /* PWG keyword */ + *password; /* Password string */ + pwg_size_t *size; /* PWG media size */ + ipp_t *media_col, /* media-col value */ + *media_size; /* media-size value */ + const char *media_source, /* media-source value */ + *media_type, /* media-type value */ + *collate_str, /* multiple-document-handling value */ + *color_attr_name, /* Supported color attribute */ + *mandatory, /* Mandatory attributes */ + *finishing_template; /* Finishing template */ + int num_finishings = 0, /* Number of finishing values */ + finishings[10]; /* Finishing enum values */ + ppd_choice_t *choice; /* Marked choice */ + int finishings_copies = copies, + /* Number of copies for finishings */ + job_pages = 0, /* job-pages value */ + number_up = 1; /* number-up value */ + const char *value; /* Option value */ + + + /* + * Send standard IPP attributes... + */ + + if (pc->password && (password = cupsGetOption("job-password", num_options, options)) != NULL && ippGetOperation(request) != IPP_OP_VALIDATE_JOB) + { + ipp_attribute_t *attr = NULL; /* job-password attribute */ + + if ((keyword = cupsGetOption("job-password-encryption", num_options, options)) == NULL) + keyword = "none"; + + if (!strcmp(keyword, "none")) + { + /* + * Add plain-text job-password... + */ + + attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", password, (int)strlen(password)); + } + else + { + /* + * Add hashed job-password... + */ + + unsigned char hash[64]; /* Hash of password */ + ssize_t hashlen; /* Length of hash */ + + if ((hashlen = cupsHashData(keyword, password, strlen(password), hash, sizeof(hash))) > 0) + attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", hash, (int)hashlen); + } + + if (attr) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "job-password-encryption", NULL, keyword); + } + + if (pc->account_id) + { + if ((keyword = cupsGetOption("job-account-id", num_options, options)) == NULL) + keyword = cupsGetOption("job-billing", num_options, options); + + if (keyword) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id", NULL, keyword); + } + + if (pc->accounting_user_id) + { + if ((keyword = cupsGetOption("job-accounting-user-id", num_options, options)) == NULL) + keyword = user; + + if (keyword) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-accounting-user-id", NULL, keyword); + } + + for (mandatory = (const char *)cupsArrayFirst(pc->mandatory); mandatory; mandatory = (const char *)cupsArrayNext(pc->mandatory)) + { + if (strcmp(mandatory, "copies") && + strcmp(mandatory, "destination-uris") && + strcmp(mandatory, "finishings") && + strcmp(mandatory, "finishings-col") && + strcmp(mandatory, "finishing-template") && + strcmp(mandatory, "job-account-id") && + strcmp(mandatory, "job-accounting-user-id") && + strcmp(mandatory, "job-password") && + strcmp(mandatory, "job-password-encryption") && + strcmp(mandatory, "media") && + strncmp(mandatory, "media-col", 9) && + strcmp(mandatory, "multiple-document-handling") && + strcmp(mandatory, "output-bin") && + strcmp(mandatory, "print-color-mode") && + strcmp(mandatory, "print-quality") && + strcmp(mandatory, "sides") && + (keyword = cupsGetOption(mandatory, num_options, options)) != NULL) + { + _ipp_option_t *opt = _ippFindOption(mandatory); + /* Option type */ + ipp_tag_t value_tag = opt ? opt->value_tag : IPP_TAG_NAME; + /* Value type */ + + switch (value_tag) + { + case IPP_TAG_INTEGER : + case IPP_TAG_ENUM : + ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory, atoi(keyword)); + break; + case IPP_TAG_BOOLEAN : + ippAddBoolean(request, IPP_TAG_JOB, mandatory, !_cups_strcasecmp(keyword, "true")); + break; + case IPP_TAG_RANGE : + { + int lower, upper; /* Range */ + + if (sscanf(keyword, "%d-%d", &lower, &upper) != 2) + lower = upper = atoi(keyword); + + ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper); + } + break; + case IPP_TAG_STRING : + ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword, (int)strlen(keyword)); + break; + default : + if (!strcmp(mandatory, "print-color-mode") && !strcmp(keyword, "monochrome")) + { + if (ippContainsString(print_color_mode_sup, "auto-monochrome")) + keyword = "auto-monochrome"; + else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome")) + keyword = "process-monochrome"; + } + + ippAddString(request, IPP_TAG_JOB, value_tag, mandatory, NULL, keyword); + break; + } + } + } + + if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL) + keyword = cupsGetOption("media", num_options, options); + + media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot", num_options, options)); + media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType", num_options, options)); + size = _ppdCacheGetSize(pc, keyword); + + if (size || media_source || media_type) + { + /* + * Add a media-col value... + */ + + media_col = ippNew(); + + if (size) + { + media_size = ippNew(); + ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER, + "x-dimension", size->width); + ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER, + "y-dimension", size->length); + + ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size); + } + + for (i = 0; i < media_col_sup->num_values; i ++) + { + if (size && !strcmp(media_col_sup->values[i].string.text, "media-left-margin")) + ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-left-margin", size->left); + else if (size && !strcmp(media_col_sup->values[i].string.text, "media-bottom-margin")) + ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-bottom-margin", size->bottom); + else if (size && !strcmp(media_col_sup->values[i].string.text, "media-right-margin")) + ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-right-margin", size->right); + else if (size && !strcmp(media_col_sup->values[i].string.text, "media-top-margin")) + ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-top-margin", size->top); + else if (media_source && !strcmp(media_col_sup->values[i].string.text, "media-source")) + ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-source", NULL, media_source); + else if (media_type && !strcmp(media_col_sup->values[i].string.text, "media-type")) + ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-type", NULL, media_type); + } + + ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col); + } + + if ((keyword = cupsGetOption("output-bin", num_options, options)) == NULL) + { + if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL) + keyword = _ppdCacheGetBin(pc, choice->choice); + } + + if (keyword) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin", NULL, keyword); + + color_attr_name = print_color_mode_sup ? "print-color-mode" : "output-mode"; + + if ((keyword = cupsGetOption("print-color-mode", num_options, options)) == NULL) + { + if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL) + { + if (!_cups_strcasecmp(choice->choice, "Gray")) + keyword = "monochrome"; + else + keyword = "color"; + } + } + + if (keyword && !strcmp(keyword, "monochrome")) + { + if (ippContainsString(print_color_mode_sup, "auto-monochrome")) + keyword = "auto-monochrome"; + else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome")) + keyword = "process-monochrome"; + } + + if (keyword) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, color_attr_name, NULL, keyword); + + if ((keyword = cupsGetOption("print-quality", num_options, options)) != NULL) + ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", atoi(keyword)); + else if ((choice = ppdFindMarkedChoice(ppd, "cupsPrintQuality")) != NULL) + { + if (!_cups_strcasecmp(choice->choice, "draft")) + ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_DRAFT); + else if (!_cups_strcasecmp(choice->choice, "normal")) + ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_NORMAL); + else if (!_cups_strcasecmp(choice->choice, "high")) + ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_HIGH); + } + + if ((keyword = cupsGetOption("sides", num_options, options)) != NULL) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, keyword); + else if (pc->sides_option && (choice = ppdFindMarkedChoice(ppd, pc->sides_option)) != NULL) + { + if (pc->sides_1sided && !_cups_strcasecmp(choice->choice, pc->sides_1sided)) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "one-sided"); + else if (pc->sides_2sided_long && !_cups_strcasecmp(choice->choice, pc->sides_2sided_long)) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-long-edge"); + else if (pc->sides_2sided_short && !_cups_strcasecmp(choice->choice, pc->sides_2sided_short)) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-short-edge"); + } + + /* + * Copies... + */ + + if ((keyword = cupsGetOption("multiple-document-handling", num_options, options)) != NULL) + { + if (strstr(keyword, "uncollated")) + keyword = "false"; + else + keyword = "true"; + } + else if ((keyword = cupsGetOption("collate", num_options, options)) == NULL) + keyword = "true"; + + if (format) + { + if (!_cups_strcasecmp(format, "image/gif") || + !_cups_strcasecmp(format, "image/jp2") || + !_cups_strcasecmp(format, "image/jpeg") || + !_cups_strcasecmp(format, "image/png") || + !_cups_strcasecmp(format, "image/tiff") || + !_cups_strncasecmp(format, "image/x-", 8)) + { + /* + * Collation makes no sense for single page image formats... + */ + + keyword = "false"; + } + else if (!_cups_strncasecmp(format, "image/", 6) || + !_cups_strcasecmp(format, "application/vnd.cups-raster")) + { + /* + * Multi-page image formats will have copies applied by the upstream + * filters... + */ + + copies = 1; + } + } + + if (doc_handling_sup) + { + if (!_cups_strcasecmp(keyword, "true")) + collate_str = "separate-documents-collated-copies"; + else + collate_str = "separate-documents-uncollated-copies"; + + for (i = 0; i < doc_handling_sup->num_values; i ++) + { + if (!strcmp(doc_handling_sup->values[i].string.text, collate_str)) + { + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "multiple-document-handling", NULL, collate_str); + break; + } + } + + if (i >= doc_handling_sup->num_values) + copies = 1; + } + + /* + * Map finishing options... + */ + + if (copies != finishings_copies) + { + // Figure out the proper job-pages-per-set value... + if ((value = cupsGetOption("job-pages", num_options, options)) == NULL) + value = cupsGetOption("com.apple.print.PrintSettings.PMTotalBeginPages..n.", num_options, options); + + if (value) + job_pages = atoi(value); + + // Adjust for number-up + if ((value = cupsGetOption("number-up", num_options, options)) != NULL) + number_up = atoi(value); + + job_pages = (job_pages + number_up - 1) / number_up; + + // When duplex printing, raster data will include an extra (blank) page to + // make the total number of pages even. Make sure this is reflected in the + // page count... + if ((job_pages & 1) && (keyword = cupsGetOption("sides", num_options, options)) != NULL && strcmp(keyword, "one-sided")) + job_pages ++; + } + + if ((finishing_template = cupsGetOption("cupsFinishingTemplate", num_options, options)) == NULL) + finishing_template = cupsGetOption("finishing-template", num_options, options); + + if (finishing_template && strcmp(finishing_template, "none")) + { + ipp_t *fin_col = ippNew(); /* finishings-col value */ + + ippAddString(fin_col, IPP_TAG_JOB, IPP_TAG_KEYWORD, "finishing-template", NULL, finishing_template); + ippAddCollection(request, IPP_TAG_JOB, "finishings-col", fin_col); + ippDelete(fin_col); + + if (copies != finishings_copies && job_pages > 0) + { + /* + * Send job-pages-per-set attribute to apply finishings correctly... + */ + + ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", job_pages); + } + } + else + { + num_finishings = _ppdCacheGetFinishingValues(ppd, pc, (int)(sizeof(finishings) / sizeof(finishings[0])), finishings); + if (num_finishings > 0) + { + ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", num_finishings, finishings); + + if (copies != finishings_copies && job_pages > 0) + { + /* + * Send job-pages-per-set attribute to apply finishings correctly... + */ + + ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", job_pages); + } + } + } + + return (copies); +} + + /* * '_ppdCacheCreateWithFile()' - Create PPD cache and mapping data from a * written file. @@ -167,24 +558,20 @@ _ppdCacheCreateWithFile( else if (!_cups_strcasecmp(line, "Filter")) { if (!pc->filters) - pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)_cupsStrAlloc, - (cups_afree_func_t)_cupsStrFree); + pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free); cupsArrayAdd(pc->filters, value); } else if (!_cups_strcasecmp(line, "PreFilter")) { if (!pc->prefilters) - pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)_cupsStrAlloc, - (cups_afree_func_t)_cupsStrFree); + pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free); cupsArrayAdd(pc->prefilters, value); } else if (!_cups_strcasecmp(line, "Product")) { - pc->product = _cupsStrAlloc(value); + pc->product = strdup(value); } else if (!_cups_strcasecmp(line, "SingleFile")) { @@ -284,8 +671,8 @@ _ppdCacheCreateWithFile( } map = pc->bins + pc->num_bins; - map->pwg = _cupsStrAlloc(pwg_keyword); - map->ppd = _cupsStrAlloc(ppd_keyword); + map->pwg = strdup(pwg_keyword); + map->ppd = strdup(ppd_keyword); pc->num_bins ++; } @@ -339,8 +726,8 @@ _ppdCacheCreateWithFile( goto create_error; } - size->map.pwg = _cupsStrAlloc(pwg_keyword); - size->map.ppd = _cupsStrAlloc(ppd_keyword); + size->map.pwg = strdup(pwg_keyword); + size->map.ppd = strdup(ppd_keyword); pc->num_sizes ++; } @@ -368,15 +755,15 @@ _ppdCacheCreateWithFile( pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max", pc->custom_max_width, pc->custom_max_length, NULL); - pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword); + pc->custom_max_keyword = strdup(pwg_keyword); pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min", pc->custom_min_width, pc->custom_min_length, NULL); - pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword); + pc->custom_min_keyword = strdup(pwg_keyword); } else if (!_cups_strcasecmp(line, "SourceOption")) { - pc->source_option = _cupsStrAlloc(value); + pc->source_option = strdup(value); } else if (!_cups_strcasecmp(line, "NumSources")) { @@ -423,8 +810,8 @@ _ppdCacheCreateWithFile( } map = pc->sources + pc->num_sources; - map->pwg = _cupsStrAlloc(pwg_keyword); - map->ppd = _cupsStrAlloc(ppd_keyword); + map->pwg = strdup(pwg_keyword); + map->ppd = strdup(ppd_keyword); pc->num_sources ++; } @@ -472,8 +859,8 @@ _ppdCacheCreateWithFile( } map = pc->types + pc->num_types; - map->pwg = _cupsStrAlloc(pwg_keyword); - map->ppd = _cupsStrAlloc(ppd_keyword); + map->pwg = strdup(pwg_keyword); + map->ppd = strdup(ppd_keyword); pc->num_types ++; } @@ -503,13 +890,13 @@ _ppdCacheCreateWithFile( pc->presets[print_color_mode] + print_quality); } else if (!_cups_strcasecmp(line, "SidesOption")) - pc->sides_option = _cupsStrAlloc(value); + pc->sides_option = strdup(value); else if (!_cups_strcasecmp(line, "Sides1Sided")) - pc->sides_1sided = _cupsStrAlloc(value); + pc->sides_1sided = strdup(value); else if (!_cups_strcasecmp(line, "Sides2SidedLong")) - pc->sides_2sided_long = _cupsStrAlloc(value); + pc->sides_2sided_long = strdup(value); else if (!_cups_strcasecmp(line, "Sides2SidedShort")) - pc->sides_2sided_short = _cupsStrAlloc(value); + pc->sides_2sided_short = strdup(value); else if (!_cups_strcasecmp(line, "Finishings")) { if (!pc->finishings) @@ -527,16 +914,23 @@ _ppdCacheCreateWithFile( cupsArrayAdd(pc->finishings, finishings); } + else if (!_cups_strcasecmp(line, "FinishingTemplate")) + { + if (!pc->templates) + pc->templates = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free); + + cupsArrayAdd(pc->templates, value); + } else if (!_cups_strcasecmp(line, "MaxCopies")) pc->max_copies = atoi(value); else if (!_cups_strcasecmp(line, "ChargeInfoURI")) - pc->charge_info_uri = _cupsStrAlloc(value); + pc->charge_info_uri = strdup(value); else if (!_cups_strcasecmp(line, "JobAccountId")) pc->account_id = !_cups_strcasecmp(value, "true"); else if (!_cups_strcasecmp(line, "JobAccountingUserId")) pc->accounting_user_id = !_cups_strcasecmp(value, "true"); else if (!_cups_strcasecmp(line, "JobPassword")) - pc->password = _cupsStrAlloc(value); + pc->password = strdup(value); else if (!_cups_strcasecmp(line, "Mandatory")) { if (pc->mandatory) @@ -547,9 +941,7 @@ _ppdCacheCreateWithFile( else if (!_cups_strcasecmp(line, "SupportFile")) { if (!pc->support_files) - pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)_cupsStrAlloc, - (cups_afree_func_t)_cupsStrFree); + pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free); cupsArrayAdd(pc->support_files, value); } @@ -620,7 +1012,8 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ *media_type, /* MediaType option */ *output_bin, /* OutputBin option */ *color_model, /* ColorModel option */ - *duplex; /* Duplex option */ + *duplex, /* Duplex option */ + *ppd_option; /* Other PPD option */ ppd_choice_t *choice; /* Current InputSlot/MediaType */ pwg_map_t *map; /* Current source/type map */ ppd_attr_t *ppd_attr; /* Current PPD preset attribute */ @@ -655,6 +1048,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ pwg_size_t *new_size; /* New size to add, if any */ const char *filter; /* Current filter */ _pwg_finishings_t *finishings; /* Current finishings value */ + char msg_id[256]; /* Message identifier */ DEBUG_printf(("_ppdCacheCreateWithPPD(ppd=%p)", ppd)); @@ -676,6 +1070,8 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ goto create_error; } + pc->strings = _cupsMessageNew(NULL); + /* * Copy and convert size data... */ @@ -704,7 +1100,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ * Convert the PPD size name to the corresponding PWG keyword name. */ - if ((pwg_media = pwgMediaForPPD(ppd_size->name)) != NULL) + if ((pwg_media = pwgMediaForSize(PWG_FROM_POINTS(ppd_size->width), PWG_FROM_POINTS(ppd_size->length))) != NULL) { /* * Standard name, do we have conflicts? @@ -789,8 +1185,8 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ */ new_size = old_size; - _cupsStrFree(old_size->map.ppd); - _cupsStrFree(old_size->map.pwg); + free(old_size->map.ppd); + free(old_size->map.pwg); } } @@ -811,8 +1207,8 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ * Save this size... */ - new_size->map.ppd = _cupsStrAlloc(ppd_size->name); - new_size->map.pwg = _cupsStrAlloc(pwg_name); + new_size->map.ppd = strdup(ppd_size->name); + new_size->map.pwg = strdup(pwg_name); new_size->width = new_width; new_size->length = new_length; new_size->left = new_left; @@ -832,14 +1228,14 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max", PWG_FROM_POINTS(ppd->custom_max[0]), PWG_FROM_POINTS(ppd->custom_max[1]), NULL); - pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword); + pc->custom_max_keyword = strdup(pwg_keyword); pc->custom_max_width = PWG_FROM_POINTS(ppd->custom_max[0]); pc->custom_max_length = PWG_FROM_POINTS(ppd->custom_max[1]); pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min", PWG_FROM_POINTS(ppd->custom_min[0]), PWG_FROM_POINTS(ppd->custom_min[1]), NULL); - pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword); + pc->custom_min_keyword = strdup(pwg_keyword); pc->custom_min_width = PWG_FROM_POINTS(ppd->custom_min[0]); pc->custom_min_length = PWG_FROM_POINTS(ppd->custom_min[1]); @@ -858,7 +1254,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ if (input_slot) { - pc->source_option = _cupsStrAlloc(input_slot->keyword); + pc->source_option = strdup(input_slot->keyword); if ((pc->sources = calloc((size_t)input_slot->num_choices, sizeof(pwg_map_t))) == NULL) { @@ -910,8 +1306,15 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ "_"); } - map->pwg = _cupsStrAlloc(pwg_name); - map->ppd = _cupsStrAlloc(choice->choice); + map->pwg = strdup(pwg_name); + map->ppd = strdup(choice->choice); + + /* + * Add localized text for PWG keyword to message catalog... + */ + + snprintf(msg_id, sizeof(msg_id), "media-source.%s", pwg_name); + pwg_add_message(pc->strings, msg_id, choice->text); } } @@ -974,8 +1377,15 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ "_"); } - map->pwg = _cupsStrAlloc(pwg_name); - map->ppd = _cupsStrAlloc(choice->choice); + map->pwg = strdup(pwg_name); + map->ppd = strdup(choice->choice); + + /* + * Add localized text for PWG keyword to message catalog... + */ + + snprintf(msg_id, sizeof(msg_id), "media-type.%s", pwg_name); + pwg_add_message(pc->strings, msg_id, choice->text); } } @@ -1001,8 +1411,15 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ { pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_"); - map->pwg = _cupsStrAlloc(pwg_keyword); - map->ppd = _cupsStrAlloc(choice->choice); + map->pwg = strdup(pwg_keyword); + map->ppd = strdup(choice->choice); + + /* + * Add localized text for PWG keyword to message catalog... + */ + + snprintf(msg_id, sizeof(msg_id), "output-bin.%s", pwg_keyword); + pwg_add_message(pc->strings, msg_id, choice->text); } } @@ -1020,6 +1437,17 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ do { + /* + * Add localized text for PWG keyword to message catalog... + */ + + snprintf(msg_id, sizeof(msg_id), "preset-name.%s", ppd_attr->spec); + pwg_add_message(pc->strings, msg_id, ppd_attr->text); + + /* + * Get the options for this preset... + */ + num_options = _ppdParseOptions(ppd_attr->value, 0, &options, _PPD_PARSE_ALL); @@ -1217,7 +1645,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ if (duplex) { - pc->sides_option = _cupsStrAlloc(duplex->keyword); + pc->sides_option = strdup(duplex->keyword); for (i = duplex->num_choices, choice = duplex->choices; i > 0; @@ -1225,16 +1653,16 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ { if ((!_cups_strcasecmp(choice->choice, "None") || !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided) - pc->sides_1sided = _cupsStrAlloc(choice->choice); + pc->sides_1sided = strdup(choice->choice); else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") || !_cups_strcasecmp(choice->choice, "LongEdge") || !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long) - pc->sides_2sided_long = _cupsStrAlloc(choice->choice); + pc->sides_2sided_long = strdup(choice->choice); else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") || !_cups_strcasecmp(choice->choice, "ShortEdge") || !_cups_strcasecmp(choice->choice, "Bottom")) && !pc->sides_2sided_short) - pc->sides_2sided_short = _cupsStrAlloc(choice->choice); + pc->sides_2sided_short = strdup(choice->choice); } } @@ -1242,9 +1670,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ * Copy filters and pre-filters... */ - pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)_cupsStrAlloc, - (cups_afree_func_t)_cupsStrFree); + pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free); cupsArrayAdd(pc->filters, "application/vnd.cups-raw application/octet-stream 0 -"); @@ -1301,9 +1727,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL) { - pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)_cupsStrAlloc, - (cups_afree_func_t)_cupsStrFree); + pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free); do { @@ -1320,7 +1744,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ */ if (ppd->product) - pc->product = _cupsStrAlloc(ppd->product); + pc->product = strdup(ppd->product); /* * Copy finishings mapping data... @@ -1328,6 +1752,10 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL) { + /* + * Have proper vendor mapping of IPP finishings values to PPD options... + */ + pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings, NULL, NULL, 0, NULL, (cups_afree_func_t)pwg_free_finishings); @@ -1347,6 +1775,129 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ while ((ppd_attr = ppdFindNextAttr(ppd, "cupsIPPFinishings", NULL)) != NULL); } + else + { + /* + * No IPP mapping data, try to map common/standard PPD keywords... + */ + + pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings, NULL, NULL, 0, NULL, (cups_afree_func_t)pwg_free_finishings); + + if ((ppd_option = ppdFindOption(ppd, "StapleLocation")) != NULL) + { + /* + * Add staple finishings... + */ + + if (ppdFindChoice(ppd_option, "SinglePortrait")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, "StapleLocation", "SinglePortrait"); + if (ppdFindChoice(ppd_option, "UpperLeft")) /* Ricoh extension */ + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, "StapleLocation", "UpperLeft"); + if (ppdFindChoice(ppd_option, "UpperRight")) /* Ricoh extension */ + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_RIGHT, "StapleLocation", "UpperRight"); + if (ppdFindChoice(ppd_option, "SingleLandscape")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_BOTTOM_LEFT, "StapleLocation", "SingleLandscape"); + if (ppdFindChoice(ppd_option, "DualLandscape")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_DUAL_LEFT, "StapleLocation", "DualLandscape"); + } + + if ((ppd_option = ppdFindOption(ppd, "RIPunch")) != NULL) + { + /* + * Add (Ricoh) punch finishings... + */ + + if (ppdFindChoice(ppd_option, "Left2")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_LEFT, "RIPunch", "Left2"); + if (ppdFindChoice(ppd_option, "Left3")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_LEFT, "RIPunch", "Left3"); + if (ppdFindChoice(ppd_option, "Left4")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_LEFT, "RIPunch", "Left4"); + if (ppdFindChoice(ppd_option, "Right2")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_RIGHT, "RIPunch", "Right2"); + if (ppdFindChoice(ppd_option, "Right3")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_RIGHT, "RIPunch", "Right3"); + if (ppdFindChoice(ppd_option, "Right4")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_RIGHT, "RIPunch", "Right4"); + if (ppdFindChoice(ppd_option, "Upper2")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_TOP, "RIPunch", "Upper2"); + if (ppdFindChoice(ppd_option, "Upper3")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_TOP, "RIPunch", "Upper3"); + if (ppdFindChoice(ppd_option, "Upper4")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_TOP, "RIPunch", "Upper4"); + } + + if ((ppd_option = ppdFindOption(ppd, "BindEdge")) != NULL) + { + /* + * Add bind finishings... + */ + + if (ppdFindChoice(ppd_option, "Left")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_LEFT, "BindEdge", "Left"); + if (ppdFindChoice(ppd_option, "Right")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_RIGHT, "BindEdge", "Right"); + if (ppdFindChoice(ppd_option, "Top")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_TOP, "BindEdge", "Top"); + if (ppdFindChoice(ppd_option, "Bottom")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_BOTTOM, "BindEdge", "Bottom"); + } + + if ((ppd_option = ppdFindOption(ppd, "FoldType")) != NULL) + { + /* + * Add (Adobe) fold finishings... + */ + + if (ppdFindChoice(ppd_option, "ZFold")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_Z, "FoldType", "ZFold"); + if (ppdFindChoice(ppd_option, "Saddle")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_HALF, "FoldType", "Saddle"); + if (ppdFindChoice(ppd_option, "DoubleGate")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_DOUBLE_GATE, "FoldType", "DoubleGate"); + if (ppdFindChoice(ppd_option, "LeftGate")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LEFT_GATE, "FoldType", "LeftGate"); + if (ppdFindChoice(ppd_option, "RightGate")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_RIGHT_GATE, "FoldType", "RightGate"); + if (ppdFindChoice(ppd_option, "Letter")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, "FoldType", "Letter"); + if (ppdFindChoice(ppd_option, "XFold")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_POSTER, "FoldType", "XFold"); + } + + if ((ppd_option = ppdFindOption(ppd, "RIFoldType")) != NULL) + { + /* + * Add (Ricoh) fold finishings... + */ + + if (ppdFindChoice(ppd_option, "OutsideTwoFold")) + pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, "RIFoldType", "OutsideTwoFold"); + } + + if (cupsArrayCount(pc->finishings) == 0) + { + cupsArrayDelete(pc->finishings); + pc->finishings = NULL; + } + } + + if ((ppd_option = ppdFindOption(ppd, "cupsFinishingTemplate")) != NULL) + { + pc->templates = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free); + + for (choice = ppd_option->choices, i = ppd_option->num_choices; i > 0; choice ++, i --) + { + cupsArrayAdd(pc->templates, (void *)choice->choice); + + /* + * Add localized text for PWG keyword to message catalog... + */ + + snprintf(msg_id, sizeof(msg_id), "finishing-template.%s", choice->choice); + pwg_add_message(pc->strings, msg_id, choice->text); + } + } /* * Max copies... @@ -1365,7 +1916,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ */ if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL) - pc->charge_info_uri = _cupsStrAlloc(ppd_attr->value); + pc->charge_info_uri = strdup(ppd_attr->value); if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountId", NULL)) != NULL) pc->account_id = !_cups_strcasecmp(ppd_attr->value, "true"); @@ -1374,7 +1925,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ pc->accounting_user_id = !_cups_strcasecmp(ppd_attr->value, "true"); if ((ppd_attr = ppdFindAttr(ppd, "cupsJobPassword", NULL)) != NULL) - pc->password = _cupsStrAlloc(ppd_attr->value); + pc->password = strdup(ppd_attr->value); if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL) pc->mandatory = _cupsArrayNewStrings(ppd_attr->value, ' '); @@ -1383,9 +1934,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */ * Support files... */ - pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, - (cups_acopy_func_t)_cupsStrAlloc, - (cups_afree_func_t)_cupsStrFree); + pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free); for (ppd_attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); ppd_attr; @@ -1441,8 +1990,8 @@ _ppdCacheDestroy(_ppd_cache_t *pc) /* I - PPD cache and mapping data */ { for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++) { - _cupsStrFree(map->pwg); - _cupsStrFree(map->ppd); + free(map->pwg); + free(map->ppd); } free(pc->bins); @@ -1452,22 +2001,21 @@ _ppdCacheDestroy(_ppd_cache_t *pc) /* I - PPD cache and mapping data */ { for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++) { - _cupsStrFree(size->map.pwg); - _cupsStrFree(size->map.ppd); + free(size->map.pwg); + free(size->map.ppd); } free(pc->sizes); } - if (pc->source_option) - _cupsStrFree(pc->source_option); + free(pc->source_option); if (pc->sources) { for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++) { - _cupsStrFree(map->pwg); - _cupsStrFree(map->ppd); + free(map->pwg); + free(map->ppd); } free(pc->sources); @@ -1477,31 +2025,30 @@ _ppdCacheDestroy(_ppd_cache_t *pc) /* I - PPD cache and mapping data */ { for (i = pc->num_types, map = pc->types; i > 0; i --, map ++) { - _cupsStrFree(map->pwg); - _cupsStrFree(map->ppd); + free(map->pwg); + free(map->ppd); } free(pc->types); } - if (pc->custom_max_keyword) - _cupsStrFree(pc->custom_max_keyword); + free(pc->custom_max_keyword); + free(pc->custom_min_keyword); - if (pc->custom_min_keyword) - _cupsStrFree(pc->custom_min_keyword); - - _cupsStrFree(pc->product); + free(pc->product); cupsArrayDelete(pc->filters); cupsArrayDelete(pc->prefilters); cupsArrayDelete(pc->finishings); - _cupsStrFree(pc->charge_info_uri); - _cupsStrFree(pc->password); + free(pc->charge_info_uri); + free(pc->password); cupsArrayDelete(pc->mandatory); cupsArrayDelete(pc->support_files); + cupsArrayDelete(pc->strings); + free(pc); } @@ -1614,9 +2161,8 @@ _ppdCacheGetFinishingOptions( int /* O - Number of finishings values */ _ppdCacheGetFinishingValues( + ppd_file_t *ppd, /* I - Marked PPD file */ _ppd_cache_t *pc, /* I - PPD cache and mapping data */ - int num_options, /* I - Number of options */ - cups_option_t *options, /* I - Options */ int max_values, /* I - Maximum number of finishings values */ int *values) /* O - Finishings values */ { @@ -1624,20 +2170,25 @@ _ppdCacheGetFinishingValues( num_values = 0; /* Number of values */ _pwg_finishings_t *f; /* Current finishings option */ cups_option_t *option; /* Current option */ - const char *val; /* Value for option */ + ppd_choice_t *choice; /* Marked PPD choice */ /* * Range check input... */ - DEBUG_printf(("_ppdCacheGetFinishingValues(pc=%p, num_options=%d, options=%p, max_values=%d, values=%p)", pc, num_options, options, max_values, values)); + DEBUG_printf(("_ppdCacheGetFinishingValues(ppd=%p, pc=%p, max_values=%d, values=%p)", ppd, pc, max_values, values)); - if (!pc || !pc->finishings || num_options < 1 || max_values < 1 || !values) + if (!ppd || !pc || max_values < 1 || !values) { DEBUG_puts("_ppdCacheGetFinishingValues: Bad arguments, returning 0."); return (0); } + else if (!pc->finishings) + { + DEBUG_puts("_ppdCacheGetFinishingValues: No finishings support, returning 0."); + return (0); + } /* * Go through the finishings options and see what is set... @@ -1647,14 +2198,13 @@ _ppdCacheGetFinishingValues( f; f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings)) { - DEBUG_printf(("_ppdCacheGetFinishingValues: Checking %d (%s)", f->value, ippEnumString("finishings", f->value))); + DEBUG_printf(("_ppdCacheGetFinishingValues: Checking %d (%s)", (int)f->value, ippEnumString("finishings", (int)f->value))); for (i = f->num_options, option = f->options; i > 0; i --, option ++) { DEBUG_printf(("_ppdCacheGetFinishingValues: %s=%s?", option->name, option->value)); - if ((val = cupsGetOption(option->name, num_options, options)) == NULL || - _cups_strcasecmp(option->value, val)) + if ((choice = ppdFindMarkedChoice(ppd, option->name)) == NULL || _cups_strcasecmp(option->value, choice->choice)) { DEBUG_puts("_ppdCacheGetFinishingValues: NO"); break; @@ -1663,15 +2213,26 @@ _ppdCacheGetFinishingValues( if (i == 0) { - DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d.", f->value)); + DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d (%s)", (int)f->value, ippEnumString("finishings", (int)f->value))); - values[num_values ++] = f->value; + values[num_values ++] = (int)f->value; if (num_values >= max_values) break; } } + if (num_values == 0) + { + /* + * Always have at least "finishings" = 'none'... + */ + + DEBUG_puts("_ppdCacheGetFinishingValues: Adding 3 (none)."); + values[0] = IPP_FINISHINGS_NONE; + num_values ++; + } + DEBUG_printf(("_ppdCacheGetFinishingValues: Returning %d.", num_values)); return (num_values); @@ -2257,7 +2818,7 @@ _ppdCacheWriteFile( pwg_map_t *map; /* Current map */ _pwg_finishings_t *f; /* Current finishing option */ cups_option_t *option; /* Current option */ - const char *value; /* Filter/pre-filter value */ + const char *value; /* String value */ char newfile[1024]; /* New filename */ @@ -2405,6 +2966,9 @@ _ppdCacheWriteFile( cupsFilePutChar(fp, '\n'); } + for (value = (const char *)cupsArrayFirst(pc->templates); value; value = (const char *)cupsArrayNext(pc->templates)) + cupsFilePutConf(fp, "FinishingTemplate", value); + /* * Max copies... */ @@ -2418,12 +2982,12 @@ _ppdCacheWriteFile( if (pc->charge_info_uri) cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri); - cupsFilePrintf(fp, "AccountId %s\n", pc->account_id ? "true" : "false"); - cupsFilePrintf(fp, "AccountingUserId %s\n", + cupsFilePrintf(fp, "JobAccountId %s\n", pc->account_id ? "true" : "false"); + cupsFilePrintf(fp, "JobAccountingUserId %s\n", pc->accounting_user_id ? "true" : "false"); if (pc->password) - cupsFilePutConf(fp, "Password", pc->password); + cupsFilePutConf(fp, "JobPassword", pc->password); for (value = (char *)cupsArrayFirst(pc->mandatory); value; @@ -2467,7 +3031,1768 @@ _ppdCacheWriteFile( /* - * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG + * '_ppdCreateFromIPP()' - Create a PPD file describing the capabilities + * of an IPP printer. + */ + +char * /* O - PPD filename or @code NULL@ on error */ +_ppdCreateFromIPP(char *buffer, /* I - Filename buffer */ + size_t bufsize, /* I - Size of filename buffer */ + ipp_t *response) /* I - Get-Printer-Attributes response */ +{ + cups_file_t *fp; /* PPD file */ + cups_array_t *sizes; /* Media sizes supported by printer */ + cups_size_t *size; /* Current media size */ + ipp_attribute_t *attr, /* xxx-supported */ + *defattr, /* xxx-default */ + *quality, /* print-quality-supported */ + *x_dim, *y_dim; /* Media dimensions */ + ipp_t *media_col, /* Media collection */ + *media_size; /* Media size collection */ + char make[256], /* Make and model */ + *model, /* Model name */ + ppdname[PPD_MAX_NAME]; + /* PPD keyword */ + int i, j, /* Looping vars */ + count, /* Number of values */ + bottom, /* Largest bottom margin */ + left, /* Largest left margin */ + right, /* Largest right margin */ + top, /* Largest top margin */ + max_length = 0, /* Maximum custom size */ + max_width = 0, + min_length = INT_MAX, + /* Minimum custom size */ + min_width = INT_MAX, + is_apple = 0, /* Does the printer support Apple raster? */ + is_pdf = 0, /* Does the printer support PDF? */ + is_pwg = 0; /* Does the printer support PWG Raster? */ + pwg_media_t *pwg; /* PWG media size */ + int xres, yres; /* Resolution values */ + int resolutions[1000]; + /* Array of resolution indices */ + char msgid[256]; /* Message identifier (attr.value) */ + const char *keyword, /* Keyword value */ + *msgstr; /* Localized string */ + cups_lang_t *lang = cupsLangDefault(); + /* Localization info */ + cups_array_t *strings = NULL;/* Printer strings file */ + struct lconv *loc = localeconv(); + /* Locale data */ + cups_array_t *fin_options = NULL; + /* Finishing options */ + + + /* + * Range check input... + */ + + if (buffer) + *buffer = '\0'; + + if (!buffer || bufsize < 1) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); + return (NULL); + } + + if (!response) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No IPP attributes."), 1); + return (NULL); + } + + /* + * Open a temporary file for the PPD... + */ + + if ((fp = cupsTempFile2(buffer, (int)bufsize)) == NULL) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); + return (NULL); + } + + /* + * Standard stuff for PPD file... + */ + + cupsFilePuts(fp, "*PPD-Adobe: \"4.3\"\n"); + cupsFilePuts(fp, "*FormatVersion: \"4.3\"\n"); + cupsFilePrintf(fp, "*FileVersion: \"%d.%d\"\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR); + cupsFilePuts(fp, "*LanguageVersion: English\n"); + cupsFilePuts(fp, "*LanguageEncoding: ISOLatin1\n"); + cupsFilePuts(fp, "*PSVersion: \"(3010.000) 0\"\n"); + cupsFilePuts(fp, "*LanguageLevel: \"3\"\n"); + cupsFilePuts(fp, "*FileSystem: False\n"); + cupsFilePuts(fp, "*PCFileName: \"ippeve.ppd\"\n"); + + if ((attr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT)) != NULL) + strlcpy(make, ippGetString(attr, 0, NULL), sizeof(make)); + else + strlcpy(make, "Unknown Printer", sizeof(make)); + + if (!_cups_strncasecmp(make, "Hewlett Packard ", 16) || + !_cups_strncasecmp(make, "Hewlett-Packard ", 16)) + { + model = make + 16; + strlcpy(make, "HP", sizeof(make)); + } + else if ((model = strchr(make, ' ')) != NULL) + *model++ = '\0'; + else + model = make; + + cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make); + cupsFilePrintf(fp, "*ModelName: \"%s\"\n", model); + cupsFilePrintf(fp, "*Product: \"(%s)\"\n", model); + cupsFilePrintf(fp, "*NickName: \"%s - IPP Everywhere\"\n", model); + cupsFilePrintf(fp, "*ShortNickName: \"%s - IPP Everywhere\"\n", model); + + if ((attr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN)) != NULL && ippGetBoolean(attr, 0)) + cupsFilePuts(fp, "*ColorDevice: True\n"); + else + cupsFilePuts(fp, "*ColorDevice: False\n"); + + cupsFilePrintf(fp, "*cupsVersion: %d.%d\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR); + cupsFilePuts(fp, "*cupsSNMPSupplies: False\n"); + cupsFilePrintf(fp, "*cupsLanguages: \"%s\"\n", lang->language); + + if ((attr = ippFindAttribute(response, "printer-more-info", IPP_TAG_URI)) != NULL) + cupsFilePrintf(fp, "*APSupplies: \"%s\"\n", ippGetString(attr, 0, NULL)); + + if ((attr = ippFindAttribute(response, "printer-charge-info-uri", IPP_TAG_URI)) != NULL) + cupsFilePrintf(fp, "*cupsChargeInfoURI: \"%s\"\n", ippGetString(attr, 0, NULL)); + + if ((attr = ippFindAttribute(response, "printer-strings-uri", IPP_TAG_URI)) != NULL) + { + http_t *http = NULL; /* Connection to printer */ + char stringsfile[1024]; /* Temporary strings file */ + + if (cups_get_url(&http, ippGetString(attr, 0, NULL), stringsfile, sizeof(stringsfile))) + { + cupsFilePrintf(fp, "*cupsStringsURI: \"%s\"\n", ippGetString(attr, 0, NULL)); + + strings = _cupsMessageLoad(stringsfile, _CUPS_MESSAGE_STRINGS | _CUPS_MESSAGE_UNQUOTE); + + unlink(stringsfile); + } + + if (http) + httpClose(http); + } + + /* + * Accounting... + */ + + if (ippGetBoolean(ippFindAttribute(response, "job-account-id-supported", IPP_TAG_BOOLEAN), 0)) + cupsFilePuts(fp, "*cupsJobAccountId: True\n"); + + if (ippGetBoolean(ippFindAttribute(response, "job-accounting-user-id-supported", IPP_TAG_BOOLEAN), 0)) + cupsFilePuts(fp, "*cupsJobAccountingUserId: True\n"); + + /* + * Password/PIN printing... + */ + + if ((attr = ippFindAttribute(response, "job-password-supported", IPP_TAG_INTEGER)) != NULL) + { + char pattern[33]; /* Password pattern */ + int maxlen = ippGetInteger(attr, 0); + /* Maximum length */ + const char *repertoire = ippGetString(ippFindAttribute(response, "job-password-repertoire-configured", IPP_TAG_KEYWORD), 0, NULL); + /* Type of password */ + + if (maxlen > (int)(sizeof(pattern) - 1)) + maxlen = (int)sizeof(pattern) - 1; + + if (!repertoire || !strcmp(repertoire, "iana_us-ascii_digits")) + memset(pattern, '1', (size_t)maxlen); + else if (!strcmp(repertoire, "iana_us-ascii_letters")) + memset(pattern, 'A', (size_t)maxlen); + else if (!strcmp(repertoire, "iana_us-ascii_complex")) + memset(pattern, 'C', (size_t)maxlen); + else if (!strcmp(repertoire, "iana_us-ascii_any")) + memset(pattern, '.', (size_t)maxlen); + else if (!strcmp(repertoire, "iana_utf-8_digits")) + memset(pattern, 'N', (size_t)maxlen); + else if (!strcmp(repertoire, "iana_utf-8_letters")) + memset(pattern, 'U', (size_t)maxlen); + else + memset(pattern, '*', (size_t)maxlen); + + pattern[maxlen] = '\0'; + + cupsFilePrintf(fp, "*cupsJobPassword: \"%s\"\n", pattern); + } + + /* + * Filters... + */ + + if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL) + { + is_apple = ippContainsString(attr, "image/urf"); + is_pdf = ippContainsString(attr, "application/pdf"); + is_pwg = ippContainsString(attr, "image/pwg-raster") && !is_apple; + + if (ippContainsString(attr, "image/jpeg")) + cupsFilePuts(fp, "*cupsFilter2: \"image/jpeg image/jpeg 0 -\"\n"); + if (ippContainsString(attr, "image/png")) + cupsFilePuts(fp, "*cupsFilter2: \"image/png image/png 0 -\"\n"); + if (is_pdf) + { + /* + * Don't locally filter PDF content when printing to a CUPS shared + * printer, otherwise the options will be applied twice... + */ + + if (ippContainsString(attr, "application/vnd.cups-pdf")) + cupsFilePuts(fp, "*cupsFilter2: \"application/pdf application/pdf 0 -\"\n"); + else + cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 10 -\"\n"); + } + else + cupsFilePuts(fp, "*cupsManualCopies: True\n"); + if (is_apple) + cupsFilePuts(fp, "*cupsFilter2: \"image/urf image/urf 100 -\"\n"); + if (is_pwg) + cupsFilePuts(fp, "*cupsFilter2: \"image/pwg-raster image/pwg-raster 100 -\"\n"); + } + + if (!is_apple && !is_pdf && !is_pwg) + goto bad_ppd; + + /* + * PageSize/PageRegion/ImageableArea/PaperDimension + */ + + if ((attr = ippFindAttribute(response, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != NULL) + { + for (i = 1, bottom = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++) + if (ippGetInteger(attr, i) > bottom) + bottom = ippGetInteger(attr, i); + } + else + bottom = 1270; + + if ((attr = ippFindAttribute(response, "media-left-margin-supported", IPP_TAG_INTEGER)) != NULL) + { + for (i = 1, left = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++) + if (ippGetInteger(attr, i) > left) + left = ippGetInteger(attr, i); + } + else + left = 635; + + if ((attr = ippFindAttribute(response, "media-right-margin-supported", IPP_TAG_INTEGER)) != NULL) + { + for (i = 1, right = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++) + if (ippGetInteger(attr, i) > right) + right = ippGetInteger(attr, i); + } + else + right = 635; + + if ((attr = ippFindAttribute(response, "media-top-margin-supported", IPP_TAG_INTEGER)) != NULL) + { + for (i = 1, top = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++) + if (ippGetInteger(attr, i) > top) + top = ippGetInteger(attr, i); + } + else + top = 1270; + + if ((defattr = ippFindAttribute(response, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != NULL) + { + if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL) + { + media_size = ippGetCollection(attr, 0); + x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER); + y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER); + + if (x_dim && y_dim && (pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL) + strlcpy(ppdname, pwg->ppd, sizeof(ppdname)); + else + strlcpy(ppdname, "Unknown", sizeof(ppdname)); + } + else + strlcpy(ppdname, "Unknown", sizeof(ppdname)); + } + else if ((pwg = pwgMediaForPWG(ippGetString(ippFindAttribute(response, "media-default", IPP_TAG_ZERO), 0, NULL))) != NULL) + strlcpy(ppdname, pwg->ppd, sizeof(ppdname)); + else + strlcpy(ppdname, "Unknown", sizeof(ppdname)); + + sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes, NULL, NULL, 0, (cups_acopy_func_t)pwg_copy_size, (cups_afree_func_t)free); + + if ((attr = ippFindAttribute(response, "media-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL) + { + for (i = 0, count = ippGetCount(attr); i < count; i ++) + { + cups_size_t temp; /* Current size */ + ipp_attribute_t *margin; /* media-xxx-margin attribute */ + + media_col = ippGetCollection(attr, i); + media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0); + x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO); + y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO); + pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0)); + + if (pwg) + { + temp.width = pwg->width; + temp.length = pwg->length; + + if ((margin = ippFindAttribute(media_col, "media-bottom-margin", IPP_TAG_INTEGER)) != NULL) + temp.bottom = ippGetInteger(margin, 0); + else + temp.bottom = bottom; + + if ((margin = ippFindAttribute(media_col, "media-left-margin", IPP_TAG_INTEGER)) != NULL) + temp.left = ippGetInteger(margin, 0); + else + temp.left = left; + + if ((margin = ippFindAttribute(media_col, "media-right-margin", IPP_TAG_INTEGER)) != NULL) + temp.right = ippGetInteger(margin, 0); + else + temp.right = right; + + if ((margin = ippFindAttribute(media_col, "media-top-margin", IPP_TAG_INTEGER)) != NULL) + temp.top = ippGetInteger(margin, 0); + else + temp.top = top; + + if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0) + snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd); + else + strlcpy(temp.media, pwg->ppd, sizeof(temp.media)); + + if (!cupsArrayFind(sizes, &temp)) + cupsArrayAdd(sizes, &temp); + } + else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE) + { + /* + * Custom size - record the min/max values... + */ + + int lower, upper; /* Range values */ + + if (ippGetValueTag(x_dim) == IPP_TAG_RANGE) + lower = ippGetRange(x_dim, 0, &upper); + else + lower = upper = ippGetInteger(x_dim, 0); + + if (lower < min_width) + min_width = lower; + if (upper > max_width) + max_width = upper; + + if (ippGetValueTag(y_dim) == IPP_TAG_RANGE) + lower = ippGetRange(y_dim, 0, &upper); + else + lower = upper = ippGetInteger(y_dim, 0); + + if (lower < min_length) + min_length = lower; + if (upper > max_length) + max_length = upper; + } + } + + if ((max_width == 0 || max_length == 0) && (attr = ippFindAttribute(response, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL) + { + /* + * Some printers don't list custom size support in media-col-database... + */ + + for (i = 0, count = ippGetCount(attr); i < count; i ++) + { + media_size = ippGetCollection(attr, i); + x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO); + y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO); + + if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE) + { + /* + * Custom size - record the min/max values... + */ + + int lower, upper; /* Range values */ + + if (ippGetValueTag(x_dim) == IPP_TAG_RANGE) + lower = ippGetRange(x_dim, 0, &upper); + else + lower = upper = ippGetInteger(x_dim, 0); + + if (lower < min_width) + min_width = lower; + if (upper > max_width) + max_width = upper; + + if (ippGetValueTag(y_dim) == IPP_TAG_RANGE) + lower = ippGetRange(y_dim, 0, &upper); + else + lower = upper = ippGetInteger(y_dim, 0); + + if (lower < min_length) + min_length = lower; + if (upper > max_length) + max_length = upper; + } + } + } + } + else if ((attr = ippFindAttribute(response, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL) + { + for (i = 0, count = ippGetCount(attr); i < count; i ++) + { + cups_size_t temp; /* Current size */ + + media_size = ippGetCollection(attr, i); + x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO); + y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO); + pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0)); + + if (pwg) + { + temp.width = pwg->width; + temp.length = pwg->length; + temp.bottom = bottom; + temp.left = left; + temp.right = right; + temp.top = top; + + if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0) + snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd); + else + strlcpy(temp.media, pwg->ppd, sizeof(temp.media)); + + if (!cupsArrayFind(sizes, &temp)) + cupsArrayAdd(sizes, &temp); + } + else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE) + { + /* + * Custom size - record the min/max values... + */ + + int lower, upper; /* Range values */ + + if (ippGetValueTag(x_dim) == IPP_TAG_RANGE) + lower = ippGetRange(x_dim, 0, &upper); + else + lower = upper = ippGetInteger(x_dim, 0); + + if (lower < min_width) + min_width = lower; + if (upper > max_width) + max_width = upper; + + if (ippGetValueTag(y_dim) == IPP_TAG_RANGE) + lower = ippGetRange(y_dim, 0, &upper); + else + lower = upper = ippGetInteger(y_dim, 0); + + if (lower < min_length) + min_length = lower; + if (upper > max_length) + max_length = upper; + } + } + } + else if ((attr = ippFindAttribute(response, "media-supported", IPP_TAG_ZERO)) != NULL) + { + for (i = 0, count = ippGetCount(attr); i < count; i ++) + { + const char *pwg_size = ippGetString(attr, i, NULL); + /* PWG size name */ + cups_size_t temp; /* Current size */ + + if ((pwg = pwgMediaForPWG(pwg_size)) != NULL) + { + if (strstr(pwg_size, "_max_") || strstr(pwg_size, "_max.")) + { + if (pwg->width > max_width) + max_width = pwg->width; + if (pwg->length > max_length) + max_length = pwg->length; + } + else if (strstr(pwg_size, "_min_") || strstr(pwg_size, "_min.")) + { + if (pwg->width < min_width) + min_width = pwg->width; + if (pwg->length < min_length) + min_length = pwg->length; + } + else + { + temp.width = pwg->width; + temp.length = pwg->length; + temp.bottom = bottom; + temp.left = left; + temp.right = right; + temp.top = top; + + if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0) + snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd); + else + strlcpy(temp.media, pwg->ppd, sizeof(temp.media)); + + if (!cupsArrayFind(sizes, &temp)) + cupsArrayAdd(sizes, &temp); + } + } + } + } + + if (cupsArrayCount(sizes) > 0) + { + /* + * List all of the standard sizes... + */ + + char tleft[256], /* Left string */ + tbottom[256], /* Bottom string */ + tright[256], /* Right string */ + ttop[256], /* Top string */ + twidth[256], /* Width string */ + tlength[256]; /* Length string */ + + cupsFilePrintf(fp, "*OpenUI *PageSize: PickOne\n" + "*OrderDependency: 10 AnySetup *PageSize\n" + "*DefaultPageSize: %s\n", ppdname); + for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes)) + { + _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc); + _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc); + + cupsFilePrintf(fp, "*PageSize %s: \"<>setpagedevice\"\n", size->media, twidth, tlength); + } + cupsFilePuts(fp, "*CloseUI: *PageSize\n"); + + cupsFilePrintf(fp, "*OpenUI *PageRegion: PickOne\n" + "*OrderDependency: 10 AnySetup *PageRegion\n" + "*DefaultPageRegion: %s\n", ppdname); + for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes)) + { + _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc); + _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc); + + cupsFilePrintf(fp, "*PageRegion %s: \"<>setpagedevice\"\n", size->media, twidth, tlength); + } + cupsFilePuts(fp, "*CloseUI: *PageRegion\n"); + + cupsFilePrintf(fp, "*DefaultImageableArea: %s\n" + "*DefaultPaperDimension: %s\n", ppdname, ppdname); + + for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes)) + { + _cupsStrFormatd(tleft, tleft + sizeof(tleft), size->left * 72.0 / 2540.0, loc); + _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), size->bottom * 72.0 / 2540.0, loc); + _cupsStrFormatd(tright, tright + sizeof(tright), (size->width - size->right) * 72.0 / 2540.0, loc); + _cupsStrFormatd(ttop, ttop + sizeof(ttop), (size->length - size->top) * 72.0 / 2540.0, loc); + _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc); + _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc); + + cupsFilePrintf(fp, "*ImageableArea %s: \"%s %s %s %s\"\n", size->media, tleft, tbottom, tright, ttop); + cupsFilePrintf(fp, "*PaperDimension %s: \"%s %s\"\n", size->media, twidth, tlength); + } + + cupsArrayDelete(sizes); + + /* + * Custom size support... + */ + + if (max_width > 0 && min_width < INT_MAX && max_length > 0 && min_length < INT_MAX) + { + char tmax[256], tmin[256]; /* Min/max values */ + + _cupsStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc); + _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), bottom * 72.0 / 2540.0, loc); + _cupsStrFormatd(tright, tright + sizeof(tright), right * 72.0 / 2540.0, loc); + _cupsStrFormatd(ttop, ttop + sizeof(ttop), top * 72.0 / 2540.0, loc); + + cupsFilePrintf(fp, "*HWMargins: \"%s %s %s %s\"\n", tleft, tbottom, tright, ttop); + + _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_width * 72.0 / 2540.0, loc); + _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_width * 72.0 / 2540.0, loc); + cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s\n", tmin, tmax); + + _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_length * 72.0 / 2540.0, loc); + _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_length * 72.0 / 2540.0, loc); + cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s\n", tmin, tmax); + + cupsFilePuts(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0\n"); + cupsFilePuts(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0\n"); + cupsFilePuts(fp, "*ParamCustomPageSize Orientation: 5 int 0 3\n"); + cupsFilePuts(fp, "*CustomPageSize True: \"pop pop pop <>setpagedevice\"\n"); + } + } + else + { + cupsArrayDelete(sizes); + goto bad_ppd; + } + + /* + * InputSlot... + */ + + if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source", IPP_TAG_ZERO)) != NULL) + pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname)); + else + ppdname[0] = '\0'; + + if ((attr = ippFindAttribute(response, "media-source-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1) + { + int have_default = ppdname[0] != '\0'; + /* Do we have a default InputSlot? */ + static const char * const sources[] = + { /* Standard "media-source" strings */ + "auto", + "main", + "alternate", + "large-capacity", + "manual", + "envelope", + "disc", + "photo", + "hagaki", + "main-roll", + "alternate-roll", + "top", + "middle", + "bottom", + "side", + "left", + "right", + "center", + "rear", + "by-pass-tray", + "tray-1", + "tray-2", + "tray-3", + "tray-4", + "tray-5", + "tray-6", + "tray-7", + "tray-8", + "tray-9", + "tray-10", + "tray-11", + "tray-12", + "tray-13", + "tray-14", + "tray-15", + "tray-16", + "tray-17", + "tray-18", + "tray-19", + "tray-20", + "roll-1", + "roll-2", + "roll-3", + "roll-4", + "roll-5", + "roll-6", + "roll-7", + "roll-8", + "roll-9", + "roll-10" + }; + + cupsFilePuts(fp, "*OpenUI *InputSlot: PickOne\n" + "*OrderDependency: 10 AnySetup *InputSlot\n"); + if (have_default) + cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname); + + for (i = 0; i < count; i ++) + { + keyword = ippGetString(attr, i, NULL); + + pwg_ppdize_name(keyword, ppdname, sizeof(ppdname)); + + if (i == 0 && !have_default) + cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname); + + for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++) + if (!strcmp(sources[j], keyword)) + { + snprintf(msgid, sizeof(msgid), "media-source.%s", keyword); + + if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr)) + if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid) + msgstr = keyword; + + cupsFilePrintf(fp, "*InputSlot %s: \"<>setpagedevice\"\n", ppdname, j); + cupsFilePrintf(fp, "*%s.InputSlot %s/%s: \"\"\n", lang->language, ppdname, msgstr); + break; + } + } + cupsFilePuts(fp, "*CloseUI: *InputSlot\n"); + } + + /* + * MediaType... + */ + + if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type", IPP_TAG_ZERO)) != NULL) + pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname)); + else + strlcpy(ppdname, "Unknown", sizeof(ppdname)); + + if ((attr = ippFindAttribute(response, "media-type-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1) + { + cupsFilePrintf(fp, "*OpenUI *MediaType: PickOne\n" + "*OrderDependency: 10 AnySetup *MediaType\n" + "*DefaultMediaType: %s\n", ppdname); + for (i = 0; i < count; i ++) + { + keyword = ippGetString(attr, i, NULL); + + pwg_ppdize_name(keyword, ppdname, sizeof(ppdname)); + + snprintf(msgid, sizeof(msgid), "media-type.%s", keyword); + if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr)) + if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid) + msgstr = keyword; + + cupsFilePrintf(fp, "*MediaType %s: \"<>setpagedevice\"\n", ppdname, ppdname); + cupsFilePrintf(fp, "*%s.MediaType %s/%s: \"\"\n", lang->language, ppdname, msgstr); + } + cupsFilePuts(fp, "*CloseUI: *MediaType\n"); + } + + /* + * ColorModel... + */ + + if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) == NULL) + if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL) + if ((attr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) == NULL) + attr = ippFindAttribute(response, "output-mode-supported", IPP_TAG_KEYWORD); + + if (attr) + { + int wrote_color = 0; + const char *default_color = NULL; /* Default */ + + cupsFilePrintf(fp, "*%% ColorModel from %s\n", ippGetName(attr)); + + for (i = 0, count = ippGetCount(attr); i < count; i ++) + { + keyword = ippGetString(attr, i, NULL); + +#define PRINTF_COLORMODEL if (!wrote_color) { cupsFilePrintf(fp, "*OpenUI *ColorModel: PickOne\n*OrderDependency: 10 AnySetup *ColorModel\n*%s.Translation ColorModel/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Color Mode"))); wrote_color = 1; } +#define PRINTF_COLOROPTION(name,text,cspace,bpp) { cupsFilePrintf(fp, "*ColorModel %s: \"<>setpagedevice\"\n", name, cspace, bpp); cupsFilePrintf(fp, "*%s.ColorModel %s/%s: \"\"\n", lang->language, name, _cupsLangString(lang, text)); } + + if (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("FastGray", _("Fast Grayscale"), CUPS_CSPACE_K, 1) + + if (!default_color) + default_color = "FastGray"; + } + else if (!strcasecmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8) + + if (!default_color || !strcmp(default_color, "FastGray")) + default_color = "Gray"; + } + else if (!strcasecmp(keyword, "sgray_16") || !strcmp(keyword, "W8-16")) + { + PRINTF_COLORMODEL + + if (!strcmp(keyword, "W8-16")) + { + PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8) + + if (!default_color || !strcmp(default_color, "FastGray")) + default_color = "Gray"; + } + + PRINTF_COLOROPTION("Gray16", _("Deep Gray"), CUPS_CSPACE_SW, 16) + } + else if (!strcasecmp(keyword, "srgb_8") || !strncmp(keyword, "SRGB24", 7) || !strcmp(keyword, "color")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("RGB", _("Color"), CUPS_CSPACE_SRGB, 8) + + default_color = "RGB"; + + // Apparently some printers only advertise color support, so make sure + // we also do grayscale for these printers... + if (!ippContainsString(attr, "sgray_8") && !ippContainsString(attr, "black_1") && !ippContainsString(attr, "black_8") && !ippContainsString(attr, "W8") && !ippContainsString(attr, "W8-16")) + PRINTF_COLOROPTION("Gray", _("GrayScale"), CUPS_CSPACE_SW, 8) + } + else if (!strcasecmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48") || !strcmp(keyword, "ADOBERGB24-48")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 16) + + if (!default_color) + default_color = "AdobeRGB"; + } + else if ((!strcasecmp(keyword, "adobe-rgb_8") && !ippContainsString(attr, "adobe-rgb_16")) || !strcmp(keyword, "ADOBERGB24")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 8) + + if (!default_color) + default_color = "AdobeRGB"; + } + else if ((!strcasecmp(keyword, "black_8") && !ippContainsString(attr, "black_16")) || !strcmp(keyword, "DEVW8")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 8) + } + else if (!strcasecmp(keyword, "black_16") || !strcmp(keyword, "DEVW16") || !strcmp(keyword, "DEVW8-16")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 16) + } + else if ((!strcasecmp(keyword, "cmyk_8") && !ippContainsString(attr, "cmyk_16")) || !strcmp(keyword, "DEVCMYK32")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 8) + } + else if (!strcasecmp(keyword, "cmyk_16") || !strcmp(keyword, "DEVCMYK32-64") || !strcmp(keyword, "DEVCMYK64")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 16) + } + else if ((!strcasecmp(keyword, "rgb_8") && ippContainsString(attr, "rgb_16")) || !strcmp(keyword, "DEVRGB24")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 8) + } + else if (!strcasecmp(keyword, "rgb_16") || !strcmp(keyword, "DEVRGB24-48") || !strcmp(keyword, "DEVRGB48")) + { + PRINTF_COLORMODEL + + PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 16) + } + } + + if (default_color) + cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color); + if (wrote_color) + cupsFilePuts(fp, "*CloseUI: *ColorModel\n"); + } + + /* + * Duplex... + */ + + if ((attr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL && ippContainsString(attr, "two-sided-long-edge")) + { + cupsFilePrintf(fp, "*OpenUI *Duplex: PickOne\n" + "*OrderDependency: 10 AnySetup *Duplex\n" + "*%s.Translation Duplex/%s: \"\"\n" + "*DefaultDuplex: None\n" + "*Duplex None: \"<>setpagedevice\"\n" + "*%s.Duplex None/%s: \"\"\n" + "*Duplex DuplexNoTumble: \"<>setpagedevice\"\n" + "*%s.Duplex DuplexNoTumble/%s: \"\"\n" + "*Duplex DuplexTumble: \"<>setpagedevice\"\n" + "*%s.Duplex DuplexTumble/%s: \"\"\n" + "*CloseUI: *Duplex\n", lang->language, _cupsLangString(lang, _("2-Sided Printing")), lang->language, _cupsLangString(lang, _("Off (1-Sided)")), lang->language, _cupsLangString(lang, _("Long-Edge (Portrait)")), lang->language, _cupsLangString(lang, _("Short-Edge (Landscape)"))); + + if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL) + { + for (i = 0, count = ippGetCount(attr); i < count; i ++) + { + const char *dm = ippGetString(attr, i, NULL); + /* DM value */ + + if (!_cups_strcasecmp(dm, "DM1")) + { + cupsFilePuts(fp, "*cupsBackSide: Normal\n"); + break; + } + else if (!_cups_strcasecmp(dm, "DM2")) + { + cupsFilePuts(fp, "*cupsBackSide: Flipped\n"); + break; + } + else if (!_cups_strcasecmp(dm, "DM3")) + { + cupsFilePuts(fp, "*cupsBackSide: Rotated\n"); + break; + } + else if (!_cups_strcasecmp(dm, "DM4")) + { + cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n"); + break; + } + } + } + else if ((attr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL) + { + keyword = ippGetString(attr, 0, NULL); + + if (!strcmp(keyword, "flipped")) + cupsFilePuts(fp, "*cupsBackSide: Flipped\n"); + else if (!strcmp(keyword, "manual-tumble")) + cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n"); + else if (!strcmp(keyword, "normal")) + cupsFilePuts(fp, "*cupsBackSide: Normal\n"); + else + cupsFilePuts(fp, "*cupsBackSide: Rotated\n"); + } + } + + /* + * Output bin... + */ + + if ((attr = ippFindAttribute(response, "output-bin-default", IPP_TAG_ZERO)) != NULL) + pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname)); + else + strlcpy(ppdname, "Unknown", sizeof(ppdname)); + + if ((attr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 0) + { + ipp_attribute_t *trays = ippFindAttribute(response, "printer-output-tray", IPP_TAG_STRING); + /* printer-output-tray attribute, if any */ + const char *tray_ptr; /* printer-output-tray value */ + int tray_len; /* Len of printer-output-tray value */ + char tray[IPP_MAX_OCTETSTRING]; + /* printer-output-tray string value */ + + cupsFilePrintf(fp, "*OpenUI *OutputBin: PickOne\n" + "*OrderDependency: 10 AnySetup *OutputBin\n" + "*DefaultOutputBin: %s\n", ppdname); + if (!strcmp(ppdname, "FaceUp")) + cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n"); + else + cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n"); + + for (i = 0; i < count; i ++) + { + keyword = ippGetString(attr, i, NULL); + + pwg_ppdize_name(keyword, ppdname, sizeof(ppdname)); + + snprintf(msgid, sizeof(msgid), "output-bin.%s", keyword); + if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr)) + if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid) + msgstr = keyword; + + cupsFilePrintf(fp, "*OutputBin %s: \"\"\n", ppdname); + cupsFilePrintf(fp, "*%s.OutputBin %s/%s: \"\"\n", lang->language, ppdname, msgstr); + + if ((tray_ptr = ippGetOctetString(trays, i, &tray_len)) != NULL) + { + if (tray_len >= (int)sizeof(tray)) + tray_len = (int)sizeof(tray) - 1; + + memcpy(tray, tray_ptr, (size_t)tray_len); + tray[tray_len] = '\0'; + + if (strstr(tray, "stackingorder=lastToFirst;")) + cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname); + else + cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname); + } + else if (!strcmp(ppdname, "FaceUp")) + cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname); + else + cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname); + } + cupsFilePuts(fp, "*CloseUI: *OutputBin\n"); + } + + /* + * Finishing options... + */ + + if ((attr = ippFindAttribute(response, "finishings-supported", IPP_TAG_ENUM)) != NULL) + { + int value; /* Enum value */ + const char *ppd_keyword; /* PPD keyword for enum */ + cups_array_t *names; /* Names we've added */ + static const char * const base_keywords[] = + { /* Base STD 92 keywords */ + NULL, /* none */ + "SingleAuto", /* staple */ + "SingleAuto", /* punch */ + NULL, /* cover */ + "BindAuto", /* bind */ + "SaddleStitch", /* saddle-stitch */ + "EdgeStitchAuto", /* edge-stitch */ + "Auto", /* fold */ + NULL, /* trim */ + NULL, /* bale */ + NULL, /* booklet-maker */ + NULL, /* jog-offset */ + NULL, /* coat */ + NULL /* laminate */ + }; + + count = ippGetCount(attr); + names = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free); + fin_options = cupsArrayNew((cups_array_func_t)strcmp, NULL); + + /* + * Staple/Bind/Stitch + */ + + for (i = 0; i < count; i ++) + { + value = ippGetInteger(attr, i); + keyword = ippEnumString("finishings", value); + + if (!strncmp(keyword, "staple-", 7) || !strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch")) + break; + } + + if (i < count) + { + static const char * const staple_keywords[] = + { /* StapleLocation keywords */ + "SinglePortrait", + "SingleRevLandscape", + "SingleLandscape", + "SingleRevPortrait", + "EdgeStitchPortrait", + "EdgeStitchLandscape", + "EdgeStitchRevPortrait", + "EdgeStitchRevLandscape", + "DualPortrait", + "DualLandscape", + "DualRevPortrait", + "DualRevLandscape", + "TriplePortrait", + "TripleLandscape", + "TripleRevPortrait", + "TripleRevLandscape" + }; + static const char * const bind_keywords[] = + { /* StapleLocation binding keywords */ + "BindPortrait", + "BindLandscape", + "BindRevPortrait", + "BindRevLandscape" + }; + + cupsArrayAdd(fin_options, "*StapleLocation"); + + cupsFilePuts(fp, "*OpenUI *StapleLocation: PickOne\n"); + cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n"); + cupsFilePrintf(fp, "*%s.Translation StapleLocation/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Staple"))); + cupsFilePuts(fp, "*DefaultStapleLocation: None\n"); + cupsFilePuts(fp, "*StapleLocation None: \"\"\n"); + cupsFilePrintf(fp, "*%s.StapleLocation None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None"))); + + for (; i < count; i ++) + { + value = ippGetInteger(attr, i); + keyword = ippEnumString("finishings", value); + + if (strncmp(keyword, "staple-", 7) && strncmp(keyword, "bind-", 5) && strncmp(keyword, "edge-stitch-", 12) && strcmp(keyword, "saddle-stitch")) + continue; + + if (cupsArrayFind(names, (char *)keyword)) + continue; /* Already did this finishing template */ + + cupsArrayAdd(names, (char *)keyword); + + snprintf(msgid, sizeof(msgid), "finishings.%d", value); + if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr)) + if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid) + msgstr = keyword; + + if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE) + ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE]; + else if (value >= IPP_FINISHINGS_STAPLE_TOP_LEFT && value <= IPP_FINISHINGS_STAPLE_TRIPLE_BOTTOM) + ppd_keyword = staple_keywords[value - IPP_FINISHINGS_STAPLE_TOP_LEFT]; + else if (value >= IPP_FINISHINGS_BIND_LEFT && value <= IPP_FINISHINGS_BIND_BOTTOM) + ppd_keyword = bind_keywords[value - IPP_FINISHINGS_BIND_LEFT]; + else + ppd_keyword = NULL; + + if (!ppd_keyword) + continue; + + cupsFilePrintf(fp, "*StapleLocation %s: \"\"\n", ppd_keyword); + cupsFilePrintf(fp, "*%s.StapleLocation %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr); + cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, keyword, ppd_keyword); + } + + cupsFilePuts(fp, "*CloseUI: *StapleLocation\n"); + } + + /* + * Fold + */ + + for (i = 0; i < count; i ++) + { + value = ippGetInteger(attr, i); + keyword = ippEnumString("finishings", value); + + if (!strncmp(keyword, "cups-fold-", 10) || !strcmp(keyword, "fold") || !strncmp(keyword, "fold-", 5)) + break; + } + + if (i < count) + { + static const char * const fold_keywords[] = + { /* FoldType keywords */ + "Accordion", + "DoubleGate", + "Gate", + "Half", + "HalfZ", + "LeftGate", + "Letter", + "Parallel", + "XFold", + "RightGate", + "ZFold", + "EngineeringZ" + }; + + cupsArrayAdd(fin_options, "*FoldType"); + + cupsFilePuts(fp, "*OpenUI *FoldType: PickOne\n"); + cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n"); + cupsFilePrintf(fp, "*%s.Translation FoldType/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Fold"))); + cupsFilePuts(fp, "*DefaultFoldType: None\n"); + cupsFilePuts(fp, "*FoldType None: \"\"\n"); + cupsFilePrintf(fp, "*%s.FoldType None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None"))); + + for (; i < count; i ++) + { + value = ippGetInteger(attr, i); + keyword = ippEnumString("finishings", value); + + if (!strncmp(keyword, "cups-fold-", 10)) + keyword += 5; + else if (strcmp(keyword, "fold") && strncmp(keyword, "fold-", 5)) + continue; + + if (cupsArrayFind(names, (char *)keyword)) + continue; /* Already did this finishing template */ + + cupsArrayAdd(names, (char *)keyword); + + snprintf(msgid, sizeof(msgid), "finishings.%d", value); + if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr)) + if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid) + msgstr = keyword; + + if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE) + ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE]; + else if (value >= IPP_FINISHINGS_FOLD_ACCORDION && value <= IPP_FINISHINGS_FOLD_ENGINEERING_Z) + ppd_keyword = fold_keywords[value - IPP_FINISHINGS_FOLD_ACCORDION]; + else if (value >= IPP_FINISHINGS_CUPS_FOLD_ACCORDION && value <= IPP_FINISHINGS_CUPS_FOLD_Z) + ppd_keyword = fold_keywords[value - IPP_FINISHINGS_CUPS_FOLD_ACCORDION]; + else + ppd_keyword = NULL; + + if (!ppd_keyword) + continue; + + cupsFilePrintf(fp, "*FoldType %s: \"\"\n", ppd_keyword); + cupsFilePrintf(fp, "*%s.FoldType %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr); + cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, keyword, ppd_keyword); + } + + cupsFilePuts(fp, "*CloseUI: *FoldType\n"); + } + + /* + * Punch + */ + + for (i = 0; i < count; i ++) + { + value = ippGetInteger(attr, i); + keyword = ippEnumString("finishings", value); + + if (!strncmp(keyword, "cups-punch-", 11) || !strncmp(keyword, "punch-", 6)) + break; + } + + if (i < count) + { + static const char * const punch_keywords[] = + { /* PunchMedia keywords */ + "SinglePortrait", + "SingleRevLandscape", + "SingleLandscape", + "SingleRevPortrait", + "DualPortrait", + "DualLandscape", + "DualRevPortrait", + "DualRevLandscape", + "TriplePortrait", + "TripleLandscape", + "TripleRevPortrait", + "TripleRevLandscape", + "QuadPortrait", + "QuadLandscape", + "QuadRevPortrait", + "QuadRevLandscape", + "MultiplePortrait", + "MultipleLandscape", + "MultipleRevPortrait", + "MultipleRevLandscape" + }; + + cupsArrayAdd(fin_options, "*PunchMedia"); + + cupsFilePuts(fp, "*OpenUI *PunchMedia: PickOne\n"); + cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n"); + cupsFilePrintf(fp, "*%s.Translation PunchMedia/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Punch"))); + cupsFilePuts(fp, "*DefaultPunchMedia: None\n"); + cupsFilePuts(fp, "*PunchMedia None: \"\"\n"); + cupsFilePrintf(fp, "*%s.PunchMedia None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None"))); + + for (i = 0; i < count; i ++) + { + value = ippGetInteger(attr, i); + keyword = ippEnumString("finishings", value); + + if (!strncmp(keyword, "cups-punch-", 11)) + keyword += 5; + else if (strncmp(keyword, "punch-", 6)) + continue; + + if (cupsArrayFind(names, (char *)keyword)) + continue; /* Already did this finishing template */ + + cupsArrayAdd(names, (char *)keyword); + + snprintf(msgid, sizeof(msgid), "finishings.%d", value); + if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr)) + if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid) + msgstr = keyword; + + if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE) + ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE]; + else if (value >= IPP_FINISHINGS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_PUNCH_MULTIPLE_BOTTOM) + ppd_keyword = punch_keywords[value - IPP_FINISHINGS_PUNCH_TOP_LEFT]; + else if (value >= IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM) + ppd_keyword = punch_keywords[value - IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT]; + else + ppd_keyword = NULL; + + if (!ppd_keyword) + continue; + + cupsFilePrintf(fp, "*PunchMedia %s: \"\"\n", ppd_keyword); + cupsFilePrintf(fp, "*%s.PunchMedia %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr); + cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, keyword, ppd_keyword); + } + + cupsFilePuts(fp, "*CloseUI: *PunchMedia\n"); + } + + /* + * Booklet + */ + + if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER)) + { + cupsArrayAdd(fin_options, "*Booklet"); + + cupsFilePuts(fp, "*OpenUI *Booklet: Boolean\n"); + cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n"); + cupsFilePrintf(fp, "*%s.Translation Booklet/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Booklet"))); + cupsFilePuts(fp, "*DefaultBooklet: False\n"); + cupsFilePuts(fp, "*Booklet False: \"\"\n"); + cupsFilePuts(fp, "*Booklet True: \"\"\n"); + cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n", IPP_FINISHINGS_BOOKLET_MAKER); + cupsFilePuts(fp, "*CloseUI: *Booklet\n"); + } + + /* + * CutMedia + */ + + for (i = 0; i < count; i ++) + { + value = ippGetInteger(attr, i); + keyword = ippEnumString("finishings", value); + + if (!strcmp(keyword, "trim") || !strncmp(keyword, "trim-", 5)) + break; + } + + if (i < count) + { + static const char * const trim_keywords[] = + { /* CutMedia keywords */ + "EndOfPage", + "EndOfDoc", + "EndOfSet", + "EndOfJob" + }; + + cupsArrayAdd(fin_options, "*CutMedia"); + + cupsFilePuts(fp, "*OpenUI *CutMedia: PickOne\n"); + cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *CutMedia\n"); + cupsFilePrintf(fp, "*%s.Translation CutMedia/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Cut"))); + cupsFilePuts(fp, "*DefaultCutMedia: None\n"); + cupsFilePuts(fp, "*CutMedia None: \"\"\n"); + cupsFilePrintf(fp, "*%s.CutMedia None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None"))); + + for (i = 0; i < count; i ++) + { + value = ippGetInteger(attr, i); + keyword = ippEnumString("finishings", value); + + if (strcmp(keyword, "trim") && strncmp(keyword, "trim-", 5)) + continue; + + if (cupsArrayFind(names, (char *)keyword)) + continue; /* Already did this finishing template */ + + cupsArrayAdd(names, (char *)keyword); + + snprintf(msgid, sizeof(msgid), "finishings.%d", value); + if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr)) + if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid) + msgstr = keyword; + + if (value == IPP_FINISHINGS_TRIM) + ppd_keyword = "Auto"; + else + ppd_keyword = trim_keywords[value - IPP_FINISHINGS_TRIM_AFTER_PAGES]; + + cupsFilePrintf(fp, "*CutMedia %s: \"\"\n", ppd_keyword); + cupsFilePrintf(fp, "*%s.CutMedia %s/%s: \"\"\n", lang->language, ppd_keyword, msgstr); + cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*CutMedia %s\"\n", value, keyword, ppd_keyword); + } + + cupsFilePuts(fp, "*CloseUI: *CutMedia\n"); + } + + cupsArrayDelete(names); + } + + if ((attr = ippFindAttribute(response, "finishings-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL) + { + ipp_t *finishing_col; /* Current finishing collection */ + ipp_attribute_t *finishing_attr; /* Current finishing member attribute */ + cups_array_t *templates; /* Finishing templates */ + + cupsFilePuts(fp, "*OpenUI *cupsFinishingTemplate: PickOne\n"); + cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n"); + cupsFilePrintf(fp, "*%s.Translation cupsFinishingTemplate/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Finishing Preset"))); + cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n"); + cupsFilePuts(fp, "*cupsFinishingTemplate none: \"\"\n"); + cupsFilePrintf(fp, "*%s.cupsFinishingTemplate none/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None"))); + + templates = cupsArrayNew((cups_array_func_t)strcmp, NULL); + count = ippGetCount(attr); + + for (i = 0; i < count; i ++) + { + finishing_col = ippGetCollection(attr, i); + keyword = ippGetString(ippFindAttribute(finishing_col, "finishing-template", IPP_TAG_ZERO), 0, NULL); + + if (!keyword || cupsArrayFind(templates, (void *)keyword)) + continue; + + if (!strcmp(keyword, "none")) + continue; + + cupsArrayAdd(templates, (void *)keyword); + + snprintf(msgid, sizeof(msgid), "finishing-template.%s", keyword); + if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr)) + if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid) + msgstr = keyword; + + cupsFilePrintf(fp, "*cupsFinishingTemplate %s: \"\n", keyword); + for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr; finishing_attr = ippNextAttribute(finishing_col)) + { + if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION) + { + const char *name = ippGetName(finishing_attr); + /* Member attribute name */ + + if (strcmp(name, "media-size")) + cupsFilePrintf(fp, "%% %s\n", name); + } + } + cupsFilePuts(fp, "\"\n"); + cupsFilePrintf(fp, "*%s.cupsFinishingTemplate %s/%s: \"\"\n", lang->language, keyword, msgstr); + cupsFilePuts(fp, "*End\n"); + } + + cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n"); + + if (cupsArrayCount(fin_options)) + { + const char *fin_option; /* Current finishing option */ + + cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate"); + for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options)) + cupsFilePrintf(fp, " %s", fin_option); + cupsFilePuts(fp, "\"\n"); + + cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate None"); + for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options)) + cupsFilePrintf(fp, " %s None", fin_option); + cupsFilePuts(fp, "\"\n"); + } + + cupsArrayDelete(templates); + } + + cupsArrayDelete(fin_options); + + /* + * cupsPrintQuality and DefaultResolution... + */ + + quality = ippFindAttribute(response, "print-quality-supported", IPP_TAG_ENUM); + + if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL) + { + int lowdpi = 0, hidpi = 0; /* Lower and higher resolution */ + + for (i = 0, count = ippGetCount(attr); i < count; i ++) + { + const char *rs = ippGetString(attr, i, NULL); + /* RS value */ + + if (_cups_strncasecmp(rs, "RS", 2)) + continue; + + lowdpi = atoi(rs + 2); + if ((rs = strrchr(rs, '-')) != NULL) + hidpi = atoi(rs + 1); + else + hidpi = lowdpi; + break; + } + + if (lowdpi == 0) + { + /* + * Invalid "urf-supported" value... + */ + + goto bad_ppd; + } + else + { + /* + * Generate print qualities based on low and high DPIs... + */ + + cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi); + + cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n" + "*OrderDependency: 10 AnySetup *cupsPrintQuality\n" + "*%s.Translation cupsPrintQuality/%s: \"\"\n" + "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality"))); + if ((lowdpi & 1) == 0) + cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", lowdpi, lowdpi / 2, lang->language, _cupsLangString(lang, _("Draft"))); + else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT)) + cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", lowdpi, lowdpi, lang->language, _cupsLangString(lang, _("Draft"))); + + cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<>setpagedevice\"\n*%s.cupsPrintQuality Normal/%s: \"\"\n", lowdpi, lowdpi, lang->language, _cupsLangString(lang, _("Normal"))); + + if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH)) + cupsFilePrintf(fp, "*cupsPrintQuality High: \"<>setpagedevice\"\n*%s.cupsPrintQuality High/%s: \"\"\n", hidpi, hidpi, lang->language, _cupsLangString(lang, _("High"))); + cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n"); + } + } + else if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL) + { + /* + * Make a sorted list of resolutions. + */ + + count = ippGetCount(attr); + if (count > (int)(sizeof(resolutions) / sizeof(resolutions[0]))) + count = (int)(sizeof(resolutions) / sizeof(resolutions[0])); + + resolutions[0] = 0; /* Not in loop to silence Clang static analyzer... */ + for (i = 1; i < count; i ++) + resolutions[i] = i; + + for (i = 0; i < (count - 1); i ++) + { + for (j = i + 1; j < count; j ++) + { + int ix, iy, /* First X and Y resolution */ + jx, jy, /* Second X and Y resolution */ + temp; /* Swap variable */ + ipp_res_t units; /* Resolution units */ + + ix = ippGetResolution(attr, resolutions[i], &iy, &units); + jx = ippGetResolution(attr, resolutions[j], &jy, &units); + + if (ix > jx || (ix == jx && iy > jy)) + { + /* + * Swap these two resolutions... + */ + + temp = resolutions[i]; + resolutions[i] = resolutions[j]; + resolutions[j] = temp; + } + } + } + + /* + * Generate print quality options... + */ + + pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, ppdname, sizeof(ppdname)); + cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname); + + cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n" + "*OrderDependency: 10 AnySetup *cupsPrintQuality\n" + "*%s.Translation cupsPrintQuality/%s: \"\"\n" + "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality"))); + if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT)) + { + pwg_ppdize_resolution(attr, resolutions[0], &xres, &yres, NULL, 0); + cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<>setpagedevice\"\n", xres, yres); + cupsFilePrintf(fp, "*%s.cupsPrintQuality Draft/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Draft"))); + } + + pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, NULL, 0); + cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<>setpagedevice\"\n", xres, yres); + cupsFilePrintf(fp, "*%s.cupsPrintQuality Normal/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Normal"))); + + if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH)) + { + pwg_ppdize_resolution(attr, resolutions[count - 1], &xres, &yres, NULL, 0); + cupsFilePrintf(fp, "*cupsPrintQuality High: \"<>setpagedevice\"\n", xres, yres); + cupsFilePrintf(fp, "*%s.cupsPrintQuality High/%s: \"\"\n", lang->language, _cupsLangString(lang, _("High"))); + } + + cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n"); + } + else if (is_apple || is_pwg) + goto bad_ppd; + else + { + if ((attr = ippFindAttribute(response, "printer-resolution-default", IPP_TAG_RESOLUTION)) != NULL) + { + pwg_ppdize_resolution(attr, 0, &xres, &yres, ppdname, sizeof(ppdname)); + } + else + { + xres = yres = 300; + strlcpy(ppdname, "300dpi", sizeof(ppdname)); + } + + cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname); + + cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n" + "*OrderDependency: 10 AnySetup *cupsPrintQuality\n" + "*%s.Translation cupsPrintQuality/%s: \"\"\n" + "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality"))); + if (ippContainsInteger(quality, IPP_QUALITY_DRAFT)) + cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("Draft"))); + + cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<>setpagedevice\"\n*%s.cupsPrintQuality Normal/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("Normal"))); + + if (ippContainsInteger(quality, IPP_QUALITY_HIGH)) + cupsFilePrintf(fp, "*cupsPrintQuality High: \"<>setpagedevice\"\n*%s.cupsPrintQuality High/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("High"))); + cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n"); + } + + /* + * Presets... + */ + + if ((attr = ippFindAttribute(response, "job-presets-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL) + { + for (i = 0, count = ippGetCount(attr); i < count; i ++) + { + ipp_t *preset = ippGetCollection(attr, i); + /* Preset collection */ + const char *preset_name = ippGetString(ippFindAttribute(preset, "preset-name", IPP_TAG_ZERO), 0, NULL), + /* Preset name */ + *localized_name; /* Localized preset name */ + ipp_attribute_t *member; /* Member attribute in preset */ + const char *member_name; /* Member attribute name */ + char member_value[256]; /* Member attribute value */ + + if (!preset || !preset_name) + continue; + + cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", preset_name); + for (member = ippFirstAttribute(preset); member; member = ippNextAttribute(preset)) + { + member_name = ippGetName(member); + + if (!member_name || !strcmp(member_name, "preset-name")) + continue; + + if (!strcmp(member_name, "finishings")) + { + for (i = 0, count = ippGetCount(member); i < count; i ++) + { + const char *option = NULL; /* PPD option name */ + + keyword = ippEnumString("finishings", ippGetInteger(member, i)); + + if (!strcmp(keyword, "booklet-maker")) + { + option = "Booklet"; + keyword = "True"; + } + else if (!strncmp(keyword, "fold-", 5)) + option = "FoldType"; + else if (!strncmp(keyword, "punch-", 6)) + option = "PunchMedia"; + else if (!strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch") || !strncmp(keyword, "staple-", 7)) + option = "StapleLocation"; + + if (option && keyword) + cupsFilePrintf(fp, "*%s %s\n", option, keyword); + } + } + else if (!strcmp(member_name, "finishings-col")) + { + ipp_t *fin_col; /* finishings-col value */ + + for (i = 0, count = ippGetCount(member); i < count; i ++) + { + fin_col = ippGetCollection(member, i); + + if ((keyword = ippGetString(ippFindAttribute(fin_col, "finishing-template", IPP_TAG_ZERO), 0, NULL)) != NULL) + cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", keyword); + } + } + else if (!strcmp(member_name, "media")) + { + /* + * Map media to PageSize... + */ + + if ((pwg = pwgMediaForPWG(ippGetString(member, 0, NULL))) != NULL && pwg->ppd) + cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd); + } + else if (!strcmp(member_name, "media-col")) + { + media_col = ippGetCollection(member, 0); + + if ((media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0)) != NULL) + { + x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER); + y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER); + if ((pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL && pwg->ppd) + cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd); + } + + if ((keyword = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL)) != NULL) + { + pwg_ppdize_name(keyword, ppdname, sizeof(ppdname)); + cupsFilePrintf(fp, "*InputSlot %s\n", keyword); + } + + if ((keyword = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL)) != NULL) + { + pwg_ppdize_name(keyword, ppdname, sizeof(ppdname)); + cupsFilePrintf(fp, "*MediaType %s\n", keyword); + } + } + else if (!strcmp(member_name, "print-quality")) + { + /* + * Map print-quality to cupsPrintQuality... + */ + + int qval = ippGetInteger(member, 0); + /* print-quality value */ + static const char * const qualities[] = { "Draft", "Normal", "High" }; + /* cupsPrintQuality values */ + + if (qval >= IPP_QUALITY_DRAFT && qval <= IPP_QUALITY_HIGH) + cupsFilePrintf(fp, "*cupsPrintQuality %s\n", qualities[qval - IPP_QUALITY_DRAFT]); + } + else if (!strcmp(member_name, "output-bin")) + { + pwg_ppdize_name(ippGetString(member, 0, NULL), ppdname, sizeof(ppdname)); + cupsFilePrintf(fp, "*OutputBin %s\n", ppdname); + } + else if (!strcmp(member_name, "sides")) + { + keyword = ippGetString(member, 0, NULL); + if (keyword && !strcmp(keyword, "one-sided")) + cupsFilePuts(fp, "*Duplex None\n"); + else if (keyword && !strcmp(keyword, "two-sided-long-edge")) + cupsFilePuts(fp, "*Duplex DuplexNoTumble\n"); + else if (keyword && !strcmp(keyword, "two-sided-short-edge")) + cupsFilePuts(fp, "*Duplex DuplexTumble\n"); + } + else + { + /* + * Add attribute name and value as-is... + */ + + ippAttributeString(member, member_value, sizeof(member_value)); + cupsFilePrintf(fp, "*%s %s\n", member_name, member_value); + } + } + + cupsFilePuts(fp, "\"\n*End\n"); + + if ((localized_name = _cupsMessageLookup(strings, preset_name)) != preset_name) + cupsFilePrintf(fp, "*%s.APPrinterPreset %s/%s: \"\"\n", lang->language, preset_name, localized_name); + } + } + + /* + * Close up and return... + */ + + cupsFileClose(fp); + + return (buffer); + + /* + * If we get here then there was a problem creating the PPD... + */ + + bad_ppd: + + cupsFileClose(fp); + unlink(buffer); + *buffer = '\0'; + + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Printer does not support required IPP attributes or document formats."), 1); + + return (NULL); +} + + +/* + * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG * media-source. */ @@ -2568,9 +4893,9 @@ _pwgMediaTypeForType( const char * /* O - PageSize name */ _pwgPageSizeForMedia( - pwg_media_t *media, /* I - Media */ - char *name, /* I - PageSize name buffer */ - size_t namesize) /* I - Size of name buffer */ + pwg_media_t *media, /* I - Media */ + char *name, /* I - PageSize name buffer */ + size_t namesize) /* I - Size of name buffer */ { const char *sizeptr, /* Pointer to size in PWG name */ *dimptr; /* Pointer to dimensions in PWG name */ @@ -2621,11 +4946,112 @@ _pwgPageSizeForMedia( } +/* + * 'cups_get_url()' - Get a copy of the file at the given URL. + */ + +static int /* O - 1 on success, 0 on failure */ +cups_get_url(http_t **http, /* IO - Current HTTP connection */ + const char *url, /* I - URL to get */ + char *name, /* I - Temporary filename */ + size_t namesize) /* I - Size of temporary filename buffer */ +{ + char scheme[32], /* URL scheme */ + userpass[256], /* URL username:password */ + host[256], /* URL host */ + curhost[256], /* Current host */ + resource[256]; /* URL resource */ + int port; /* URL port */ + http_encryption_t encryption; /* Type of encryption to use */ + http_status_t status; /* Status of GET request */ + int fd; /* Temporary file */ + + + if (httpSeparateURI(HTTP_URI_CODING_ALL, url, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK) + return (0); + + if (port == 443 || !strcmp(scheme, "https")) + encryption = HTTP_ENCRYPTION_ALWAYS; + else + encryption = HTTP_ENCRYPTION_IF_REQUESTED; + + if (!*http || strcasecmp(host, httpGetHostname(*http, curhost, sizeof(curhost))) || httpAddrPort(httpGetAddress(*http)) != port) + { + httpClose(*http); + *http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 5000, NULL); + } + + if (!*http) + return (0); + + if ((fd = cupsTempFd(name, (int)namesize)) < 0) + return (0); + + status = cupsGetFd(*http, resource, fd); + + close(fd); + + if (status != HTTP_STATUS_OK) + { + unlink(name); + *name = '\0'; + return (0); + } + + return (1); +} + + +/* + * 'pwg_add_finishing()' - Add a finishings value. + */ + +static void +pwg_add_finishing( + cups_array_t *finishings, /* I - Finishings array */ + ipp_finishings_t template, /* I - Finishing template */ + const char *name, /* I - PPD option */ + const char *value) /* I - PPD choice */ +{ + _pwg_finishings_t *f; /* New finishings value */ + + + if ((f = (_pwg_finishings_t *)calloc(1, sizeof(_pwg_finishings_t))) != NULL) + { + f->value = template; + f->num_options = cupsAddOption(name, value, 0, &f->options); + + cupsArrayAdd(finishings, f); + } +} + + +/* + * 'pwg_add_message()' - Add a message to the PPD cached strings. + */ + +static void +pwg_add_message(cups_array_t *a, /* I - Message catalog */ + const char *msg, /* I - Message identifier */ + const char *str) /* I - Localized string */ +{ + _cups_message_t *m; /* New message */ + + + if ((m = calloc(1, sizeof(_cups_message_t))) != NULL) + { + m->msg = strdup(msg); + m->str = strdup(str); + cupsArrayAdd(a, m); + } +} + + /* * 'pwg_compare_finishings()' - Compare two finishings values. */ -static int /* O- Result of comparison */ +static int /* O - Result of comparison */ pwg_compare_finishings( _pwg_finishings_t *a, /* I - First finishings value */ _pwg_finishings_t *b) /* I - Second finishings value */ @@ -2634,6 +5060,35 @@ pwg_compare_finishings( } +/* + * 'pwg_compare_sizes()' - Compare two media sizes... + */ + +static int /* O - Result of comparison */ +pwg_compare_sizes(cups_size_t *a, /* I - First media size */ + cups_size_t *b) /* I - Second media size */ +{ + return (strcmp(a->media, b->media)); +} + + +/* + * 'pwg_copy_size()' - Copy a media size. + */ + +static cups_size_t * /* O - New media size */ +pwg_copy_size(cups_size_t *size) /* I - Media size to copy */ +{ + cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t)); + /* New media size */ + + if (newsize) + memcpy(newsize, size, sizeof(cups_size_t)); + + return (newsize); +} + + /* * 'pwg_free_finishings()' - Free a finishings value. */ @@ -2660,11 +5115,17 @@ pwg_ppdize_name(const char *ipp, /* I - IPP keyword */ *end; /* End of name buffer */ + if (!ipp) + { + *name = '\0'; + return; + } + *name = (char)toupper(*ipp++); for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;) { - if (*ipp == '-' && _cups_isalpha(ipp[1])) + if (*ipp == '-' && _cups_isalnum(ipp[1])) { ipp ++; *ptr++ = (char)toupper(*ipp++ & 255); @@ -2677,6 +5138,40 @@ pwg_ppdize_name(const char *ipp, /* I - IPP keyword */ } +/* + * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values. + */ + +static void +pwg_ppdize_resolution( + ipp_attribute_t *attr, /* I - Attribute to convert */ + int element, /* I - Element to convert */ + int *xres, /* O - X resolution in DPI */ + int *yres, /* O - Y resolution in DPI */ + char *name, /* I - Name buffer */ + size_t namesize) /* I - Size of name buffer */ +{ + ipp_res_t units; /* Units for resolution */ + + + *xres = ippGetResolution(attr, element, yres, &units); + + if (units == IPP_RES_PER_CM) + { + *xres = (int)(*xres * 2.54); + *yres = (int)(*yres * 2.54); + } + + if (name && namesize > 4) + { + if (*xres == *yres) + snprintf(name, namesize, "%ddpi", *xres); + else + snprintf(name, namesize, "%dx%ddpi", *xres, *yres); + } +} + + /* * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword. */ @@ -2689,6 +5184,8 @@ pwg_unppdize_name(const char *ppd, /* I - PPD keyword */ { char *ptr, /* Pointer into name buffer */ *end; /* End of name buffer */ + int nodash = 1; /* Next char in IPP name cannot be a + dash (first char or after a dash) */ if (_cups_islower(*ppd)) @@ -2700,7 +5197,9 @@ pwg_unppdize_name(const char *ppd, /* I - PPD keyword */ const char *ppdptr; /* Pointer into PPD keyword */ for (ppdptr = ppd + 1; *ppdptr; ppdptr ++) - if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr)) + if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr) || + (*ppdptr == '-' && *(ppdptr - 1) == '-') || + (*ppdptr == '-' && *(ppdptr + 1) == '\0')) break; if (!*ppdptr) @@ -2712,24 +5211,44 @@ pwg_unppdize_name(const char *ppd, /* I - PPD keyword */ for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++) { - if (_cups_isalnum(*ppd) || *ppd == '-') + if (_cups_isalnum(*ppd)) + { *ptr++ = (char)tolower(*ppd & 255); - else if (strchr(dashchars, *ppd)) - *ptr++ = '-'; + nodash = 0; + } + else if (*ppd == '-' || strchr(dashchars, *ppd)) + { + if (nodash == 0) + { + *ptr++ = '-'; + nodash = 1; + } + } else + { *ptr++ = *ppd; + nodash = 0; + } - if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) && - _cups_isupper(ppd[1]) && ptr < end) - *ptr++ = '-'; - else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255)) - *ptr++ = '-'; + if (nodash == 0) + { + if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) && + _cups_isupper(ppd[1]) && ptr < end) + { + *ptr++ = '-'; + nodash = 1; + } + else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255)) + { + *ptr++ = '-'; + nodash = 1; + } + } } + /* Remove trailing dashes */ + while (ptr > name && *(ptr - 1) == '-') + ptr --; + *ptr = '\0'; } - - -/* - * End of "$Id$". - */