2 * Destination option/media support for CUPS.
4 * Copyright 2012-2016 by Apple Inc.
6 * These coded instructions, statements, and computer programs are the
7 * property of Apple Inc. and are protected by Federal copyright
8 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
9 * which should have been included with this file. If this file is
10 * missing or damaged, see the license at "http://www.cups.org/".
12 * This file is subject to the Apple OS-Developed Software exception.
16 * Include necessary headers...
19 #include "cups-private.h"
26 #define _CUPS_MEDIA_READY_TTL 30 /* Life of xxx-ready values */
33 static void cups_add_dconstres(cups_array_t *a, ipp_t *collection);
34 static int cups_compare_dconstres(_cups_dconstres_t *a,
35 _cups_dconstres_t *b);
36 static int cups_compare_media_db(_cups_media_db_t *a,
38 static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb);
39 static void cups_create_cached(http_t *http, cups_dinfo_t *dinfo,
41 static void cups_create_constraints(cups_dinfo_t *dinfo);
42 static void cups_create_defaults(cups_dinfo_t *dinfo);
43 static void cups_create_media_db(cups_dinfo_t *dinfo,
45 static void cups_free_media_db(_cups_media_db_t *mdb);
46 static int cups_get_media_db(http_t *http, cups_dinfo_t *dinfo,
47 pwg_media_t *pwg, unsigned flags,
49 static int cups_is_close_media_db(_cups_media_db_t *a,
51 static cups_array_t *cups_test_constraints(cups_dinfo_t *dinfo,
52 const char *new_option,
53 const char *new_value,
55 cups_option_t *options,
57 cups_option_t **conflicts);
58 static void cups_update_ready(http_t *http, cups_dinfo_t *dinfo);
62 * 'cupsCheckDestSupported()' - Check that the option and value are supported
65 * Returns 1 if supported, 0 otherwise.
67 * @since CUPS 1.6/macOS 10.8@
70 int /* O - 1 if supported, 0 otherwise */
71 cupsCheckDestSupported(
72 http_t *http, /* I - Connection to destination */
73 cups_dest_t *dest, /* I - Destination */
74 cups_dinfo_t *dinfo, /* I - Destination information */
75 const char *option, /* I - Option */
76 const char *value) /* I - Value */
78 int i; /* Looping var */
79 char temp[1024]; /* Temporary string */
80 int int_value; /* Integer value */
81 int xres_value, /* Horizontal resolution */
82 yres_value; /* Vertical resolution */
83 ipp_res_t units_value; /* Resolution units */
84 ipp_attribute_t *attr; /* Attribute */
85 _ipp_value_t *attrval; /* Current attribute value */
89 * Range check input...
92 if (!http || !dest || !dinfo || !option || !value)
96 * Lookup the attribute...
99 if (strstr(option, "-supported"))
100 attr = ippFindAttribute(dinfo->attrs, option, IPP_TAG_ZERO);
103 snprintf(temp, sizeof(temp), "%s-supported", option);
104 attr = ippFindAttribute(dinfo->attrs, temp, IPP_TAG_ZERO);
114 if (!strcmp(option, "media") && !strncmp(value, "custom_", 7))
117 * Check range of custom media sizes...
120 pwg_media_t *pwg; /* Current PWG media size info */
121 int min_width, /* Minimum width */
122 min_length, /* Minimum length */
123 max_width, /* Maximum width */
124 max_length; /* Maximum length */
127 * Get the minimum and maximum size...
130 min_width = min_length = INT_MAX;
131 max_width = max_length = 0;
133 for (i = attr->num_values, attrval = attr->values;
137 if (!strncmp(attrval->string.text, "custom_min_", 11) &&
138 (pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
140 min_width = pwg->width;
141 min_length = pwg->length;
143 else if (!strncmp(attrval->string.text, "custom_max_", 11) &&
144 (pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
146 max_width = pwg->width;
147 max_length = pwg->length;
155 if (min_width < INT_MAX && max_width > 0 &&
156 (pwg = pwgMediaForPWG(value)) != NULL &&
157 pwg->width >= min_width && pwg->width <= max_width &&
158 pwg->length >= min_length && pwg->length <= max_length)
164 * Check literal values...
167 switch (attr->value_tag)
169 case IPP_TAG_INTEGER :
171 int_value = atoi(value);
173 for (i = 0; i < attr->num_values; i ++)
174 if (attr->values[i].integer == int_value)
178 case IPP_TAG_BOOLEAN :
179 return (attr->values[0].boolean);
182 int_value = atoi(value);
184 for (i = 0; i < attr->num_values; i ++)
185 if (int_value >= attr->values[i].range.lower &&
186 int_value <= attr->values[i].range.upper)
190 case IPP_TAG_RESOLUTION :
191 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
193 if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
196 yres_value = xres_value;
199 if (!strcmp(temp, "dpi"))
200 units_value = IPP_RES_PER_INCH;
201 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
202 units_value = IPP_RES_PER_CM;
206 for (i = attr->num_values, attrval = attr->values;
210 if (attrval->resolution.xres == xres_value &&
211 attrval->resolution.yres == yres_value &&
212 attrval->resolution.units == units_value)
219 case IPP_TAG_KEYWORD :
220 case IPP_TAG_CHARSET :
222 case IPP_TAG_URISCHEME :
223 case IPP_TAG_MIMETYPE :
224 case IPP_TAG_LANGUAGE :
225 case IPP_TAG_TEXTLANG :
226 case IPP_TAG_NAMELANG :
227 for (i = 0; i < attr->num_values; i ++)
228 if (!strcmp(attr->values[i].string.text, value))
238 * If we get there the option+value is not supported...
246 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
249 * "num_options" and "options" represent the currently selected options by the
250 * user. "new_option" and "new_value" are the setting the user has just
253 * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
254 * there was an unrecoverable error such as a resolver loop.
256 * If "num_conflicts" and "conflicts" are not @code NULL@, they are set to
257 * contain the list of conflicting option/value pairs. Similarly, if
258 * "num_resolved" and "resolved" are not @code NULL@ they will be set to the
259 * list of changes needed to resolve the conflict.
261 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
262 * to 0 and @code NULL@, respectively, then the conflict cannot be resolved.
264 * @since CUPS 1.6/macOS 10.8@
267 int /* O - 1 if there is a conflict, 0 if none, -1 on error */
268 cupsCopyDestConflicts(
269 http_t *http, /* I - Connection to destination */
270 cups_dest_t *dest, /* I - Destination */
271 cups_dinfo_t *dinfo, /* I - Destination information */
272 int num_options, /* I - Number of current options */
273 cups_option_t *options, /* I - Current options */
274 const char *new_option, /* I - New option */
275 const char *new_value, /* I - New value */
276 int *num_conflicts, /* O - Number of conflicting options */
277 cups_option_t **conflicts, /* O - Conflicting options */
278 int *num_resolved, /* O - Number of options to resolve */
279 cups_option_t **resolved) /* O - Resolved options */
281 int i, /* Looping var */
282 have_conflicts = 0, /* Do we have conflicts? */
283 changed, /* Did we change something? */
284 tries, /* Number of tries for resolution */
285 num_myconf = 0, /* My number of conflicting options */
286 num_myres = 0; /* My number of resolved options */
287 cups_option_t *myconf = NULL, /* My conflicting options */
288 *myres = NULL, /* My resolved options */
289 *myoption, /* My current option */
290 *option; /* Current option */
291 cups_array_t *active = NULL, /* Active conflicts */
292 *pass = NULL, /* Resolvers for this pass */
293 *resolvers = NULL, /* Resolvers we have used */
294 *test; /* Test array for conflicts */
295 _cups_dconstres_t *c, /* Current constraint */
296 *r; /* Current resolver */
297 ipp_attribute_t *attr; /* Current attribute */
298 char value[2048]; /* Current attribute value as string */
299 const char *myvalue; /* Current value of an option */
303 * Clear returned values...
319 * Range check input...
322 if (!http || !dest || !dinfo ||
323 (num_conflicts != NULL) != (conflicts != NULL) ||
324 (num_resolved != NULL) != (resolved != NULL))
328 * Load constraints as needed...
331 if (!dinfo->constraints)
332 cups_create_constraints(dinfo);
334 if (cupsArrayCount(dinfo->constraints) == 0)
337 if (!dinfo->num_defaults)
338 cups_create_defaults(dinfo);
341 * If we are resolving, create a shadow array...
346 for (i = num_options, option = options; i > 0; i --, option ++)
347 num_myres = cupsAddOption(option->name, option->value, num_myres, &myres);
349 if (new_option && new_value)
350 num_myres = cupsAddOption(new_option, new_value, num_myres, &myres);
354 num_myres = num_options;
359 * Check for any conflicts...
363 pass = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
365 for (tries = 0; tries < 100; tries ++)
368 * Check for any conflicts...
371 if (num_conflicts || num_resolved)
373 cupsFreeOptions(num_myconf, myconf);
377 active = cups_test_constraints(dinfo, new_option, new_value,
378 num_myres, myres, &num_myconf,
382 active = cups_test_constraints(dinfo, new_option, new_value, num_myres,
385 have_conflicts = (active != NULL);
387 if (!active || !num_resolved)
388 break; /* All done */
391 * Scan the constraints that were triggered to apply resolvers...
395 resolvers = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
397 for (c = (_cups_dconstres_t *)cupsArrayFirst(active), changed = 0;
399 c = (_cups_dconstres_t *)cupsArrayNext(active))
401 if (cupsArrayFind(pass, c))
402 continue; /* Already applied this resolver... */
404 if (cupsArrayFind(resolvers, c))
406 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
412 if ((r = cupsArrayFind(dinfo->resolvers, c)) == NULL)
414 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
421 * Add the options from the resolver...
424 cupsArrayAdd(pass, r);
425 cupsArrayAdd(resolvers, r);
427 for (attr = ippFirstAttribute(r->collection);
429 attr = ippNextAttribute(r->collection))
431 if (new_option && !strcmp(attr->name, new_option))
432 continue; /* Ignore this if we just changed it */
434 if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
435 continue; /* Ignore if the value is too long */
437 if ((test = cups_test_constraints(dinfo, attr->name, value, num_myres,
438 myres, NULL, NULL)) == NULL)
441 * That worked, flag it...
447 cupsArrayDelete(test);
450 * Add the option/value from the resolver regardless of whether it
451 * worked; this makes sure that we can cascade several changes to
452 * make things resolve...
455 num_myres = cupsAddOption(attr->name, value, num_myres, &myres);
461 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
466 cupsArrayClear(pass);
468 cupsArrayDelete(active);
474 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
480 * Copy resolved options as needed...
485 for (i = num_myres, myoption = myres; i > 0; i --, myoption ++)
487 if ((myvalue = cupsGetOption(myoption->name, num_options,
489 strcmp(myvalue, myoption->value))
491 if (new_option && !strcmp(new_option, myoption->name) &&
492 new_value && !strcmp(new_value, myoption->value))
495 *num_resolved = cupsAddOption(myoption->name, myoption->value,
496 *num_resolved, resolved);
507 cupsArrayDelete(active);
508 cupsArrayDelete(pass);
509 cupsArrayDelete(resolvers);
514 * Free shadow copy of options...
517 cupsFreeOptions(num_myres, myres);
523 * Return conflicting options to caller...
526 *num_conflicts = num_myconf;
532 * Free conflicting options...
535 cupsFreeOptions(num_myconf, myconf);
538 return (have_conflicts);
543 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
546 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
547 * value. @code NULL@ is returned on error.
549 * @since CUPS 1.6/macOS 10.8@
552 cups_dinfo_t * /* O - Destination information */
554 http_t *http, /* I - Connection to destination */
555 cups_dest_t *dest) /* I - Destination */
557 cups_dinfo_t *dinfo; /* Destination information */
558 ipp_t *request, /* Get-Printer-Attributes request */
559 *response; /* Supported attributes */
560 int tries, /* Number of tries so far */
561 delay, /* Current retry delay */
562 prev_delay; /* Next retry delay */
563 const char *uri; /* Printer URI */
564 char resource[1024]; /* Resource path */
565 int version; /* IPP version */
566 ipp_status_t status; /* Status of request */
567 static const char * const requested_attrs[] =
568 { /* Requested attributes */
570 "media-col-database",
571 "printer-description"
575 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", (void *)http, (void *)dest, dest ? dest->name : ""));
578 * Range check input...
585 * Get the printer URI and resource path...
588 if ((uri = _cupsGetDestResource(dest, resource, sizeof(resource))) == NULL)
592 * Get the supported attributes...
603 * Send a Get-Printer-Attributes request...
606 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
607 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
609 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
610 "requesting-user-name", NULL, cupsUser());
611 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
612 "requested-attributes",
613 (int)(sizeof(requested_attrs) / sizeof(requested_attrs[0])),
614 NULL, requested_attrs);
615 response = cupsDoRequest(http, request, resource);
616 status = cupsLastError();
618 if (status > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
620 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
621 "returned %s (%s)", dest->name, ippErrorString(status),
622 cupsLastErrorString()));
627 if (status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED && version > 11)
629 else if (status == IPP_STATUS_ERROR_BUSY)
631 sleep((unsigned)delay);
633 delay = _cupsNextDelay(delay, &prev_delay);
641 while (!response && tries < 10);
647 * Allocate a cups_dinfo_t structure and return it...
650 if ((dinfo = calloc(1, sizeof(cups_dinfo_t))) == NULL)
652 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
657 dinfo->version = version;
659 dinfo->resource = _cupsStrAlloc(resource);
660 dinfo->attrs = response;
667 * 'cupsFindDestDefault()' - Find the default value(s) for the given option.
669 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
670 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
671 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
672 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
673 * functions to inspect the default value(s) as needed.
675 * @since CUPS 1.7/macOS 10.9@
678 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
680 http_t *http, /* I - Connection to destination */
681 cups_dest_t *dest, /* I - Destination */
682 cups_dinfo_t *dinfo, /* I - Destination information */
683 const char *option) /* I - Option/attribute name */
685 char name[IPP_MAX_NAME]; /* Attribute name */
689 * Range check input...
692 if (!http || !dest || !dinfo || !option)
694 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
699 * Find and return the attribute...
702 snprintf(name, sizeof(name), "%s-default", option);
703 return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
708 * 'cupsFindDestReady()' - Find the default value(s) for the given option.
710 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
711 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
712 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
713 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
714 * functions to inspect the default value(s) as needed.
716 * @since CUPS 1.7/macOS 10.9@
719 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
721 http_t *http, /* I - Connection to destination */
722 cups_dest_t *dest, /* I - Destination */
723 cups_dinfo_t *dinfo, /* I - Destination information */
724 const char *option) /* I - Option/attribute name */
726 char name[IPP_MAX_NAME]; /* Attribute name */
730 * Range check input...
733 if (!http || !dest || !dinfo || !option)
735 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
740 * Find and return the attribute...
743 cups_update_ready(http, dinfo);
745 snprintf(name, sizeof(name), "%s-ready", option);
746 return (ippFindAttribute(dinfo->ready_attrs, name, IPP_TAG_ZERO));
751 * 'cupsFindDestSupported()' - Find the default value(s) for the given option.
753 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
754 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
755 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
756 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
757 * functions to inspect the default value(s) as needed.
759 * @since CUPS 1.7/macOS 10.9@
762 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
763 cupsFindDestSupported(
764 http_t *http, /* I - Connection to destination */
765 cups_dest_t *dest, /* I - Destination */
766 cups_dinfo_t *dinfo, /* I - Destination information */
767 const char *option) /* I - Option/attribute name */
769 char name[IPP_MAX_NAME]; /* Attribute name */
773 * Range check input...
776 if (!http || !dest || !dinfo || !option)
778 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
783 * Find and return the attribute...
786 snprintf(name, sizeof(name), "%s-supported", option);
787 return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
792 * 'cupsFreeDestInfo()' - Free destination information obtained using
793 * @link cupsCopyDestInfo@.
797 cupsFreeDestInfo(cups_dinfo_t *dinfo) /* I - Destination information */
800 * Range check input...
807 * Free memory and return...
810 _cupsStrFree(dinfo->resource);
812 cupsArrayDelete(dinfo->constraints);
813 cupsArrayDelete(dinfo->resolvers);
815 cupsArrayDelete(dinfo->localizations);
817 cupsArrayDelete(dinfo->media_db);
819 cupsArrayDelete(dinfo->cached_db);
821 ippDelete(dinfo->ready_attrs);
822 cupsArrayDelete(dinfo->ready_db);
824 ippDelete(dinfo->attrs);
831 * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a
834 * The @code flags@ parameter determines which set of media are indexed. For
835 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth
836 * borderless size supported by the printer.
838 * @since CUPS 1.7/macOS 10.9@
841 int /* O - 1 on success, 0 on failure */
842 cupsGetDestMediaByIndex(
843 http_t *http, /* I - Connection to destination */
844 cups_dest_t *dest, /* I - Destination */
845 cups_dinfo_t *dinfo, /* I - Destination information */
846 int n, /* I - Media size number (0-based) */
847 unsigned flags, /* I - Media flags */
848 cups_size_t *size) /* O - Media size information */
850 _cups_media_db_t *nsize; /* Size for N */
851 pwg_media_t *pwg; /* PWG media name for size */
855 * Range check input...
859 memset(size, 0, sizeof(cups_size_t));
861 if (!http || !dest || !dinfo || n < 0 || !size)
863 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
868 * Load media list as needed...
871 if (flags & CUPS_MEDIA_FLAGS_READY)
872 cups_update_ready(http, dinfo);
874 if (!dinfo->cached_db || dinfo->cached_flags != flags)
875 cups_create_cached(http, dinfo, flags);
878 * Copy the size over and return...
881 if ((nsize = (_cups_media_db_t *)cupsArrayIndex(dinfo->cached_db, n)) == NULL)
883 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
887 if (nsize->size_name)
888 strlcpy(size->media, nsize->size_name, sizeof(size->media));
890 strlcpy(size->media, nsize->key, sizeof(size->media));
891 else if ((pwg = pwgMediaForSize(nsize->width, nsize->length)) != NULL)
892 strlcpy(size->media, pwg->pwg, sizeof(size->media));
895 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
899 size->width = nsize->width;
900 size->length = nsize->length;
901 size->bottom = nsize->bottom;
902 size->left = nsize->left;
903 size->right = nsize->right;
904 size->top = nsize->top;
911 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
913 * The "media" string is a PWG media name. "Flags" provides some matching
914 * guidance (multiple flags can be combined):
916 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
917 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
918 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
919 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
920 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
921 * size amongst the "ready" media.
923 * The matching result (if any) is returned in the "cups_size_t" structure.
925 * Returns 1 when there is a match and 0 if there is not a match.
927 * @since CUPS 1.6/macOS 10.8@
930 int /* O - 1 on match, 0 on failure */
931 cupsGetDestMediaByName(
932 http_t *http, /* I - Connection to destination */
933 cups_dest_t *dest, /* I - Destination */
934 cups_dinfo_t *dinfo, /* I - Destination information */
935 const char *media, /* I - Media name */
936 unsigned flags, /* I - Media matching flags */
937 cups_size_t *size) /* O - Media size information */
939 pwg_media_t *pwg; /* PWG media info */
943 * Range check input...
947 memset(size, 0, sizeof(cups_size_t));
949 if (!http || !dest || !dinfo || !media || !size)
951 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
956 * Lookup the media size name...
959 if ((pwg = pwgMediaForPWG(media)) == NULL)
960 if ((pwg = pwgMediaForLegacy(media)) == NULL)
962 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media));
963 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown media size name."), 1);
971 return (cups_get_media_db(http, dinfo, pwg, flags, size));
976 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
978 * "Width" and "length" are the dimensions in hundredths of millimeters.
979 * "Flags" provides some matching guidance (multiple flags can be combined):
981 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
982 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
983 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
984 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
985 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
986 * size amongst the "ready" media.
988 * The matching result (if any) is returned in the "cups_size_t" structure.
990 * Returns 1 when there is a match and 0 if there is not a match.
992 * @since CUPS 1.6/macOS 10.8@
995 int /* O - 1 on match, 0 on failure */
996 cupsGetDestMediaBySize(
997 http_t *http, /* I - Connection to destination */
998 cups_dest_t *dest, /* I - Destination */
999 cups_dinfo_t *dinfo, /* I - Destination information */
1000 int width, /* I - Media width in hundredths of
1002 int length, /* I - Media length in hundredths of
1004 unsigned flags, /* I - Media matching flags */
1005 cups_size_t *size) /* O - Media size information */
1007 pwg_media_t *pwg; /* PWG media info */
1011 * Range check input...
1015 memset(size, 0, sizeof(cups_size_t));
1017 if (!http || !dest || !dinfo || width <= 0 || length <= 0 || !size)
1019 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1024 * Lookup the media size name...
1027 if ((pwg = pwgMediaForSize(width, length)) == NULL)
1029 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width,
1031 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid media size."), 1);
1036 * Lookup the size...
1039 return (cups_get_media_db(http, dinfo, pwg, flags, size));
1044 * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a
1047 * The @code flags@ parameter determines the set of media sizes that are
1048 * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return
1049 * the number of borderless sizes.
1051 * @since CUPS 1.7/macOS 10.9@
1054 int /* O - Number of sizes */
1055 cupsGetDestMediaCount(
1056 http_t *http, /* I - Connection to destination */
1057 cups_dest_t *dest, /* I - Destination */
1058 cups_dinfo_t *dinfo, /* I - Destination information */
1059 unsigned flags) /* I - Media flags */
1062 * Range check input...
1065 if (!http || !dest || !dinfo)
1067 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1072 * Load media list as needed...
1075 if (flags & CUPS_MEDIA_FLAGS_READY)
1076 cups_update_ready(http, dinfo);
1078 if (!dinfo->cached_db || dinfo->cached_flags != flags)
1079 cups_create_cached(http, dinfo, flags);
1081 return (cupsArrayCount(dinfo->cached_db));
1086 * 'cupsGetDestMediaDefault()' - Get the default size for a destination.
1088 * The @code flags@ parameter determines which default size is returned. For
1089 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default
1090 * borderless size, typically US Letter or A4, but sometimes 4x6 photo media.
1092 * @since CUPS 1.7/macOS 10.9@
1095 int /* O - 1 on success, 0 on failure */
1096 cupsGetDestMediaDefault(
1097 http_t *http, /* I - Connection to destination */
1098 cups_dest_t *dest, /* I - Destination */
1099 cups_dinfo_t *dinfo, /* I - Destination information */
1100 unsigned flags, /* I - Media flags */
1101 cups_size_t *size) /* O - Media size information */
1103 const char *media; /* Default media size */
1107 * Range check input...
1111 memset(size, 0, sizeof(cups_size_t));
1113 if (!http || !dest || !dinfo || !size)
1115 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1120 * Get the default media size, if any...
1123 if ((media = cupsGetOption("media", dest->num_options,
1124 dest->options)) == NULL)
1125 media = "na_letter_8.5x11in";
1127 if (cupsGetDestMediaByName(http, dest, dinfo, media, flags, size))
1130 if (strcmp(media, "na_letter_8.5x11in") &&
1131 cupsGetDestMediaByName(http, dest, dinfo, "iso_a4_210x297mm", flags,
1135 if (strcmp(media, "iso_a4_210x297mm") &&
1136 cupsGetDestMediaByName(http, dest, dinfo, "na_letter_8.5x11in", flags,
1140 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
1141 cupsGetDestMediaByName(http, dest, dinfo, "na_index_4x6in", flags, size))
1145 * Fall back to the first matching media size...
1148 return (cupsGetDestMediaByIndex(http, dest, dinfo, 0, flags, size));
1153 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
1158 cups_array_t *a, /* I - Array */
1159 ipp_t *collection) /* I - Collection value */
1161 ipp_attribute_t *attr; /* Attribute */
1162 _cups_dconstres_t *temp; /* Current constraint/resolver */
1165 if ((attr = ippFindAttribute(collection, "resolver-name",
1166 IPP_TAG_NAME)) == NULL)
1169 if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL)
1172 temp->name = attr->values[0].string.text;
1173 temp->collection = collection;
1175 cupsArrayAdd(a, temp);
1180 * 'cups_compare_dconstres()' - Compare to resolver entries.
1183 static int /* O - Result of comparison */
1184 cups_compare_dconstres(
1185 _cups_dconstres_t *a, /* I - First resolver */
1186 _cups_dconstres_t *b) /* I - Second resolver */
1188 return (strcmp(a->name, b->name));
1193 * 'cups_compare_media_db()' - Compare two media entries.
1196 static int /* O - Result of comparison */
1197 cups_compare_media_db(
1198 _cups_media_db_t *a, /* I - First media entries */
1199 _cups_media_db_t *b) /* I - Second media entries */
1201 int result; /* Result of comparison */
1204 if ((result = a->width - b->width) == 0)
1205 result = a->length - b->length;
1212 * 'cups_copy_media_db()' - Copy a media entry.
1215 static _cups_media_db_t * /* O - New media entry */
1217 _cups_media_db_t *mdb) /* I - Media entry to copy */
1219 _cups_media_db_t *temp; /* New media entry */
1222 if ((temp = calloc(1, sizeof(_cups_media_db_t))) == NULL)
1226 temp->color = _cupsStrAlloc(mdb->color);
1228 temp->key = _cupsStrAlloc(mdb->key);
1230 temp->info = _cupsStrAlloc(mdb->info);
1232 temp->size_name = _cupsStrAlloc(mdb->size_name);
1234 temp->source = _cupsStrAlloc(mdb->source);
1236 temp->type = _cupsStrAlloc(mdb->type);
1238 temp->width = mdb->width;
1239 temp->length = mdb->length;
1240 temp->bottom = mdb->bottom;
1241 temp->left = mdb->left;
1242 temp->right = mdb->right;
1243 temp->top = mdb->top;
1250 * 'cups_create_cached()' - Create the media selection cache.
1254 cups_create_cached(http_t *http, /* I - Connection to destination */
1255 cups_dinfo_t *dinfo, /* I - Destination information */
1256 unsigned flags) /* I - Media selection flags */
1258 cups_array_t *db; /* Media database array to use */
1259 _cups_media_db_t *mdb, /* Media database entry */
1260 *first; /* First entry this size */
1263 DEBUG_printf(("3cups_create_cached(http=%p, dinfo=%p, flags=%u)", (void *)http, (void *)dinfo, flags));
1265 if (dinfo->cached_db)
1266 cupsArrayDelete(dinfo->cached_db);
1268 dinfo->cached_db = cupsArrayNew(NULL, NULL);
1269 dinfo->cached_flags = flags;
1271 if (flags & CUPS_MEDIA_FLAGS_READY)
1273 DEBUG_puts("4cups_create_cached: ready media");
1275 cups_update_ready(http, dinfo);
1276 db = dinfo->ready_db;
1280 DEBUG_puts("4cups_create_cached: supported media");
1282 if (!dinfo->media_db)
1283 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
1285 db = dinfo->media_db;
1288 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db), first = mdb;
1290 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1292 DEBUG_printf(("4cups_create_cached: %p key=\"%s\", type=\"%s\", %dx%d, B%d L%d R%d T%d", (void *)mdb, mdb->key, mdb->type, mdb->width, mdb->length, mdb->bottom, mdb->left, mdb->right, mdb->top));
1294 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1296 if (!mdb->left && !mdb->right && !mdb->top && !mdb->bottom)
1298 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb));
1299 cupsArrayAdd(dinfo->cached_db, mdb);
1302 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1304 if (first->width != mdb->width || first->length != mdb->length)
1306 DEBUG_printf(("4cups_create_cached: add %p", (void *)first));
1307 cupsArrayAdd(dinfo->cached_db, first);
1310 else if (mdb->left >= first->left && mdb->right >= first->right && mdb->top >= first->top && mdb->bottom >= first->bottom &&
1311 (mdb->left != first->left || mdb->right != first->right || mdb->top != first->top || mdb->bottom != first->bottom))
1316 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb));
1317 cupsArrayAdd(dinfo->cached_db, mdb);
1321 if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1323 DEBUG_printf(("4cups_create_cached: add %p", (void *)first));
1324 cupsArrayAdd(dinfo->cached_db, first);
1330 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
1334 cups_create_constraints(
1335 cups_dinfo_t *dinfo) /* I - Destination information */
1337 int i; /* Looping var */
1338 ipp_attribute_t *attr; /* Attribute */
1339 _ipp_value_t *val; /* Current value */
1342 dinfo->constraints = cupsArrayNew3(NULL, NULL, NULL, 0, NULL,
1343 (cups_afree_func_t)free);
1344 dinfo->resolvers = cupsArrayNew3((cups_array_func_t)cups_compare_dconstres,
1345 NULL, NULL, 0, NULL,
1346 (cups_afree_func_t)free);
1348 if ((attr = ippFindAttribute(dinfo->attrs, "job-constraints-supported",
1349 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1351 for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
1352 cups_add_dconstres(dinfo->constraints, val->collection);
1355 if ((attr = ippFindAttribute(dinfo->attrs, "job-resolvers-supported",
1356 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1358 for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
1359 cups_add_dconstres(dinfo->resolvers, val->collection);
1365 * 'cups_create_defaults()' - Create the -default option array.
1367 * TODO: Need to support collection defaults...
1371 cups_create_defaults(
1372 cups_dinfo_t *dinfo) /* I - Destination information */
1374 ipp_attribute_t *attr; /* Current attribute */
1375 char name[IPP_MAX_NAME + 1],
1377 *nameptr, /* Pointer into current name */
1378 value[2048]; /* Current value */
1382 * Iterate through the printer attributes looking for xxx-default and adding
1383 * xxx=value to the defaults option array.
1386 for (attr = ippFirstAttribute(dinfo->attrs);
1388 attr = ippNextAttribute(dinfo->attrs))
1390 if (!attr->name || attr->group_tag != IPP_TAG_PRINTER)
1393 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
1394 continue; /* TODO: STR #4096 */
1396 if ((nameptr = attr->name + strlen(attr->name) - 8) <= attr->name ||
1397 strcmp(nameptr, "-default"))
1400 strlcpy(name, attr->name, sizeof(name));
1401 if ((nameptr = name + strlen(name) - 8) <= name ||
1402 strcmp(nameptr, "-default"))
1407 if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
1410 dinfo->num_defaults = cupsAddOption(name, value, dinfo->num_defaults,
1417 * 'cups_create_media_db()' - Create the media database.
1421 cups_create_media_db(
1422 cups_dinfo_t *dinfo, /* I - Destination information */
1423 unsigned flags) /* I - Media flags */
1425 int i; /* Looping var */
1426 _ipp_value_t *val; /* Current value */
1427 ipp_attribute_t *media_col_db, /* media-col-database */
1428 *media_attr, /* media-xxx */
1429 *x_dimension, /* x-dimension */
1430 *y_dimension; /* y-dimension */
1431 pwg_media_t *pwg; /* PWG media info */
1432 cups_array_t *db; /* New media database array */
1433 _cups_media_db_t mdb; /* Media entry */
1436 db = cupsArrayNew3((cups_array_func_t)cups_compare_media_db,
1438 (cups_acopy_func_t)cups_copy_media_db,
1439 (cups_afree_func_t)cups_free_media_db);
1441 if (flags == CUPS_MEDIA_FLAGS_READY)
1443 dinfo->ready_db = db;
1445 media_col_db = ippFindAttribute(dinfo->ready_attrs, "media-col-ready",
1446 IPP_TAG_BEGIN_COLLECTION);
1447 media_attr = ippFindAttribute(dinfo->ready_attrs, "media-ready",
1452 dinfo->media_db = db;
1453 dinfo->min_size.width = INT_MAX;
1454 dinfo->min_size.length = INT_MAX;
1455 dinfo->max_size.width = 0;
1456 dinfo->max_size.length = 0;
1458 media_col_db = ippFindAttribute(dinfo->attrs, "media-col-database",
1459 IPP_TAG_BEGIN_COLLECTION);
1460 media_attr = ippFindAttribute(dinfo->attrs, "media-supported",
1466 _ipp_value_t *custom = NULL; /* Custom size range value */
1468 for (i = media_col_db->num_values, val = media_col_db->values;
1472 memset(&mdb, 0, sizeof(mdb));
1474 if ((media_attr = ippFindAttribute(val->collection, "media-size",
1475 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1477 ipp_t *media_size = media_attr->values[0].collection;
1478 /* media-size collection value */
1480 if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
1481 IPP_TAG_INTEGER)) != NULL &&
1482 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1483 IPP_TAG_INTEGER)) != NULL)
1489 mdb.width = x_dimension->values[0].integer;
1490 mdb.length = y_dimension->values[0].integer;
1492 else if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
1493 IPP_TAG_INTEGER)) != NULL &&
1494 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1495 IPP_TAG_RANGE)) != NULL)
1501 mdb.width = x_dimension->values[0].integer;
1502 mdb.length = y_dimension->values[0].range.upper;
1504 else if (flags != CUPS_MEDIA_FLAGS_READY &&
1505 (x_dimension = ippFindAttribute(media_size, "x-dimension",
1506 IPP_TAG_RANGE)) != NULL &&
1507 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1508 IPP_TAG_RANGE)) != NULL)
1511 * Custom size range; save this as the custom size value with default
1512 * margins, then continue; we'll capture the real margins below...
1517 dinfo->min_size.width = x_dimension->values[0].range.lower;
1518 dinfo->min_size.length = y_dimension->values[0].range.lower;
1519 dinfo->min_size.left =
1520 dinfo->min_size.right = 635; /* Default 1/4" side margins */
1521 dinfo->min_size.top =
1522 dinfo->min_size.bottom = 1270; /* Default 1/2" top/bottom margins */
1524 dinfo->max_size.width = x_dimension->values[0].range.upper;
1525 dinfo->max_size.length = y_dimension->values[0].range.upper;
1526 dinfo->max_size.left =
1527 dinfo->max_size.right = 635; /* Default 1/4" side margins */
1528 dinfo->max_size.top =
1529 dinfo->max_size.bottom = 1270; /* Default 1/2" top/bottom margins */
1534 if ((media_attr = ippFindAttribute(val->collection, "media-color",
1535 IPP_TAG_ZERO)) != NULL &&
1536 (media_attr->value_tag == IPP_TAG_NAME ||
1537 media_attr->value_tag == IPP_TAG_NAMELANG ||
1538 media_attr->value_tag == IPP_TAG_KEYWORD))
1539 mdb.color = media_attr->values[0].string.text;
1541 if ((media_attr = ippFindAttribute(val->collection, "media-info",
1542 IPP_TAG_TEXT)) != NULL)
1543 mdb.info = media_attr->values[0].string.text;
1545 if ((media_attr = ippFindAttribute(val->collection, "media-key",
1546 IPP_TAG_ZERO)) != NULL &&
1547 (media_attr->value_tag == IPP_TAG_NAME ||
1548 media_attr->value_tag == IPP_TAG_NAMELANG ||
1549 media_attr->value_tag == IPP_TAG_KEYWORD))
1550 mdb.key = media_attr->values[0].string.text;
1552 if ((media_attr = ippFindAttribute(val->collection, "media-size-name",
1553 IPP_TAG_ZERO)) != NULL &&
1554 (media_attr->value_tag == IPP_TAG_NAME ||
1555 media_attr->value_tag == IPP_TAG_NAMELANG ||
1556 media_attr->value_tag == IPP_TAG_KEYWORD))
1557 mdb.size_name = media_attr->values[0].string.text;
1559 if ((media_attr = ippFindAttribute(val->collection, "media-source",
1560 IPP_TAG_ZERO)) != NULL &&
1561 (media_attr->value_tag == IPP_TAG_NAME ||
1562 media_attr->value_tag == IPP_TAG_NAMELANG ||
1563 media_attr->value_tag == IPP_TAG_KEYWORD))
1564 mdb.source = media_attr->values[0].string.text;
1566 if ((media_attr = ippFindAttribute(val->collection, "media-type",
1567 IPP_TAG_ZERO)) != NULL &&
1568 (media_attr->value_tag == IPP_TAG_NAME ||
1569 media_attr->value_tag == IPP_TAG_NAMELANG ||
1570 media_attr->value_tag == IPP_TAG_KEYWORD))
1571 mdb.type = media_attr->values[0].string.text;
1573 if ((media_attr = ippFindAttribute(val->collection, "media-bottom-margin",
1574 IPP_TAG_INTEGER)) != NULL)
1575 mdb.bottom = media_attr->values[0].integer;
1577 if ((media_attr = ippFindAttribute(val->collection, "media-left-margin",
1578 IPP_TAG_INTEGER)) != NULL)
1579 mdb.left = media_attr->values[0].integer;
1581 if ((media_attr = ippFindAttribute(val->collection, "media-right-margin",
1582 IPP_TAG_INTEGER)) != NULL)
1583 mdb.right = media_attr->values[0].integer;
1585 if ((media_attr = ippFindAttribute(val->collection, "media-top-margin",
1586 IPP_TAG_INTEGER)) != NULL)
1587 mdb.top = media_attr->values[0].integer;
1589 cupsArrayAdd(db, &mdb);
1594 if ((media_attr = ippFindAttribute(custom->collection,
1595 "media-bottom-margin",
1596 IPP_TAG_INTEGER)) != NULL)
1598 dinfo->min_size.top =
1599 dinfo->max_size.top = media_attr->values[0].integer;
1602 if ((media_attr = ippFindAttribute(custom->collection,
1603 "media-left-margin",
1604 IPP_TAG_INTEGER)) != NULL)
1606 dinfo->min_size.left =
1607 dinfo->max_size.left = media_attr->values[0].integer;
1610 if ((media_attr = ippFindAttribute(custom->collection,
1611 "media-right-margin",
1612 IPP_TAG_INTEGER)) != NULL)
1614 dinfo->min_size.right =
1615 dinfo->max_size.right = media_attr->values[0].integer;
1618 if ((media_attr = ippFindAttribute(custom->collection,
1620 IPP_TAG_INTEGER)) != NULL)
1622 dinfo->min_size.top =
1623 dinfo->max_size.top = media_attr->values[0].integer;
1627 else if (media_attr &&
1628 (media_attr->value_tag == IPP_TAG_NAME ||
1629 media_attr->value_tag == IPP_TAG_NAMELANG ||
1630 media_attr->value_tag == IPP_TAG_KEYWORD))
1632 memset(&mdb, 0, sizeof(mdb));
1635 mdb.right = 635; /* Default 1/4" side margins */
1637 mdb.bottom = 1270; /* Default 1/2" top/bottom margins */
1639 for (i = media_attr->num_values, val = media_attr->values;
1643 if ((pwg = pwgMediaForPWG(val->string.text)) == NULL)
1644 if ((pwg = pwgMediaForLegacy(val->string.text)) == NULL)
1646 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
1651 mdb.width = pwg->width;
1652 mdb.length = pwg->length;
1654 if (flags != CUPS_MEDIA_FLAGS_READY &&
1655 !strncmp(val->string.text, "custom_min_", 11))
1657 mdb.size_name = NULL;
1658 dinfo->min_size = mdb;
1660 else if (flags != CUPS_MEDIA_FLAGS_READY &&
1661 !strncmp(val->string.text, "custom_max_", 11))
1663 mdb.size_name = NULL;
1664 dinfo->max_size = mdb;
1668 mdb.size_name = val->string.text;
1670 cupsArrayAdd(db, &mdb);
1678 * 'cups_free_media_cb()' - Free a media entry.
1683 _cups_media_db_t *mdb) /* I - Media entry to free */
1686 _cupsStrFree(mdb->color);
1688 _cupsStrFree(mdb->key);
1690 _cupsStrFree(mdb->info);
1692 _cupsStrFree(mdb->size_name);
1694 _cupsStrFree(mdb->source);
1696 _cupsStrFree(mdb->type);
1703 * 'cups_get_media_db()' - Lookup the media entry for a given size.
1706 static int /* O - 1 on match, 0 on failure */
1707 cups_get_media_db(http_t *http, /* I - Connection to destination */
1708 cups_dinfo_t *dinfo, /* I - Destination information */
1709 pwg_media_t *pwg, /* I - PWG media info */
1710 unsigned flags, /* I - Media matching flags */
1711 cups_size_t *size) /* O - Media size/margin/name info */
1713 cups_array_t *db; /* Which media database to query */
1714 _cups_media_db_t *mdb, /* Current media database entry */
1715 *best = NULL, /* Best matching entry */
1716 key; /* Search key */
1720 * Create the media database as needed...
1723 if (flags & CUPS_MEDIA_FLAGS_READY)
1725 cups_update_ready(http, dinfo);
1726 db = dinfo->ready_db;
1730 if (!dinfo->media_db)
1731 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
1733 db = dinfo->media_db;
1740 memset(&key, 0, sizeof(key));
1741 key.width = pwg->width;
1742 key.length = pwg->length;
1744 if ((mdb = cupsArrayFind(db, &key)) != NULL)
1747 * Found an exact match, let's figure out the best margins for the flags
1753 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1756 * Look for the smallest margins...
1759 if (best->left != 0 || best->right != 0 || best->top != 0 || best->bottom != 0)
1761 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1762 mdb && !cups_compare_media_db(mdb, &key);
1763 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1765 if (mdb->left <= best->left && mdb->right <= best->right &&
1766 mdb->top <= best->top && mdb->bottom <= best->bottom)
1769 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
1777 * If we need an exact match, return no-match if the size is not
1781 if ((flags & CUPS_MEDIA_FLAGS_EXACT) &&
1782 (best->left || best->right || best->top || best->bottom))
1785 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1788 * Look for the largest margins...
1791 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1792 mdb && !cups_compare_media_db(mdb, &key);
1793 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1795 if (mdb->left >= best->left && mdb->right >= best->right &&
1796 mdb->top >= best->top && mdb->bottom >= best->bottom &&
1797 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
1804 * Look for the smallest non-zero margins...
1807 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1808 mdb && !cups_compare_media_db(mdb, &key);
1809 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1811 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
1812 ((mdb->right > 0 && mdb->right <= best->right) || best->right == 0) &&
1813 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
1814 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) || best->bottom == 0) &&
1815 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
1820 else if (flags & CUPS_MEDIA_FLAGS_EXACT)
1823 * See if we can do this as a custom size...
1826 if (pwg->width < dinfo->min_size.width ||
1827 pwg->width > dinfo->max_size.width ||
1828 pwg->length < dinfo->min_size.length ||
1829 pwg->length > dinfo->max_size.length)
1830 return (0); /* Out of range */
1832 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
1833 (dinfo->min_size.left > 0 || dinfo->min_size.right > 0 ||
1834 dinfo->min_size.top > 0 || dinfo->min_size.bottom > 0))
1835 return (0); /* Not borderless */
1837 key.size_name = (char *)pwg->pwg;
1838 key.bottom = dinfo->min_size.bottom;
1839 key.left = dinfo->min_size.left;
1840 key.right = dinfo->min_size.right;
1841 key.top = dinfo->min_size.top;
1845 else if (pwg->width >= dinfo->min_size.width &&
1846 pwg->width <= dinfo->max_size.width &&
1847 pwg->length >= dinfo->min_size.length &&
1848 pwg->length <= dinfo->max_size.length)
1851 * Map to custom size...
1854 key.size_name = (char *)pwg->pwg;
1855 key.bottom = dinfo->min_size.bottom;
1856 key.left = dinfo->min_size.left;
1857 key.right = dinfo->min_size.right;
1858 key.top = dinfo->min_size.top;
1865 * Find a close size...
1868 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db);
1870 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1871 if (cups_is_close_media_db(mdb, &key))
1879 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1882 * Look for the smallest margins...
1885 if (best->left != 0 || best->right != 0 || best->top != 0 ||
1888 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1889 mdb && cups_is_close_media_db(mdb, &key);
1890 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1892 if (mdb->left <= best->left && mdb->right <= best->right &&
1893 mdb->top <= best->top && mdb->bottom <= best->bottom &&
1894 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
1897 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
1904 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1907 * Look for the largest margins...
1910 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1911 mdb && cups_is_close_media_db(mdb, &key);
1912 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1914 if (mdb->left >= best->left && mdb->right >= best->right &&
1915 mdb->top >= best->top && mdb->bottom >= best->bottom &&
1916 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
1923 * Look for the smallest non-zero margins...
1926 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1927 mdb && cups_is_close_media_db(mdb, &key);
1928 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1930 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
1931 ((mdb->right > 0 && mdb->right <= best->right) ||
1932 best->right == 0) &&
1933 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
1934 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
1935 best->bottom == 0) &&
1936 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
1945 * Return the matching size...
1948 if (best->size_name)
1949 strlcpy(size->media, best->size_name, sizeof(size->media));
1951 strlcpy(size->media, best->key, sizeof(size->media));
1953 strlcpy(size->media, pwg->pwg, sizeof(size->media));
1955 size->width = best->width;
1956 size->length = best->length;
1957 size->bottom = best->bottom;
1958 size->left = best->left;
1959 size->right = best->right;
1960 size->top = best->top;
1970 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
1971 * close to the same size.
1973 * Currently we use 5 points (from PostScript) as the matching range...
1976 static int /* O - 1 if the sizes are close */
1977 cups_is_close_media_db(
1978 _cups_media_db_t *a, /* I - First media entries */
1979 _cups_media_db_t *b) /* I - Second media entries */
1981 int dwidth, /* Difference in width */
1982 dlength; /* Difference in length */
1985 dwidth = a->width - b->width;
1986 dlength = a->length - b->length;
1988 return (dwidth >= -176 && dwidth <= 176 &&
1989 dlength >= -176 && dlength <= 176);
1994 * 'cups_test_constraints()' - Test constraints.
1996 * TODO: STR #4096 - Need to properly support media-col contraints...
1999 static cups_array_t * /* O - Active constraints */
2000 cups_test_constraints(
2001 cups_dinfo_t *dinfo, /* I - Destination information */
2002 const char *new_option, /* I - Newly selected option */
2003 const char *new_value, /* I - Newly selected value */
2004 int num_options, /* I - Number of options */
2005 cups_option_t *options, /* I - Options */
2006 int *num_conflicts, /* O - Number of conflicting options */
2007 cups_option_t **conflicts) /* O - Conflicting options */
2009 int i, /* Looping var */
2010 match; /* Value matches? */
2011 int num_matching; /* Number of matching options */
2012 cups_option_t *matching; /* Matching options */
2013 _cups_dconstres_t *c; /* Current constraint */
2014 cups_array_t *active = NULL; /* Active constraints */
2015 ipp_attribute_t *attr; /* Current attribute */
2016 _ipp_value_t *attrval; /* Current attribute value */
2017 const char *value; /* Current value */
2018 char temp[1024]; /* Temporary string */
2019 int int_value; /* Integer value */
2020 int xres_value, /* Horizontal resolution */
2021 yres_value; /* Vertical resolution */
2022 ipp_res_t units_value; /* Resolution units */
2025 for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints);
2027 c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints))
2032 for (attr = ippFirstAttribute(c->collection);
2034 attr = ippNextAttribute(c->collection))
2036 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
2037 break; /* TODO: STR #4096 */
2040 * Get the value for the current attribute in the constraint...
2043 if (new_option && new_value && !strcmp(attr->name, new_option))
2045 else if ((value = cupsGetOption(attr->name, num_options,
2047 value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults);
2052 * Not set so this constraint does not apply...
2060 switch (attr->value_tag)
2062 case IPP_TAG_INTEGER :
2064 int_value = atoi(value);
2066 for (i = attr->num_values, attrval = attr->values;
2070 if (attrval->integer == int_value)
2078 case IPP_TAG_BOOLEAN :
2079 int_value = !strcmp(value, "true");
2081 for (i = attr->num_values, attrval = attr->values;
2085 if (attrval->boolean == int_value)
2093 case IPP_TAG_RANGE :
2094 int_value = atoi(value);
2096 for (i = attr->num_values, attrval = attr->values;
2100 if (int_value >= attrval->range.lower &&
2101 int_value <= attrval->range.upper)
2109 case IPP_TAG_RESOLUTION :
2110 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
2112 if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
2115 yres_value = xres_value;
2118 if (!strcmp(temp, "dpi"))
2119 units_value = IPP_RES_PER_INCH;
2120 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
2121 units_value = IPP_RES_PER_CM;
2125 for (i = attr->num_values, attrval = attr->values;
2129 if (attrval->resolution.xres == xres_value &&
2130 attrval->resolution.yres == yres_value &&
2131 attrval->resolution.units == units_value)
2141 case IPP_TAG_KEYWORD :
2142 case IPP_TAG_CHARSET :
2144 case IPP_TAG_URISCHEME :
2145 case IPP_TAG_MIMETYPE :
2146 case IPP_TAG_LANGUAGE :
2147 case IPP_TAG_TEXTLANG :
2148 case IPP_TAG_NAMELANG :
2149 for (i = attr->num_values, attrval = attr->values;
2153 if (!strcmp(attrval->string.text, value))
2168 num_matching = cupsAddOption(attr->name, value, num_matching, &matching);
2174 active = cupsArrayNew(NULL, NULL);
2176 cupsArrayAdd(active, c);
2178 if (num_conflicts && conflicts)
2180 cups_option_t *moption; /* Matching option */
2182 for (i = num_matching, moption = matching; i > 0; i --, moption ++)
2183 *num_conflicts = cupsAddOption(moption->name, moption->value,
2184 *num_conflicts, conflicts);
2188 cupsFreeOptions(num_matching, matching);
2196 * 'cups_update_ready()' - Update xxx-ready attributes for the printer.
2200 cups_update_ready(http_t *http, /* I - Connection to destination */
2201 cups_dinfo_t *dinfo) /* I - Destination information */
2203 ipp_t *request; /* Get-Printer-Attributes request */
2204 static const char * const pattrs[] = /* Printer attributes we want */
2206 "finishings-col-ready",
2208 "job-finishings-col-ready",
2209 "job-finishings-ready",
2216 * Don't update more than once every 30 seconds...
2219 if ((time(NULL) - dinfo->ready_time) < _CUPS_MEDIA_READY_TTL)
2223 * Free any previous results...
2226 if (dinfo->cached_flags & CUPS_MEDIA_FLAGS_READY)
2228 cupsArrayDelete(dinfo->cached_db);
2229 dinfo->cached_db = NULL;
2230 dinfo->cached_flags = CUPS_MEDIA_FLAGS_DEFAULT;
2233 ippDelete(dinfo->ready_attrs);
2234 dinfo->ready_attrs = NULL;
2236 cupsArrayDelete(dinfo->ready_db);
2237 dinfo->ready_db = NULL;
2240 * Query the xxx-ready values...
2243 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
2244 ippSetVersion(request, dinfo->version / 10, dinfo->version % 10);
2246 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
2248 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
2250 ippAddStrings(request, IPP_TAG_OPERATION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
2252 dinfo->ready_attrs = cupsDoRequest(http, request, dinfo->resource);
2255 * Update the ready media database...
2258 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_READY);
2261 * Update last lookup time and return...
2264 dinfo->ready_time = time(NULL);