4 * Destination option/media support for CUPS.
6 * Copyright 2012-2013 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * cupsCheckDestSupported() - Check that the option and value are supported
20 * cupsCopyDestConflicts() - Get conflicts and resolutions for a new
22 * cupsCopyDestInfo() - Get the supported values/capabilities for the
24 * cupsFindDestDefault() - Find the default value(s) for the given
26 * cupsFindDestReady() - Find the default value(s) for the given
28 * cupsFindDestSupported() - Find the default value(s) for the given
30 * cupsFreeDestInfo() - Free destination information obtained using
31 * @link cupsCopyDestInfo@.
32 * cupsGetDestMediaByIndex() - Get a media name, dimension, and margins for a
34 * cupsGetDestMediaByName() - Get media names, dimensions, and margins.
35 * cupsGetDestMediaBySize() - Get media names, dimensions, and margins.
36 * cupsGetDestMediaCount() - Get the number of sizes supported by a
38 * cupsGetDestMediaDefault() - Get the default size for a destination.
39 * cups_add_dconstres() - Add a constraint or resolver to an array.
40 * cups_compare_dconstres() - Compare to resolver entries.
41 * cups_compare_media_db() - Compare two media entries.
42 * cups_copy_media_db() - Copy a media entry.
43 * cups_create_cached() - Create the media selection cache.
44 * cups_create_constraints() - Create the constraints and resolvers arrays.
45 * cups_create_defaults() - Create the -default option array.
46 * cups_create_media_db() - Create the media database.
47 * cups_free_media_cb() - Free a media entry.
48 * cups_get_media_db() - Lookup the media entry for a given size.
49 * cups_is_close_media_db() - Compare two media entries to see if they are
50 * close to the same size.
51 * cups_test_constraints() - Test constraints.
52 * cups_update_ready() - Update xxx-ready attributes for the printer.
56 * Include necessary headers...
59 #include "cups-private.h"
66 #define _CUPS_MEDIA_READY_TTL 30 /* Life of xxx-ready values */
73 static void cups_add_dconstres(cups_array_t
*a
, ipp_t
*collection
);
74 static int cups_compare_dconstres(_cups_dconstres_t
*a
,
75 _cups_dconstres_t
*b
);
76 static int cups_compare_media_db(_cups_media_db_t
*a
,
78 static _cups_media_db_t
*cups_copy_media_db(_cups_media_db_t
*mdb
);
79 static void cups_create_cached(http_t
*http
, cups_dinfo_t
*dinfo
,
81 static void cups_create_constraints(cups_dinfo_t
*dinfo
);
82 static void cups_create_defaults(cups_dinfo_t
*dinfo
);
83 static void cups_create_media_db(cups_dinfo_t
*dinfo
,
85 static void cups_free_media_db(_cups_media_db_t
*mdb
);
86 static int cups_get_media_db(http_t
*http
, cups_dinfo_t
*dinfo
,
87 pwg_media_t
*pwg
, unsigned flags
,
89 static int cups_is_close_media_db(_cups_media_db_t
*a
,
91 static cups_array_t
*cups_test_constraints(cups_dinfo_t
*dinfo
,
92 const char *new_option
,
93 const char *new_value
,
95 cups_option_t
*options
,
97 cups_option_t
**conflicts
);
98 static void cups_update_ready(http_t
*http
, cups_dinfo_t
*dinfo
);
102 * 'cupsCheckDestSupported()' - Check that the option and value are supported
103 * by the destination.
105 * Returns 1 if supported, 0 otherwise.
107 * @since CUPS 1.6/OS X 10.8@
110 int /* O - 1 if supported, 0 otherwise */
111 cupsCheckDestSupported(
112 http_t
*http
, /* I - Connection to destination */
113 cups_dest_t
*dest
, /* I - Destination */
114 cups_dinfo_t
*dinfo
, /* I - Destination information */
115 const char *option
, /* I - Option */
116 const char *value
) /* I - Value */
118 int i
; /* Looping var */
119 char temp
[1024]; /* Temporary string */
120 int int_value
; /* Integer value */
121 int xres_value
, /* Horizontal resolution */
122 yres_value
; /* Vertical resolution */
123 ipp_res_t units_value
; /* Resolution units */
124 ipp_attribute_t
*attr
; /* Attribute */
125 _ipp_value_t
*attrval
; /* Current attribute value */
129 * Range check input...
132 if (!http
|| !dest
|| !dinfo
|| !option
|| !value
)
136 * Lookup the attribute...
139 if (strstr(option
, "-supported"))
140 attr
= ippFindAttribute(dinfo
->attrs
, option
, IPP_TAG_ZERO
);
143 snprintf(temp
, sizeof(temp
), "%s-supported", option
);
144 attr
= ippFindAttribute(dinfo
->attrs
, temp
, IPP_TAG_ZERO
);
154 if (!strcmp(option
, "media") && !strncmp(value
, "custom_", 7))
157 * Check range of custom media sizes...
160 pwg_media_t
*pwg
; /* Current PWG media size info */
161 int min_width
, /* Minimum width */
162 min_length
, /* Minimum length */
163 max_width
, /* Maximum width */
164 max_length
; /* Maximum length */
167 * Get the minimum and maximum size...
170 min_width
= min_length
= INT_MAX
;
171 max_width
= max_length
= 0;
173 for (i
= attr
->num_values
, attrval
= attr
->values
;
177 if (!strncmp(attrval
->string
.text
, "custom_min_", 11) &&
178 (pwg
= pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
180 min_width
= pwg
->width
;
181 min_length
= pwg
->length
;
183 else if (!strncmp(attrval
->string
.text
, "custom_max_", 11) &&
184 (pwg
= pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
186 max_width
= pwg
->width
;
187 max_length
= pwg
->length
;
195 if (min_width
< INT_MAX
&& max_width
> 0 &&
196 (pwg
= pwgMediaForPWG(value
)) != NULL
&&
197 pwg
->width
>= min_width
&& pwg
->width
<= max_width
&&
198 pwg
->length
>= min_length
&& pwg
->length
<= max_length
)
204 * Check literal values...
207 switch (attr
->value_tag
)
209 case IPP_TAG_INTEGER
:
211 int_value
= atoi(value
);
213 for (i
= 0; i
< attr
->num_values
; i
++)
214 if (attr
->values
[i
].integer
== int_value
)
218 case IPP_TAG_BOOLEAN
:
219 return (attr
->values
[0].boolean
);
222 int_value
= atoi(value
);
224 for (i
= 0; i
< attr
->num_values
; i
++)
225 if (int_value
>= attr
->values
[i
].range
.lower
&&
226 int_value
<= attr
->values
[i
].range
.upper
)
230 case IPP_TAG_RESOLUTION
:
231 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
233 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
236 yres_value
= xres_value
;
239 if (!strcmp(temp
, "dpi"))
240 units_value
= IPP_RES_PER_INCH
;
241 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
242 units_value
= IPP_RES_PER_CM
;
246 for (i
= attr
->num_values
, attrval
= attr
->values
;
250 if (attrval
->resolution
.xres
== xres_value
&&
251 attrval
->resolution
.yres
== yres_value
&&
252 attrval
->resolution
.units
== units_value
)
259 case IPP_TAG_KEYWORD
:
260 case IPP_TAG_CHARSET
:
262 case IPP_TAG_URISCHEME
:
263 case IPP_TAG_MIMETYPE
:
264 case IPP_TAG_LANGUAGE
:
265 case IPP_TAG_TEXTLANG
:
266 case IPP_TAG_NAMELANG
:
267 for (i
= 0; i
< attr
->num_values
; i
++)
268 if (!strcmp(attr
->values
[i
].string
.text
, value
))
278 * If we get there the option+value is not supported...
286 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
289 * "num_options" and "options" represent the currently selected options by the
290 * user. "new_option" and "new_value" are the setting the user has just
293 * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
294 * there was an unrecoverable error such as a resolver loop.
296 * If "num_conflicts" and "conflicts" are not @code NULL@, they are set to
297 * contain the list of conflicting option/value pairs. Similarly, if
298 * "num_resolved" and "resolved" are not @code NULL@ they will be set to the
299 * list of changes needed to resolve the conflict.
301 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
302 * to 0 and @code NULL@, respectively, then the conflict cannot be resolved.
304 * @since CUPS 1.6/OS X 10.8@
307 int /* O - 1 if there is a conflict, 0 if none, -1 on error */
308 cupsCopyDestConflicts(
309 http_t
*http
, /* I - Connection to destination */
310 cups_dest_t
*dest
, /* I - Destination */
311 cups_dinfo_t
*dinfo
, /* I - Destination information */
312 int num_options
, /* I - Number of current options */
313 cups_option_t
*options
, /* I - Current options */
314 const char *new_option
, /* I - New option */
315 const char *new_value
, /* I - New value */
316 int *num_conflicts
, /* O - Number of conflicting options */
317 cups_option_t
**conflicts
, /* O - Conflicting options */
318 int *num_resolved
, /* O - Number of options to resolve */
319 cups_option_t
**resolved
) /* O - Resolved options */
321 int i
, /* Looping var */
322 have_conflicts
= 0, /* Do we have conflicts? */
323 changed
, /* Did we change something? */
324 tries
, /* Number of tries for resolution */
325 num_myconf
= 0, /* My number of conflicting options */
326 num_myres
= 0; /* My number of resolved options */
327 cups_option_t
*myconf
= NULL
, /* My conflicting options */
328 *myres
= NULL
, /* My resolved options */
329 *myoption
, /* My current option */
330 *option
; /* Current option */
331 cups_array_t
*active
, /* Active conflicts */
332 *pass
= NULL
, /* Resolvers for this pass */
333 *resolvers
= NULL
, /* Resolvers we have used */
334 *test
; /* Test array for conflicts */
335 _cups_dconstres_t
*c
, /* Current constraint */
336 *r
; /* Current resolver */
337 ipp_attribute_t
*attr
; /* Current attribute */
338 char value
[2048]; /* Current attribute value as string */
339 const char *myvalue
; /* Current value of an option */
343 * Clear returned values...
359 * Range check input...
362 if (!http
|| !dest
|| !dinfo
||
363 (num_conflicts
!= NULL
) != (conflicts
!= NULL
) ||
364 (num_resolved
!= NULL
) != (resolved
!= NULL
))
368 * Load constraints as needed...
371 if (!dinfo
->constraints
)
372 cups_create_constraints(dinfo
);
374 if (cupsArrayCount(dinfo
->constraints
) == 0)
377 if (!dinfo
->num_defaults
)
378 cups_create_defaults(dinfo
);
381 * If we are resolving, create a shadow array...
386 for (i
= num_options
, option
= options
; i
> 0; i
--, option
++)
387 num_myres
= cupsAddOption(option
->name
, option
->value
, num_myres
, &myres
);
389 if (new_option
&& new_value
)
390 num_myres
= cupsAddOption(new_option
, new_value
, num_myres
, &myres
);
394 num_myres
= num_options
;
399 * Check for any conflicts...
403 pass
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
405 for (tries
= 0; tries
< 100; tries
++)
408 * Check for any conflicts...
411 if (num_conflicts
|| num_resolved
)
413 cupsFreeOptions(num_myconf
, myconf
);
417 active
= cups_test_constraints(dinfo
, new_option
, new_value
,
418 num_myres
, myres
, &num_myconf
,
422 active
= cups_test_constraints(dinfo
, new_option
, new_value
, num_myres
,
425 have_conflicts
= (active
!= NULL
);
427 if (!active
|| !num_resolved
)
428 break; /* All done */
431 * Scan the constraints that were triggered to apply resolvers...
435 resolvers
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
437 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(active
), changed
= 0;
439 c
= (_cups_dconstres_t
*)cupsArrayNext(active
))
441 if (cupsArrayFind(pass
, c
))
442 continue; /* Already applied this resolver... */
444 if (cupsArrayFind(resolvers
, c
))
446 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
452 if ((r
= cupsArrayFind(dinfo
->resolvers
, c
)) == NULL
)
454 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
461 * Add the options from the resolver...
464 cupsArrayAdd(pass
, r
);
465 cupsArrayAdd(resolvers
, r
);
467 for (attr
= ippFirstAttribute(r
->collection
);
469 attr
= ippNextAttribute(r
->collection
))
471 if (new_option
&& !strcmp(attr
->name
, new_option
))
472 continue; /* Ignore this if we just changed it */
474 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
475 continue; /* Ignore if the value is too long */
477 if ((test
= cups_test_constraints(dinfo
, attr
->name
, value
, num_myres
,
478 myres
, NULL
, NULL
)) == NULL
)
481 * That worked, flag it...
487 cupsArrayDelete(test
);
490 * Add the option/value from the resolver regardless of whether it
491 * worked; this makes sure that we can cascade several changes to
492 * make things resolve...
495 num_myres
= cupsAddOption(attr
->name
, value
, num_myres
, &myres
);
501 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
506 cupsArrayClear(pass
);
508 cupsArrayDelete(active
);
514 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
520 * Copy resolved options as needed...
525 for (i
= num_myres
, myoption
= myres
; i
> 0; i
--, myoption
++)
527 if ((myvalue
= cupsGetOption(myoption
->name
, num_options
,
529 strcmp(myvalue
, myoption
->value
))
531 if (new_option
&& !strcmp(new_option
, myoption
->name
) &&
532 new_value
&& !strcmp(new_value
, myoption
->value
))
535 *num_resolved
= cupsAddOption(myoption
->name
, myoption
->value
,
536 *num_resolved
, resolved
);
547 cupsArrayDelete(active
);
548 cupsArrayDelete(pass
);
549 cupsArrayDelete(resolvers
);
554 * Free shadow copy of options...
557 cupsFreeOptions(num_myres
, myres
);
563 * Return conflicting options to caller...
566 *num_conflicts
= num_myconf
;
572 * Free conflicting options...
575 cupsFreeOptions(num_myconf
, myconf
);
578 return (have_conflicts
);
583 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
586 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
587 * value. @code NULL@ is returned on error.
589 * @since CUPS 1.6/OS X 10.8@
592 cups_dinfo_t
* /* O - Destination information */
594 http_t
*http
, /* I - Connection to destination */
595 cups_dest_t
*dest
) /* I - Destination */
597 cups_dinfo_t
*dinfo
; /* Destination information */
598 ipp_t
*request
, /* Get-Printer-Attributes request */
599 *response
; /* Supported attributes */
600 int tries
, /* Number of tries so far */
601 delay
, /* Current retry delay */
602 prev_delay
; /* Next retry delay */
603 const char *uri
; /* Printer URI */
604 char resource
[1024]; /* Resource path */
605 int version
; /* IPP version */
606 ipp_status_t status
; /* Status of request */
607 static const char * const requested_attrs
[] =
608 { /* Requested attributes */
610 "media-col-database",
611 "printer-description"
615 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", http
, dest
,
616 dest
? dest
->name
: ""));
619 * Range check input...
626 * Get the printer URI and resource path...
629 if ((uri
= _cupsGetDestResource(dest
, resource
, sizeof(resource
))) == NULL
)
633 * Get the supported attributes...
644 * Send a Get-Printer-Attributes request...
647 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
648 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
650 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
651 "requesting-user-name", NULL
, cupsUser());
652 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
653 "requested-attributes",
654 (int)(sizeof(requested_attrs
) / sizeof(requested_attrs
[0])),
655 NULL
, requested_attrs
);
656 response
= cupsDoRequest(http
, request
, resource
);
657 status
= cupsLastError();
659 if (status
> IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED
)
661 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
662 "returned %s (%s)", dest
->name
, ippErrorString(status
),
663 cupsLastErrorString()));
668 if (status
== IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
&& version
> 11)
670 else if (status
== IPP_STATUS_ERROR_BUSY
)
674 delay
= _cupsNextDelay(delay
, &prev_delay
);
682 while (!response
&& tries
< 10);
688 * Allocate a cups_dinfo_t structure and return it...
691 if ((dinfo
= calloc(1, sizeof(cups_dinfo_t
))) == NULL
)
693 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
698 dinfo
->version
= version
;
700 dinfo
->resource
= _cupsStrAlloc(resource
);
701 dinfo
->attrs
= response
;
708 * 'cupsFindDestDefault()' - Find the default value(s) for the given option.
710 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
711 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
712 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
713 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
714 * functions to inspect the default value(s) as needed.
716 * @since CUPS 1.7/OS X 10.9@
719 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
721 http_t
*http
, /* I - Connection to destination */
722 cups_dest_t
*dest
, /* I - Destination */
723 cups_dinfo_t
*dinfo
, /* I - Destination information */
724 const char *option
) /* I - Option/attribute name */
726 char name
[IPP_MAX_NAME
]; /* Attribute name */
730 * Range check input...
733 if (!http
|| !dest
|| !dinfo
|| !option
)
735 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
740 * Find and return the attribute...
743 snprintf(name
, sizeof(name
), "%s-default", option
);
744 return (ippFindAttribute(dinfo
->attrs
, name
, IPP_TAG_ZERO
));
748 * 'cupsFindDestReady()' - Find the default value(s) for the given option.
750 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
751 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
752 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
753 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
754 * functions to inspect the default value(s) as needed.
756 * @since CUPS 1.7/OS X 10.9@
759 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
761 http_t
*http
, /* I - Connection to destination */
762 cups_dest_t
*dest
, /* I - Destination */
763 cups_dinfo_t
*dinfo
, /* I - Destination information */
764 const char *option
) /* I - Option/attribute name */
766 char name
[IPP_MAX_NAME
]; /* Attribute name */
770 * Range check input...
773 if (!http
|| !dest
|| !dinfo
|| !option
)
775 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
780 * Find and return the attribute...
783 cups_update_ready(http
, dinfo
);
785 snprintf(name
, sizeof(name
), "%s-ready", option
);
786 return (ippFindAttribute(dinfo
->ready_attrs
, name
, IPP_TAG_ZERO
));
790 * 'cupsFindDestSupported()' - Find the default value(s) for the given option.
792 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
793 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
794 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
795 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
796 * functions to inspect the default value(s) as needed.
798 * @since CUPS 1.7/OS X 10.9@
801 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
802 cupsFindDestSupported(
803 http_t
*http
, /* I - Connection to destination */
804 cups_dest_t
*dest
, /* I - Destination */
805 cups_dinfo_t
*dinfo
, /* I - Destination information */
806 const char *option
) /* I - Option/attribute name */
808 char name
[IPP_MAX_NAME
]; /* Attribute name */
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@.
836 cupsFreeDestInfo(cups_dinfo_t
*dinfo
) /* I - Destination information */
839 * Range check input...
846 * Free memory and return...
849 _cupsStrFree(dinfo
->resource
);
851 cupsArrayDelete(dinfo
->constraints
);
852 cupsArrayDelete(dinfo
->resolvers
);
854 cupsArrayDelete(dinfo
->localizations
);
856 cupsArrayDelete(dinfo
->media_db
);
858 cupsArrayDelete(dinfo
->cached_db
);
860 ippDelete(dinfo
->ready_attrs
);
861 cupsArrayDelete(dinfo
->ready_db
);
863 ippDelete(dinfo
->attrs
);
870 * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a
873 * The @code flags@ parameter determines which set of media are indexed. For
874 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth
875 * borderless size supported by the printer.
877 * @since CUPS 1.7/OS X 10.9@
880 int /* O - 1 on success, 0 on failure */
881 cupsGetDestMediaByIndex(
882 http_t
*http
, /* I - Connection to destination */
883 cups_dest_t
*dest
, /* I - Destination */
884 cups_dinfo_t
*dinfo
, /* I - Destination information */
885 int n
, /* I - Media size number (0-based) */
886 unsigned flags
, /* I - Media flags */
887 cups_size_t
*size
) /* O - Media size information */
889 cups_size_t
*nsize
; /* Size for N */
893 * Range check input...
897 memset(size
, 0, sizeof(cups_size_t
));
899 if (!http
|| !dest
|| !dinfo
|| n
< 0 || !size
)
901 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
906 * Load media list as needed...
909 if (flags
& CUPS_MEDIA_FLAGS_READY
)
910 cups_update_ready(http
, dinfo
);
912 if (!dinfo
->cached_db
|| dinfo
->cached_flags
!= flags
)
913 cups_create_cached(http
, dinfo
, flags
);
916 * Copy the size over and return...
919 if ((nsize
= (cups_size_t
*)cupsArrayIndex(dinfo
->cached_db
, n
)) == NULL
)
921 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
925 memcpy(size
, nsize
, sizeof(cups_size_t
));
932 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
934 * The "media" string is a PWG media name. "Flags" provides some matching
935 * guidance (multiple flags can be combined):
937 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
938 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
939 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
940 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
941 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
942 * size amongst the "ready" media.
944 * The matching result (if any) is returned in the "cups_size_t" structure.
946 * Returns 1 when there is a match and 0 if there is not a match.
948 * @since CUPS 1.6/OS X 10.8@
951 int /* O - 1 on match, 0 on failure */
952 cupsGetDestMediaByName(
953 http_t
*http
, /* I - Connection to destination */
954 cups_dest_t
*dest
, /* I - Destination */
955 cups_dinfo_t
*dinfo
, /* I - Destination information */
956 const char *media
, /* I - Media name */
957 unsigned flags
, /* I - Media matching flags */
958 cups_size_t
*size
) /* O - Media size information */
960 pwg_media_t
*pwg
; /* PWG media info */
964 * Range check input...
968 memset(size
, 0, sizeof(cups_size_t
));
970 if (!http
|| !dest
|| !dinfo
|| !media
|| !size
)
972 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
977 * Lookup the media size name...
980 if ((pwg
= pwgMediaForPWG(media
)) == NULL
)
981 if ((pwg
= pwgMediaForLegacy(media
)) == NULL
)
983 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media
));
984 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unknown media size name."), 1);
992 return (cups_get_media_db(http
, dinfo
, pwg
, flags
, size
));
997 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
999 * "Width" and "length" are the dimensions in hundredths of millimeters.
1000 * "Flags" provides some matching guidance (multiple flags can be combined):
1002 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
1003 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
1004 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
1005 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
1006 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
1007 * size amongst the "ready" media.
1009 * The matching result (if any) is returned in the "cups_size_t" structure.
1011 * Returns 1 when there is a match and 0 if there is not a match.
1013 * @since CUPS 1.6/OS X 10.8@
1016 int /* O - 1 on match, 0 on failure */
1017 cupsGetDestMediaBySize(
1018 http_t
*http
, /* I - Connection to destination */
1019 cups_dest_t
*dest
, /* I - Destination */
1020 cups_dinfo_t
*dinfo
, /* I - Destination information */
1021 int width
, /* I - Media width in hundredths of
1023 int length
, /* I - Media length in hundredths of
1025 unsigned flags
, /* I - Media matching flags */
1026 cups_size_t
*size
) /* O - Media size information */
1028 pwg_media_t
*pwg
; /* PWG media info */
1032 * Range check input...
1036 memset(size
, 0, sizeof(cups_size_t
));
1038 if (!http
|| !dest
|| !dinfo
|| width
<= 0 || length
<= 0 || !size
)
1040 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1045 * Lookup the media size name...
1048 if ((pwg
= pwgMediaForSize(width
, length
)) == NULL
)
1050 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width
,
1052 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Invalid media size."), 1);
1057 * Lookup the size...
1060 return (cups_get_media_db(http
, dinfo
, pwg
, flags
, size
));
1065 * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a
1068 * The @code flags@ parameter determines the set of media sizes that are
1069 * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return
1070 * the number of borderless sizes.
1072 * @since CUPS 1.7/OS X 10.9@
1075 int /* O - Number of sizes */
1076 cupsGetDestMediaCount(
1077 http_t
*http
, /* I - Connection to destination */
1078 cups_dest_t
*dest
, /* I - Destination */
1079 cups_dinfo_t
*dinfo
, /* I - Destination information */
1080 unsigned flags
) /* I - Media flags */
1083 * Range check input...
1086 if (!http
|| !dest
|| !dinfo
)
1088 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1093 * Load media list as needed...
1096 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1097 cups_update_ready(http
, dinfo
);
1099 if (!dinfo
->cached_db
|| dinfo
->cached_flags
!= flags
)
1100 cups_create_cached(http
, dinfo
, flags
);
1102 return (cupsArrayCount(dinfo
->cached_db
));
1107 * 'cupsGetDestMediaDefault()' - Get the default size for a destination.
1109 * The @code flags@ parameter determines which default size is returned. For
1110 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default
1111 * borderless size, typically US Letter or A4, but sometimes 4x6 photo media.
1113 * @since CUPS 1.7/OS X 10.9@
1116 int /* O - 1 on success, 0 on failure */
1117 cupsGetDestMediaDefault(
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 */
1122 cups_size_t
*size
) /* O - Media size information */
1124 const char *media
; /* Default media size */
1128 * Range check input...
1132 memset(size
, 0, sizeof(cups_size_t
));
1134 if (!http
|| !dest
|| !dinfo
|| !size
)
1136 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1141 * Get the default media size, if any...
1144 if ((media
= cupsGetOption("media", dest
->num_options
,
1145 dest
->options
)) == NULL
)
1146 media
= "na_letter_8.5x11in";
1148 if (cupsGetDestMediaByName(http
, dest
, dinfo
, media
, flags
, size
))
1151 if (strcmp(media
, "na_letter_8.5x11in") &&
1152 cupsGetDestMediaByName(http
, dest
, dinfo
, "iso_a4_210x297mm", flags
,
1156 if (strcmp(media
, "iso_a4_210x297mm") &&
1157 cupsGetDestMediaByName(http
, dest
, dinfo
, "na_letter_8.5x11in", flags
,
1161 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) &&
1162 cupsGetDestMediaByName(http
, dest
, dinfo
, "na_index_4x6in", flags
, size
))
1166 * Fall back to the first matching media size...
1169 return (cupsGetDestMediaByIndex(http
, dest
, dinfo
, flags
, 0, size
));
1174 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
1179 cups_array_t
*a
, /* I - Array */
1180 ipp_t
*collection
) /* I - Collection value */
1182 ipp_attribute_t
*attr
; /* Attribute */
1183 _cups_dconstres_t
*temp
; /* Current constraint/resolver */
1186 if ((attr
= ippFindAttribute(collection
, "resolver-name",
1187 IPP_TAG_NAME
)) == NULL
)
1190 if ((temp
= calloc(1, sizeof(_cups_dconstres_t
))) == NULL
)
1193 temp
->name
= attr
->values
[0].string
.text
;
1194 temp
->collection
= collection
;
1196 cupsArrayAdd(a
, temp
);
1201 * 'cups_compare_dconstres()' - Compare to resolver entries.
1204 static int /* O - Result of comparison */
1205 cups_compare_dconstres(
1206 _cups_dconstres_t
*a
, /* I - First resolver */
1207 _cups_dconstres_t
*b
) /* I - Second resolver */
1209 return (strcmp(a
->name
, b
->name
));
1214 * 'cups_compare_media_db()' - Compare two media entries.
1217 static int /* O - Result of comparison */
1218 cups_compare_media_db(
1219 _cups_media_db_t
*a
, /* I - First media entries */
1220 _cups_media_db_t
*b
) /* I - Second media entries */
1222 int result
; /* Result of comparison */
1225 if ((result
= a
->width
- b
->width
) == 0)
1226 result
= a
->length
- b
->length
;
1233 * 'cups_copy_media_db()' - Copy a media entry.
1236 static _cups_media_db_t
* /* O - New media entry */
1238 _cups_media_db_t
*mdb
) /* I - Media entry to copy */
1240 _cups_media_db_t
*temp
; /* New media entry */
1243 if ((temp
= calloc(1, sizeof(_cups_media_db_t
))) == NULL
)
1247 temp
->color
= _cupsStrAlloc(mdb
->color
);
1249 temp
->key
= _cupsStrAlloc(mdb
->key
);
1251 temp
->info
= _cupsStrAlloc(mdb
->info
);
1253 temp
->size_name
= _cupsStrAlloc(mdb
->size_name
);
1255 temp
->source
= _cupsStrAlloc(mdb
->source
);
1257 temp
->type
= _cupsStrAlloc(mdb
->type
);
1259 temp
->width
= mdb
->width
;
1260 temp
->length
= mdb
->length
;
1261 temp
->bottom
= mdb
->bottom
;
1262 temp
->left
= mdb
->left
;
1263 temp
->right
= mdb
->right
;
1264 temp
->top
= mdb
->top
;
1271 * 'cups_create_cached()' - Create the media selection cache.
1275 cups_create_cached(http_t
*http
, /* I - Connection to destination */
1276 cups_dinfo_t
*dinfo
, /* I - Destination information */
1277 unsigned flags
) /* I - Media selection flags */
1279 cups_array_t
*db
; /* Media database array to use */
1280 _cups_media_db_t
*mdb
, /* Media database entry */
1281 *first
; /* First entry this size */
1284 if (dinfo
->cached_db
)
1285 cupsArrayDelete(dinfo
->cached_db
);
1287 dinfo
->cached_db
= cupsArrayNew(NULL
, NULL
);
1288 dinfo
->cached_flags
= flags
;
1290 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1292 cups_update_ready(http
, dinfo
);
1293 db
= dinfo
->ready_db
;
1297 if (!dinfo
->media_db
)
1298 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
);
1300 db
= dinfo
->media_db
;
1303 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
), first
= mdb
;
1305 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1307 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1309 if (!mdb
->left
&& !mdb
->right
&& !mdb
->top
&& !mdb
->bottom
)
1310 cupsArrayAdd(dinfo
->cached_db
, mdb
);
1312 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1314 if (first
->width
!= mdb
->width
|| first
->length
!= mdb
->length
)
1316 cupsArrayAdd(dinfo
->cached_db
, first
);
1319 else if (mdb
->left
>= first
->left
&& mdb
->right
>= first
->right
&&
1320 mdb
->top
>= first
->top
&& mdb
->bottom
>= first
->bottom
)
1325 if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1326 cupsArrayAdd(dinfo
->cached_db
, first
);
1331 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
1335 cups_create_constraints(
1336 cups_dinfo_t
*dinfo
) /* I - Destination information */
1338 int i
; /* Looping var */
1339 ipp_attribute_t
*attr
; /* Attribute */
1340 _ipp_value_t
*val
; /* Current value */
1343 dinfo
->constraints
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, NULL
,
1344 (cups_afree_func_t
)free
);
1345 dinfo
->resolvers
= cupsArrayNew3((cups_array_func_t
)cups_compare_dconstres
,
1346 NULL
, NULL
, 0, NULL
,
1347 (cups_afree_func_t
)free
);
1349 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-constraints-supported",
1350 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1352 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
1353 cups_add_dconstres(dinfo
->constraints
, val
->collection
);
1356 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-resolvers-supported",
1357 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1359 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
1360 cups_add_dconstres(dinfo
->resolvers
, val
->collection
);
1366 * 'cups_create_defaults()' - Create the -default option array.
1368 * TODO: Need to support collection defaults...
1372 cups_create_defaults(
1373 cups_dinfo_t
*dinfo
) /* I - Destination information */
1375 ipp_attribute_t
*attr
; /* Current attribute */
1376 char name
[IPP_MAX_NAME
+ 1],
1378 *nameptr
, /* Pointer into current name */
1379 value
[2048]; /* Current value */
1383 * Iterate through the printer attributes looking for xxx-default and adding
1384 * xxx=value to the defaults option array.
1387 for (attr
= ippFirstAttribute(dinfo
->attrs
);
1389 attr
= ippNextAttribute(dinfo
->attrs
))
1391 if (!attr
->name
|| attr
->group_tag
!= IPP_TAG_PRINTER
)
1394 if (attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
)
1395 continue; /* TODO: STR #4096 */
1397 if ((nameptr
= attr
->name
+ strlen(attr
->name
) - 8) <= attr
->name
||
1398 strcmp(nameptr
, "-default"))
1401 strlcpy(name
, attr
->name
, sizeof(name
));
1402 if ((nameptr
= name
+ strlen(name
) - 8) <= name
||
1403 strcmp(nameptr
, "-default"))
1408 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
1411 dinfo
->num_defaults
= cupsAddOption(name
, value
, dinfo
->num_defaults
,
1418 * 'cups_create_media_db()' - Create the media database.
1422 cups_create_media_db(
1423 cups_dinfo_t
*dinfo
, /* I - Destination information */
1424 unsigned flags
) /* I - Media flags */
1426 int i
; /* Looping var */
1427 _ipp_value_t
*val
; /* Current value */
1428 ipp_attribute_t
*media_col_db
, /* media-col-database */
1429 *media_attr
, /* media-xxx */
1430 *x_dimension
, /* x-dimension */
1431 *y_dimension
; /* y-dimension */
1432 pwg_media_t
*pwg
; /* PWG media info */
1433 cups_array_t
*db
; /* New media database array */
1434 _cups_media_db_t mdb
; /* Media entry */
1437 db
= cupsArrayNew3((cups_array_func_t
)cups_compare_media_db
,
1439 (cups_acopy_func_t
)cups_copy_media_db
,
1440 (cups_afree_func_t
)cups_free_media_db
);
1442 if (flags
== CUPS_MEDIA_FLAGS_READY
)
1444 dinfo
->ready_db
= db
;
1446 media_col_db
= ippFindAttribute(dinfo
->ready_attrs
, "media-col-ready",
1447 IPP_TAG_BEGIN_COLLECTION
);
1448 media_attr
= ippFindAttribute(dinfo
->ready_attrs
, "media-ready",
1453 dinfo
->media_db
= db
;
1454 dinfo
->min_size
.width
= INT_MAX
;
1455 dinfo
->min_size
.length
= INT_MAX
;
1456 dinfo
->max_size
.width
= 0;
1457 dinfo
->max_size
.length
= 0;
1459 media_col_db
= ippFindAttribute(dinfo
->attrs
, "media-col-database",
1460 IPP_TAG_BEGIN_COLLECTION
);
1461 media_attr
= ippFindAttribute(dinfo
->attrs
, "media-supported",
1467 _ipp_value_t
*custom
= NULL
; /* Custom size range value */
1469 for (i
= media_col_db
->num_values
, val
= media_col_db
->values
;
1473 memset(&mdb
, 0, sizeof(mdb
));
1475 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size",
1476 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1478 ipp_t
*media_size
= media_attr
->values
[0].collection
;
1479 /* media-size collection value */
1481 if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1482 IPP_TAG_INTEGER
)) != NULL
&&
1483 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1484 IPP_TAG_INTEGER
)) != NULL
)
1490 mdb
.width
= x_dimension
->values
[0].integer
;
1491 mdb
.length
= y_dimension
->values
[0].integer
;
1493 else if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1494 IPP_TAG_INTEGER
)) != NULL
&&
1495 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1496 IPP_TAG_RANGE
)) != NULL
)
1502 mdb
.width
= x_dimension
->values
[0].integer
;
1503 mdb
.length
= y_dimension
->values
[0].range
.upper
;
1505 else if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
1506 (x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1507 IPP_TAG_RANGE
)) != NULL
&&
1508 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1509 IPP_TAG_RANGE
)) != NULL
)
1512 * Custom size range; save this as the custom size value with default
1513 * margins, then continue; we'll capture the real margins below...
1518 dinfo
->min_size
.width
= x_dimension
->values
[0].range
.lower
;
1519 dinfo
->min_size
.length
= y_dimension
->values
[0].range
.lower
;
1520 dinfo
->min_size
.left
=
1521 dinfo
->min_size
.right
= 635; /* Default 1/4" side margins */
1522 dinfo
->min_size
.top
=
1523 dinfo
->min_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1525 dinfo
->max_size
.width
= x_dimension
->values
[0].range
.upper
;
1526 dinfo
->max_size
.length
= y_dimension
->values
[0].range
.upper
;
1527 dinfo
->max_size
.left
=
1528 dinfo
->max_size
.right
= 635; /* Default 1/4" side margins */
1529 dinfo
->max_size
.top
=
1530 dinfo
->max_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1535 if ((media_attr
= ippFindAttribute(val
->collection
, "media-color",
1536 IPP_TAG_ZERO
)) != NULL
&&
1537 (media_attr
->value_tag
== IPP_TAG_NAME
||
1538 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1539 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1540 mdb
.color
= media_attr
->values
[0].string
.text
;
1542 if ((media_attr
= ippFindAttribute(val
->collection
, "media-info",
1543 IPP_TAG_TEXT
)) != NULL
)
1544 mdb
.info
= media_attr
->values
[0].string
.text
;
1546 if ((media_attr
= ippFindAttribute(val
->collection
, "media-key",
1547 IPP_TAG_ZERO
)) != NULL
&&
1548 (media_attr
->value_tag
== IPP_TAG_NAME
||
1549 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1550 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1551 mdb
.key
= media_attr
->values
[0].string
.text
;
1553 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size-name",
1554 IPP_TAG_ZERO
)) != NULL
&&
1555 (media_attr
->value_tag
== IPP_TAG_NAME
||
1556 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1557 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1558 mdb
.size_name
= media_attr
->values
[0].string
.text
;
1560 if ((media_attr
= ippFindAttribute(val
->collection
, "media-source",
1561 IPP_TAG_ZERO
)) != NULL
&&
1562 (media_attr
->value_tag
== IPP_TAG_NAME
||
1563 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1564 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1565 mdb
.source
= media_attr
->values
[0].string
.text
;
1567 if ((media_attr
= ippFindAttribute(val
->collection
, "media-type",
1568 IPP_TAG_ZERO
)) != NULL
&&
1569 (media_attr
->value_tag
== IPP_TAG_NAME
||
1570 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1571 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1572 mdb
.type
= media_attr
->values
[0].string
.text
;
1574 if ((media_attr
= ippFindAttribute(val
->collection
, "media-bottom-margin",
1575 IPP_TAG_INTEGER
)) != NULL
)
1576 mdb
.bottom
= media_attr
->values
[0].integer
;
1578 if ((media_attr
= ippFindAttribute(val
->collection
, "media-left-margin",
1579 IPP_TAG_INTEGER
)) != NULL
)
1580 mdb
.left
= media_attr
->values
[0].integer
;
1582 if ((media_attr
= ippFindAttribute(val
->collection
, "media-right-margin",
1583 IPP_TAG_INTEGER
)) != NULL
)
1584 mdb
.right
= media_attr
->values
[0].integer
;
1586 if ((media_attr
= ippFindAttribute(val
->collection
, "media-top-margin",
1587 IPP_TAG_INTEGER
)) != NULL
)
1588 mdb
.top
= media_attr
->values
[0].integer
;
1590 cupsArrayAdd(db
, &mdb
);
1595 if ((media_attr
= ippFindAttribute(custom
->collection
,
1596 "media-bottom-margin",
1597 IPP_TAG_INTEGER
)) != NULL
)
1599 dinfo
->min_size
.top
=
1600 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
1603 if ((media_attr
= ippFindAttribute(custom
->collection
,
1604 "media-left-margin",
1605 IPP_TAG_INTEGER
)) != NULL
)
1607 dinfo
->min_size
.left
=
1608 dinfo
->max_size
.left
= media_attr
->values
[0].integer
;
1611 if ((media_attr
= ippFindAttribute(custom
->collection
,
1612 "media-right-margin",
1613 IPP_TAG_INTEGER
)) != NULL
)
1615 dinfo
->min_size
.right
=
1616 dinfo
->max_size
.right
= media_attr
->values
[0].integer
;
1619 if ((media_attr
= ippFindAttribute(custom
->collection
,
1621 IPP_TAG_INTEGER
)) != NULL
)
1623 dinfo
->min_size
.top
=
1624 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
1628 else if (media_attr
&&
1629 (media_attr
->value_tag
== IPP_TAG_NAME
||
1630 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1631 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1633 memset(&mdb
, 0, sizeof(mdb
));
1636 mdb
.right
= 635; /* Default 1/4" side margins */
1638 mdb
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1640 for (i
= media_attr
->num_values
, val
= media_attr
->values
;
1644 if ((pwg
= pwgMediaForPWG(val
->string
.text
)) == NULL
)
1645 if ((pwg
= pwgMediaForLegacy(val
->string
.text
)) == NULL
)
1647 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
1652 mdb
.width
= pwg
->width
;
1653 mdb
.length
= pwg
->length
;
1655 if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
1656 !strncmp(val
->string
.text
, "custom_min_", 11))
1658 mdb
.size_name
= NULL
;
1659 dinfo
->min_size
= mdb
;
1661 else if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
1662 !strncmp(val
->string
.text
, "custom_max_", 11))
1664 mdb
.size_name
= NULL
;
1665 dinfo
->max_size
= mdb
;
1669 mdb
.size_name
= val
->string
.text
;
1671 cupsArrayAdd(db
, &mdb
);
1679 * 'cups_free_media_cb()' - Free a media entry.
1684 _cups_media_db_t
*mdb
) /* I - Media entry to free */
1687 _cupsStrFree(mdb
->color
);
1689 _cupsStrFree(mdb
->key
);
1691 _cupsStrFree(mdb
->info
);
1693 _cupsStrFree(mdb
->size_name
);
1695 _cupsStrFree(mdb
->source
);
1697 _cupsStrFree(mdb
->type
);
1704 * 'cups_get_media_db()' - Lookup the media entry for a given size.
1707 static int /* O - 1 on match, 0 on failure */
1708 cups_get_media_db(http_t
*http
, /* I - Connection to destination */
1709 cups_dinfo_t
*dinfo
, /* I - Destination information */
1710 pwg_media_t
*pwg
, /* I - PWG media info */
1711 unsigned flags
, /* I - Media matching flags */
1712 cups_size_t
*size
) /* O - Media size/margin/name info */
1714 cups_array_t
*db
; /* Which media database to query */
1715 _cups_media_db_t
*mdb
, /* Current media database entry */
1716 *best
= NULL
, /* Best matching entry */
1717 key
; /* Search key */
1721 * Create the media database as needed...
1724 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1726 cups_update_ready(http
, dinfo
);
1727 db
= dinfo
->ready_db
;
1731 if (!dinfo
->media_db
)
1732 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
);
1734 db
= dinfo
->media_db
;
1741 memset(&key
, 0, sizeof(key
));
1742 key
.width
= pwg
->width
;
1743 key
.length
= pwg
->length
;
1745 if ((mdb
= cupsArrayFind(db
, &key
)) != NULL
)
1748 * Found an exact match, let's figure out the best margins for the flags
1754 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1757 * Look for the smallest margins...
1760 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 ||
1763 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1764 mdb
&& !cups_compare_media_db(mdb
, &key
);
1765 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1767 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
1768 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
)
1771 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
1779 * If we need an exact match, return no-match if the size is not
1783 if ((flags
& CUPS_MEDIA_FLAGS_EXACT
) &&
1784 (best
->left
|| best
->right
|| best
->top
|| best
->bottom
))
1787 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1790 * Look for the largest margins...
1793 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1794 mdb
&& !cups_compare_media_db(mdb
, &key
);
1795 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1797 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
1798 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
)
1805 * Look for the smallest non-zero margins...
1808 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1809 mdb
&& !cups_compare_media_db(mdb
, &key
);
1810 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1812 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
1813 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) ||
1814 best
->right
== 0) &&
1815 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
1816 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) ||
1822 else if (flags
& CUPS_MEDIA_FLAGS_EXACT
)
1825 * See if we can do this as a custom size...
1828 if (pwg
->width
< dinfo
->min_size
.width
||
1829 pwg
->width
> dinfo
->max_size
.width
||
1830 pwg
->length
< dinfo
->min_size
.length
||
1831 pwg
->length
> dinfo
->max_size
.length
)
1832 return (0); /* Out of range */
1834 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) &&
1835 (dinfo
->min_size
.left
> 0 || dinfo
->min_size
.right
> 0 ||
1836 dinfo
->min_size
.top
> 0 || dinfo
->min_size
.bottom
> 0))
1837 return (0); /* Not borderless */
1839 key
.size_name
= (char *)pwg
->pwg
;
1840 key
.bottom
= dinfo
->min_size
.bottom
;
1841 key
.left
= dinfo
->min_size
.left
;
1842 key
.right
= dinfo
->min_size
.right
;
1843 key
.top
= dinfo
->min_size
.top
;
1847 else if (pwg
->width
>= dinfo
->min_size
.width
&&
1848 pwg
->width
<= dinfo
->max_size
.width
&&
1849 pwg
->length
>= dinfo
->min_size
.length
&&
1850 pwg
->length
<= dinfo
->max_size
.length
)
1853 * Map to custom size...
1856 key
.size_name
= (char *)pwg
->pwg
;
1857 key
.bottom
= dinfo
->min_size
.bottom
;
1858 key
.left
= dinfo
->min_size
.left
;
1859 key
.right
= dinfo
->min_size
.right
;
1860 key
.top
= dinfo
->min_size
.top
;
1867 * Find a close size...
1870 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
);
1872 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1873 if (cups_is_close_media_db(mdb
, &key
))
1881 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1884 * Look for the smallest margins...
1887 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 ||
1890 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1891 mdb
&& cups_is_close_media_db(mdb
, &key
);
1892 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1894 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
1895 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
)
1898 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
1905 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1908 * Look for the largest margins...
1911 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1912 mdb
&& cups_is_close_media_db(mdb
, &key
);
1913 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1915 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
1916 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
)
1923 * Look for the smallest non-zero margins...
1926 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
1927 mdb
&& cups_is_close_media_db(mdb
, &key
);
1928 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1930 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
1931 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) ||
1932 best
->right
== 0) &&
1933 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
1934 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) ||
1944 * Return the matching size...
1947 if (best
->size_name
)
1948 strlcpy(size
->media
, best
->size_name
, sizeof(size
->media
));
1950 strlcpy(size
->media
, best
->key
, sizeof(size
->media
));
1952 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
1954 size
->width
= best
->width
;
1955 size
->length
= best
->length
;
1956 size
->bottom
= best
->bottom
;
1957 size
->left
= best
->left
;
1958 size
->right
= best
->right
;
1959 size
->top
= best
->top
;
1969 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
1970 * close to the same size.
1972 * Currently we use 5 points (from PostScript) as the matching range...
1975 static int /* O - 1 if the sizes are close */
1976 cups_is_close_media_db(
1977 _cups_media_db_t
*a
, /* I - First media entries */
1978 _cups_media_db_t
*b
) /* I - Second media entries */
1980 int dwidth
, /* Difference in width */
1981 dlength
; /* Difference in length */
1984 dwidth
= a
->width
- b
->width
;
1985 dlength
= a
->length
- b
->length
;
1987 return (dwidth
>= -176 && dwidth
<= 176 &&
1988 dlength
>= -176 && dlength
<= 176);
1993 * 'cups_test_constraints()' - Test constraints.
1995 * TODO: STR #4096 - Need to properly support media-col contraints...
1998 static cups_array_t
* /* O - Active constraints */
1999 cups_test_constraints(
2000 cups_dinfo_t
*dinfo
, /* I - Destination information */
2001 const char *new_option
, /* I - Newly selected option */
2002 const char *new_value
, /* I - Newly selected value */
2003 int num_options
, /* I - Number of options */
2004 cups_option_t
*options
, /* I - Options */
2005 int *num_conflicts
, /* O - Number of conflicting options */
2006 cups_option_t
**conflicts
) /* O - Conflicting options */
2008 int i
, /* Looping var */
2009 match
; /* Value matches? */
2010 int num_matching
; /* Number of matching options */
2011 cups_option_t
*matching
; /* Matching options */
2012 _cups_dconstres_t
*c
; /* Current constraint */
2013 cups_array_t
*active
= NULL
; /* Active constraints */
2014 ipp_attribute_t
*attr
; /* Current attribute */
2015 _ipp_value_t
*attrval
; /* Current attribute value */
2016 const char *value
; /* Current value */
2017 char temp
[1024]; /* Temporary string */
2018 int int_value
; /* Integer value */
2019 int xres_value
, /* Horizontal resolution */
2020 yres_value
; /* Vertical resolution */
2021 ipp_res_t units_value
; /* Resolution units */
2024 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(dinfo
->constraints
);
2026 c
= (_cups_dconstres_t
*)cupsArrayNext(dinfo
->constraints
))
2031 for (attr
= ippFirstAttribute(c
->collection
);
2033 attr
= ippNextAttribute(c
->collection
))
2035 if (attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
)
2036 break; /* TODO: STR #4096 */
2039 * Get the value for the current attribute in the constraint...
2042 if (new_option
&& new_value
&& !strcmp(attr
->name
, new_option
))
2044 else if ((value
= cupsGetOption(attr
->name
, num_options
,
2046 value
= cupsGetOption(attr
->name
, dinfo
->num_defaults
, dinfo
->defaults
);
2051 * Not set so this constraint does not apply...
2059 switch (attr
->value_tag
)
2061 case IPP_TAG_INTEGER
:
2063 int_value
= atoi(value
);
2065 for (i
= attr
->num_values
, attrval
= attr
->values
;
2069 if (attrval
->integer
== int_value
)
2077 case IPP_TAG_BOOLEAN
:
2078 int_value
= !strcmp(value
, "true");
2080 for (i
= attr
->num_values
, attrval
= attr
->values
;
2084 if (attrval
->boolean
== int_value
)
2092 case IPP_TAG_RANGE
:
2093 int_value
= atoi(value
);
2095 for (i
= attr
->num_values
, attrval
= attr
->values
;
2099 if (int_value
>= attrval
->range
.lower
&&
2100 int_value
<= attrval
->range
.upper
)
2108 case IPP_TAG_RESOLUTION
:
2109 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
2111 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
2114 yres_value
= xres_value
;
2117 if (!strcmp(temp
, "dpi"))
2118 units_value
= IPP_RES_PER_INCH
;
2119 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
2120 units_value
= IPP_RES_PER_CM
;
2124 for (i
= attr
->num_values
, attrval
= attr
->values
;
2128 if (attrval
->resolution
.xres
== xres_value
&&
2129 attrval
->resolution
.yres
== yres_value
&&
2130 attrval
->resolution
.units
== units_value
)
2140 case IPP_TAG_KEYWORD
:
2141 case IPP_TAG_CHARSET
:
2143 case IPP_TAG_URISCHEME
:
2144 case IPP_TAG_MIMETYPE
:
2145 case IPP_TAG_LANGUAGE
:
2146 case IPP_TAG_TEXTLANG
:
2147 case IPP_TAG_NAMELANG
:
2148 for (i
= attr
->num_values
, attrval
= attr
->values
;
2152 if (!strcmp(attrval
->string
.text
, value
))
2167 num_matching
= cupsAddOption(attr
->name
, value
, num_matching
, &matching
);
2173 active
= cupsArrayNew(NULL
, NULL
);
2175 cupsArrayAdd(active
, c
);
2177 if (num_conflicts
&& conflicts
)
2179 cups_option_t
*moption
; /* Matching option */
2181 for (i
= num_matching
, moption
= matching
; i
> 0; i
--, moption
++)
2182 *num_conflicts
= cupsAddOption(moption
->name
, moption
->value
,
2183 *num_conflicts
, conflicts
);
2187 cupsFreeOptions(num_matching
, matching
);
2195 * 'cups_update_ready()' - Update xxx-ready attributes for the printer.
2199 cups_update_ready(http_t
*http
, /* I - Connection to destination */
2200 cups_dinfo_t
*dinfo
) /* I - Destination information */
2202 ipp_t
*request
; /* Get-Printer-Attributes request */
2203 static const char * const pattrs
[] = /* Printer attributes we want */
2205 "finishings-col-ready",
2207 "job-finishings-col-ready",
2208 "job-finishings-ready",
2215 * Don't update more than once every 30 seconds...
2218 if ((time(NULL
) - dinfo
->ready_time
) < _CUPS_MEDIA_READY_TTL
)
2222 * Free any previous results...
2225 if (dinfo
->cached_flags
& CUPS_MEDIA_FLAGS_READY
)
2227 cupsArrayDelete(dinfo
->cached_db
);
2228 dinfo
->cached_db
= NULL
;
2229 dinfo
->cached_flags
= CUPS_MEDIA_FLAGS_DEFAULT
;
2232 ippDelete(dinfo
->ready_attrs
);
2233 dinfo
->ready_attrs
= NULL
;
2235 cupsArrayDelete(dinfo
->ready_db
);
2236 dinfo
->ready_db
= NULL
;
2239 * Query the xxx-ready values...
2242 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
2243 ippSetVersion(request
, dinfo
->version
/ 10, dinfo
->version
% 10);
2245 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
2247 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
2249 ippAddStrings(request
, IPP_TAG_OPERATION
,
2250 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "requested-attributes",
2251 (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
2253 dinfo
->ready_attrs
= cupsDoRequest(http
, request
, dinfo
->resource
);
2256 * Update the ready media database...
2259 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_READY
);
2262 * Update last lookup time and return...
2265 dinfo
->ready_time
= time(NULL
);