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 * file is 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
);