2 * Destination option/media support for CUPS.
4 * Copyright 2012-2017 by Apple Inc.
6 * These coded instructions, statements, and computer programs are the
7 * property of Apple Inc. and are protected by Federal copyright
8 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
9 * which should have been included with this file. If this file is
10 * missing or damaged, see the license at "http://www.cups.org/".
12 * This file is subject to the Apple OS-Developed Software exception.
16 * Include necessary headers...
19 #include "cups-private.h"
26 #define _CUPS_MEDIA_READY_TTL 30 /* Life of xxx-ready values */
33 static void cups_add_dconstres(cups_array_t
*a
, ipp_t
*collection
);
34 static int cups_compare_dconstres(_cups_dconstres_t
*a
,
35 _cups_dconstres_t
*b
);
36 static int cups_compare_media_db(_cups_media_db_t
*a
,
38 static _cups_media_db_t
*cups_copy_media_db(_cups_media_db_t
*mdb
);
39 static void cups_create_cached(http_t
*http
, cups_dinfo_t
*dinfo
,
41 static void cups_create_constraints(cups_dinfo_t
*dinfo
);
42 static void cups_create_defaults(cups_dinfo_t
*dinfo
);
43 static void cups_create_media_db(cups_dinfo_t
*dinfo
,
45 static void cups_free_media_db(_cups_media_db_t
*mdb
);
46 static int cups_get_media_db(http_t
*http
, cups_dinfo_t
*dinfo
,
47 pwg_media_t
*pwg
, unsigned flags
,
49 static int cups_is_close_media_db(_cups_media_db_t
*a
,
51 static cups_array_t
*cups_test_constraints(cups_dinfo_t
*dinfo
,
52 const char *new_option
,
53 const char *new_value
,
55 cups_option_t
*options
,
57 cups_option_t
**conflicts
);
58 static void cups_update_ready(http_t
*http
, cups_dinfo_t
*dinfo
);
62 * 'cupsCheckDestSupported()' - Check that the option and value are supported
65 * Returns 1 if supported, 0 otherwise.
67 * @since CUPS 1.6/macOS 10.8@
70 int /* O - 1 if supported, 0 otherwise */
71 cupsCheckDestSupported(
72 http_t
*http
, /* I - Connection to destination */
73 cups_dest_t
*dest
, /* I - Destination */
74 cups_dinfo_t
*dinfo
, /* I - Destination information */
75 const char *option
, /* I - Option */
76 const char *value
) /* I - Value or @code NULL@ */
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 * Get the default connection as needed...
93 http
= _cupsConnect();
96 * Range check input...
99 if (!http
|| !dest
|| !dinfo
|| !option
)
103 * Lookup the attribute...
106 if (strstr(option
, "-supported"))
107 attr
= ippFindAttribute(dinfo
->attrs
, option
, IPP_TAG_ZERO
);
110 snprintf(temp
, sizeof(temp
), "%s-supported", option
);
111 attr
= ippFindAttribute(dinfo
->attrs
, temp
, IPP_TAG_ZERO
);
124 if (!strcmp(option
, "media") && !strncmp(value
, "custom_", 7))
127 * Check range of custom media sizes...
130 pwg_media_t
*pwg
; /* Current PWG media size info */
131 int min_width
, /* Minimum width */
132 min_length
, /* Minimum length */
133 max_width
, /* Maximum width */
134 max_length
; /* Maximum length */
137 * Get the minimum and maximum size...
140 min_width
= min_length
= INT_MAX
;
141 max_width
= max_length
= 0;
143 for (i
= attr
->num_values
, attrval
= attr
->values
;
147 if (!strncmp(attrval
->string
.text
, "custom_min_", 11) &&
148 (pwg
= pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
150 min_width
= pwg
->width
;
151 min_length
= pwg
->length
;
153 else if (!strncmp(attrval
->string
.text
, "custom_max_", 11) &&
154 (pwg
= pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
156 max_width
= pwg
->width
;
157 max_length
= pwg
->length
;
165 if (min_width
< INT_MAX
&& max_width
> 0 &&
166 (pwg
= pwgMediaForPWG(value
)) != NULL
&&
167 pwg
->width
>= min_width
&& pwg
->width
<= max_width
&&
168 pwg
->length
>= min_length
&& pwg
->length
<= max_length
)
174 * Check literal values...
177 switch (attr
->value_tag
)
179 case IPP_TAG_INTEGER
:
181 int_value
= atoi(value
);
183 for (i
= 0; i
< attr
->num_values
; i
++)
184 if (attr
->values
[i
].integer
== int_value
)
188 case IPP_TAG_BOOLEAN
:
189 return (attr
->values
[0].boolean
);
192 int_value
= atoi(value
);
194 for (i
= 0; i
< attr
->num_values
; i
++)
195 if (int_value
>= attr
->values
[i
].range
.lower
&&
196 int_value
<= attr
->values
[i
].range
.upper
)
200 case IPP_TAG_RESOLUTION
:
201 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
203 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
206 yres_value
= xres_value
;
209 if (!strcmp(temp
, "dpi"))
210 units_value
= IPP_RES_PER_INCH
;
211 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
212 units_value
= IPP_RES_PER_CM
;
216 for (i
= attr
->num_values
, attrval
= attr
->values
;
220 if (attrval
->resolution
.xres
== xres_value
&&
221 attrval
->resolution
.yres
== yres_value
&&
222 attrval
->resolution
.units
== units_value
)
229 case IPP_TAG_KEYWORD
:
230 case IPP_TAG_CHARSET
:
232 case IPP_TAG_URISCHEME
:
233 case IPP_TAG_MIMETYPE
:
234 case IPP_TAG_LANGUAGE
:
235 case IPP_TAG_TEXTLANG
:
236 case IPP_TAG_NAMELANG
:
237 for (i
= 0; i
< attr
->num_values
; i
++)
238 if (!strcmp(attr
->values
[i
].string
.text
, value
))
248 * If we get there the option+value is not supported...
256 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
259 * "num_options" and "options" represent the currently selected options by the
260 * user. "new_option" and "new_value" are the setting the user has just
263 * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
264 * there was an unrecoverable error such as a resolver loop.
266 * If "num_conflicts" and "conflicts" are not @code NULL@, they are set to
267 * contain the list of conflicting option/value pairs. Similarly, if
268 * "num_resolved" and "resolved" are not @code NULL@ they will be set to the
269 * list of changes needed to resolve the conflict.
271 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
272 * to 0 and @code NULL@, respectively, then the conflict cannot be resolved.
274 * @since CUPS 1.6/macOS 10.8@
277 int /* O - 1 if there is a conflict, 0 if none, -1 on error */
278 cupsCopyDestConflicts(
279 http_t
*http
, /* I - Connection to destination */
280 cups_dest_t
*dest
, /* I - Destination */
281 cups_dinfo_t
*dinfo
, /* I - Destination information */
282 int num_options
, /* I - Number of current options */
283 cups_option_t
*options
, /* I - Current options */
284 const char *new_option
, /* I - New option */
285 const char *new_value
, /* I - New value */
286 int *num_conflicts
, /* O - Number of conflicting options */
287 cups_option_t
**conflicts
, /* O - Conflicting options */
288 int *num_resolved
, /* O - Number of options to resolve */
289 cups_option_t
**resolved
) /* O - Resolved options */
291 int i
, /* Looping var */
292 have_conflicts
= 0, /* Do we have conflicts? */
293 changed
, /* Did we change something? */
294 tries
, /* Number of tries for resolution */
295 num_myconf
= 0, /* My number of conflicting options */
296 num_myres
= 0; /* My number of resolved options */
297 cups_option_t
*myconf
= NULL
, /* My conflicting options */
298 *myres
= NULL
, /* My resolved options */
299 *myoption
, /* My current option */
300 *option
; /* Current option */
301 cups_array_t
*active
= NULL
, /* Active conflicts */
302 *pass
= NULL
, /* Resolvers for this pass */
303 *resolvers
= NULL
, /* Resolvers we have used */
304 *test
; /* Test array for conflicts */
305 _cups_dconstres_t
*c
, /* Current constraint */
306 *r
; /* Current resolver */
307 ipp_attribute_t
*attr
; /* Current attribute */
308 char value
[2048]; /* Current attribute value as string */
309 const char *myvalue
; /* Current value of an option */
313 * Clear returned values...
329 * Get the default connection as needed...
333 http
= _cupsConnect();
336 * Range check input...
339 if (!http
|| !dest
|| !dinfo
||
340 (num_conflicts
!= NULL
) != (conflicts
!= NULL
) ||
341 (num_resolved
!= NULL
) != (resolved
!= NULL
))
345 * Load constraints as needed...
348 if (!dinfo
->constraints
)
349 cups_create_constraints(dinfo
);
351 if (cupsArrayCount(dinfo
->constraints
) == 0)
354 if (!dinfo
->num_defaults
)
355 cups_create_defaults(dinfo
);
358 * If we are resolving, create a shadow array...
363 for (i
= num_options
, option
= options
; i
> 0; i
--, option
++)
364 num_myres
= cupsAddOption(option
->name
, option
->value
, num_myres
, &myres
);
366 if (new_option
&& new_value
)
367 num_myres
= cupsAddOption(new_option
, new_value
, num_myres
, &myres
);
371 num_myres
= num_options
;
376 * Check for any conflicts...
380 pass
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
382 for (tries
= 0; tries
< 100; tries
++)
385 * Check for any conflicts...
388 if (num_conflicts
|| num_resolved
)
390 cupsFreeOptions(num_myconf
, myconf
);
394 active
= cups_test_constraints(dinfo
, new_option
, new_value
,
395 num_myres
, myres
, &num_myconf
,
399 active
= cups_test_constraints(dinfo
, new_option
, new_value
, num_myres
,
402 have_conflicts
= (active
!= NULL
);
404 if (!active
|| !num_resolved
)
405 break; /* All done */
408 * Scan the constraints that were triggered to apply resolvers...
412 resolvers
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
414 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(active
), changed
= 0;
416 c
= (_cups_dconstres_t
*)cupsArrayNext(active
))
418 if (cupsArrayFind(pass
, c
))
419 continue; /* Already applied this resolver... */
421 if (cupsArrayFind(resolvers
, c
))
423 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
429 if ((r
= cupsArrayFind(dinfo
->resolvers
, c
)) == NULL
)
431 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
438 * Add the options from the resolver...
441 cupsArrayAdd(pass
, r
);
442 cupsArrayAdd(resolvers
, r
);
444 for (attr
= ippFirstAttribute(r
->collection
);
446 attr
= ippNextAttribute(r
->collection
))
448 if (new_option
&& !strcmp(attr
->name
, new_option
))
449 continue; /* Ignore this if we just changed it */
451 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
452 continue; /* Ignore if the value is too long */
454 if ((test
= cups_test_constraints(dinfo
, attr
->name
, value
, num_myres
,
455 myres
, NULL
, NULL
)) == NULL
)
458 * That worked, flag it...
464 cupsArrayDelete(test
);
467 * Add the option/value from the resolver regardless of whether it
468 * worked; this makes sure that we can cascade several changes to
469 * make things resolve...
472 num_myres
= cupsAddOption(attr
->name
, value
, num_myres
, &myres
);
478 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
483 cupsArrayClear(pass
);
485 cupsArrayDelete(active
);
491 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
497 * Copy resolved options as needed...
502 for (i
= num_myres
, myoption
= myres
; i
> 0; i
--, myoption
++)
504 if ((myvalue
= cupsGetOption(myoption
->name
, num_options
,
506 strcmp(myvalue
, myoption
->value
))
508 if (new_option
&& !strcmp(new_option
, myoption
->name
) &&
509 new_value
&& !strcmp(new_value
, myoption
->value
))
512 *num_resolved
= cupsAddOption(myoption
->name
, myoption
->value
,
513 *num_resolved
, resolved
);
524 cupsArrayDelete(active
);
525 cupsArrayDelete(pass
);
526 cupsArrayDelete(resolvers
);
531 * Free shadow copy of options...
534 cupsFreeOptions(num_myres
, myres
);
540 * Return conflicting options to caller...
543 *num_conflicts
= num_myconf
;
549 * Free conflicting options...
552 cupsFreeOptions(num_myconf
, myconf
);
555 return (have_conflicts
);
560 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
563 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
564 * value. @code NULL@ is returned on error.
566 * @since CUPS 1.6/macOS 10.8@
569 cups_dinfo_t
* /* O - Destination information */
571 http_t
*http
, /* I - Connection to destination */
572 cups_dest_t
*dest
) /* I - Destination */
574 cups_dinfo_t
*dinfo
; /* Destination information */
575 ipp_t
*request
, /* Get-Printer-Attributes request */
576 *response
; /* Supported attributes */
577 int tries
, /* Number of tries so far */
578 delay
, /* Current retry delay */
579 prev_delay
; /* Next retry delay */
580 const char *uri
; /* Printer URI */
581 char resource
[1024]; /* Resource path */
582 int version
; /* IPP version */
583 ipp_status_t status
; /* Status of request */
584 static const char * const requested_attrs
[] =
585 { /* Requested attributes */
587 "media-col-database",
588 "printer-description"
592 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", (void *)http
, (void *)dest
, dest
? dest
->name
: ""));
595 * Get the default connection as needed...
599 http
= _cupsConnect();
602 * Range check input...
609 * Get the printer URI and resource path...
612 if ((uri
= _cupsGetDestResource(dest
, resource
, sizeof(resource
))) == NULL
)
616 * Get the supported attributes...
627 * Send a Get-Printer-Attributes request...
630 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
631 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
633 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
634 "requesting-user-name", NULL
, cupsUser());
635 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
636 "requested-attributes",
637 (int)(sizeof(requested_attrs
) / sizeof(requested_attrs
[0])),
638 NULL
, requested_attrs
);
639 response
= cupsDoRequest(http
, request
, resource
);
640 status
= cupsLastError();
642 if (status
> IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED
)
644 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
645 "returned %s (%s)", dest
->name
, ippErrorString(status
),
646 cupsLastErrorString()));
651 if (status
== IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
&& version
> 11)
653 else if (status
== IPP_STATUS_ERROR_BUSY
)
655 sleep((unsigned)delay
);
657 delay
= _cupsNextDelay(delay
, &prev_delay
);
665 while (!response
&& tries
< 10);
671 * Allocate a cups_dinfo_t structure and return it...
674 if ((dinfo
= calloc(1, sizeof(cups_dinfo_t
))) == NULL
)
676 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
681 dinfo
->version
= version
;
683 dinfo
->resource
= _cupsStrAlloc(resource
);
684 dinfo
->attrs
= response
;
691 * 'cupsFindDestDefault()' - Find the default value(s) for the given option.
693 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
694 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
695 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
696 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
697 * functions to inspect the default value(s) as needed.
699 * @since CUPS 1.7/macOS 10.9@
702 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
704 http_t
*http
, /* I - Connection to destination */
705 cups_dest_t
*dest
, /* I - Destination */
706 cups_dinfo_t
*dinfo
, /* I - Destination information */
707 const char *option
) /* I - Option/attribute name */
709 char name
[IPP_MAX_NAME
]; /* Attribute name */
713 * Get the default connection as needed...
717 http
= _cupsConnect();
720 * Range check input...
723 if (!http
|| !dest
|| !dinfo
|| !option
)
725 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
730 * Find and return the attribute...
733 snprintf(name
, sizeof(name
), "%s-default", option
);
734 return (ippFindAttribute(dinfo
->attrs
, name
, IPP_TAG_ZERO
));
739 * 'cupsFindDestReady()' - Find the default value(s) for the given option.
741 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
742 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
743 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
744 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
745 * functions to inspect the default value(s) as needed.
747 * @since CUPS 1.7/macOS 10.9@
750 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
752 http_t
*http
, /* I - Connection to destination */
753 cups_dest_t
*dest
, /* I - Destination */
754 cups_dinfo_t
*dinfo
, /* I - Destination information */
755 const char *option
) /* I - Option/attribute name */
757 char name
[IPP_MAX_NAME
]; /* Attribute name */
761 * Get the default connection as needed...
765 http
= _cupsConnect();
768 * Range check input...
771 if (!http
|| !dest
|| !dinfo
|| !option
)
773 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
778 * Find and return the attribute...
781 cups_update_ready(http
, dinfo
);
783 snprintf(name
, sizeof(name
), "%s-ready", option
);
784 return (ippFindAttribute(dinfo
->ready_attrs
, name
, IPP_TAG_ZERO
));
789 * 'cupsFindDestSupported()' - Find the default value(s) for the given option.
791 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
792 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
793 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
794 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
795 * functions to inspect the default value(s) as needed.
797 * @since CUPS 1.7/macOS 10.9@
800 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
801 cupsFindDestSupported(
802 http_t
*http
, /* I - Connection to destination */
803 cups_dest_t
*dest
, /* I - Destination */
804 cups_dinfo_t
*dinfo
, /* I - Destination information */
805 const char *option
) /* I - Option/attribute name */
807 char name
[IPP_MAX_NAME
]; /* Attribute name */
811 * Get the default connection as needed...
815 http
= _cupsConnect();
818 * Range check input...
821 if (!http
|| !dest
|| !dinfo
|| !option
)
823 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
828 * Find and return the attribute...
831 snprintf(name
, sizeof(name
), "%s-supported", option
);
832 return (ippFindAttribute(dinfo
->attrs
, name
, IPP_TAG_ZERO
));
837 * 'cupsFreeDestInfo()' - Free destination information obtained using
838 * @link cupsCopyDestInfo@.
840 * @since CUPS 1.6/macOS 10.8@
844 cupsFreeDestInfo(cups_dinfo_t
*dinfo
) /* I - Destination information */
847 * Range check input...
854 * Free memory and return...
857 _cupsStrFree(dinfo
->resource
);
859 cupsArrayDelete(dinfo
->constraints
);
860 cupsArrayDelete(dinfo
->resolvers
);
862 cupsArrayDelete(dinfo
->localizations
);
864 cupsArrayDelete(dinfo
->media_db
);
866 cupsArrayDelete(dinfo
->cached_db
);
868 ippDelete(dinfo
->ready_attrs
);
869 cupsArrayDelete(dinfo
->ready_db
);
871 ippDelete(dinfo
->attrs
);
878 * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a
881 * The @code flags@ parameter determines which set of media are indexed. For
882 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth
883 * borderless size supported by the printer.
885 * @since CUPS 1.7/macOS 10.9@
888 int /* O - 1 on success, 0 on failure */
889 cupsGetDestMediaByIndex(
890 http_t
*http
, /* I - Connection to destination */
891 cups_dest_t
*dest
, /* I - Destination */
892 cups_dinfo_t
*dinfo
, /* I - Destination information */
893 int n
, /* I - Media size number (0-based) */
894 unsigned flags
, /* I - Media flags */
895 cups_size_t
*size
) /* O - Media size information */
897 _cups_media_db_t
*nsize
; /* Size for N */
898 pwg_media_t
*pwg
; /* PWG media name for size */
902 * Get the default connection as needed...
906 http
= _cupsConnect();
909 * Range check input...
913 memset(size
, 0, sizeof(cups_size_t
));
915 if (!http
|| !dest
|| !dinfo
|| n
< 0 || !size
)
917 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
922 * Load media list as needed...
925 if (flags
& CUPS_MEDIA_FLAGS_READY
)
926 cups_update_ready(http
, dinfo
);
928 if (!dinfo
->cached_db
|| dinfo
->cached_flags
!= flags
)
929 cups_create_cached(http
, dinfo
, flags
);
932 * Copy the size over and return...
935 if ((nsize
= (_cups_media_db_t
*)cupsArrayIndex(dinfo
->cached_db
, n
)) == NULL
)
937 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
941 if (nsize
->size_name
)
942 strlcpy(size
->media
, nsize
->size_name
, sizeof(size
->media
));
944 strlcpy(size
->media
, nsize
->key
, sizeof(size
->media
));
945 else if ((pwg
= pwgMediaForSize(nsize
->width
, nsize
->length
)) != NULL
)
946 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
949 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
953 size
->width
= nsize
->width
;
954 size
->length
= nsize
->length
;
955 size
->bottom
= nsize
->bottom
;
956 size
->left
= nsize
->left
;
957 size
->right
= nsize
->right
;
958 size
->top
= nsize
->top
;
965 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
967 * The "media" string is a PWG media name. "Flags" provides some matching
968 * guidance (multiple flags can be combined):
970 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
971 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
972 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
973 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
974 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
975 * size amongst the "ready" media.
977 * The matching result (if any) is returned in the "cups_size_t" structure.
979 * Returns 1 when there is a match and 0 if there is not a match.
981 * @since CUPS 1.6/macOS 10.8@
984 int /* O - 1 on match, 0 on failure */
985 cupsGetDestMediaByName(
986 http_t
*http
, /* I - Connection to destination */
987 cups_dest_t
*dest
, /* I - Destination */
988 cups_dinfo_t
*dinfo
, /* I - Destination information */
989 const char *media
, /* I - Media name */
990 unsigned flags
, /* I - Media matching flags */
991 cups_size_t
*size
) /* O - Media size information */
993 pwg_media_t
*pwg
; /* PWG media info */
997 * Get the default connection as needed...
1001 http
= _cupsConnect();
1004 * Range check input...
1008 memset(size
, 0, sizeof(cups_size_t
));
1010 if (!http
|| !dest
|| !dinfo
|| !media
|| !size
)
1012 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1017 * Lookup the media size name...
1020 if ((pwg
= pwgMediaForPWG(media
)) == NULL
)
1021 if ((pwg
= pwgMediaForLegacy(media
)) == NULL
)
1023 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media
));
1024 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unknown media size name."), 1);
1029 * Lookup the size...
1032 return (cups_get_media_db(http
, dinfo
, pwg
, flags
, size
));
1037 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
1039 * "Width" and "length" are the dimensions in hundredths of millimeters.
1040 * "Flags" provides some matching guidance (multiple flags can be combined):
1042 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
1043 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
1044 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
1045 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
1046 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
1047 * size amongst the "ready" media.
1049 * The matching result (if any) is returned in the "cups_size_t" structure.
1051 * Returns 1 when there is a match and 0 if there is not a match.
1053 * @since CUPS 1.6/macOS 10.8@
1056 int /* O - 1 on match, 0 on failure */
1057 cupsGetDestMediaBySize(
1058 http_t
*http
, /* I - Connection to destination */
1059 cups_dest_t
*dest
, /* I - Destination */
1060 cups_dinfo_t
*dinfo
, /* I - Destination information */
1061 int width
, /* I - Media width in hundredths of
1063 int length
, /* I - Media length in hundredths of
1065 unsigned flags
, /* I - Media matching flags */
1066 cups_size_t
*size
) /* O - Media size information */
1068 pwg_media_t
*pwg
; /* PWG media info */
1072 * Get the default connection as needed...
1076 http
= _cupsConnect();
1079 * Range check input...
1083 memset(size
, 0, sizeof(cups_size_t
));
1085 if (!http
|| !dest
|| !dinfo
|| width
<= 0 || length
<= 0 || !size
)
1087 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1092 * Lookup the media size name...
1095 if ((pwg
= pwgMediaForSize(width
, length
)) == NULL
)
1097 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width
,
1099 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Invalid media size."), 1);
1104 * Lookup the size...
1107 return (cups_get_media_db(http
, dinfo
, pwg
, flags
, size
));
1112 * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a
1115 * The @code flags@ parameter determines the set of media sizes that are
1116 * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return
1117 * the number of borderless sizes.
1119 * @since CUPS 1.7/macOS 10.9@
1122 int /* O - Number of sizes */
1123 cupsGetDestMediaCount(
1124 http_t
*http
, /* I - Connection to destination */
1125 cups_dest_t
*dest
, /* I - Destination */
1126 cups_dinfo_t
*dinfo
, /* I - Destination information */
1127 unsigned flags
) /* I - Media flags */
1130 * Get the default connection as needed...
1134 http
= _cupsConnect();
1137 * Range check input...
1140 if (!http
|| !dest
|| !dinfo
)
1142 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1147 * Load media list as needed...
1150 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1151 cups_update_ready(http
, dinfo
);
1153 if (!dinfo
->cached_db
|| dinfo
->cached_flags
!= flags
)
1154 cups_create_cached(http
, dinfo
, flags
);
1156 return (cupsArrayCount(dinfo
->cached_db
));
1161 * 'cupsGetDestMediaDefault()' - Get the default size for a destination.
1163 * The @code flags@ parameter determines which default size is returned. For
1164 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default
1165 * borderless size, typically US Letter or A4, but sometimes 4x6 photo media.
1167 * @since CUPS 1.7/macOS 10.9@
1170 int /* O - 1 on success, 0 on failure */
1171 cupsGetDestMediaDefault(
1172 http_t
*http
, /* I - Connection to destination */
1173 cups_dest_t
*dest
, /* I - Destination */
1174 cups_dinfo_t
*dinfo
, /* I - Destination information */
1175 unsigned flags
, /* I - Media flags */
1176 cups_size_t
*size
) /* O - Media size information */
1178 const char *media
; /* Default media size */
1182 * Get the default connection as needed...
1186 http
= _cupsConnect();
1189 * Range check input...
1193 memset(size
, 0, sizeof(cups_size_t
));
1195 if (!http
|| !dest
|| !dinfo
|| !size
)
1197 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1202 * Get the default media size, if any...
1205 if ((media
= cupsGetOption("media", dest
->num_options
,
1206 dest
->options
)) == NULL
)
1207 media
= "na_letter_8.5x11in";
1209 if (cupsGetDestMediaByName(http
, dest
, dinfo
, media
, flags
, size
))
1212 if (strcmp(media
, "na_letter_8.5x11in") &&
1213 cupsGetDestMediaByName(http
, dest
, dinfo
, "iso_a4_210x297mm", flags
,
1217 if (strcmp(media
, "iso_a4_210x297mm") &&
1218 cupsGetDestMediaByName(http
, dest
, dinfo
, "na_letter_8.5x11in", flags
,
1222 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) &&
1223 cupsGetDestMediaByName(http
, dest
, dinfo
, "na_index_4x6in", flags
, size
))
1227 * Fall back to the first matching media size...
1230 return (cupsGetDestMediaByIndex(http
, dest
, dinfo
, 0, flags
, size
));
1235 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
1240 cups_array_t
*a
, /* I - Array */
1241 ipp_t
*collection
) /* I - Collection value */
1243 ipp_attribute_t
*attr
; /* Attribute */
1244 _cups_dconstres_t
*temp
; /* Current constraint/resolver */
1247 if ((attr
= ippFindAttribute(collection
, "resolver-name",
1248 IPP_TAG_NAME
)) == NULL
)
1251 if ((temp
= calloc(1, sizeof(_cups_dconstres_t
))) == NULL
)
1254 temp
->name
= attr
->values
[0].string
.text
;
1255 temp
->collection
= collection
;
1257 cupsArrayAdd(a
, temp
);
1262 * 'cups_compare_dconstres()' - Compare to resolver entries.
1265 static int /* O - Result of comparison */
1266 cups_compare_dconstres(
1267 _cups_dconstres_t
*a
, /* I - First resolver */
1268 _cups_dconstres_t
*b
) /* I - Second resolver */
1270 return (strcmp(a
->name
, b
->name
));
1275 * 'cups_compare_media_db()' - Compare two media entries.
1278 static int /* O - Result of comparison */
1279 cups_compare_media_db(
1280 _cups_media_db_t
*a
, /* I - First media entries */
1281 _cups_media_db_t
*b
) /* I - Second media entries */
1283 int result
; /* Result of comparison */
1286 if ((result
= a
->width
- b
->width
) == 0)
1287 result
= a
->length
- b
->length
;
1294 * 'cups_copy_media_db()' - Copy a media entry.
1297 static _cups_media_db_t
* /* O - New media entry */
1299 _cups_media_db_t
*mdb
) /* I - Media entry to copy */
1301 _cups_media_db_t
*temp
; /* New media entry */
1304 if ((temp
= calloc(1, sizeof(_cups_media_db_t
))) == NULL
)
1308 temp
->color
= _cupsStrAlloc(mdb
->color
);
1310 temp
->key
= _cupsStrAlloc(mdb
->key
);
1312 temp
->info
= _cupsStrAlloc(mdb
->info
);
1314 temp
->size_name
= _cupsStrAlloc(mdb
->size_name
);
1316 temp
->source
= _cupsStrAlloc(mdb
->source
);
1318 temp
->type
= _cupsStrAlloc(mdb
->type
);
1320 temp
->width
= mdb
->width
;
1321 temp
->length
= mdb
->length
;
1322 temp
->bottom
= mdb
->bottom
;
1323 temp
->left
= mdb
->left
;
1324 temp
->right
= mdb
->right
;
1325 temp
->top
= mdb
->top
;
1332 * 'cups_create_cached()' - Create the media selection cache.
1336 cups_create_cached(http_t
*http
, /* I - Connection to destination */
1337 cups_dinfo_t
*dinfo
, /* I - Destination information */
1338 unsigned flags
) /* I - Media selection flags */
1340 cups_array_t
*db
; /* Media database array to use */
1341 _cups_media_db_t
*mdb
, /* Media database entry */
1342 *first
; /* First entry this size */
1345 DEBUG_printf(("3cups_create_cached(http=%p, dinfo=%p, flags=%u)", (void *)http
, (void *)dinfo
, flags
));
1347 if (dinfo
->cached_db
)
1348 cupsArrayDelete(dinfo
->cached_db
);
1350 dinfo
->cached_db
= cupsArrayNew(NULL
, NULL
);
1351 dinfo
->cached_flags
= flags
;
1353 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1355 DEBUG_puts("4cups_create_cached: ready media");
1357 cups_update_ready(http
, dinfo
);
1358 db
= dinfo
->ready_db
;
1362 DEBUG_puts("4cups_create_cached: supported media");
1364 if (!dinfo
->media_db
)
1365 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
);
1367 db
= dinfo
->media_db
;
1370 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
), first
= mdb
;
1372 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1374 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
));
1376 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1378 if (!mdb
->left
&& !mdb
->right
&& !mdb
->top
&& !mdb
->bottom
)
1380 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb
));
1381 cupsArrayAdd(dinfo
->cached_db
, mdb
);
1384 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1386 if (first
->width
!= mdb
->width
|| first
->length
!= mdb
->length
)
1388 DEBUG_printf(("4cups_create_cached: add %p", (void *)first
));
1389 cupsArrayAdd(dinfo
->cached_db
, first
);
1392 else if (mdb
->left
>= first
->left
&& mdb
->right
>= first
->right
&& mdb
->top
>= first
->top
&& mdb
->bottom
>= first
->bottom
&&
1393 (mdb
->left
!= first
->left
|| mdb
->right
!= first
->right
|| mdb
->top
!= first
->top
|| mdb
->bottom
!= first
->bottom
))
1398 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb
));
1399 cupsArrayAdd(dinfo
->cached_db
, mdb
);
1403 if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1405 DEBUG_printf(("4cups_create_cached: add %p", (void *)first
));
1406 cupsArrayAdd(dinfo
->cached_db
, first
);
1412 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
1416 cups_create_constraints(
1417 cups_dinfo_t
*dinfo
) /* I - Destination information */
1419 int i
; /* Looping var */
1420 ipp_attribute_t
*attr
; /* Attribute */
1421 _ipp_value_t
*val
; /* Current value */
1424 dinfo
->constraints
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, NULL
,
1425 (cups_afree_func_t
)free
);
1426 dinfo
->resolvers
= cupsArrayNew3((cups_array_func_t
)cups_compare_dconstres
,
1427 NULL
, NULL
, 0, NULL
,
1428 (cups_afree_func_t
)free
);
1430 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-constraints-supported",
1431 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1433 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
1434 cups_add_dconstres(dinfo
->constraints
, val
->collection
);
1437 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-resolvers-supported",
1438 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1440 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
1441 cups_add_dconstres(dinfo
->resolvers
, val
->collection
);
1447 * 'cups_create_defaults()' - Create the -default option array.
1449 * TODO: Need to support collection defaults...
1453 cups_create_defaults(
1454 cups_dinfo_t
*dinfo
) /* I - Destination information */
1456 ipp_attribute_t
*attr
; /* Current attribute */
1457 char name
[IPP_MAX_NAME
+ 1],
1459 *nameptr
, /* Pointer into current name */
1460 value
[2048]; /* Current value */
1464 * Iterate through the printer attributes looking for xxx-default and adding
1465 * xxx=value to the defaults option array.
1468 for (attr
= ippFirstAttribute(dinfo
->attrs
);
1470 attr
= ippNextAttribute(dinfo
->attrs
))
1472 if (!attr
->name
|| attr
->group_tag
!= IPP_TAG_PRINTER
)
1475 if (attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
)
1476 continue; /* TODO: STR #4096 */
1478 if ((nameptr
= attr
->name
+ strlen(attr
->name
) - 8) <= attr
->name
||
1479 strcmp(nameptr
, "-default"))
1482 strlcpy(name
, attr
->name
, sizeof(name
));
1483 if ((nameptr
= name
+ strlen(name
) - 8) <= name
||
1484 strcmp(nameptr
, "-default"))
1489 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
1492 dinfo
->num_defaults
= cupsAddOption(name
, value
, dinfo
->num_defaults
,
1499 * 'cups_create_media_db()' - Create the media database.
1503 cups_create_media_db(
1504 cups_dinfo_t
*dinfo
, /* I - Destination information */
1505 unsigned flags
) /* I - Media flags */
1507 int i
; /* Looping var */
1508 _ipp_value_t
*val
; /* Current value */
1509 ipp_attribute_t
*media_col_db
, /* media-col-database */
1510 *media_attr
, /* media-xxx */
1511 *x_dimension
, /* x-dimension */
1512 *y_dimension
; /* y-dimension */
1513 pwg_media_t
*pwg
; /* PWG media info */
1514 cups_array_t
*db
; /* New media database array */
1515 _cups_media_db_t mdb
; /* Media entry */
1518 db
= cupsArrayNew3((cups_array_func_t
)cups_compare_media_db
,
1520 (cups_acopy_func_t
)cups_copy_media_db
,
1521 (cups_afree_func_t
)cups_free_media_db
);
1523 if (flags
== CUPS_MEDIA_FLAGS_READY
)
1525 dinfo
->ready_db
= db
;
1527 media_col_db
= ippFindAttribute(dinfo
->ready_attrs
, "media-col-ready",
1528 IPP_TAG_BEGIN_COLLECTION
);
1529 media_attr
= ippFindAttribute(dinfo
->ready_attrs
, "media-ready",
1534 dinfo
->media_db
= db
;
1535 dinfo
->min_size
.width
= INT_MAX
;
1536 dinfo
->min_size
.length
= INT_MAX
;
1537 dinfo
->max_size
.width
= 0;
1538 dinfo
->max_size
.length
= 0;
1540 media_col_db
= ippFindAttribute(dinfo
->attrs
, "media-col-database",
1541 IPP_TAG_BEGIN_COLLECTION
);
1542 media_attr
= ippFindAttribute(dinfo
->attrs
, "media-supported",
1548 _ipp_value_t
*custom
= NULL
; /* Custom size range value */
1550 for (i
= media_col_db
->num_values
, val
= media_col_db
->values
;
1554 memset(&mdb
, 0, sizeof(mdb
));
1556 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size",
1557 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1559 ipp_t
*media_size
= media_attr
->values
[0].collection
;
1560 /* media-size collection value */
1562 if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1563 IPP_TAG_INTEGER
)) != NULL
&&
1564 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1565 IPP_TAG_INTEGER
)) != NULL
)
1571 mdb
.width
= x_dimension
->values
[0].integer
;
1572 mdb
.length
= y_dimension
->values
[0].integer
;
1574 else if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1575 IPP_TAG_INTEGER
)) != NULL
&&
1576 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1577 IPP_TAG_RANGE
)) != NULL
)
1583 mdb
.width
= x_dimension
->values
[0].integer
;
1584 mdb
.length
= y_dimension
->values
[0].range
.upper
;
1586 else if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
1587 (x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1588 IPP_TAG_RANGE
)) != NULL
&&
1589 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1590 IPP_TAG_RANGE
)) != NULL
)
1593 * Custom size range; save this as the custom size value with default
1594 * margins, then continue; we'll capture the real margins below...
1599 dinfo
->min_size
.width
= x_dimension
->values
[0].range
.lower
;
1600 dinfo
->min_size
.length
= y_dimension
->values
[0].range
.lower
;
1601 dinfo
->min_size
.left
=
1602 dinfo
->min_size
.right
= 635; /* Default 1/4" side margins */
1603 dinfo
->min_size
.top
=
1604 dinfo
->min_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1606 dinfo
->max_size
.width
= x_dimension
->values
[0].range
.upper
;
1607 dinfo
->max_size
.length
= y_dimension
->values
[0].range
.upper
;
1608 dinfo
->max_size
.left
=
1609 dinfo
->max_size
.right
= 635; /* Default 1/4" side margins */
1610 dinfo
->max_size
.top
=
1611 dinfo
->max_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1616 if ((media_attr
= ippFindAttribute(val
->collection
, "media-color",
1617 IPP_TAG_ZERO
)) != NULL
&&
1618 (media_attr
->value_tag
== IPP_TAG_NAME
||
1619 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1620 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1621 mdb
.color
= media_attr
->values
[0].string
.text
;
1623 if ((media_attr
= ippFindAttribute(val
->collection
, "media-info",
1624 IPP_TAG_TEXT
)) != NULL
)
1625 mdb
.info
= media_attr
->values
[0].string
.text
;
1627 if ((media_attr
= ippFindAttribute(val
->collection
, "media-key",
1628 IPP_TAG_ZERO
)) != NULL
&&
1629 (media_attr
->value_tag
== IPP_TAG_NAME
||
1630 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1631 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1632 mdb
.key
= media_attr
->values
[0].string
.text
;
1634 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size-name",
1635 IPP_TAG_ZERO
)) != NULL
&&
1636 (media_attr
->value_tag
== IPP_TAG_NAME
||
1637 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1638 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1639 mdb
.size_name
= media_attr
->values
[0].string
.text
;
1641 if ((media_attr
= ippFindAttribute(val
->collection
, "media-source",
1642 IPP_TAG_ZERO
)) != NULL
&&
1643 (media_attr
->value_tag
== IPP_TAG_NAME
||
1644 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1645 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1646 mdb
.source
= media_attr
->values
[0].string
.text
;
1648 if ((media_attr
= ippFindAttribute(val
->collection
, "media-type",
1649 IPP_TAG_ZERO
)) != NULL
&&
1650 (media_attr
->value_tag
== IPP_TAG_NAME
||
1651 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1652 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1653 mdb
.type
= media_attr
->values
[0].string
.text
;
1655 if ((media_attr
= ippFindAttribute(val
->collection
, "media-bottom-margin",
1656 IPP_TAG_INTEGER
)) != NULL
)
1657 mdb
.bottom
= media_attr
->values
[0].integer
;
1659 if ((media_attr
= ippFindAttribute(val
->collection
, "media-left-margin",
1660 IPP_TAG_INTEGER
)) != NULL
)
1661 mdb
.left
= media_attr
->values
[0].integer
;
1663 if ((media_attr
= ippFindAttribute(val
->collection
, "media-right-margin",
1664 IPP_TAG_INTEGER
)) != NULL
)
1665 mdb
.right
= media_attr
->values
[0].integer
;
1667 if ((media_attr
= ippFindAttribute(val
->collection
, "media-top-margin",
1668 IPP_TAG_INTEGER
)) != NULL
)
1669 mdb
.top
= media_attr
->values
[0].integer
;
1671 cupsArrayAdd(db
, &mdb
);
1676 if ((media_attr
= ippFindAttribute(custom
->collection
,
1677 "media-bottom-margin",
1678 IPP_TAG_INTEGER
)) != NULL
)
1680 dinfo
->min_size
.top
=
1681 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
1684 if ((media_attr
= ippFindAttribute(custom
->collection
,
1685 "media-left-margin",
1686 IPP_TAG_INTEGER
)) != NULL
)
1688 dinfo
->min_size
.left
=
1689 dinfo
->max_size
.left
= media_attr
->values
[0].integer
;
1692 if ((media_attr
= ippFindAttribute(custom
->collection
,
1693 "media-right-margin",
1694 IPP_TAG_INTEGER
)) != NULL
)
1696 dinfo
->min_size
.right
=
1697 dinfo
->max_size
.right
= media_attr
->values
[0].integer
;
1700 if ((media_attr
= ippFindAttribute(custom
->collection
,
1702 IPP_TAG_INTEGER
)) != NULL
)
1704 dinfo
->min_size
.top
=
1705 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
1709 else if (media_attr
&&
1710 (media_attr
->value_tag
== IPP_TAG_NAME
||
1711 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1712 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1714 memset(&mdb
, 0, sizeof(mdb
));
1717 mdb
.right
= 635; /* Default 1/4" side margins */
1719 mdb
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1721 for (i
= media_attr
->num_values
, val
= media_attr
->values
;
1725 if ((pwg
= pwgMediaForPWG(val
->string
.text
)) == NULL
)
1726 if ((pwg
= pwgMediaForLegacy(val
->string
.text
)) == NULL
)
1728 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
1733 mdb
.width
= pwg
->width
;
1734 mdb
.length
= pwg
->length
;
1736 if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
1737 !strncmp(val
->string
.text
, "custom_min_", 11))
1739 mdb
.size_name
= NULL
;
1740 dinfo
->min_size
= mdb
;
1742 else if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
1743 !strncmp(val
->string
.text
, "custom_max_", 11))
1745 mdb
.size_name
= NULL
;
1746 dinfo
->max_size
= mdb
;
1750 mdb
.size_name
= val
->string
.text
;
1752 cupsArrayAdd(db
, &mdb
);
1760 * 'cups_free_media_cb()' - Free a media entry.
1765 _cups_media_db_t
*mdb
) /* I - Media entry to free */
1768 _cupsStrFree(mdb
->color
);
1770 _cupsStrFree(mdb
->key
);
1772 _cupsStrFree(mdb
->info
);
1774 _cupsStrFree(mdb
->size_name
);
1776 _cupsStrFree(mdb
->source
);
1778 _cupsStrFree(mdb
->type
);
1785 * 'cups_get_media_db()' - Lookup the media entry for a given size.
1788 static int /* O - 1 on match, 0 on failure */
1789 cups_get_media_db(http_t
*http
, /* I - Connection to destination */
1790 cups_dinfo_t
*dinfo
, /* I - Destination information */
1791 pwg_media_t
*pwg
, /* I - PWG media info */
1792 unsigned flags
, /* I - Media matching flags */
1793 cups_size_t
*size
) /* O - Media size/margin/name info */
1795 cups_array_t
*db
; /* Which media database to query */
1796 _cups_media_db_t
*mdb
, /* Current media database entry */
1797 *best
= NULL
, /* Best matching entry */
1798 key
; /* Search key */
1802 * Create the media database as needed...
1805 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1807 cups_update_ready(http
, dinfo
);
1808 db
= dinfo
->ready_db
;
1812 if (!dinfo
->media_db
)
1813 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
);
1815 db
= dinfo
->media_db
;
1822 memset(&key
, 0, sizeof(key
));
1823 key
.width
= pwg
->width
;
1824 key
.length
= pwg
->length
;
1826 if ((mdb
= cupsArrayFind(db
, &key
)) != NULL
)
1829 * Found an exact match, let's figure out the best margins for the flags
1835 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1838 * Look for the smallest margins...
1841 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 || best
->bottom
!= 0)
1843 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1844 mdb
&& !cups_compare_media_db(mdb
, &key
);
1845 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1847 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
1848 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
)
1851 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
1859 * If we need an exact match, return no-match if the size is not
1863 if ((flags
& CUPS_MEDIA_FLAGS_EXACT
) &&
1864 (best
->left
|| best
->right
|| best
->top
|| best
->bottom
))
1867 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1870 * Look for the largest margins...
1873 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1874 mdb
&& !cups_compare_media_db(mdb
, &key
);
1875 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1877 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
1878 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
&&
1879 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
1886 * Look for the smallest non-zero margins...
1889 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1890 mdb
&& !cups_compare_media_db(mdb
, &key
);
1891 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1893 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
1894 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) || best
->right
== 0) &&
1895 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
1896 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) || best
->bottom
== 0) &&
1897 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
1902 else if (flags
& CUPS_MEDIA_FLAGS_EXACT
)
1905 * See if we can do this as a custom size...
1908 if (pwg
->width
< dinfo
->min_size
.width
||
1909 pwg
->width
> dinfo
->max_size
.width
||
1910 pwg
->length
< dinfo
->min_size
.length
||
1911 pwg
->length
> dinfo
->max_size
.length
)
1912 return (0); /* Out of range */
1914 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) &&
1915 (dinfo
->min_size
.left
> 0 || dinfo
->min_size
.right
> 0 ||
1916 dinfo
->min_size
.top
> 0 || dinfo
->min_size
.bottom
> 0))
1917 return (0); /* Not borderless */
1919 key
.size_name
= (char *)pwg
->pwg
;
1920 key
.bottom
= dinfo
->min_size
.bottom
;
1921 key
.left
= dinfo
->min_size
.left
;
1922 key
.right
= dinfo
->min_size
.right
;
1923 key
.top
= dinfo
->min_size
.top
;
1927 else if (pwg
->width
>= dinfo
->min_size
.width
&&
1928 pwg
->width
<= dinfo
->max_size
.width
&&
1929 pwg
->length
>= dinfo
->min_size
.length
&&
1930 pwg
->length
<= dinfo
->max_size
.length
)
1933 * Map to custom size...
1936 key
.size_name
= (char *)pwg
->pwg
;
1937 key
.bottom
= dinfo
->min_size
.bottom
;
1938 key
.left
= dinfo
->min_size
.left
;
1939 key
.right
= dinfo
->min_size
.right
;
1940 key
.top
= dinfo
->min_size
.top
;
1947 * Find a close size...
1950 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
);
1952 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1953 if (cups_is_close_media_db(mdb
, &key
))
1961 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1964 * Look for the smallest margins...
1967 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 ||
1970 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1971 mdb
&& cups_is_close_media_db(mdb
, &key
);
1972 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1974 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
1975 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
&&
1976 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
1979 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
1986 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1989 * Look for the largest margins...
1992 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1993 mdb
&& cups_is_close_media_db(mdb
, &key
);
1994 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1996 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
1997 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
&&
1998 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2005 * Look for the smallest non-zero margins...
2008 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2009 mdb
&& cups_is_close_media_db(mdb
, &key
);
2010 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2012 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
2013 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) ||
2014 best
->right
== 0) &&
2015 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
2016 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) ||
2017 best
->bottom
== 0) &&
2018 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2027 * Return the matching size...
2030 if (best
->size_name
)
2031 strlcpy(size
->media
, best
->size_name
, sizeof(size
->media
));
2033 strlcpy(size
->media
, best
->key
, sizeof(size
->media
));
2035 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
2037 size
->width
= best
->width
;
2038 size
->length
= best
->length
;
2039 size
->bottom
= best
->bottom
;
2040 size
->left
= best
->left
;
2041 size
->right
= best
->right
;
2042 size
->top
= best
->top
;
2052 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
2053 * close to the same size.
2055 * Currently we use 5 points (from PostScript) as the matching range...
2058 static int /* O - 1 if the sizes are close */
2059 cups_is_close_media_db(
2060 _cups_media_db_t
*a
, /* I - First media entries */
2061 _cups_media_db_t
*b
) /* I - Second media entries */
2063 int dwidth
, /* Difference in width */
2064 dlength
; /* Difference in length */
2067 dwidth
= a
->width
- b
->width
;
2068 dlength
= a
->length
- b
->length
;
2070 return (dwidth
>= -176 && dwidth
<= 176 &&
2071 dlength
>= -176 && dlength
<= 176);
2076 * 'cups_test_constraints()' - Test constraints.
2078 * TODO: STR #4096 - Need to properly support media-col contraints...
2081 static cups_array_t
* /* O - Active constraints */
2082 cups_test_constraints(
2083 cups_dinfo_t
*dinfo
, /* I - Destination information */
2084 const char *new_option
, /* I - Newly selected option */
2085 const char *new_value
, /* I - Newly selected value */
2086 int num_options
, /* I - Number of options */
2087 cups_option_t
*options
, /* I - Options */
2088 int *num_conflicts
, /* O - Number of conflicting options */
2089 cups_option_t
**conflicts
) /* O - Conflicting options */
2091 int i
, /* Looping var */
2092 match
; /* Value matches? */
2093 int num_matching
; /* Number of matching options */
2094 cups_option_t
*matching
; /* Matching options */
2095 _cups_dconstres_t
*c
; /* Current constraint */
2096 cups_array_t
*active
= NULL
; /* Active constraints */
2097 ipp_attribute_t
*attr
; /* Current attribute */
2098 _ipp_value_t
*attrval
; /* Current attribute value */
2099 const char *value
; /* Current value */
2100 char temp
[1024]; /* Temporary string */
2101 int int_value
; /* Integer value */
2102 int xres_value
, /* Horizontal resolution */
2103 yres_value
; /* Vertical resolution */
2104 ipp_res_t units_value
; /* Resolution units */
2107 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(dinfo
->constraints
);
2109 c
= (_cups_dconstres_t
*)cupsArrayNext(dinfo
->constraints
))
2114 for (attr
= ippFirstAttribute(c
->collection
);
2116 attr
= ippNextAttribute(c
->collection
))
2118 if (attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
)
2119 break; /* TODO: STR #4096 */
2122 * Get the value for the current attribute in the constraint...
2125 if (new_option
&& new_value
&& !strcmp(attr
->name
, new_option
))
2127 else if ((value
= cupsGetOption(attr
->name
, num_options
,
2129 value
= cupsGetOption(attr
->name
, dinfo
->num_defaults
, dinfo
->defaults
);
2134 * Not set so this constraint does not apply...
2142 switch (attr
->value_tag
)
2144 case IPP_TAG_INTEGER
:
2146 int_value
= atoi(value
);
2148 for (i
= attr
->num_values
, attrval
= attr
->values
;
2152 if (attrval
->integer
== int_value
)
2160 case IPP_TAG_BOOLEAN
:
2161 int_value
= !strcmp(value
, "true");
2163 for (i
= attr
->num_values
, attrval
= attr
->values
;
2167 if (attrval
->boolean
== int_value
)
2175 case IPP_TAG_RANGE
:
2176 int_value
= atoi(value
);
2178 for (i
= attr
->num_values
, attrval
= attr
->values
;
2182 if (int_value
>= attrval
->range
.lower
&&
2183 int_value
<= attrval
->range
.upper
)
2191 case IPP_TAG_RESOLUTION
:
2192 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
2194 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
2197 yres_value
= xres_value
;
2200 if (!strcmp(temp
, "dpi"))
2201 units_value
= IPP_RES_PER_INCH
;
2202 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
2203 units_value
= IPP_RES_PER_CM
;
2207 for (i
= attr
->num_values
, attrval
= attr
->values
;
2211 if (attrval
->resolution
.xres
== xres_value
&&
2212 attrval
->resolution
.yres
== yres_value
&&
2213 attrval
->resolution
.units
== units_value
)
2223 case IPP_TAG_KEYWORD
:
2224 case IPP_TAG_CHARSET
:
2226 case IPP_TAG_URISCHEME
:
2227 case IPP_TAG_MIMETYPE
:
2228 case IPP_TAG_LANGUAGE
:
2229 case IPP_TAG_TEXTLANG
:
2230 case IPP_TAG_NAMELANG
:
2231 for (i
= attr
->num_values
, attrval
= attr
->values
;
2235 if (!strcmp(attrval
->string
.text
, value
))
2250 num_matching
= cupsAddOption(attr
->name
, value
, num_matching
, &matching
);
2256 active
= cupsArrayNew(NULL
, NULL
);
2258 cupsArrayAdd(active
, c
);
2260 if (num_conflicts
&& conflicts
)
2262 cups_option_t
*moption
; /* Matching option */
2264 for (i
= num_matching
, moption
= matching
; i
> 0; i
--, moption
++)
2265 *num_conflicts
= cupsAddOption(moption
->name
, moption
->value
,
2266 *num_conflicts
, conflicts
);
2270 cupsFreeOptions(num_matching
, matching
);
2278 * 'cups_update_ready()' - Update xxx-ready attributes for the printer.
2282 cups_update_ready(http_t
*http
, /* I - Connection to destination */
2283 cups_dinfo_t
*dinfo
) /* I - Destination information */
2285 ipp_t
*request
; /* Get-Printer-Attributes request */
2286 static const char * const pattrs
[] = /* Printer attributes we want */
2288 "finishings-col-ready",
2290 "job-finishings-col-ready",
2291 "job-finishings-ready",
2298 * Don't update more than once every 30 seconds...
2301 if ((time(NULL
) - dinfo
->ready_time
) < _CUPS_MEDIA_READY_TTL
)
2305 * Free any previous results...
2308 if (dinfo
->cached_flags
& CUPS_MEDIA_FLAGS_READY
)
2310 cupsArrayDelete(dinfo
->cached_db
);
2311 dinfo
->cached_db
= NULL
;
2312 dinfo
->cached_flags
= CUPS_MEDIA_FLAGS_DEFAULT
;
2315 ippDelete(dinfo
->ready_attrs
);
2316 dinfo
->ready_attrs
= NULL
;
2318 cupsArrayDelete(dinfo
->ready_db
);
2319 dinfo
->ready_db
= NULL
;
2322 * Query the xxx-ready values...
2325 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
2326 ippSetVersion(request
, dinfo
->version
/ 10, dinfo
->version
% 10);
2328 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
2330 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
2332 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "requested-attributes", (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
2334 dinfo
->ready_attrs
= cupsDoRequest(http
, request
, dinfo
->resource
);
2337 * Update the ready media database...
2340 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_READY
);
2343 * Update last lookup time and return...
2346 dinfo
->ready_time
= time(NULL
);