2 * Destination option/media support for CUPS.
4 * Copyright 2012-2017 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
10 * Include necessary headers...
13 #include "cups-private.h"
20 #define _CUPS_MEDIA_READY_TTL 30 /* Life of xxx-ready values */
27 static void cups_add_dconstres(cups_array_t
*a
, ipp_t
*collection
);
28 static int cups_compare_dconstres(_cups_dconstres_t
*a
,
29 _cups_dconstres_t
*b
);
30 static int cups_compare_media_db(_cups_media_db_t
*a
,
32 static _cups_media_db_t
*cups_copy_media_db(_cups_media_db_t
*mdb
);
33 static void cups_create_cached(http_t
*http
, cups_dinfo_t
*dinfo
,
35 static void cups_create_constraints(cups_dinfo_t
*dinfo
);
36 static void cups_create_defaults(cups_dinfo_t
*dinfo
);
37 static void cups_create_media_db(cups_dinfo_t
*dinfo
,
39 static void cups_free_media_db(_cups_media_db_t
*mdb
);
40 static int cups_get_media_db(http_t
*http
, cups_dinfo_t
*dinfo
,
41 pwg_media_t
*pwg
, unsigned flags
,
43 static int cups_is_close_media_db(_cups_media_db_t
*a
,
45 static cups_array_t
*cups_test_constraints(cups_dinfo_t
*dinfo
,
46 const char *new_option
,
47 const char *new_value
,
49 cups_option_t
*options
,
51 cups_option_t
**conflicts
);
52 static void cups_update_ready(http_t
*http
, cups_dinfo_t
*dinfo
);
56 * 'cupsCheckDestSupported()' - Check that the option and value are supported
59 * Returns 1 if supported, 0 otherwise.
61 * @since CUPS 1.6/macOS 10.8@
64 int /* O - 1 if supported, 0 otherwise */
65 cupsCheckDestSupported(
66 http_t
*http
, /* I - Connection to destination */
67 cups_dest_t
*dest
, /* I - Destination */
68 cups_dinfo_t
*dinfo
, /* I - Destination information */
69 const char *option
, /* I - Option */
70 const char *value
) /* I - Value or @code NULL@ */
72 int i
; /* Looping var */
73 char temp
[1024]; /* Temporary string */
74 int int_value
; /* Integer value */
75 int xres_value
, /* Horizontal resolution */
76 yres_value
; /* Vertical resolution */
77 ipp_res_t units_value
; /* Resolution units */
78 ipp_attribute_t
*attr
; /* Attribute */
79 _ipp_value_t
*attrval
; /* Current attribute value */
83 * Get the default connection as needed...
87 http
= _cupsConnect();
90 * Range check input...
93 if (!http
|| !dest
|| !dinfo
|| !option
)
97 * Lookup the attribute...
100 if (strstr(option
, "-supported"))
101 attr
= ippFindAttribute(dinfo
->attrs
, option
, IPP_TAG_ZERO
);
104 snprintf(temp
, sizeof(temp
), "%s-supported", option
);
105 attr
= ippFindAttribute(dinfo
->attrs
, temp
, IPP_TAG_ZERO
);
118 if (!strcmp(option
, "media") && !strncmp(value
, "custom_", 7))
121 * Check range of custom media sizes...
124 pwg_media_t
*pwg
; /* Current PWG media size info */
125 int min_width
, /* Minimum width */
126 min_length
, /* Minimum length */
127 max_width
, /* Maximum width */
128 max_length
; /* Maximum length */
131 * Get the minimum and maximum size...
134 min_width
= min_length
= INT_MAX
;
135 max_width
= max_length
= 0;
137 for (i
= attr
->num_values
, attrval
= attr
->values
;
141 if (!strncmp(attrval
->string
.text
, "custom_min_", 11) &&
142 (pwg
= pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
144 min_width
= pwg
->width
;
145 min_length
= pwg
->length
;
147 else if (!strncmp(attrval
->string
.text
, "custom_max_", 11) &&
148 (pwg
= pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
150 max_width
= pwg
->width
;
151 max_length
= pwg
->length
;
159 if (min_width
< INT_MAX
&& max_width
> 0 &&
160 (pwg
= pwgMediaForPWG(value
)) != NULL
&&
161 pwg
->width
>= min_width
&& pwg
->width
<= max_width
&&
162 pwg
->length
>= min_length
&& pwg
->length
<= max_length
)
168 * Check literal values...
171 switch (attr
->value_tag
)
173 case IPP_TAG_INTEGER
:
175 int_value
= atoi(value
);
177 for (i
= 0; i
< attr
->num_values
; i
++)
178 if (attr
->values
[i
].integer
== int_value
)
182 case IPP_TAG_BOOLEAN
:
183 return (attr
->values
[0].boolean
);
186 int_value
= atoi(value
);
188 for (i
= 0; i
< attr
->num_values
; i
++)
189 if (int_value
>= attr
->values
[i
].range
.lower
&&
190 int_value
<= attr
->values
[i
].range
.upper
)
194 case IPP_TAG_RESOLUTION
:
195 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
197 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
200 yres_value
= xres_value
;
203 if (!strcmp(temp
, "dpi"))
204 units_value
= IPP_RES_PER_INCH
;
205 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
206 units_value
= IPP_RES_PER_CM
;
210 for (i
= attr
->num_values
, attrval
= attr
->values
;
214 if (attrval
->resolution
.xres
== xres_value
&&
215 attrval
->resolution
.yres
== yres_value
&&
216 attrval
->resolution
.units
== units_value
)
223 case IPP_TAG_KEYWORD
:
224 case IPP_TAG_CHARSET
:
226 case IPP_TAG_URISCHEME
:
227 case IPP_TAG_MIMETYPE
:
228 case IPP_TAG_LANGUAGE
:
229 case IPP_TAG_TEXTLANG
:
230 case IPP_TAG_NAMELANG
:
231 for (i
= 0; i
< attr
->num_values
; i
++)
232 if (!strcmp(attr
->values
[i
].string
.text
, value
))
242 * If we get there the option+value is not supported...
250 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
253 * "num_options" and "options" represent the currently selected options by the
254 * user. "new_option" and "new_value" are the setting the user has just
257 * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
258 * there was an unrecoverable error such as a resolver loop.
260 * If "num_conflicts" and "conflicts" are not @code NULL@, they are set to
261 * contain the list of conflicting option/value pairs. Similarly, if
262 * "num_resolved" and "resolved" are not @code NULL@ they will be set to the
263 * list of changes needed to resolve the conflict.
265 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
266 * to 0 and @code NULL@, respectively, then the conflict cannot be resolved.
268 * @since CUPS 1.6/macOS 10.8@
271 int /* O - 1 if there is a conflict, 0 if none, -1 on error */
272 cupsCopyDestConflicts(
273 http_t
*http
, /* I - Connection to destination */
274 cups_dest_t
*dest
, /* I - Destination */
275 cups_dinfo_t
*dinfo
, /* I - Destination information */
276 int num_options
, /* I - Number of current options */
277 cups_option_t
*options
, /* I - Current options */
278 const char *new_option
, /* I - New option */
279 const char *new_value
, /* I - New value */
280 int *num_conflicts
, /* O - Number of conflicting options */
281 cups_option_t
**conflicts
, /* O - Conflicting options */
282 int *num_resolved
, /* O - Number of options to resolve */
283 cups_option_t
**resolved
) /* O - Resolved options */
285 int i
, /* Looping var */
286 have_conflicts
= 0, /* Do we have conflicts? */
287 changed
, /* Did we change something? */
288 tries
, /* Number of tries for resolution */
289 num_myconf
= 0, /* My number of conflicting options */
290 num_myres
= 0; /* My number of resolved options */
291 cups_option_t
*myconf
= NULL
, /* My conflicting options */
292 *myres
= NULL
, /* My resolved options */
293 *myoption
, /* My current option */
294 *option
; /* Current option */
295 cups_array_t
*active
= NULL
, /* Active conflicts */
296 *pass
= NULL
, /* Resolvers for this pass */
297 *resolvers
= NULL
, /* Resolvers we have used */
298 *test
; /* Test array for conflicts */
299 _cups_dconstres_t
*c
, /* Current constraint */
300 *r
; /* Current resolver */
301 ipp_attribute_t
*attr
; /* Current attribute */
302 char value
[2048]; /* Current attribute value as string */
303 const char *myvalue
; /* Current value of an option */
307 * Clear returned values...
323 * Get the default connection as needed...
327 http
= _cupsConnect();
330 * Range check input...
333 if (!http
|| !dest
|| !dinfo
||
334 (num_conflicts
!= NULL
) != (conflicts
!= NULL
) ||
335 (num_resolved
!= NULL
) != (resolved
!= NULL
))
339 * Load constraints as needed...
342 if (!dinfo
->constraints
)
343 cups_create_constraints(dinfo
);
345 if (cupsArrayCount(dinfo
->constraints
) == 0)
348 if (!dinfo
->num_defaults
)
349 cups_create_defaults(dinfo
);
352 * If we are resolving, create a shadow array...
357 for (i
= num_options
, option
= options
; i
> 0; i
--, option
++)
358 num_myres
= cupsAddOption(option
->name
, option
->value
, num_myres
, &myres
);
360 if (new_option
&& new_value
)
361 num_myres
= cupsAddOption(new_option
, new_value
, num_myres
, &myres
);
365 num_myres
= num_options
;
370 * Check for any conflicts...
374 pass
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
376 for (tries
= 0; tries
< 100; tries
++)
379 * Check for any conflicts...
382 if (num_conflicts
|| num_resolved
)
384 cupsFreeOptions(num_myconf
, myconf
);
388 active
= cups_test_constraints(dinfo
, new_option
, new_value
,
389 num_myres
, myres
, &num_myconf
,
393 active
= cups_test_constraints(dinfo
, new_option
, new_value
, num_myres
,
396 have_conflicts
= (active
!= NULL
);
398 if (!active
|| !num_resolved
)
399 break; /* All done */
402 * Scan the constraints that were triggered to apply resolvers...
406 resolvers
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
408 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(active
), changed
= 0;
410 c
= (_cups_dconstres_t
*)cupsArrayNext(active
))
412 if (cupsArrayFind(pass
, c
))
413 continue; /* Already applied this resolver... */
415 if (cupsArrayFind(resolvers
, c
))
417 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
423 if ((r
= cupsArrayFind(dinfo
->resolvers
, c
)) == NULL
)
425 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
432 * Add the options from the resolver...
435 cupsArrayAdd(pass
, r
);
436 cupsArrayAdd(resolvers
, r
);
438 for (attr
= ippFirstAttribute(r
->collection
);
440 attr
= ippNextAttribute(r
->collection
))
442 if (new_option
&& !strcmp(attr
->name
, new_option
))
443 continue; /* Ignore this if we just changed it */
445 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
446 continue; /* Ignore if the value is too long */
448 if ((test
= cups_test_constraints(dinfo
, attr
->name
, value
, num_myres
,
449 myres
, NULL
, NULL
)) == NULL
)
452 * That worked, flag it...
458 cupsArrayDelete(test
);
461 * Add the option/value from the resolver regardless of whether it
462 * worked; this makes sure that we can cascade several changes to
463 * make things resolve...
466 num_myres
= cupsAddOption(attr
->name
, value
, num_myres
, &myres
);
472 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
477 cupsArrayClear(pass
);
479 cupsArrayDelete(active
);
485 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
491 * Copy resolved options as needed...
496 for (i
= num_myres
, myoption
= myres
; i
> 0; i
--, myoption
++)
498 if ((myvalue
= cupsGetOption(myoption
->name
, num_options
,
500 strcmp(myvalue
, myoption
->value
))
502 if (new_option
&& !strcmp(new_option
, myoption
->name
) &&
503 new_value
&& !strcmp(new_value
, myoption
->value
))
506 *num_resolved
= cupsAddOption(myoption
->name
, myoption
->value
,
507 *num_resolved
, resolved
);
518 cupsArrayDelete(active
);
519 cupsArrayDelete(pass
);
520 cupsArrayDelete(resolvers
);
525 * Free shadow copy of options...
528 cupsFreeOptions(num_myres
, myres
);
534 * Return conflicting options to caller...
537 *num_conflicts
= num_myconf
;
543 * Free conflicting options...
546 cupsFreeOptions(num_myconf
, myconf
);
549 return (have_conflicts
);
554 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
557 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
558 * value. @code NULL@ is returned on error.
560 * @since CUPS 1.6/macOS 10.8@
563 cups_dinfo_t
* /* O - Destination information */
565 http_t
*http
, /* I - Connection to destination */
566 cups_dest_t
*dest
) /* I - Destination */
568 cups_dinfo_t
*dinfo
; /* Destination information */
569 ipp_t
*request
, /* Get-Printer-Attributes request */
570 *response
; /* Supported attributes */
571 int tries
, /* Number of tries so far */
572 delay
, /* Current retry delay */
573 prev_delay
; /* Next retry delay */
574 const char *uri
; /* Printer URI */
575 char resource
[1024]; /* Resource path */
576 int version
; /* IPP version */
577 ipp_status_t status
; /* Status of request */
578 static const char * const requested_attrs
[] =
579 { /* Requested attributes */
581 "media-col-database",
582 "printer-description"
586 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", (void *)http
, (void *)dest
, dest
? dest
->name
: ""));
589 * Get the default connection as needed...
593 http
= _cupsConnect();
596 * Range check input...
603 * Get the printer URI and resource path...
606 if ((uri
= _cupsGetDestResource(dest
, resource
, sizeof(resource
))) == NULL
)
610 * Get the supported attributes...
621 * Send a Get-Printer-Attributes request...
624 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
625 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
627 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
628 "requesting-user-name", NULL
, cupsUser());
629 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
630 "requested-attributes",
631 (int)(sizeof(requested_attrs
) / sizeof(requested_attrs
[0])),
632 NULL
, requested_attrs
);
633 response
= cupsDoRequest(http
, request
, resource
);
634 status
= cupsLastError();
636 if (status
> IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED
)
638 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
639 "returned %s (%s)", dest
->name
, ippErrorString(status
),
640 cupsLastErrorString()));
645 if (status
== IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
&& version
> 11)
647 else if (status
== IPP_STATUS_ERROR_BUSY
)
649 sleep((unsigned)delay
);
651 delay
= _cupsNextDelay(delay
, &prev_delay
);
659 while (!response
&& tries
< 10);
665 * Allocate a cups_dinfo_t structure and return it...
668 if ((dinfo
= calloc(1, sizeof(cups_dinfo_t
))) == NULL
)
670 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
675 dinfo
->version
= version
;
677 dinfo
->resource
= _cupsStrAlloc(resource
);
678 dinfo
->attrs
= response
;
685 * 'cupsFindDestDefault()' - Find the default value(s) for the given option.
687 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
688 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
689 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
690 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
691 * functions to inspect the default value(s) as needed.
693 * @since CUPS 1.7/macOS 10.9@
696 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
698 http_t
*http
, /* I - Connection to destination */
699 cups_dest_t
*dest
, /* I - Destination */
700 cups_dinfo_t
*dinfo
, /* I - Destination information */
701 const char *option
) /* I - Option/attribute name */
703 char name
[IPP_MAX_NAME
]; /* Attribute name */
707 * Get the default connection as needed...
711 http
= _cupsConnect();
714 * Range check input...
717 if (!http
|| !dest
|| !dinfo
|| !option
)
719 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
724 * Find and return the attribute...
727 snprintf(name
, sizeof(name
), "%s-default", option
);
728 return (ippFindAttribute(dinfo
->attrs
, name
, IPP_TAG_ZERO
));
733 * 'cupsFindDestReady()' - Find the default value(s) for the given option.
735 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
736 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
737 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
738 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
739 * functions to inspect the default value(s) as needed.
741 * @since CUPS 1.7/macOS 10.9@
744 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
746 http_t
*http
, /* I - Connection to destination */
747 cups_dest_t
*dest
, /* I - Destination */
748 cups_dinfo_t
*dinfo
, /* I - Destination information */
749 const char *option
) /* I - Option/attribute name */
751 char name
[IPP_MAX_NAME
]; /* Attribute name */
755 * Get the default connection as needed...
759 http
= _cupsConnect();
762 * Range check input...
765 if (!http
|| !dest
|| !dinfo
|| !option
)
767 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
772 * Find and return the attribute...
775 cups_update_ready(http
, dinfo
);
777 snprintf(name
, sizeof(name
), "%s-ready", option
);
778 return (ippFindAttribute(dinfo
->ready_attrs
, name
, IPP_TAG_ZERO
));
783 * 'cupsFindDestSupported()' - Find the default value(s) for the given option.
785 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
786 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
787 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
788 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
789 * functions to inspect the default value(s) as needed.
791 * @since CUPS 1.7/macOS 10.9@
794 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
795 cupsFindDestSupported(
796 http_t
*http
, /* I - Connection to destination */
797 cups_dest_t
*dest
, /* I - Destination */
798 cups_dinfo_t
*dinfo
, /* I - Destination information */
799 const char *option
) /* I - Option/attribute name */
801 char name
[IPP_MAX_NAME
]; /* Attribute name */
805 * Get the default connection as needed...
809 http
= _cupsConnect();
812 * Range check input...
815 if (!http
|| !dest
|| !dinfo
|| !option
)
817 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
822 * Find and return the attribute...
825 snprintf(name
, sizeof(name
), "%s-supported", option
);
826 return (ippFindAttribute(dinfo
->attrs
, name
, IPP_TAG_ZERO
));
831 * 'cupsFreeDestInfo()' - Free destination information obtained using
832 * @link cupsCopyDestInfo@.
834 * @since CUPS 1.6/macOS 10.8@
838 cupsFreeDestInfo(cups_dinfo_t
*dinfo
) /* I - Destination information */
841 * Range check input...
848 * Free memory and return...
851 _cupsStrFree(dinfo
->resource
);
853 cupsArrayDelete(dinfo
->constraints
);
854 cupsArrayDelete(dinfo
->resolvers
);
856 cupsArrayDelete(dinfo
->localizations
);
858 cupsArrayDelete(dinfo
->media_db
);
860 cupsArrayDelete(dinfo
->cached_db
);
862 ippDelete(dinfo
->ready_attrs
);
863 cupsArrayDelete(dinfo
->ready_db
);
865 ippDelete(dinfo
->attrs
);
872 * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a
875 * The @code flags@ parameter determines which set of media are indexed. For
876 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth
877 * borderless size supported by the printer.
879 * @since CUPS 1.7/macOS 10.9@
882 int /* O - 1 on success, 0 on failure */
883 cupsGetDestMediaByIndex(
884 http_t
*http
, /* I - Connection to destination */
885 cups_dest_t
*dest
, /* I - Destination */
886 cups_dinfo_t
*dinfo
, /* I - Destination information */
887 int n
, /* I - Media size number (0-based) */
888 unsigned flags
, /* I - Media flags */
889 cups_size_t
*size
) /* O - Media size information */
891 _cups_media_db_t
*nsize
; /* Size for N */
892 pwg_media_t
*pwg
; /* PWG media name for size */
896 * Get the default connection as needed...
900 http
= _cupsConnect();
903 * Range check input...
907 memset(size
, 0, sizeof(cups_size_t
));
909 if (!http
|| !dest
|| !dinfo
|| n
< 0 || !size
)
911 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
916 * Load media list as needed...
919 if (flags
& CUPS_MEDIA_FLAGS_READY
)
920 cups_update_ready(http
, dinfo
);
922 if (!dinfo
->cached_db
|| dinfo
->cached_flags
!= flags
)
923 cups_create_cached(http
, dinfo
, flags
);
926 * Copy the size over and return...
929 if ((nsize
= (_cups_media_db_t
*)cupsArrayIndex(dinfo
->cached_db
, n
)) == NULL
)
931 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
935 if (nsize
->size_name
)
936 strlcpy(size
->media
, nsize
->size_name
, sizeof(size
->media
));
938 strlcpy(size
->media
, nsize
->key
, sizeof(size
->media
));
939 else if ((pwg
= pwgMediaForSize(nsize
->width
, nsize
->length
)) != NULL
)
940 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
943 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
947 size
->width
= nsize
->width
;
948 size
->length
= nsize
->length
;
949 size
->bottom
= nsize
->bottom
;
950 size
->left
= nsize
->left
;
951 size
->right
= nsize
->right
;
952 size
->top
= nsize
->top
;
959 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
961 * The "media" string is a PWG media name. "Flags" provides some matching
962 * guidance (multiple flags can be combined):
964 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
965 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
966 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
967 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
968 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
969 * size amongst the "ready" media.
971 * The matching result (if any) is returned in the "cups_size_t" structure.
973 * Returns 1 when there is a match and 0 if there is not a match.
975 * @since CUPS 1.6/macOS 10.8@
978 int /* O - 1 on match, 0 on failure */
979 cupsGetDestMediaByName(
980 http_t
*http
, /* I - Connection to destination */
981 cups_dest_t
*dest
, /* I - Destination */
982 cups_dinfo_t
*dinfo
, /* I - Destination information */
983 const char *media
, /* I - Media name */
984 unsigned flags
, /* I - Media matching flags */
985 cups_size_t
*size
) /* O - Media size information */
987 pwg_media_t
*pwg
; /* PWG media info */
991 * Get the default connection as needed...
995 http
= _cupsConnect();
998 * Range check input...
1002 memset(size
, 0, sizeof(cups_size_t
));
1004 if (!http
|| !dest
|| !dinfo
|| !media
|| !size
)
1006 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1011 * Lookup the media size name...
1014 if ((pwg
= pwgMediaForPWG(media
)) == NULL
)
1015 if ((pwg
= pwgMediaForLegacy(media
)) == NULL
)
1017 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media
));
1018 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unknown media size name."), 1);
1023 * Lookup the size...
1026 return (cups_get_media_db(http
, dinfo
, pwg
, flags
, size
));
1031 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
1033 * "Width" and "length" are the dimensions in hundredths of millimeters.
1034 * "Flags" provides some matching guidance (multiple flags can be combined):
1036 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
1037 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
1038 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
1039 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
1040 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
1041 * size amongst the "ready" media.
1043 * The matching result (if any) is returned in the "cups_size_t" structure.
1045 * Returns 1 when there is a match and 0 if there is not a match.
1047 * @since CUPS 1.6/macOS 10.8@
1050 int /* O - 1 on match, 0 on failure */
1051 cupsGetDestMediaBySize(
1052 http_t
*http
, /* I - Connection to destination */
1053 cups_dest_t
*dest
, /* I - Destination */
1054 cups_dinfo_t
*dinfo
, /* I - Destination information */
1055 int width
, /* I - Media width in hundredths of
1057 int length
, /* I - Media length in hundredths of
1059 unsigned flags
, /* I - Media matching flags */
1060 cups_size_t
*size
) /* O - Media size information */
1062 pwg_media_t
*pwg
; /* PWG media info */
1066 * Get the default connection as needed...
1070 http
= _cupsConnect();
1073 * Range check input...
1077 memset(size
, 0, sizeof(cups_size_t
));
1079 if (!http
|| !dest
|| !dinfo
|| width
<= 0 || length
<= 0 || !size
)
1081 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1086 * Lookup the media size name...
1089 if ((pwg
= pwgMediaForSize(width
, length
)) == NULL
)
1091 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width
,
1093 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Invalid media size."), 1);
1098 * Lookup the size...
1101 return (cups_get_media_db(http
, dinfo
, pwg
, flags
, size
));
1106 * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a
1109 * The @code flags@ parameter determines the set of media sizes that are
1110 * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return
1111 * the number of borderless sizes.
1113 * @since CUPS 1.7/macOS 10.9@
1116 int /* O - Number of sizes */
1117 cupsGetDestMediaCount(
1118 http_t
*http
, /* I - Connection to destination */
1119 cups_dest_t
*dest
, /* I - Destination */
1120 cups_dinfo_t
*dinfo
, /* I - Destination information */
1121 unsigned flags
) /* I - Media flags */
1124 * Get the default connection as needed...
1128 http
= _cupsConnect();
1131 * Range check input...
1134 if (!http
|| !dest
|| !dinfo
)
1136 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1141 * Load media list as needed...
1144 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1145 cups_update_ready(http
, dinfo
);
1147 if (!dinfo
->cached_db
|| dinfo
->cached_flags
!= flags
)
1148 cups_create_cached(http
, dinfo
, flags
);
1150 return (cupsArrayCount(dinfo
->cached_db
));
1155 * 'cupsGetDestMediaDefault()' - Get the default size for a destination.
1157 * The @code flags@ parameter determines which default size is returned. For
1158 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default
1159 * borderless size, typically US Letter or A4, but sometimes 4x6 photo media.
1161 * @since CUPS 1.7/macOS 10.9@
1164 int /* O - 1 on success, 0 on failure */
1165 cupsGetDestMediaDefault(
1166 http_t
*http
, /* I - Connection to destination */
1167 cups_dest_t
*dest
, /* I - Destination */
1168 cups_dinfo_t
*dinfo
, /* I - Destination information */
1169 unsigned flags
, /* I - Media flags */
1170 cups_size_t
*size
) /* O - Media size information */
1172 const char *media
; /* Default media size */
1176 * Get the default connection as needed...
1180 http
= _cupsConnect();
1183 * Range check input...
1187 memset(size
, 0, sizeof(cups_size_t
));
1189 if (!http
|| !dest
|| !dinfo
|| !size
)
1191 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1196 * Get the default media size, if any...
1199 if ((media
= cupsGetOption("media", dest
->num_options
,
1200 dest
->options
)) == NULL
)
1201 media
= "na_letter_8.5x11in";
1203 if (cupsGetDestMediaByName(http
, dest
, dinfo
, media
, flags
, size
))
1206 if (strcmp(media
, "na_letter_8.5x11in") &&
1207 cupsGetDestMediaByName(http
, dest
, dinfo
, "iso_a4_210x297mm", flags
,
1211 if (strcmp(media
, "iso_a4_210x297mm") &&
1212 cupsGetDestMediaByName(http
, dest
, dinfo
, "na_letter_8.5x11in", flags
,
1216 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) &&
1217 cupsGetDestMediaByName(http
, dest
, dinfo
, "na_index_4x6in", flags
, size
))
1221 * Fall back to the first matching media size...
1224 return (cupsGetDestMediaByIndex(http
, dest
, dinfo
, 0, flags
, size
));
1229 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
1234 cups_array_t
*a
, /* I - Array */
1235 ipp_t
*collection
) /* I - Collection value */
1237 ipp_attribute_t
*attr
; /* Attribute */
1238 _cups_dconstres_t
*temp
; /* Current constraint/resolver */
1241 if ((attr
= ippFindAttribute(collection
, "resolver-name",
1242 IPP_TAG_NAME
)) == NULL
)
1245 if ((temp
= calloc(1, sizeof(_cups_dconstres_t
))) == NULL
)
1248 temp
->name
= attr
->values
[0].string
.text
;
1249 temp
->collection
= collection
;
1251 cupsArrayAdd(a
, temp
);
1256 * 'cups_compare_dconstres()' - Compare to resolver entries.
1259 static int /* O - Result of comparison */
1260 cups_compare_dconstres(
1261 _cups_dconstres_t
*a
, /* I - First resolver */
1262 _cups_dconstres_t
*b
) /* I - Second resolver */
1264 return (strcmp(a
->name
, b
->name
));
1269 * 'cups_compare_media_db()' - Compare two media entries.
1272 static int /* O - Result of comparison */
1273 cups_compare_media_db(
1274 _cups_media_db_t
*a
, /* I - First media entries */
1275 _cups_media_db_t
*b
) /* I - Second media entries */
1277 int result
; /* Result of comparison */
1280 if ((result
= a
->width
- b
->width
) == 0)
1281 result
= a
->length
- b
->length
;
1288 * 'cups_copy_media_db()' - Copy a media entry.
1291 static _cups_media_db_t
* /* O - New media entry */
1293 _cups_media_db_t
*mdb
) /* I - Media entry to copy */
1295 _cups_media_db_t
*temp
; /* New media entry */
1298 if ((temp
= calloc(1, sizeof(_cups_media_db_t
))) == NULL
)
1302 temp
->color
= _cupsStrAlloc(mdb
->color
);
1304 temp
->key
= _cupsStrAlloc(mdb
->key
);
1306 temp
->info
= _cupsStrAlloc(mdb
->info
);
1308 temp
->size_name
= _cupsStrAlloc(mdb
->size_name
);
1310 temp
->source
= _cupsStrAlloc(mdb
->source
);
1312 temp
->type
= _cupsStrAlloc(mdb
->type
);
1314 temp
->width
= mdb
->width
;
1315 temp
->length
= mdb
->length
;
1316 temp
->bottom
= mdb
->bottom
;
1317 temp
->left
= mdb
->left
;
1318 temp
->right
= mdb
->right
;
1319 temp
->top
= mdb
->top
;
1326 * 'cups_create_cached()' - Create the media selection cache.
1330 cups_create_cached(http_t
*http
, /* I - Connection to destination */
1331 cups_dinfo_t
*dinfo
, /* I - Destination information */
1332 unsigned flags
) /* I - Media selection flags */
1334 cups_array_t
*db
; /* Media database array to use */
1335 _cups_media_db_t
*mdb
, /* Media database entry */
1336 *first
; /* First entry this size */
1339 DEBUG_printf(("3cups_create_cached(http=%p, dinfo=%p, flags=%u)", (void *)http
, (void *)dinfo
, flags
));
1341 if (dinfo
->cached_db
)
1342 cupsArrayDelete(dinfo
->cached_db
);
1344 dinfo
->cached_db
= cupsArrayNew(NULL
, NULL
);
1345 dinfo
->cached_flags
= flags
;
1347 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1349 DEBUG_puts("4cups_create_cached: ready media");
1351 cups_update_ready(http
, dinfo
);
1352 db
= dinfo
->ready_db
;
1356 DEBUG_puts("4cups_create_cached: supported media");
1358 if (!dinfo
->media_db
)
1359 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
);
1361 db
= dinfo
->media_db
;
1364 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
), first
= mdb
;
1366 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1368 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
));
1370 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1372 if (!mdb
->left
&& !mdb
->right
&& !mdb
->top
&& !mdb
->bottom
)
1374 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb
));
1375 cupsArrayAdd(dinfo
->cached_db
, mdb
);
1378 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1380 if (first
->width
!= mdb
->width
|| first
->length
!= mdb
->length
)
1382 DEBUG_printf(("4cups_create_cached: add %p", (void *)first
));
1383 cupsArrayAdd(dinfo
->cached_db
, first
);
1386 else if (mdb
->left
>= first
->left
&& mdb
->right
>= first
->right
&& mdb
->top
>= first
->top
&& mdb
->bottom
>= first
->bottom
&&
1387 (mdb
->left
!= first
->left
|| mdb
->right
!= first
->right
|| mdb
->top
!= first
->top
|| mdb
->bottom
!= first
->bottom
))
1392 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb
));
1393 cupsArrayAdd(dinfo
->cached_db
, mdb
);
1397 if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1399 DEBUG_printf(("4cups_create_cached: add %p", (void *)first
));
1400 cupsArrayAdd(dinfo
->cached_db
, first
);
1406 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
1410 cups_create_constraints(
1411 cups_dinfo_t
*dinfo
) /* I - Destination information */
1413 int i
; /* Looping var */
1414 ipp_attribute_t
*attr
; /* Attribute */
1415 _ipp_value_t
*val
; /* Current value */
1418 dinfo
->constraints
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, NULL
,
1419 (cups_afree_func_t
)free
);
1420 dinfo
->resolvers
= cupsArrayNew3((cups_array_func_t
)cups_compare_dconstres
,
1421 NULL
, NULL
, 0, NULL
,
1422 (cups_afree_func_t
)free
);
1424 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-constraints-supported",
1425 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1427 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
1428 cups_add_dconstres(dinfo
->constraints
, val
->collection
);
1431 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-resolvers-supported",
1432 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1434 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
1435 cups_add_dconstres(dinfo
->resolvers
, val
->collection
);
1441 * 'cups_create_defaults()' - Create the -default option array.
1443 * TODO: Need to support collection defaults...
1447 cups_create_defaults(
1448 cups_dinfo_t
*dinfo
) /* I - Destination information */
1450 ipp_attribute_t
*attr
; /* Current attribute */
1451 char name
[IPP_MAX_NAME
+ 1],
1453 *nameptr
, /* Pointer into current name */
1454 value
[2048]; /* Current value */
1458 * Iterate through the printer attributes looking for xxx-default and adding
1459 * xxx=value to the defaults option array.
1462 for (attr
= ippFirstAttribute(dinfo
->attrs
);
1464 attr
= ippNextAttribute(dinfo
->attrs
))
1466 if (!attr
->name
|| attr
->group_tag
!= IPP_TAG_PRINTER
)
1469 if (attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
)
1470 continue; /* TODO: STR #4096 */
1472 if ((nameptr
= attr
->name
+ strlen(attr
->name
) - 8) <= attr
->name
||
1473 strcmp(nameptr
, "-default"))
1476 strlcpy(name
, attr
->name
, sizeof(name
));
1477 if ((nameptr
= name
+ strlen(name
) - 8) <= name
||
1478 strcmp(nameptr
, "-default"))
1483 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
1486 dinfo
->num_defaults
= cupsAddOption(name
, value
, dinfo
->num_defaults
,
1493 * 'cups_create_media_db()' - Create the media database.
1497 cups_create_media_db(
1498 cups_dinfo_t
*dinfo
, /* I - Destination information */
1499 unsigned flags
) /* I - Media flags */
1501 int i
; /* Looping var */
1502 _ipp_value_t
*val
; /* Current value */
1503 ipp_attribute_t
*media_col_db
, /* media-col-database */
1504 *media_attr
, /* media-xxx */
1505 *x_dimension
, /* x-dimension */
1506 *y_dimension
; /* y-dimension */
1507 pwg_media_t
*pwg
; /* PWG media info */
1508 cups_array_t
*db
; /* New media database array */
1509 _cups_media_db_t mdb
; /* Media entry */
1512 db
= cupsArrayNew3((cups_array_func_t
)cups_compare_media_db
,
1514 (cups_acopy_func_t
)cups_copy_media_db
,
1515 (cups_afree_func_t
)cups_free_media_db
);
1517 if (flags
== CUPS_MEDIA_FLAGS_READY
)
1519 dinfo
->ready_db
= db
;
1521 media_col_db
= ippFindAttribute(dinfo
->ready_attrs
, "media-col-ready",
1522 IPP_TAG_BEGIN_COLLECTION
);
1523 media_attr
= ippFindAttribute(dinfo
->ready_attrs
, "media-ready",
1528 dinfo
->media_db
= db
;
1529 dinfo
->min_size
.width
= INT_MAX
;
1530 dinfo
->min_size
.length
= INT_MAX
;
1531 dinfo
->max_size
.width
= 0;
1532 dinfo
->max_size
.length
= 0;
1534 media_col_db
= ippFindAttribute(dinfo
->attrs
, "media-col-database",
1535 IPP_TAG_BEGIN_COLLECTION
);
1536 media_attr
= ippFindAttribute(dinfo
->attrs
, "media-supported",
1542 _ipp_value_t
*custom
= NULL
; /* Custom size range value */
1544 for (i
= media_col_db
->num_values
, val
= media_col_db
->values
;
1548 memset(&mdb
, 0, sizeof(mdb
));
1550 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size",
1551 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1553 ipp_t
*media_size
= media_attr
->values
[0].collection
;
1554 /* media-size collection value */
1556 if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1557 IPP_TAG_INTEGER
)) != NULL
&&
1558 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1559 IPP_TAG_INTEGER
)) != NULL
)
1565 mdb
.width
= x_dimension
->values
[0].integer
;
1566 mdb
.length
= y_dimension
->values
[0].integer
;
1568 else if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1569 IPP_TAG_INTEGER
)) != NULL
&&
1570 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1571 IPP_TAG_RANGE
)) != NULL
)
1577 mdb
.width
= x_dimension
->values
[0].integer
;
1578 mdb
.length
= y_dimension
->values
[0].range
.upper
;
1580 else if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
1581 (x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1582 IPP_TAG_RANGE
)) != NULL
&&
1583 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1584 IPP_TAG_RANGE
)) != NULL
)
1587 * Custom size range; save this as the custom size value with default
1588 * margins, then continue; we'll capture the real margins below...
1593 dinfo
->min_size
.width
= x_dimension
->values
[0].range
.lower
;
1594 dinfo
->min_size
.length
= y_dimension
->values
[0].range
.lower
;
1595 dinfo
->min_size
.left
=
1596 dinfo
->min_size
.right
= 635; /* Default 1/4" side margins */
1597 dinfo
->min_size
.top
=
1598 dinfo
->min_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1600 dinfo
->max_size
.width
= x_dimension
->values
[0].range
.upper
;
1601 dinfo
->max_size
.length
= y_dimension
->values
[0].range
.upper
;
1602 dinfo
->max_size
.left
=
1603 dinfo
->max_size
.right
= 635; /* Default 1/4" side margins */
1604 dinfo
->max_size
.top
=
1605 dinfo
->max_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1610 if ((media_attr
= ippFindAttribute(val
->collection
, "media-color",
1611 IPP_TAG_ZERO
)) != NULL
&&
1612 (media_attr
->value_tag
== IPP_TAG_NAME
||
1613 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1614 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1615 mdb
.color
= media_attr
->values
[0].string
.text
;
1617 if ((media_attr
= ippFindAttribute(val
->collection
, "media-info",
1618 IPP_TAG_TEXT
)) != NULL
)
1619 mdb
.info
= media_attr
->values
[0].string
.text
;
1621 if ((media_attr
= ippFindAttribute(val
->collection
, "media-key",
1622 IPP_TAG_ZERO
)) != NULL
&&
1623 (media_attr
->value_tag
== IPP_TAG_NAME
||
1624 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1625 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1626 mdb
.key
= media_attr
->values
[0].string
.text
;
1628 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size-name",
1629 IPP_TAG_ZERO
)) != NULL
&&
1630 (media_attr
->value_tag
== IPP_TAG_NAME
||
1631 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1632 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1633 mdb
.size_name
= media_attr
->values
[0].string
.text
;
1635 if ((media_attr
= ippFindAttribute(val
->collection
, "media-source",
1636 IPP_TAG_ZERO
)) != NULL
&&
1637 (media_attr
->value_tag
== IPP_TAG_NAME
||
1638 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1639 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1640 mdb
.source
= media_attr
->values
[0].string
.text
;
1642 if ((media_attr
= ippFindAttribute(val
->collection
, "media-type",
1643 IPP_TAG_ZERO
)) != NULL
&&
1644 (media_attr
->value_tag
== IPP_TAG_NAME
||
1645 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1646 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1647 mdb
.type
= media_attr
->values
[0].string
.text
;
1649 if ((media_attr
= ippFindAttribute(val
->collection
, "media-bottom-margin",
1650 IPP_TAG_INTEGER
)) != NULL
)
1651 mdb
.bottom
= media_attr
->values
[0].integer
;
1653 if ((media_attr
= ippFindAttribute(val
->collection
, "media-left-margin",
1654 IPP_TAG_INTEGER
)) != NULL
)
1655 mdb
.left
= media_attr
->values
[0].integer
;
1657 if ((media_attr
= ippFindAttribute(val
->collection
, "media-right-margin",
1658 IPP_TAG_INTEGER
)) != NULL
)
1659 mdb
.right
= media_attr
->values
[0].integer
;
1661 if ((media_attr
= ippFindAttribute(val
->collection
, "media-top-margin",
1662 IPP_TAG_INTEGER
)) != NULL
)
1663 mdb
.top
= media_attr
->values
[0].integer
;
1665 cupsArrayAdd(db
, &mdb
);
1670 if ((media_attr
= ippFindAttribute(custom
->collection
,
1671 "media-bottom-margin",
1672 IPP_TAG_INTEGER
)) != NULL
)
1674 dinfo
->min_size
.top
=
1675 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
1678 if ((media_attr
= ippFindAttribute(custom
->collection
,
1679 "media-left-margin",
1680 IPP_TAG_INTEGER
)) != NULL
)
1682 dinfo
->min_size
.left
=
1683 dinfo
->max_size
.left
= media_attr
->values
[0].integer
;
1686 if ((media_attr
= ippFindAttribute(custom
->collection
,
1687 "media-right-margin",
1688 IPP_TAG_INTEGER
)) != NULL
)
1690 dinfo
->min_size
.right
=
1691 dinfo
->max_size
.right
= media_attr
->values
[0].integer
;
1694 if ((media_attr
= ippFindAttribute(custom
->collection
,
1696 IPP_TAG_INTEGER
)) != NULL
)
1698 dinfo
->min_size
.top
=
1699 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
1703 else if (media_attr
&&
1704 (media_attr
->value_tag
== IPP_TAG_NAME
||
1705 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1706 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1708 memset(&mdb
, 0, sizeof(mdb
));
1711 mdb
.right
= 635; /* Default 1/4" side margins */
1713 mdb
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1715 for (i
= media_attr
->num_values
, val
= media_attr
->values
;
1719 if ((pwg
= pwgMediaForPWG(val
->string
.text
)) == NULL
)
1720 if ((pwg
= pwgMediaForLegacy(val
->string
.text
)) == NULL
)
1722 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
1727 mdb
.width
= pwg
->width
;
1728 mdb
.length
= pwg
->length
;
1730 if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
1731 !strncmp(val
->string
.text
, "custom_min_", 11))
1733 mdb
.size_name
= NULL
;
1734 dinfo
->min_size
= mdb
;
1736 else if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
1737 !strncmp(val
->string
.text
, "custom_max_", 11))
1739 mdb
.size_name
= NULL
;
1740 dinfo
->max_size
= mdb
;
1744 mdb
.size_name
= val
->string
.text
;
1746 cupsArrayAdd(db
, &mdb
);
1754 * 'cups_free_media_cb()' - Free a media entry.
1759 _cups_media_db_t
*mdb
) /* I - Media entry to free */
1762 _cupsStrFree(mdb
->color
);
1764 _cupsStrFree(mdb
->key
);
1766 _cupsStrFree(mdb
->info
);
1768 _cupsStrFree(mdb
->size_name
);
1770 _cupsStrFree(mdb
->source
);
1772 _cupsStrFree(mdb
->type
);
1779 * 'cups_get_media_db()' - Lookup the media entry for a given size.
1782 static int /* O - 1 on match, 0 on failure */
1783 cups_get_media_db(http_t
*http
, /* I - Connection to destination */
1784 cups_dinfo_t
*dinfo
, /* I - Destination information */
1785 pwg_media_t
*pwg
, /* I - PWG media info */
1786 unsigned flags
, /* I - Media matching flags */
1787 cups_size_t
*size
) /* O - Media size/margin/name info */
1789 cups_array_t
*db
; /* Which media database to query */
1790 _cups_media_db_t
*mdb
, /* Current media database entry */
1791 *best
= NULL
, /* Best matching entry */
1792 key
; /* Search key */
1796 * Create the media database as needed...
1799 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1801 cups_update_ready(http
, dinfo
);
1802 db
= dinfo
->ready_db
;
1806 if (!dinfo
->media_db
)
1807 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
);
1809 db
= dinfo
->media_db
;
1816 memset(&key
, 0, sizeof(key
));
1817 key
.width
= pwg
->width
;
1818 key
.length
= pwg
->length
;
1820 if ((mdb
= cupsArrayFind(db
, &key
)) != NULL
)
1823 * Found an exact match, let's figure out the best margins for the flags
1829 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1832 * Look for the smallest margins...
1835 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 || best
->bottom
!= 0)
1837 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1838 mdb
&& !cups_compare_media_db(mdb
, &key
);
1839 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1841 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
1842 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
)
1845 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
1853 * If we need an exact match, return no-match if the size is not
1857 if ((flags
& CUPS_MEDIA_FLAGS_EXACT
) &&
1858 (best
->left
|| best
->right
|| best
->top
|| best
->bottom
))
1861 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1864 * Look for the largest margins...
1867 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1868 mdb
&& !cups_compare_media_db(mdb
, &key
);
1869 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1871 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
1872 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
&&
1873 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
1880 * Look for the smallest non-zero margins...
1883 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1884 mdb
&& !cups_compare_media_db(mdb
, &key
);
1885 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1887 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
1888 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) || best
->right
== 0) &&
1889 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
1890 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) || best
->bottom
== 0) &&
1891 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
1896 else if (flags
& CUPS_MEDIA_FLAGS_EXACT
)
1899 * See if we can do this as a custom size...
1902 if (pwg
->width
< dinfo
->min_size
.width
||
1903 pwg
->width
> dinfo
->max_size
.width
||
1904 pwg
->length
< dinfo
->min_size
.length
||
1905 pwg
->length
> dinfo
->max_size
.length
)
1906 return (0); /* Out of range */
1908 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) &&
1909 (dinfo
->min_size
.left
> 0 || dinfo
->min_size
.right
> 0 ||
1910 dinfo
->min_size
.top
> 0 || dinfo
->min_size
.bottom
> 0))
1911 return (0); /* Not borderless */
1913 key
.size_name
= (char *)pwg
->pwg
;
1914 key
.bottom
= dinfo
->min_size
.bottom
;
1915 key
.left
= dinfo
->min_size
.left
;
1916 key
.right
= dinfo
->min_size
.right
;
1917 key
.top
= dinfo
->min_size
.top
;
1921 else if (pwg
->width
>= dinfo
->min_size
.width
&&
1922 pwg
->width
<= dinfo
->max_size
.width
&&
1923 pwg
->length
>= dinfo
->min_size
.length
&&
1924 pwg
->length
<= dinfo
->max_size
.length
)
1927 * Map to custom size...
1930 key
.size_name
= (char *)pwg
->pwg
;
1931 key
.bottom
= dinfo
->min_size
.bottom
;
1932 key
.left
= dinfo
->min_size
.left
;
1933 key
.right
= dinfo
->min_size
.right
;
1934 key
.top
= dinfo
->min_size
.top
;
1941 * Find a close size...
1944 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
);
1946 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1947 if (cups_is_close_media_db(mdb
, &key
))
1955 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1958 * Look for the smallest margins...
1961 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 ||
1964 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1965 mdb
&& cups_is_close_media_db(mdb
, &key
);
1966 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1968 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
1969 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
&&
1970 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
1973 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
1980 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1983 * Look for the largest margins...
1986 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1987 mdb
&& cups_is_close_media_db(mdb
, &key
);
1988 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1990 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
1991 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
&&
1992 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
1999 * Look for the smallest non-zero margins...
2002 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2003 mdb
&& cups_is_close_media_db(mdb
, &key
);
2004 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2006 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
2007 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) ||
2008 best
->right
== 0) &&
2009 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
2010 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) ||
2011 best
->bottom
== 0) &&
2012 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2021 * Return the matching size...
2024 if (best
->size_name
)
2025 strlcpy(size
->media
, best
->size_name
, sizeof(size
->media
));
2027 strlcpy(size
->media
, best
->key
, sizeof(size
->media
));
2029 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
2031 size
->width
= best
->width
;
2032 size
->length
= best
->length
;
2033 size
->bottom
= best
->bottom
;
2034 size
->left
= best
->left
;
2035 size
->right
= best
->right
;
2036 size
->top
= best
->top
;
2046 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
2047 * close to the same size.
2049 * Currently we use 5 points (from PostScript) as the matching range...
2052 static int /* O - 1 if the sizes are close */
2053 cups_is_close_media_db(
2054 _cups_media_db_t
*a
, /* I - First media entries */
2055 _cups_media_db_t
*b
) /* I - Second media entries */
2057 int dwidth
, /* Difference in width */
2058 dlength
; /* Difference in length */
2061 dwidth
= a
->width
- b
->width
;
2062 dlength
= a
->length
- b
->length
;
2064 return (dwidth
>= -176 && dwidth
<= 176 &&
2065 dlength
>= -176 && dlength
<= 176);
2070 * 'cups_test_constraints()' - Test constraints.
2072 * TODO: STR #4096 - Need to properly support media-col contraints...
2075 static cups_array_t
* /* O - Active constraints */
2076 cups_test_constraints(
2077 cups_dinfo_t
*dinfo
, /* I - Destination information */
2078 const char *new_option
, /* I - Newly selected option */
2079 const char *new_value
, /* I - Newly selected value */
2080 int num_options
, /* I - Number of options */
2081 cups_option_t
*options
, /* I - Options */
2082 int *num_conflicts
, /* O - Number of conflicting options */
2083 cups_option_t
**conflicts
) /* O - Conflicting options */
2085 int i
, /* Looping var */
2086 match
; /* Value matches? */
2087 int num_matching
; /* Number of matching options */
2088 cups_option_t
*matching
; /* Matching options */
2089 _cups_dconstres_t
*c
; /* Current constraint */
2090 cups_array_t
*active
= NULL
; /* Active constraints */
2091 ipp_attribute_t
*attr
; /* Current attribute */
2092 _ipp_value_t
*attrval
; /* Current attribute value */
2093 const char *value
; /* Current value */
2094 char temp
[1024]; /* Temporary string */
2095 int int_value
; /* Integer value */
2096 int xres_value
, /* Horizontal resolution */
2097 yres_value
; /* Vertical resolution */
2098 ipp_res_t units_value
; /* Resolution units */
2101 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(dinfo
->constraints
);
2103 c
= (_cups_dconstres_t
*)cupsArrayNext(dinfo
->constraints
))
2108 for (attr
= ippFirstAttribute(c
->collection
);
2110 attr
= ippNextAttribute(c
->collection
))
2112 if (attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
)
2113 break; /* TODO: STR #4096 */
2116 * Get the value for the current attribute in the constraint...
2119 if (new_option
&& new_value
&& !strcmp(attr
->name
, new_option
))
2121 else if ((value
= cupsGetOption(attr
->name
, num_options
,
2123 value
= cupsGetOption(attr
->name
, dinfo
->num_defaults
, dinfo
->defaults
);
2128 * Not set so this constraint does not apply...
2136 switch (attr
->value_tag
)
2138 case IPP_TAG_INTEGER
:
2140 int_value
= atoi(value
);
2142 for (i
= attr
->num_values
, attrval
= attr
->values
;
2146 if (attrval
->integer
== int_value
)
2154 case IPP_TAG_BOOLEAN
:
2155 int_value
= !strcmp(value
, "true");
2157 for (i
= attr
->num_values
, attrval
= attr
->values
;
2161 if (attrval
->boolean
== int_value
)
2169 case IPP_TAG_RANGE
:
2170 int_value
= atoi(value
);
2172 for (i
= attr
->num_values
, attrval
= attr
->values
;
2176 if (int_value
>= attrval
->range
.lower
&&
2177 int_value
<= attrval
->range
.upper
)
2185 case IPP_TAG_RESOLUTION
:
2186 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
2188 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
2191 yres_value
= xres_value
;
2194 if (!strcmp(temp
, "dpi"))
2195 units_value
= IPP_RES_PER_INCH
;
2196 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
2197 units_value
= IPP_RES_PER_CM
;
2201 for (i
= attr
->num_values
, attrval
= attr
->values
;
2205 if (attrval
->resolution
.xres
== xres_value
&&
2206 attrval
->resolution
.yres
== yres_value
&&
2207 attrval
->resolution
.units
== units_value
)
2217 case IPP_TAG_KEYWORD
:
2218 case IPP_TAG_CHARSET
:
2220 case IPP_TAG_URISCHEME
:
2221 case IPP_TAG_MIMETYPE
:
2222 case IPP_TAG_LANGUAGE
:
2223 case IPP_TAG_TEXTLANG
:
2224 case IPP_TAG_NAMELANG
:
2225 for (i
= attr
->num_values
, attrval
= attr
->values
;
2229 if (!strcmp(attrval
->string
.text
, value
))
2244 num_matching
= cupsAddOption(attr
->name
, value
, num_matching
, &matching
);
2250 active
= cupsArrayNew(NULL
, NULL
);
2252 cupsArrayAdd(active
, c
);
2254 if (num_conflicts
&& conflicts
)
2256 cups_option_t
*moption
; /* Matching option */
2258 for (i
= num_matching
, moption
= matching
; i
> 0; i
--, moption
++)
2259 *num_conflicts
= cupsAddOption(moption
->name
, moption
->value
,
2260 *num_conflicts
, conflicts
);
2264 cupsFreeOptions(num_matching
, matching
);
2272 * 'cups_update_ready()' - Update xxx-ready attributes for the printer.
2276 cups_update_ready(http_t
*http
, /* I - Connection to destination */
2277 cups_dinfo_t
*dinfo
) /* I - Destination information */
2279 ipp_t
*request
; /* Get-Printer-Attributes request */
2280 static const char * const pattrs
[] = /* Printer attributes we want */
2282 "finishings-col-ready",
2284 "job-finishings-col-ready",
2285 "job-finishings-ready",
2292 * Don't update more than once every 30 seconds...
2295 if ((time(NULL
) - dinfo
->ready_time
) < _CUPS_MEDIA_READY_TTL
)
2299 * Free any previous results...
2302 if (dinfo
->cached_flags
& CUPS_MEDIA_FLAGS_READY
)
2304 cupsArrayDelete(dinfo
->cached_db
);
2305 dinfo
->cached_db
= NULL
;
2306 dinfo
->cached_flags
= CUPS_MEDIA_FLAGS_DEFAULT
;
2309 ippDelete(dinfo
->ready_attrs
);
2310 dinfo
->ready_attrs
= NULL
;
2312 cupsArrayDelete(dinfo
->ready_db
);
2313 dinfo
->ready_db
= NULL
;
2316 * Query the xxx-ready values...
2319 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
2320 ippSetVersion(request
, dinfo
->version
/ 10, dinfo
->version
% 10);
2322 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
2324 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
2326 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "requested-attributes", (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
2328 dinfo
->ready_attrs
= cupsDoRequest(http
, request
, dinfo
->resource
);
2331 * Update the ready media database...
2334 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_READY
);
2337 * Update last lookup time and return...
2340 dinfo
->ready_time
= time(NULL
);