--- /dev/null
+/*
+ * "$Id$"
+ *
+ * Option marking routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2007-2008 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * 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/".
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * ppdConflicts() - Check to see if there are any conflicts among
+ * the marked option choices.
+ * ppdInstallableConflict() - Test whether an option choice conflicts with an
+ * installable option.
+ * cupsResolveConflicts() - Resolve conflicts in a marked PPD.
+ * ppd_is_installable() - Determine whether an option is in the
+ * InstallableOptions group.
+ * ppd_load_constraints() - Load constraints from a PPD file.
+ * ppd_test_constraints() - See if any constraints are active.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "ppd-private.h"
+#include "string.h"
+#include "debug.h"
+
+
+/*
+ * Local constants...
+ */
+
+enum
+{
+ _PPD_NORMAL_CONSTRAINTS,
+ _PPD_INSTALLABLE_CONSTRAINTS,
+ _PPD_ALL_CONSTRAINTS
+};
+
+
+/*
+ * Local functions...
+ */
+
+static int ppd_is_installable(ppd_group_t *installable,
+ const char *option);
+static void ppd_load_constraints(ppd_file_t *ppd);
+static cups_array_t *ppd_test_constraints(ppd_file_t *ppd, int num_options,
+ cups_option_t *options,
+ int which);
+
+
+/*
+ * 'ppdConflicts()' - Check to see if there are any conflicts among the
+ * marked option choices.
+ *
+ * The returned value is the same as returned by @link ppdMarkOption@.
+ */
+
+int /* O - Number of conflicts found */
+ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */
+{
+ int i, /* Looping variable */
+ conflicts; /* Number of conflicts */
+ cups_array_t *active; /* Active conflicts */
+ _ppd_cups_uiconsts_t *c; /* Current constraints */
+ _ppd_cups_uiconst_t *cptr; /* Current constraint */
+ ppd_option_t *o; /* Current option */
+
+
+ if (!ppd)
+ return (0);
+
+ /*
+ * Clear all conflicts...
+ */
+
+ conflicts = 0;
+
+ for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
+ o->conflicted = 0;
+
+ /*
+ * Test for conflicts...
+ */
+
+ active = ppd_test_constraints(ppd, 0, NULL, _PPD_ALL_CONSTRAINTS);
+ conflicts = cupsArrayCount(active);
+
+ /*
+ * Loop through all of the UI constraints and flag any options
+ * that conflict...
+ */
+
+ for (c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
+ c;
+ c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
+ {
+ for (i = c->num_constraints, cptr = c->constraints;
+ i > 0;
+ i --, cptr ++)
+ cptr->option->conflicted = 1;
+ }
+
+ cupsArrayDelete(active);
+
+ /*
+ * Return the number of conflicts found...
+ */
+
+ return (conflicts);
+}
+
+
+/*
+ * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
+ * an installable option.
+ *
+ * This function tests whether a particular option choice is available based
+ * on constraints against options in the "InstallableOptions" group.
+ *
+ * @since CUPS 1.4@
+ */
+
+int /* O - 1 if conflicting, 0 if not conflicting */
+ppdInstallableConflict(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *option, /* I - Option */
+ const char *choice) /* I - Choice */
+{
+ cups_array_t *active; /* Active conflicts */
+ cups_option_t test; /* Test against this option */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !option || !choice)
+ return (0);
+
+ /*
+ * Test constraints using the new option...
+ */
+
+ test.name = (char *)option;
+ test.value = (char *)choice;
+ active = ppd_test_constraints(ppd, 1, &test,
+ _PPD_INSTALLABLE_CONSTRAINTS);
+
+ cupsArrayDelete(active);
+
+ return (active != NULL);
+}
+
+
+/*
+ * 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD.
+ *
+ * This function attempts to resolve any conflicts in a marked PPD, returning
+ * a list of option changes that are required to resolve any conflicts. On
+ * input, "num_options" and "options" contain any pending option changes that
+ * have not yet been marked, while "option" and "choice" contain the most recent
+ * selection which may or may not be in "num_options" or "options".
+ *
+ * On successful return, "num_options" and "options" are updated to contain
+ * "option" and "choice" along with any changes required to resolve conflicts
+ * specified in the PPD file. If option conflicts cannot be resolved,
+ * "num_options" and "options" are not changed.
+ *
+ * @code ppdResolveConflicts@ uses one of two sources of option constraint
+ * information. The preferred constraint information is defined by
+ * @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this
+ * case, the PPD file provides constraint resolution actions. In this case,
+ * it should not be possible for @ppdResolveConflicts@ to fail, however it
+ * will do so if a resolver loop is detected.
+ *
+ * The backup constraint infomration is defined by the
+ * @code UIConstraints@ and @code NonUIConstraints@ attributes. These
+ * constraints are resolved algorithmically by selecting the default choice
+ * for the conflicting option. Unfortunately, this method is far more likely
+ * to fail.
+ *
+ * @since CUPS 1.4@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsResolveConflicts(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *option, /* I - Newly selected option or @code NULL@ for none */
+ const char *choice, /* I - Newly selected choice or @code NULL@ for none */
+ int *num_options, /* IO - Number of additional selected options */
+ cups_option_t **options) /* IO - Additional selected options */
+{
+ int i, /* Looping var */
+ num_newopts; /* Number of new options */
+ cups_option_t *newopts; /* New options */
+ cups_array_t *active, /* Active constraints */
+ *pass, /* Resolvers for this pass */
+ *resolvers; /* Resolvers we have used */
+ _ppd_cups_uiconsts_t *consts; /* Current constraints */
+ _ppd_cups_uiconst_t *constptr; /* Current constraint */
+ ppd_attr_t *resolver; /* Current resolver */
+ const char *value; /* Selected option value */
+ int changed; /* Did we change anything? */
+ ppd_choice_t *marked; /* Marked choice */
+ ppd_option_t *ignored; /* Ignored option */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !num_options || !options || (option == NULL) != (choice == NULL))
+ return (0);
+
+ /*
+ * Build a shadow option array...
+ */
+
+ num_newopts = 0;
+ newopts = NULL;
+
+ for (i = 0; i < *num_options; i ++)
+ num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value,
+ num_newopts, &newopts);
+ if (option)
+ num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
+
+ /*
+ * Loop until we have no conflicts...
+ */
+
+ cupsArraySave(ppd->sorted_attrs);
+
+ resolvers = NULL;
+ pass = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
+
+ while ((active = ppd_test_constraints(ppd, num_newopts, newopts,
+ _PPD_ALL_CONSTRAINTS)) != NULL)
+ {
+ if (!resolvers)
+ resolvers = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
+
+ for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0;
+ consts;
+ consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
+ {
+ if (consts->resolver[0])
+ {
+ /*
+ * Look up the resolver...
+ */
+
+ if (cupsArrayFind(pass, consts->resolver))
+ continue; /* Already applied this resolver... */
+
+ if (cupsArrayFind(resolvers, consts->resolver))
+ {
+ /*
+ * Resolver loop!
+ */
+
+ DEBUG_printf(("ppdResolveConflicts: Resolver loop with %s!\n",
+ consts->resolver));
+ goto error;
+ }
+
+ if ((resolver = ppdFindAttr(ppd, "cupsUIResolver",
+ consts->resolver)) == NULL)
+ {
+ DEBUG_printf(("ppdResolveConflicts: Resolver %s not found!\n",
+ consts->resolver));
+ goto error;
+ }
+
+ if (!resolver->value)
+ {
+ DEBUG_printf(("ppdResolveConflicts: Resolver %s has no value!\n",
+ consts->resolver));
+ goto error;
+ }
+
+ /*
+ * Add the options from the resolver...
+ */
+
+ cupsArrayAdd(pass, consts->resolver);
+ cupsArrayAdd(resolvers, consts->resolver);
+
+ num_newopts = _ppdParseOptions(resolver->value, num_newopts, &newopts);
+ changed = 1;
+ }
+ else
+ {
+ /*
+ * Try resolving by choosing the default values for non-installable
+ * options...
+ */
+
+ for (i = consts->num_constraints, constptr = consts->constraints,
+ ignored = NULL;
+ i > 0;
+ i --, constptr ++)
+ {
+ if (constptr->installable ||
+ !strcasecmp(constptr->option->keyword, "PageSize") ||
+ !strcasecmp(constptr->option->keyword, "PageRegion"))
+ continue;
+
+ if (option && !strcasecmp(constptr->option->keyword, option))
+ {
+ ignored = constptr->option;
+ continue;
+ }
+
+ if ((value = cupsGetOption(constptr->option->keyword, num_newopts,
+ newopts)) == NULL)
+ {
+ marked = ppdFindMarkedChoice(ppd, constptr->option->keyword);
+ value = marked ? marked->choice : "";
+ }
+
+ if (strcasecmp(value, constptr->option->defchoice))
+ {
+ num_newopts = cupsAddOption(constptr->option->keyword,
+ constptr->option->defchoice,
+ num_newopts, &newopts);
+ changed = 1;
+ }
+ }
+
+ if (ignored && !changed)
+ {
+ /*
+ * No choice, have to back out this selection...
+ */
+
+ if ((value = cupsGetOption(ignored->keyword, num_newopts,
+ newopts)) == NULL)
+ {
+ marked = ppdFindMarkedChoice(ppd, ignored->keyword);
+ value = marked ? marked->choice : "";
+ }
+
+ if (strcasecmp(value, ignored->defchoice))
+ {
+ num_newopts = cupsAddOption(ignored->keyword, ignored->defchoice,
+ num_newopts, &newopts);
+ changed = 1;
+ }
+ }
+ }
+
+ if (!changed)
+ {
+ DEBUG_puts("ppdResolveConflicts: Unable to automatically resolve "
+ "constraint!");
+ goto error;
+ }
+ }
+
+ cupsArrayClear(pass);
+ cupsArrayDelete(active);
+ }
+
+ /*
+ * Free either the old or the new options depending on whether we had to
+ * apply any resolvers...
+ */
+
+ if (resolvers)
+ {
+ cupsFreeOptions(*num_options, *options);
+ *num_options = num_newopts;
+ *options = newopts;
+ }
+ else
+ cupsFreeOptions(num_newopts, newopts);
+
+ cupsArrayDelete(pass);
+ cupsArrayDelete(resolvers);
+
+ cupsArrayRestore(ppd->sorted_attrs);
+
+ return (1);
+
+ /*
+ * If we get here, we failed to resolve...
+ */
+
+ error:
+
+ cupsFreeOptions(num_newopts, newopts);
+
+ cupsArrayDelete(pass);
+ cupsArrayDelete(resolvers);
+
+ cupsArrayRestore(ppd->sorted_attrs);
+
+ return (0);
+}
+
+
+/*
+ * 'ppd_is_installable()' - Determine whether an option is in the
+ * InstallableOptions group.
+ */
+
+static int /* O - 1 if installable, 0 if normal */
+ppd_is_installable(
+ ppd_group_t *installable, /* I - InstallableOptions group */
+ const char *name) /* I - Option name */
+{
+ if (installable)
+ {
+ int i; /* Looping var */
+ ppd_option_t *option; /* Current option */
+
+
+ for (i = installable->num_options, option = installable->options;
+ i > 0;
+ i --, option ++)
+ if (!strcasecmp(option->keyword, name))
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'ppd_load_constraints()' - Load constraints from a PPD file.
+ */
+
+static void
+ppd_load_constraints(ppd_file_t *ppd) /* I - PPD file */
+{
+ int i; /* Looping var */
+ ppd_const_t *oldconst; /* Current UIConstraints data */
+ ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */
+ _ppd_cups_uiconsts_t *consts; /* Current cupsUIConstraints data */
+ _ppd_cups_uiconst_t *constptr; /* Current constraint */
+ ppd_group_t *installable; /* Installable options group */
+ const char *vptr; /* Pointer into constraint value */
+ char option[PPD_MAX_NAME], /* Option name/MainKeyword */
+ choice[PPD_MAX_NAME], /* Choice/OptionKeyword */
+ *ptr; /* Pointer into option or choice */
+
+
+ /*
+ * Create an array to hold the constraint data...
+ */
+
+ ppd->cups_uiconstraints = cupsArrayNew(NULL, NULL);
+
+ /*
+ * Find the installable options group if it exists...
+ */
+
+ for (i = ppd->num_groups, installable = ppd->groups;
+ i > 0;
+ i --, installable ++)
+ if (!strcasecmp(installable->name, "InstallableOptions"))
+ break;
+
+ if (i <= 0)
+ installable = NULL;
+
+ /*
+ * See what kind of constraint data we have in the PPD...
+ */
+
+ if ((constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL)) != NULL)
+ {
+ /*
+ * Load new-style cupsUIConstraints data...
+ */
+
+ for (; constattr;
+ constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
+ {
+ if (!constattr->value)
+ {
+ DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
+ continue;
+ }
+
+ for (i = 0, vptr = strchr(constattr->value, '*');
+ vptr;
+ i ++, vptr = strchr(vptr + 1, '*'));
+
+ if (i == 0)
+ {
+ DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
+ continue;
+ }
+
+ if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
+ {
+ DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
+ "cupsUIConstraints!");
+ return;
+ }
+
+ if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL)
+ {
+ free(consts);
+ DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
+ "cupsUIConstraints!");
+ return;
+ }
+
+ consts->num_constraints = i;
+ consts->constraints = constptr;
+
+ strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver));
+
+ for (i = 0, vptr = strchr(constattr->value, '*');
+ vptr;
+ i ++, vptr = strchr(vptr + 1, '*'), constptr ++)
+ {
+ /*
+ * Extract "*Option Choice" or just "*Option"...
+ */
+
+ for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++)
+ if (ptr < (option + sizeof(option) - 1))
+ *ptr++ = *vptr;
+
+ *ptr = '\0';
+
+ while (isspace(*vptr & 255))
+ vptr ++;
+
+ if (*vptr == '*')
+ {
+ vptr --;
+ choice[0] = '\0';
+ }
+ else
+ {
+ for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++)
+ if (ptr < (choice + sizeof(choice) - 1))
+ *ptr++ = *vptr;
+
+ *ptr = '\0';
+ }
+
+ if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
+ {
+ _cups_strcpy(option, option + 6);
+ strcpy(choice, "Custom");
+ }
+
+ constptr->option = ppdFindOption(ppd, option);
+ constptr->choice = ppdFindChoice(constptr->option, choice);
+ constptr->installable = ppd_is_installable(installable, option);
+ consts->installable |= constptr->installable;
+
+ if (!constptr->option)
+ {
+ DEBUG_printf(("ppd_load_constraints: Unknown option %s!\n", option));
+ break;
+ }
+ }
+
+ if (!vptr)
+ cupsArrayAdd(ppd->cups_uiconstraints, consts);
+ else
+ {
+ free(consts->constraints);
+ free(consts);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Load old-style [Non]UIConstraints data...
+ */
+
+ for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++)
+ {
+ /*
+ * Weed out nearby duplicates, since the PPD spec requires that you
+ * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
+ */
+
+ if (i > 1 &&
+ !strcasecmp(oldconst[0].option1, oldconst[1].option2) &&
+ !strcasecmp(oldconst[0].choice1, oldconst[1].choice2) &&
+ !strcasecmp(oldconst[0].option2, oldconst[1].option1) &&
+ !strcasecmp(oldconst[0].choice2, oldconst[1].choice1))
+ continue;
+
+ /*
+ * Allocate memory...
+ */
+
+ if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
+ {
+ DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
+ "UIConstraints!");
+ return;
+ }
+
+ if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL)
+ {
+ free(consts);
+ DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
+ "UIConstraints!");
+ return;
+ }
+
+ /*
+ * Fill in the information...
+ */
+
+ consts->num_constraints = 2;
+ consts->constraints = constptr;
+
+ if (!strncasecmp(oldconst->option1, "Custom", 6) &&
+ !strcasecmp(oldconst->choice1, "True"))
+ {
+ constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6);
+ constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom");
+ constptr[0].installable = 0;
+ }
+ else
+ {
+ constptr[0].option = ppdFindOption(ppd, oldconst->option1);
+ constptr[0].choice = ppdFindChoice(constptr[0].option,
+ oldconst->choice1);
+ constptr[0].installable = ppd_is_installable(installable,
+ oldconst->option1);
+ }
+
+ if (!constptr[0].option)
+ {
+ DEBUG_printf(("ppd_load_constraints: Unknown option %s!\n",
+ oldconst->option1));
+ free(consts->constraints);
+ free(consts);
+ continue;
+ }
+
+ if (!strncasecmp(oldconst->option2, "Custom", 6) &&
+ !strcasecmp(oldconst->choice2, "True"))
+ {
+ constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6);
+ constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom");
+ constptr[1].installable = 0;
+ }
+ else
+ {
+ constptr[1].option = ppdFindOption(ppd, oldconst->option2);
+ constptr[1].choice = ppdFindChoice(constptr[1].option,
+ oldconst->choice2);
+ constptr[1].installable = ppd_is_installable(installable,
+ oldconst->option2);
+ }
+
+ if (!constptr->option)
+ {
+ DEBUG_printf(("ppd_load_constraints: Unknown option %s!\n",
+ oldconst->option2));
+ free(consts->constraints);
+ free(consts);
+ continue;
+ }
+
+ consts->installable = constptr[0].installable || constptr[1].installable;
+
+ /*
+ * Add it to the constraints array...
+ */
+
+ cupsArrayAdd(ppd->cups_uiconstraints, consts);
+ }
+ }
+}
+
+
+
+/*
+ * 'ppd_test_constraints()' - See if any constraints are active.
+ */
+
+static cups_array_t * /* O - Array of active constraints */
+ppd_test_constraints(
+ ppd_file_t *ppd, /* I - PPD file */
+ int num_options, /* I - Number of additional options */
+ cups_option_t *options, /* I - Additional options */
+ int which) /* I - Which constraints to test */
+{
+ int i; /* Looping var */
+ _ppd_cups_uiconsts_t *consts; /* Current constraints */
+ _ppd_cups_uiconst_t *constptr; /* Current constraint */
+ ppd_choice_t key, /* Search key */
+ *marked; /* Marked choice */
+ cups_array_t *active = NULL; /* Active constraints */
+ const char *value; /* Current value */
+
+
+ if (!ppd->cups_uiconstraints)
+ ppd_load_constraints(ppd);
+
+ cupsArraySave(ppd->marked);
+
+ for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
+ consts;
+ consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
+ {
+ if (which != _PPD_ALL_CONSTRAINTS && which != consts->installable)
+ continue;
+
+ for (i = consts->num_constraints, constptr = consts->constraints;
+ i > 0;
+ i --, constptr ++)
+ {
+ if (constptr->choice &&
+ (!strcasecmp(constptr->option->keyword, "PageSize") ||
+ !strcasecmp(constptr->option->keyword, "PageRegion")))
+ {
+ /*
+ * PageSize and PageRegion are used depending on the selected input slot
+ * and manual feed mode. Validate against the selected page size instead
+ * of an individual option...
+ */
+
+ if ((value = cupsGetOption("PageSize", num_options, options)) == NULL)
+ if ((value = cupsGetOption("PageRegion", num_options,
+ options)) == NULL)
+ if ((value = cupsGetOption("media", num_options, options)) == NULL)
+ {
+ ppd_size_t *size = ppdPageSize(ppd, NULL);
+
+ if (size)
+ value = size->name;
+ }
+
+ if (!value || strcasecmp(value, constptr->choice->choice))
+ break;
+ }
+ else if (constptr->choice)
+ {
+ if ((value = cupsGetOption(constptr->option->keyword, num_options,
+ options)) != NULL)
+ {
+ if (strcasecmp(value, constptr->choice->choice))
+ break;
+ }
+ else if (!constptr->choice->marked)
+ break;
+ }
+ else if ((value = cupsGetOption(constptr->option->keyword, num_options,
+ options)) != NULL)
+ {
+ if (!strcasecmp(value, "None") || !strcasecmp(value, "Off") ||
+ !strcasecmp(value, "False"))
+ break;
+ }
+ else
+ {
+ key.option = constptr->option;
+
+ if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key))
+ != NULL &&
+ (!strcasecmp(marked->choice, "None") ||
+ !strcasecmp(marked->choice, "Off") ||
+ !strcasecmp(marked->choice, "False")))
+ break;
+ }
+ }
+
+ if (i <= 0)
+ {
+ if (!active)
+ active = cupsArrayNew(NULL, NULL);
+
+ cupsArrayAdd(active, consts);
+ }
+ }
+
+ cupsArrayRestore(ppd->marked);
+
+ return (active);
+}
+
+
+/*
+ * End of "$Id$".
+ */
--- /dev/null
+*PPD-Adobe: "4.3"
+*%
+*% "$Id$"
+*%
+*% Test PPD file #2 for the Common UNIX Printing System (CUPS).
+*%
+*% This file is used to test the CUPS PPD API functions and cannot be
+*% used with any known printers. Look at the PPD files in the "ppd"
+*% subdirectory as well as the CUPS web site for working PPD files.
+*%
+*% If you are a PPD file developer, consider using the PPD compiler (ppdc)
+*% to create your PPD files - not only will it save you time, it produces
+*% consistently high-quality files.
+*%
+*% Copyright 2007-2008 by Apple Inc.
+*% Copyright 2002-2006 by Easy Software Products.
+*%
+*% 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/".
+*FormatVersion: "4.3"
+*FileVersion: "1.3"
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*PCFileName: "TEST.PPD"
+*Manufacturer: "ESP"
+*Product: "(Test2)"
+*cupsVersion: 1.4
+*ModelName: "Test2"
+*ShortNickName: "Test2"
+*NickName: "Test2 for CUPS"
+*PSVersion: "(3010.000) 0"
+*LanguageLevel: "3"
+*ColorDevice: True
+*DefaultColorSpace: RGB
+*FileSystem: False
+*Throughput: "1"
+*LandscapeOrientation: Plus90
+*TTRasterizer: Type42
+
+*% These constraints are used to test ppdConflicts() and ppdResolveConflicts()
+*cupsUIConstraints envelope: "*PageSize Letter *InputSlot Envelope"
+*cupsUIConstraints envelope: "*PageRegion Letter *InputSlot Envelope"
+*cupsUIResolver envelope: "*InputSlot Manual"
+
+*cupsUIConstraints envphoto: "*PageSize Env10 *InputSlot Envelope *Quality Photo"
+*cupsUIConstraints envphoto: "*PageRegion Env10 *InputSlot Envelope *Quality Photo"
+*cupsUIResolver envphoto: "*Quality Normal"
+
+*% This constraint is used to test ppdInstallableConflict()
+*cupsUIConstraints: "*Duplex *InstalledDuplexer False"
+
+*% These constraints are used to test the loop detection code in ppdResolveConflicts()
+*cupsUIConstraints loop1: "*PageSize A4 *Quality Photo"
+*cupsUIResolver loop1: "*Quality Normal"
+*cupsUIConstraints loop2: "*PageSize A4 *Quality Normal"
+*cupsUIResolver loop2: "*PageSize Letter *Quality Draft"
+*cupsUIConstraints loop3: "*PageSize Letter *Quality Draft"
+*cupsUIResolver loop3: "*PageSize A4 *Quality Photo"
+
+*% For PageSize, we have put all of the translations in-line...
+*OpenUI *PageSize/Page Size: PickOne
+*fr.Translation PageSize/French Page Size: ""
+*fr_CA.Translation PageSize/French Canadian Page Size: ""
+*OrderDependency: 10 AnySetup *PageSize
+*DefaultPageSize: Letter
+*PageSize Letter/US Letter: "PageSize=Letter"
+*fr.PageSize Letter/French US Letter: ""
+*fr_CA.PageSize Letter/French Canadian US Letter: ""
+*PageSize A4/A4: "PageSize=A4"
+*fr.PageSize A4/French A4: ""
+*fr_CA.PageSize A4/French Canadian A4: ""
+*PageSize Env10/#10 Envelope: "PageSize=Env10"
+*fr.PageSize Env10/French #10 Envelope: ""
+*fr_CA.PageSize Env10/French Canadian #10 Envelope: ""
+*CloseUI: *PageSize
+
+*% For PageRegion, we have separated the translations...
+*OpenUI *PageRegion/Page Region: PickOne
+*OrderDependency: 10 AnySetup *PageRegion
+*DefaultPageRegion: Letter
+*PageRegion Letter/US Letter: "PageRegion=Letter"
+*PageRegion A4/A4: "PageRegion=A4"
+*PageRegion Env10/#10 Envelope: "PageRegion=Env10"
+*CloseUI: *PageRegion
+
+*fr.Translation PageRegion/French Page Region: ""
+*fr.PageRegion Letter/French US Letter: ""
+*fr.PageRegion A4/French A4: ""
+*fr.PageRegion Env10/French #10 Envelope: ""
+
+*fr_CA.Translation PageRegion/French Canadian Page Region: ""
+*fr_CA.PageRegion Letter/French Canadian US Letter: ""
+*fr_CA.PageRegion A4/French Canadian A4: ""
+*fr_CA.PageRegion Env10/French Canadian #10 Envelope: ""
+
+*DefaultImageableArea: Letter
+*ImageableArea Letter: "18 36 594 756"
+*ImageableArea A4: "18 36 577 806"
+*ImageableArea Env10: "18 36 279 648"
+
+*DefaultPaperDimension: Letter
+*PaperDimension Letter: "612 792"
+*PaperDimension A4: "595 842"
+*PaperDimension Env10: "297 684"
+
+*% Custom page size support
+*HWMargins: 0 0 0 0
+*NonUIOrderDependency: 100 AnySetup *CustomPageSize True
+*CustomPageSize True/Custom Page Size: "PageSize=Custom"
+*ParamCustomPageSize Width: 1 points 36 1080
+*ParamCustomPageSize Height: 2 points 36 86400
+*ParamCustomPageSize WidthOffset/Width Offset: 3 points 0 0
+*ParamCustomPageSize HeightOffset/Height Offset: 4 points 0 0
+*ParamCustomPageSize Orientation: 5 int 0 0
+
+*OpenUI *InputSlot/Input Slot: PickOne
+*OrderDependency: 20 AnySetup *InputSlot
+*DefaultInputSlot: Tray
+*InputSlot Tray/Tray: "InputSlot=Tray"
+*InputSlot Manual/Manual Feed: "InputSlot=Manual"
+*InputSlot Envelope/Envelope Feed: "InputSlot=Envelope"
+*CloseUI: *InputSlot
+
+*OpenUI *Quality/Output Mode: PickOne
+*OrderDependency: 20 AnySetup *Quality
+*DefaultQuality: Normal
+*Quality Draft: "Quality=Draft"
+*Quality Normal: "Quality=Normal"
+*Quality Photo: "Quality=Photo"
+*CloseUI: *Quality
+
+*OpenUI *Duplex/2-Sided Printing: PickOne
+*OrderDependency: 10 DocumentSetup *Duplex
+*DefaultDuplex: None
+*Duplex None/Off: "Duplex=None"
+*Duplex DuplexNoTumble/Long Edge: "Duplex=DuplexNoTumble"
+*Duplex DuplexTumble/Short Edge: "Duplex=DuplexTumble"
+*CloseUI: *Duplex
+
+*% Installable option...
+*OpenGroup: InstallableOptions/Installable Options
+*OpenUI InstalledDuplexer/Duplexer Installed: Boolean
+*DefaultInstalledDuplexer: False
+*InstalledDuplexer False: ""
+*InstalledDuplexer True: ""
+*CloseUI: *InstalledDuplexer
+*CloseGroup: InstallableOptions
+
+*% Custom options...
+*OpenGroup: Extended/Extended Options
+
+*OpenUI IntOption/Integer: PickOne
+*OrderDependency: 30 AnySetup *IntOption
+*DefaultIntOption: None
+*IntOption None: ""
+*IntOption 1: "IntOption=1"
+*IntOption 2: "IntOption=2"
+*IntOption 3: "IntOption=3"
+*CloseUI: *IntOption
+
+*CustomIntOption True/Custom Integer: "IntOption=Custom"
+*ParamCustomIntOption Integer: 1 int -100 100
+
+*OpenUI StringOption/String: PickOne
+*OrderDependency: 40 AnySetup *StringOption
+*DefaultStringOption: None
+*StringOption None: ""
+*StringOption foo: "StringOption=foo"
+*StringOption bar: "StringOption=bar"
+*CloseUI: *StringOption
+
+*CustomStringOption True/Custom String: "StringOption=Custom"
+*ParamCustomStringOption String: 1 string 1 10
+
+*CloseGroup: Extended
+
+*% IPP reasons for ppdLocalizeIPPReason tests
+*cupsIPPReason foo/Foo Reason: "http://foo/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/foo/bar.html"
+*End
+*fr.cupsIPPReason foo/La Foo Reason: "text:La%20Long%20
+text:Foo%20Reason
+http://foo/fr/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/fr/foo/bar.html"
+*End
+*zh_TW.cupsIPPReason foo/Number 1 Foo Reason: "text:Number%201%20
+text:Foo%20Reason
+http://foo/zh_TW/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/zh_TW/foo/bar.html"
+*End
+*zh.cupsIPPReason foo/Number 2 Foo Reason: "text:Number%202%20
+text:Foo%20Reason
+http://foo/zh/bar.html
+help:anchor='foo'%20bookID=Vendor%20Help
+/help/zh/foo/bar.html"
+*End
+
+*% Marker names for ppdLocalizeMarkerName tests
+*cupsMarkerName cyan/Cyan Toner: ""
+*fr.cupsMarkerName cyan/La Toner Cyan: ""
+*zh_TW.cupsMarkerName cyan/Number 1 Cyan Toner: ""
+*zh.cupsMarkerName cyan/Number 2 Cyan Toner: ""
+
+*DefaultFont: Courier
+*Font AvantGarde-Book: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-BookOblique: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-Demi: Standard "(001.007S)" Standard ROM
+*Font AvantGarde-DemiOblique: Standard "(001.007S)" Standard ROM
+*Font Bookman-Demi: Standard "(001.004S)" Standard ROM
+*Font Bookman-DemiItalic: Standard "(001.004S)" Standard ROM
+*Font Bookman-Light: Standard "(001.004S)" Standard ROM
+*Font Bookman-LightItalic: Standard "(001.004S)" Standard ROM
+*Font Courier: Standard "(002.004S)" Standard ROM
+*Font Courier-Bold: Standard "(002.004S)" Standard ROM
+*Font Courier-BoldOblique: Standard "(002.004S)" Standard ROM
+*Font Courier-Oblique: Standard "(002.004S)" Standard ROM
+*Font Helvetica: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Narrow-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-Oblique: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Oblique: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Bold: Standard "(001.009S)" Standard ROM
+*Font NewCenturySchlbk-BoldItalic: Standard "(001.007S)" Standard ROM
+*Font NewCenturySchlbk-Italic: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Roman: Standard "(001.007S)" Standard ROM
+*Font Palatino-Bold: Standard "(001.005S)" Standard ROM
+*Font Palatino-BoldItalic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Italic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Roman: Standard "(001.005S)" Standard ROM
+*Font Symbol: Special "(001.007S)" Special ROM
+*Font Times-Bold: Standard "(001.007S)" Standard ROM
+*Font Times-BoldItalic: Standard "(001.009S)" Standard ROM
+*Font Times-Italic: Standard "(001.007S)" Standard ROM
+*Font Times-Roman: Standard "(001.007S)" Standard ROM
+*Font ZapfChancery-MediumItalic: Standard "(001.007S)" Standard ROM
+*Font ZapfDingbats: Special "(001.004S)" Standard ROM
+*%
+*% End of "$Id$".
+*%