2 * Destination option/media support for CUPS.
4 * Copyright 2012-2018 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
10 * Include necessary headers...
13 #include "cups-private.h"
20 #define _CUPS_MEDIA_READY_TTL 30 /* Life of xxx-ready values */
27 static void cups_add_dconstres(cups_array_t
*a
, ipp_t
*collection
);
28 static int cups_collection_contains(ipp_t
*test
, ipp_t
*match
);
29 static size_t cups_collection_string(ipp_attribute_t
*attr
, char *buffer
, size_t bufsize
);
30 static int cups_compare_dconstres(_cups_dconstres_t
*a
,
31 _cups_dconstres_t
*b
);
32 static int cups_compare_media_db(_cups_media_db_t
*a
,
34 static _cups_media_db_t
*cups_copy_media_db(_cups_media_db_t
*mdb
);
35 static void cups_create_cached(http_t
*http
, cups_dinfo_t
*dinfo
,
37 static void cups_create_constraints(cups_dinfo_t
*dinfo
);
38 static void cups_create_defaults(cups_dinfo_t
*dinfo
);
39 static void cups_create_media_db(cups_dinfo_t
*dinfo
,
41 static void cups_free_media_db(_cups_media_db_t
*mdb
);
42 static int cups_get_media_db(http_t
*http
, cups_dinfo_t
*dinfo
,
43 pwg_media_t
*pwg
, unsigned flags
,
45 static int cups_is_close_media_db(_cups_media_db_t
*a
,
47 static cups_array_t
*cups_test_constraints(cups_dinfo_t
*dinfo
,
48 const char *new_option
,
49 const char *new_value
,
51 cups_option_t
*options
,
53 cups_option_t
**conflicts
);
54 static void cups_update_ready(http_t
*http
, cups_dinfo_t
*dinfo
);
58 * 'cupsAddDestMediaOptions()' - Add the option corresponding to the specified media size.
63 int /* O - New number of options */
64 cupsAddDestMediaOptions(
65 http_t
*http
, /* I - Connection to destination */
66 cups_dest_t
*dest
, /* I - Destination */
67 cups_dinfo_t
*dinfo
, /* I - Destination information */
68 unsigned flags
, /* I - Media matching flags */
69 cups_size_t
*size
, /* I - Media size */
70 int num_options
, /* I - Current number of options */
71 cups_option_t
**options
) /* IO - Options */
73 cups_array_t
*db
; /* Media database */
74 _cups_media_db_t
*mdb
; /* Media database entry */
75 char value
[2048]; /* Option value */
79 * Range check input...
82 if (!http
|| !dest
|| !dinfo
|| !size
|| !options
)
84 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
89 * Find the matching media size...
92 if (flags
& CUPS_MEDIA_FLAGS_READY
)
97 DEBUG_printf(("1cupsAddDestMediaOptions: size->media=\"%s\"", size
->media
));
99 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
); mdb
; mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
101 if (mdb
->key
&& !strcmp(mdb
->key
, size
->media
))
103 else if (mdb
->size_name
&& !strcmp(mdb
->size_name
, size
->media
))
109 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
); mdb
; mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
111 if (mdb
->width
== size
->width
&& mdb
->length
== size
->length
&& mdb
->bottom
== size
->bottom
&& mdb
->left
== size
->left
&& mdb
->right
== size
->right
&& mdb
->top
== size
->top
)
118 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
); mdb
; mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
120 if (mdb
->width
== size
->width
&& mdb
->length
== size
->length
)
127 DEBUG_puts("1cupsAddDestMediaOptions: Unable to find matching size.");
128 return (num_options
);
131 DEBUG_printf(("1cupsAddDestMediaOptions: MATCH mdb%p [key=\"%s\" size_name=\"%s\" source=\"%s\" type=\"%s\" width=%d length=%d B%d L%d R%d T%d]", (void *)mdb
, mdb
->key
, mdb
->size_name
, mdb
->source
, mdb
->type
, mdb
->width
, mdb
->length
, mdb
->bottom
, mdb
->left
, mdb
->right
, mdb
->top
));
136 snprintf(value
, sizeof(value
), "{media-size={x-dimension=%d y-dimension=%d} media-bottom-margin=%d media-left-margin=%d media-right-margin=%d media-top-margin=%d media-source=\"%s\" media-type=\"%s\"}", mdb
->width
, mdb
->length
, mdb
->bottom
, mdb
->left
, mdb
->right
, mdb
->top
, mdb
->source
, mdb
->type
);
138 snprintf(value
, sizeof(value
), "{media-size={x-dimension=%d y-dimension=%d} media-bottom-margin=%d media-left-margin=%d media-right-margin=%d media-top-margin=%d media-source=\"%s\"}", mdb
->width
, mdb
->length
, mdb
->bottom
, mdb
->left
, mdb
->right
, mdb
->top
, mdb
->source
);
142 snprintf(value
, sizeof(value
), "{media-size={x-dimension=%d y-dimension=%d} media-bottom-margin=%d media-left-margin=%d media-right-margin=%d media-top-margin=%d media-type=\"%s\"}", mdb
->width
, mdb
->length
, mdb
->bottom
, mdb
->left
, mdb
->right
, mdb
->top
, mdb
->type
);
146 snprintf(value
, sizeof(value
), "{media-size={x-dimension=%d y-dimension=%d} media-bottom-margin=%d media-left-margin=%d media-right-margin=%d media-top-margin=%d}", mdb
->width
, mdb
->length
, mdb
->bottom
, mdb
->left
, mdb
->right
, mdb
->top
);
149 num_options
= cupsAddOption("media-col", value
, num_options
, options
);
151 return (num_options
);
156 * 'cupsCheckDestSupported()' - Check that the option and value are supported
157 * by the destination.
159 * Returns 1 if supported, 0 otherwise.
161 * @since CUPS 1.6/macOS 10.8@
164 int /* O - 1 if supported, 0 otherwise */
165 cupsCheckDestSupported(
166 http_t
*http
, /* I - Connection to destination */
167 cups_dest_t
*dest
, /* I - Destination */
168 cups_dinfo_t
*dinfo
, /* I - Destination information */
169 const char *option
, /* I - Option */
170 const char *value
) /* I - Value or @code NULL@ */
172 int i
; /* Looping var */
173 char temp
[1024]; /* Temporary string */
174 int int_value
; /* Integer value */
175 int xres_value
, /* Horizontal resolution */
176 yres_value
; /* Vertical resolution */
177 ipp_res_t units_value
; /* Resolution units */
178 ipp_attribute_t
*attr
; /* Attribute */
179 _ipp_value_t
*attrval
; /* Current attribute value */
183 * Get the default connection as needed...
187 http
= _cupsConnect();
190 * Range check input...
193 if (!http
|| !dest
|| !dinfo
|| !option
)
197 * Lookup the attribute...
200 if (strstr(option
, "-supported"))
201 attr
= ippFindAttribute(dinfo
->attrs
, option
, IPP_TAG_ZERO
);
204 snprintf(temp
, sizeof(temp
), "%s-supported", option
);
205 attr
= ippFindAttribute(dinfo
->attrs
, temp
, IPP_TAG_ZERO
);
218 if (!strcmp(option
, "media") && !strncmp(value
, "custom_", 7))
221 * Check range of custom media sizes...
224 pwg_media_t
*pwg
; /* Current PWG media size info */
225 int min_width
, /* Minimum width */
226 min_length
, /* Minimum length */
227 max_width
, /* Maximum width */
228 max_length
; /* Maximum length */
231 * Get the minimum and maximum size...
234 min_width
= min_length
= INT_MAX
;
235 max_width
= max_length
= 0;
237 for (i
= attr
->num_values
, attrval
= attr
->values
;
241 if (!strncmp(attrval
->string
.text
, "custom_min_", 11) &&
242 (pwg
= pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
244 min_width
= pwg
->width
;
245 min_length
= pwg
->length
;
247 else if (!strncmp(attrval
->string
.text
, "custom_max_", 11) &&
248 (pwg
= pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
250 max_width
= pwg
->width
;
251 max_length
= pwg
->length
;
259 if (min_width
< INT_MAX
&& max_width
> 0 &&
260 (pwg
= pwgMediaForPWG(value
)) != NULL
&&
261 pwg
->width
>= min_width
&& pwg
->width
<= max_width
&&
262 pwg
->length
>= min_length
&& pwg
->length
<= max_length
)
268 * Check literal values...
271 switch (attr
->value_tag
)
273 case IPP_TAG_INTEGER
:
275 int_value
= atoi(value
);
277 for (i
= 0; i
< attr
->num_values
; i
++)
278 if (attr
->values
[i
].integer
== int_value
)
282 case IPP_TAG_BOOLEAN
:
283 return (attr
->values
[0].boolean
);
286 int_value
= atoi(value
);
288 for (i
= 0; i
< attr
->num_values
; i
++)
289 if (int_value
>= attr
->values
[i
].range
.lower
&&
290 int_value
<= attr
->values
[i
].range
.upper
)
294 case IPP_TAG_RESOLUTION
:
295 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
297 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
300 yres_value
= xres_value
;
303 if (!strcmp(temp
, "dpi"))
304 units_value
= IPP_RES_PER_INCH
;
305 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
306 units_value
= IPP_RES_PER_CM
;
310 for (i
= attr
->num_values
, attrval
= attr
->values
;
314 if (attrval
->resolution
.xres
== xres_value
&&
315 attrval
->resolution
.yres
== yres_value
&&
316 attrval
->resolution
.units
== units_value
)
323 case IPP_TAG_KEYWORD
:
324 case IPP_TAG_CHARSET
:
326 case IPP_TAG_URISCHEME
:
327 case IPP_TAG_MIMETYPE
:
328 case IPP_TAG_LANGUAGE
:
329 case IPP_TAG_TEXTLANG
:
330 case IPP_TAG_NAMELANG
:
331 for (i
= 0; i
< attr
->num_values
; i
++)
332 if (!strcmp(attr
->values
[i
].string
.text
, value
))
342 * If we get there the option+value is not supported...
350 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
353 * "num_options" and "options" represent the currently selected options by the
354 * user. "new_option" and "new_value" are the setting the user has just
357 * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
358 * there was an unrecoverable error such as a resolver loop.
360 * If "num_conflicts" and "conflicts" are not @code NULL@, they are set to
361 * contain the list of conflicting option/value pairs. Similarly, if
362 * "num_resolved" and "resolved" are not @code NULL@ they will be set to the
363 * list of changes needed to resolve the conflict.
365 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
366 * to 0 and @code NULL@, respectively, then the conflict cannot be resolved.
368 * @since CUPS 1.6/macOS 10.8@
371 int /* O - 1 if there is a conflict, 0 if none, -1 on error */
372 cupsCopyDestConflicts(
373 http_t
*http
, /* I - Connection to destination */
374 cups_dest_t
*dest
, /* I - Destination */
375 cups_dinfo_t
*dinfo
, /* I - Destination information */
376 int num_options
, /* I - Number of current options */
377 cups_option_t
*options
, /* I - Current options */
378 const char *new_option
, /* I - New option */
379 const char *new_value
, /* I - New value */
380 int *num_conflicts
, /* O - Number of conflicting options */
381 cups_option_t
**conflicts
, /* O - Conflicting options */
382 int *num_resolved
, /* O - Number of options to resolve */
383 cups_option_t
**resolved
) /* O - Resolved options */
385 int i
, /* Looping var */
386 have_conflicts
= 0, /* Do we have conflicts? */
387 changed
, /* Did we change something? */
388 tries
, /* Number of tries for resolution */
389 num_myconf
= 0, /* My number of conflicting options */
390 num_myres
= 0; /* My number of resolved options */
391 cups_option_t
*myconf
= NULL
, /* My conflicting options */
392 *myres
= NULL
, /* My resolved options */
393 *myoption
, /* My current option */
394 *option
; /* Current option */
395 cups_array_t
*active
= NULL
, /* Active conflicts */
396 *pass
= NULL
, /* Resolvers for this pass */
397 *resolvers
= NULL
, /* Resolvers we have used */
398 *test
; /* Test array for conflicts */
399 _cups_dconstres_t
*c
, /* Current constraint */
400 *r
; /* Current resolver */
401 ipp_attribute_t
*attr
; /* Current attribute */
402 char value
[2048]; /* Current attribute value as string */
403 const char *myvalue
; /* Current value of an option */
407 * Clear returned values...
423 * Get the default connection as needed...
427 http
= _cupsConnect();
430 * Range check input...
433 if (!http
|| !dest
|| !dinfo
||
434 (num_conflicts
!= NULL
) != (conflicts
!= NULL
) ||
435 (num_resolved
!= NULL
) != (resolved
!= NULL
))
439 * Load constraints as needed...
442 if (!dinfo
->constraints
)
443 cups_create_constraints(dinfo
);
445 if (cupsArrayCount(dinfo
->constraints
) == 0)
448 if (!dinfo
->num_defaults
)
449 cups_create_defaults(dinfo
);
452 * If we are resolving, create a shadow array...
457 for (i
= num_options
, option
= options
; i
> 0; i
--, option
++)
458 num_myres
= cupsAddOption(option
->name
, option
->value
, num_myres
, &myres
);
460 if (new_option
&& new_value
)
461 num_myres
= cupsAddOption(new_option
, new_value
, num_myres
, &myres
);
465 num_myres
= num_options
;
470 * Check for any conflicts...
474 pass
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
476 for (tries
= 0; tries
< 100; tries
++)
479 * Check for any conflicts...
482 if (num_conflicts
|| num_resolved
)
484 cupsFreeOptions(num_myconf
, myconf
);
488 active
= cups_test_constraints(dinfo
, new_option
, new_value
,
489 num_myres
, myres
, &num_myconf
,
493 active
= cups_test_constraints(dinfo
, new_option
, new_value
, num_myres
,
496 have_conflicts
= (active
!= NULL
);
498 if (!active
|| !num_resolved
)
499 break; /* All done */
502 * Scan the constraints that were triggered to apply resolvers...
506 resolvers
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
508 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(active
), changed
= 0;
510 c
= (_cups_dconstres_t
*)cupsArrayNext(active
))
512 if (cupsArrayFind(pass
, c
))
513 continue; /* Already applied this resolver... */
515 if (cupsArrayFind(resolvers
, c
))
517 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
523 if ((r
= cupsArrayFind(dinfo
->resolvers
, c
)) == NULL
)
525 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
532 * Add the options from the resolver...
535 cupsArrayAdd(pass
, r
);
536 cupsArrayAdd(resolvers
, r
);
538 for (attr
= ippFirstAttribute(r
->collection
);
540 attr
= ippNextAttribute(r
->collection
))
542 if (new_option
&& !strcmp(attr
->name
, new_option
))
543 continue; /* Ignore this if we just changed it */
545 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
546 continue; /* Ignore if the value is too long */
548 if ((test
= cups_test_constraints(dinfo
, attr
->name
, value
, num_myres
,
549 myres
, NULL
, NULL
)) == NULL
)
552 * That worked, flag it...
558 cupsArrayDelete(test
);
561 * Add the option/value from the resolver regardless of whether it
562 * worked; this makes sure that we can cascade several changes to
563 * make things resolve...
566 num_myres
= cupsAddOption(attr
->name
, value
, num_myres
, &myres
);
572 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
577 cupsArrayClear(pass
);
579 cupsArrayDelete(active
);
585 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
591 * Copy resolved options as needed...
596 for (i
= num_myres
, myoption
= myres
; i
> 0; i
--, myoption
++)
598 if ((myvalue
= cupsGetOption(myoption
->name
, num_options
,
600 strcmp(myvalue
, myoption
->value
))
602 if (new_option
&& !strcmp(new_option
, myoption
->name
) &&
603 new_value
&& !strcmp(new_value
, myoption
->value
))
606 *num_resolved
= cupsAddOption(myoption
->name
, myoption
->value
,
607 *num_resolved
, resolved
);
618 cupsArrayDelete(active
);
619 cupsArrayDelete(pass
);
620 cupsArrayDelete(resolvers
);
625 * Free shadow copy of options...
628 cupsFreeOptions(num_myres
, myres
);
634 * Return conflicting options to caller...
637 *num_conflicts
= num_myconf
;
643 * Free conflicting options...
646 cupsFreeOptions(num_myconf
, myconf
);
649 return (have_conflicts
);
654 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
657 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
658 * value. @code NULL@ is returned on error.
660 * @since CUPS 1.6/macOS 10.8@
663 cups_dinfo_t
* /* O - Destination information */
665 http_t
*http
, /* I - Connection to destination */
666 cups_dest_t
*dest
) /* I - Destination */
668 cups_dinfo_t
*dinfo
; /* Destination information */
669 ipp_t
*request
, /* Get-Printer-Attributes request */
670 *response
; /* Supported attributes */
671 int tries
, /* Number of tries so far */
672 delay
, /* Current retry delay */
673 prev_delay
; /* Next retry delay */
674 const char *uri
; /* Printer URI */
675 char resource
[1024]; /* Resource path */
676 int version
; /* IPP version */
677 ipp_status_t status
; /* Status of request */
678 static const char * const requested_attrs
[] =
679 { /* Requested attributes */
681 "media-col-database",
682 "printer-description"
686 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", (void *)http
, (void *)dest
, dest
? dest
->name
: ""));
689 * Get the default connection as needed...
693 http
= _cupsConnect();
696 * Range check input...
703 * Get the printer URI and resource path...
706 if ((uri
= _cupsGetDestResource(dest
, resource
, sizeof(resource
))) == NULL
)
710 * Get the supported attributes...
721 * Send a Get-Printer-Attributes request...
724 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
726 ippSetVersion(request
, version
/ 10, version
% 10);
727 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
729 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
730 "requesting-user-name", NULL
, cupsUser());
731 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
732 "requested-attributes",
733 (int)(sizeof(requested_attrs
) / sizeof(requested_attrs
[0])),
734 NULL
, requested_attrs
);
735 response
= cupsDoRequest(http
, request
, resource
);
736 status
= cupsLastError();
738 if (status
> IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED
)
740 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
741 "returned %s (%s)", dest
->name
, ippErrorString(status
),
742 cupsLastErrorString()));
747 if (status
== IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
&& version
> 11)
749 else if (status
== IPP_STATUS_ERROR_BUSY
)
751 sleep((unsigned)delay
);
753 delay
= _cupsNextDelay(delay
, &prev_delay
);
761 while (!response
&& tries
< 10);
767 * Allocate a cups_dinfo_t structure and return it...
770 if ((dinfo
= calloc(1, sizeof(cups_dinfo_t
))) == NULL
)
772 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
777 dinfo
->version
= version
;
779 dinfo
->resource
= _cupsStrAlloc(resource
);
780 dinfo
->attrs
= response
;
787 * 'cupsFindDestDefault()' - Find the default value(s) for the given option.
789 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
790 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
791 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
792 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
793 * functions to inspect the default value(s) as needed.
795 * @since CUPS 1.7/macOS 10.9@
798 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
800 http_t
*http
, /* I - Connection to destination */
801 cups_dest_t
*dest
, /* I - Destination */
802 cups_dinfo_t
*dinfo
, /* I - Destination information */
803 const char *option
) /* I - Option/attribute name */
805 char name
[IPP_MAX_NAME
]; /* Attribute name */
809 * Get the default connection as needed...
813 http
= _cupsConnect();
816 * Range check input...
819 if (!http
|| !dest
|| !dinfo
|| !option
)
821 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
826 * Find and return the attribute...
829 snprintf(name
, sizeof(name
), "%s-default", option
);
830 return (ippFindAttribute(dinfo
->attrs
, name
, IPP_TAG_ZERO
));
835 * 'cupsFindDestReady()' - Find the default value(s) for the given option.
837 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
838 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
839 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
840 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
841 * functions to inspect the default value(s) as needed.
843 * @since CUPS 1.7/macOS 10.9@
846 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
848 http_t
*http
, /* I - Connection to destination */
849 cups_dest_t
*dest
, /* I - Destination */
850 cups_dinfo_t
*dinfo
, /* I - Destination information */
851 const char *option
) /* I - Option/attribute name */
853 char name
[IPP_MAX_NAME
]; /* Attribute name */
857 * Get the default connection as needed...
861 http
= _cupsConnect();
864 * Range check input...
867 if (!http
|| !dest
|| !dinfo
|| !option
)
869 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
874 * Find and return the attribute...
877 cups_update_ready(http
, dinfo
);
879 snprintf(name
, sizeof(name
), "%s-ready", option
);
880 return (ippFindAttribute(dinfo
->ready_attrs
, name
, IPP_TAG_ZERO
));
885 * 'cupsFindDestSupported()' - Find the default value(s) for the given option.
887 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
888 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
889 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
890 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
891 * functions to inspect the default value(s) as needed.
893 * @since CUPS 1.7/macOS 10.9@
896 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
897 cupsFindDestSupported(
898 http_t
*http
, /* I - Connection to destination */
899 cups_dest_t
*dest
, /* I - Destination */
900 cups_dinfo_t
*dinfo
, /* I - Destination information */
901 const char *option
) /* I - Option/attribute name */
903 char name
[IPP_MAX_NAME
]; /* Attribute name */
907 * Get the default connection as needed...
911 http
= _cupsConnect();
914 * Range check input...
917 if (!http
|| !dest
|| !dinfo
|| !option
)
919 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
924 * Find and return the attribute...
927 snprintf(name
, sizeof(name
), "%s-supported", option
);
928 return (ippFindAttribute(dinfo
->attrs
, name
, IPP_TAG_ZERO
));
933 * 'cupsFreeDestInfo()' - Free destination information obtained using
934 * @link cupsCopyDestInfo@.
936 * @since CUPS 1.6/macOS 10.8@
940 cupsFreeDestInfo(cups_dinfo_t
*dinfo
) /* I - Destination information */
943 * Range check input...
950 * Free memory and return...
953 _cupsStrFree(dinfo
->resource
);
955 cupsArrayDelete(dinfo
->constraints
);
956 cupsArrayDelete(dinfo
->resolvers
);
958 cupsArrayDelete(dinfo
->localizations
);
960 cupsArrayDelete(dinfo
->media_db
);
962 cupsArrayDelete(dinfo
->cached_db
);
964 ippDelete(dinfo
->ready_attrs
);
965 cupsArrayDelete(dinfo
->ready_db
);
967 ippDelete(dinfo
->attrs
);
974 * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a
977 * The @code flags@ parameter determines which set of media are indexed. For
978 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth
979 * borderless size supported by the printer.
981 * @since CUPS 1.7/macOS 10.9@
984 int /* O - 1 on success, 0 on failure */
985 cupsGetDestMediaByIndex(
986 http_t
*http
, /* I - Connection to destination */
987 cups_dest_t
*dest
, /* I - Destination */
988 cups_dinfo_t
*dinfo
, /* I - Destination information */
989 int n
, /* I - Media size number (0-based) */
990 unsigned flags
, /* I - Media flags */
991 cups_size_t
*size
) /* O - Media size information */
993 _cups_media_db_t
*nsize
; /* Size for N */
994 pwg_media_t
*pwg
; /* PWG media name for size */
998 * Get the default connection as needed...
1002 http
= _cupsConnect();
1005 * Range check input...
1009 memset(size
, 0, sizeof(cups_size_t
));
1011 if (!http
|| !dest
|| !dinfo
|| n
< 0 || !size
)
1013 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1018 * Load media list as needed...
1021 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1022 cups_update_ready(http
, dinfo
);
1024 if (!dinfo
->cached_db
|| dinfo
->cached_flags
!= flags
)
1025 cups_create_cached(http
, dinfo
, flags
);
1028 * Copy the size over and return...
1031 if ((nsize
= (_cups_media_db_t
*)cupsArrayIndex(dinfo
->cached_db
, n
)) == NULL
)
1033 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1038 strlcpy(size
->media
, nsize
->key
, sizeof(size
->media
));
1039 else if (nsize
->size_name
)
1040 strlcpy(size
->media
, nsize
->size_name
, sizeof(size
->media
));
1041 else if ((pwg
= pwgMediaForSize(nsize
->width
, nsize
->length
)) != NULL
)
1042 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
1045 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1049 size
->width
= nsize
->width
;
1050 size
->length
= nsize
->length
;
1051 size
->bottom
= nsize
->bottom
;
1052 size
->left
= nsize
->left
;
1053 size
->right
= nsize
->right
;
1054 size
->top
= nsize
->top
;
1061 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
1063 * The "media" string is a PWG media name. "Flags" provides some matching
1064 * guidance (multiple flags can be combined):
1066 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
1067 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
1068 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
1069 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
1070 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
1071 * size amongst the "ready" media.
1073 * The matching result (if any) is returned in the "cups_size_t" structure.
1075 * Returns 1 when there is a match and 0 if there is not a match.
1077 * @since CUPS 1.6/macOS 10.8@
1080 int /* O - 1 on match, 0 on failure */
1081 cupsGetDestMediaByName(
1082 http_t
*http
, /* I - Connection to destination */
1083 cups_dest_t
*dest
, /* I - Destination */
1084 cups_dinfo_t
*dinfo
, /* I - Destination information */
1085 const char *media
, /* I - Media name */
1086 unsigned flags
, /* I - Media matching flags */
1087 cups_size_t
*size
) /* O - Media size information */
1089 pwg_media_t
*pwg
; /* PWG media info */
1093 * Get the default connection as needed...
1097 http
= _cupsConnect();
1100 * Range check input...
1104 memset(size
, 0, sizeof(cups_size_t
));
1106 if (!http
|| !dest
|| !dinfo
|| !media
|| !size
)
1108 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1113 * Lookup the media size name...
1116 if ((pwg
= pwgMediaForPWG(media
)) == NULL
)
1117 if ((pwg
= pwgMediaForLegacy(media
)) == NULL
)
1119 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media
));
1120 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unknown media size name."), 1);
1125 * Lookup the size...
1128 return (cups_get_media_db(http
, dinfo
, pwg
, flags
, size
));
1133 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
1135 * "Width" and "length" are the dimensions in hundredths of millimeters.
1136 * "Flags" provides some matching guidance (multiple flags can be combined):
1138 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
1139 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
1140 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
1141 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
1142 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
1143 * size amongst the "ready" media.
1145 * The matching result (if any) is returned in the "cups_size_t" structure.
1147 * Returns 1 when there is a match and 0 if there is not a match.
1149 * @since CUPS 1.6/macOS 10.8@
1152 int /* O - 1 on match, 0 on failure */
1153 cupsGetDestMediaBySize(
1154 http_t
*http
, /* I - Connection to destination */
1155 cups_dest_t
*dest
, /* I - Destination */
1156 cups_dinfo_t
*dinfo
, /* I - Destination information */
1157 int width
, /* I - Media width in hundredths of
1159 int length
, /* I - Media length in hundredths of
1161 unsigned flags
, /* I - Media matching flags */
1162 cups_size_t
*size
) /* O - Media size information */
1164 pwg_media_t
*pwg
; /* PWG media info */
1168 * Get the default connection as needed...
1172 http
= _cupsConnect();
1175 * Range check input...
1179 memset(size
, 0, sizeof(cups_size_t
));
1181 if (!http
|| !dest
|| !dinfo
|| width
<= 0 || length
<= 0 || !size
)
1183 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1188 * Lookup the media size name...
1191 if ((pwg
= pwgMediaForSize(width
, length
)) == NULL
)
1193 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width
,
1195 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Invalid media size."), 1);
1200 * Lookup the size...
1203 return (cups_get_media_db(http
, dinfo
, pwg
, flags
, size
));
1208 * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a
1211 * The @code flags@ parameter determines the set of media sizes that are
1212 * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return
1213 * the number of borderless sizes.
1215 * @since CUPS 1.7/macOS 10.9@
1218 int /* O - Number of sizes */
1219 cupsGetDestMediaCount(
1220 http_t
*http
, /* I - Connection to destination */
1221 cups_dest_t
*dest
, /* I - Destination */
1222 cups_dinfo_t
*dinfo
, /* I - Destination information */
1223 unsigned flags
) /* I - Media flags */
1226 * Get the default connection as needed...
1230 http
= _cupsConnect();
1233 * Range check input...
1236 if (!http
|| !dest
|| !dinfo
)
1238 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1243 * Load media list as needed...
1246 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1247 cups_update_ready(http
, dinfo
);
1249 if (!dinfo
->cached_db
|| dinfo
->cached_flags
!= flags
)
1250 cups_create_cached(http
, dinfo
, flags
);
1252 return (cupsArrayCount(dinfo
->cached_db
));
1257 * 'cupsGetDestMediaDefault()' - Get the default size for a destination.
1259 * The @code flags@ parameter determines which default size is returned. For
1260 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default
1261 * borderless size, typically US Letter or A4, but sometimes 4x6 photo media.
1263 * @since CUPS 1.7/macOS 10.9@
1266 int /* O - 1 on success, 0 on failure */
1267 cupsGetDestMediaDefault(
1268 http_t
*http
, /* I - Connection to destination */
1269 cups_dest_t
*dest
, /* I - Destination */
1270 cups_dinfo_t
*dinfo
, /* I - Destination information */
1271 unsigned flags
, /* I - Media flags */
1272 cups_size_t
*size
) /* O - Media size information */
1274 const char *media
; /* Default media size */
1278 * Get the default connection as needed...
1282 http
= _cupsConnect();
1285 * Range check input...
1289 memset(size
, 0, sizeof(cups_size_t
));
1291 if (!http
|| !dest
|| !dinfo
|| !size
)
1293 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1298 * Get the default media size, if any...
1301 if ((media
= cupsGetOption("media", dest
->num_options
, dest
->options
)) == NULL
)
1302 media
= "na_letter_8.5x11in";
1304 if (cupsGetDestMediaByName(http
, dest
, dinfo
, media
, flags
, size
))
1307 if (strcmp(media
, "na_letter_8.5x11in") && cupsGetDestMediaByName(http
, dest
, dinfo
, "iso_a4_210x297mm", flags
, size
))
1310 if (strcmp(media
, "iso_a4_210x297mm") && cupsGetDestMediaByName(http
, dest
, dinfo
, "na_letter_8.5x11in", flags
, size
))
1313 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) && cupsGetDestMediaByName(http
, dest
, dinfo
, "na_index_4x6in", flags
, size
))
1317 * Fall back to the first matching media size...
1320 return (cupsGetDestMediaByIndex(http
, dest
, dinfo
, 0, flags
, size
));
1325 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
1330 cups_array_t
*a
, /* I - Array */
1331 ipp_t
*collection
) /* I - Collection value */
1333 ipp_attribute_t
*attr
; /* Attribute */
1334 _cups_dconstres_t
*temp
; /* Current constraint/resolver */
1337 if ((attr
= ippFindAttribute(collection
, "resolver-name",
1338 IPP_TAG_NAME
)) == NULL
)
1341 if ((temp
= calloc(1, sizeof(_cups_dconstres_t
))) == NULL
)
1344 temp
->name
= attr
->values
[0].string
.text
;
1345 temp
->collection
= collection
;
1347 cupsArrayAdd(a
, temp
);
1352 * 'cups_collection_contains()' - Check whether test collection is contained in the matching collection.
1355 static int /* O - 1 on a match, 0 on a non-match */
1356 cups_collection_contains(ipp_t
*test
, /* I - Collection to test */
1357 ipp_t
*match
) /* I - Matching values */
1359 int i
, j
, /* Looping vars */
1360 mcount
, /* Number of match values */
1361 tcount
; /* Number of test values */
1362 ipp_attribute_t
*tattr
, /* Testing attribute */
1363 *mattr
; /* Matching attribute */
1364 const char *tval
; /* Testing string value */
1367 for (mattr
= ippFirstAttribute(match
); mattr
; mattr
= ippNextAttribute(match
))
1369 if ((tattr
= ippFindAttribute(test
, ippGetName(mattr
), IPP_TAG_ZERO
)) == NULL
)
1372 tcount
= ippGetCount(tattr
);
1374 switch (ippGetValueTag(mattr
))
1376 case IPP_TAG_INTEGER
:
1378 if (ippGetValueTag(tattr
) != ippGetValueTag(mattr
))
1381 for (i
= 0; i
< tcount
; i
++)
1383 if (!ippContainsInteger(mattr
, ippGetInteger(tattr
, i
)))
1388 case IPP_TAG_RANGE
:
1389 if (ippGetValueTag(tattr
) != IPP_TAG_INTEGER
)
1392 for (i
= 0; i
< tcount
; i
++)
1394 if (!ippContainsInteger(mattr
, ippGetInteger(tattr
, i
)))
1399 case IPP_TAG_BOOLEAN
:
1400 if (ippGetValueTag(tattr
) != IPP_TAG_BOOLEAN
|| ippGetBoolean(tattr
, 0) != ippGetBoolean(mattr
, 0))
1404 case IPP_TAG_TEXTLANG
:
1405 case IPP_TAG_NAMELANG
:
1408 case IPP_TAG_KEYWORD
:
1410 case IPP_TAG_URISCHEME
:
1411 case IPP_TAG_CHARSET
:
1412 case IPP_TAG_LANGUAGE
:
1413 case IPP_TAG_MIMETYPE
:
1414 for (i
= 0; i
< tcount
; i
++)
1416 if ((tval
= ippGetString(tattr
, i
, NULL
)) == NULL
|| !ippContainsString(mattr
, tval
))
1421 case IPP_TAG_BEGIN_COLLECTION
:
1422 for (i
= 0; i
< tcount
; i
++)
1424 ipp_t
*tcol
= ippGetCollection(tattr
, i
);
1425 /* Testing collection */
1427 for (j
= 0, mcount
= ippGetCount(mattr
); j
< mcount
; j
++)
1428 if (!cups_collection_contains(tcol
, ippGetCollection(mattr
, j
)))
1443 * 'cups_collection_string()' - Convert an IPP collection to an option string.
1446 static size_t /* O - Number of bytes needed */
1447 cups_collection_string(
1448 ipp_attribute_t
*attr
, /* I - Collection attribute */
1449 char *buffer
, /* I - String buffer */
1450 size_t bufsize
) /* I - Size of buffer */
1452 int i
, j
, /* Looping vars */
1453 count
, /* Number of collection values */
1454 mcount
; /* Number of member values */
1455 ipp_t
*col
; /* Collection */
1456 ipp_attribute_t
*first
, /* First member attribute */
1457 *member
; /* Member attribute */
1458 char *bufptr
, /* Pointer into buffer */
1459 *bufend
, /* End of buffer */
1460 temp
[100]; /* Temporary string */
1461 const char *mptr
; /* Pointer into member value */
1462 int mlen
; /* Length of octetString */
1466 bufend
= buffer
+ bufsize
- 1;
1468 for (i
= 0, count
= ippGetCount(attr
); i
< count
; i
++)
1470 col
= ippGetCollection(attr
, i
);
1474 if (bufptr
< bufend
)
1480 if (bufptr
< bufend
)
1485 for (member
= first
= ippFirstAttribute(col
); member
; member
= ippNextAttribute(col
))
1487 const char *mname
= ippGetName(member
);
1489 if (member
!= first
)
1491 if (bufptr
< bufend
)
1497 if (ippGetValueTag(member
) == IPP_TAG_BOOLEAN
)
1499 if (!ippGetBoolean(member
, 0))
1501 if (bufptr
< bufend
)
1502 strlcpy(bufptr
, "no", bufend
- bufptr
+ 1);
1506 if (bufptr
< bufend
)
1507 strlcpy(bufptr
, mname
, bufend
- bufptr
+ 1);
1508 bufptr
+= strlen(mname
);
1512 if (bufptr
< bufend
)
1513 strlcpy(bufptr
, mname
, bufend
- bufptr
+ 1);
1514 bufptr
+= strlen(mname
);
1516 if (bufptr
< bufend
)
1521 if (ippGetValueTag(member
) == IPP_TAG_BEGIN_COLLECTION
)
1524 * Convert sub-collection...
1527 bufptr
+= cups_collection_string(member
, bufptr
, bufptr
< bufend
? (size_t)(bufend
- bufptr
+ 1) : 0);
1532 * Convert simple type...
1535 for (j
= 0, mcount
= ippGetCount(member
); j
< mcount
; j
++)
1539 if (bufptr
< bufend
)
1545 switch (ippGetValueTag(member
))
1547 case IPP_TAG_INTEGER
:
1549 bufptr
+= snprintf(bufptr
, bufptr
< bufend
? (bufend
- bufptr
+ 1) : 0, "%d", ippGetInteger(member
, j
));
1552 case IPP_TAG_STRING
:
1553 if (bufptr
< bufend
)
1558 for (mptr
= (const char *)ippGetOctetString(member
, j
, &mlen
); mlen
> 0; mlen
--, mptr
++)
1560 if (*mptr
== '\"' || *mptr
== '\\')
1562 if (bufptr
< bufend
)
1568 if (bufptr
< bufend
)
1574 if (bufptr
< bufend
)
1582 unsigned year
; /* Year */
1583 const ipp_uchar_t
*date
= ippGetDate(member
, j
);
1586 year
= ((unsigned)date
[0] << 8) + (unsigned)date
[1];
1588 if (date
[9] == 0 && date
[10] == 0)
1589 snprintf(temp
, sizeof(temp
), "%04u-%02u-%02uT%02u:%02u:%02uZ", year
, date
[2], date
[3], date
[4], date
[5], date
[6]);
1591 snprintf(temp
, sizeof(temp
), "%04u-%02u-%02uT%02u:%02u:%02u%c%02u%02u", year
, date
[2], date
[3], date
[4], date
[5], date
[6], date
[8], date
[9], date
[10]);
1593 if (buffer
&& bufptr
< bufend
)
1594 strlcpy(bufptr
, temp
, (size_t)(bufend
- bufptr
+ 1));
1596 bufptr
+= strlen(temp
);
1600 case IPP_TAG_RESOLUTION
:
1602 int xres
, /* Horizontal resolution */
1603 yres
; /* Vertical resolution */
1604 ipp_res_t units
; /* Resolution units */
1606 xres
= ippGetResolution(member
, j
, &yres
, &units
);
1609 snprintf(temp
, sizeof(temp
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1611 snprintf(temp
, sizeof(temp
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1613 if (buffer
&& bufptr
< bufend
)
1614 strlcpy(bufptr
, temp
, (size_t)(bufend
- bufptr
+ 1));
1616 bufptr
+= strlen(temp
);
1620 case IPP_TAG_RANGE
:
1622 int lower
, /* Lower bound */
1623 upper
; /* Upper bound */
1625 lower
= ippGetRange(member
, j
, &upper
);
1627 snprintf(temp
, sizeof(temp
), "%d-%d", lower
, upper
);
1629 if (buffer
&& bufptr
< bufend
)
1630 strlcpy(bufptr
, temp
, (size_t)(bufend
- bufptr
+ 1));
1632 bufptr
+= strlen(temp
);
1636 case IPP_TAG_TEXTLANG
:
1637 case IPP_TAG_NAMELANG
:
1640 case IPP_TAG_KEYWORD
:
1642 case IPP_TAG_URISCHEME
:
1643 case IPP_TAG_CHARSET
:
1644 case IPP_TAG_LANGUAGE
:
1645 case IPP_TAG_MIMETYPE
:
1646 if (bufptr
< bufend
)
1651 for (mptr
= ippGetString(member
, j
, NULL
); *mptr
; mptr
++)
1653 if (*mptr
== '\"' || *mptr
== '\\')
1655 if (bufptr
< bufend
)
1661 if (bufptr
< bufend
)
1667 if (bufptr
< bufend
)
1680 if (bufptr
< bufend
)
1687 return ((size_t)(bufptr
- buffer
+ 1));
1692 * 'cups_compare_dconstres()' - Compare to resolver entries.
1695 static int /* O - Result of comparison */
1696 cups_compare_dconstres(
1697 _cups_dconstres_t
*a
, /* I - First resolver */
1698 _cups_dconstres_t
*b
) /* I - Second resolver */
1700 return (strcmp(a
->name
, b
->name
));
1705 * 'cups_compare_media_db()' - Compare two media entries.
1708 static int /* O - Result of comparison */
1709 cups_compare_media_db(
1710 _cups_media_db_t
*a
, /* I - First media entries */
1711 _cups_media_db_t
*b
) /* I - Second media entries */
1713 int result
; /* Result of comparison */
1716 if ((result
= a
->width
- b
->width
) == 0)
1717 result
= a
->length
- b
->length
;
1724 * 'cups_copy_media_db()' - Copy a media entry.
1727 static _cups_media_db_t
* /* O - New media entry */
1729 _cups_media_db_t
*mdb
) /* I - Media entry to copy */
1731 _cups_media_db_t
*temp
; /* New media entry */
1734 if ((temp
= calloc(1, sizeof(_cups_media_db_t
))) == NULL
)
1738 temp
->color
= _cupsStrAlloc(mdb
->color
);
1740 temp
->key
= _cupsStrAlloc(mdb
->key
);
1742 temp
->info
= _cupsStrAlloc(mdb
->info
);
1744 temp
->size_name
= _cupsStrAlloc(mdb
->size_name
);
1746 temp
->source
= _cupsStrAlloc(mdb
->source
);
1748 temp
->type
= _cupsStrAlloc(mdb
->type
);
1750 temp
->width
= mdb
->width
;
1751 temp
->length
= mdb
->length
;
1752 temp
->bottom
= mdb
->bottom
;
1753 temp
->left
= mdb
->left
;
1754 temp
->right
= mdb
->right
;
1755 temp
->top
= mdb
->top
;
1762 * 'cups_create_cached()' - Create the media selection cache.
1766 cups_create_cached(http_t
*http
, /* I - Connection to destination */
1767 cups_dinfo_t
*dinfo
, /* I - Destination information */
1768 unsigned flags
) /* I - Media selection flags */
1770 cups_array_t
*db
; /* Media database array to use */
1771 _cups_media_db_t
*mdb
, /* Media database entry */
1772 *first
; /* First entry this size */
1775 DEBUG_printf(("3cups_create_cached(http=%p, dinfo=%p, flags=%u)", (void *)http
, (void *)dinfo
, flags
));
1777 if (dinfo
->cached_db
)
1778 cupsArrayDelete(dinfo
->cached_db
);
1780 dinfo
->cached_db
= cupsArrayNew(NULL
, NULL
);
1781 dinfo
->cached_flags
= flags
;
1783 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1785 DEBUG_puts("4cups_create_cached: ready media");
1787 cups_update_ready(http
, dinfo
);
1788 db
= dinfo
->ready_db
;
1792 DEBUG_puts("4cups_create_cached: supported media");
1794 if (!dinfo
->media_db
)
1795 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
);
1797 db
= dinfo
->media_db
;
1800 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
), first
= mdb
;
1802 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1804 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
));
1806 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1808 if (!mdb
->left
&& !mdb
->right
&& !mdb
->top
&& !mdb
->bottom
)
1810 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb
));
1811 cupsArrayAdd(dinfo
->cached_db
, mdb
);
1814 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1816 if (first
->width
!= mdb
->width
|| first
->length
!= mdb
->length
)
1818 DEBUG_printf(("4cups_create_cached: add %p", (void *)first
));
1819 cupsArrayAdd(dinfo
->cached_db
, first
);
1822 else if (mdb
->left
>= first
->left
&& mdb
->right
>= first
->right
&& mdb
->top
>= first
->top
&& mdb
->bottom
>= first
->bottom
&&
1823 (mdb
->left
!= first
->left
|| mdb
->right
!= first
->right
|| mdb
->top
!= first
->top
|| mdb
->bottom
!= first
->bottom
))
1828 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb
));
1829 cupsArrayAdd(dinfo
->cached_db
, mdb
);
1833 if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1835 DEBUG_printf(("4cups_create_cached: add %p", (void *)first
));
1836 cupsArrayAdd(dinfo
->cached_db
, first
);
1842 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
1846 cups_create_constraints(
1847 cups_dinfo_t
*dinfo
) /* I - Destination information */
1849 int i
; /* Looping var */
1850 ipp_attribute_t
*attr
; /* Attribute */
1851 _ipp_value_t
*val
; /* Current value */
1854 dinfo
->constraints
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, NULL
,
1855 (cups_afree_func_t
)free
);
1856 dinfo
->resolvers
= cupsArrayNew3((cups_array_func_t
)cups_compare_dconstres
,
1857 NULL
, NULL
, 0, NULL
,
1858 (cups_afree_func_t
)free
);
1860 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-constraints-supported",
1861 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1863 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
1864 cups_add_dconstres(dinfo
->constraints
, val
->collection
);
1867 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-resolvers-supported",
1868 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1870 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
1871 cups_add_dconstres(dinfo
->resolvers
, val
->collection
);
1877 * 'cups_create_defaults()' - Create the -default option array.
1881 cups_create_defaults(
1882 cups_dinfo_t
*dinfo
) /* I - Destination information */
1884 ipp_attribute_t
*attr
; /* Current attribute */
1885 char name
[IPP_MAX_NAME
+ 1],
1887 *nameptr
, /* Pointer into current name */
1888 value
[2048]; /* Current value */
1892 * Iterate through the printer attributes looking for xxx-default and adding
1893 * xxx=value to the defaults option array.
1896 for (attr
= ippFirstAttribute(dinfo
->attrs
); attr
; attr
= ippNextAttribute(dinfo
->attrs
))
1898 if (!ippGetName(attr
) || ippGetGroupTag(attr
) != IPP_TAG_PRINTER
)
1901 strlcpy(name
, ippGetName(attr
), sizeof(name
));
1902 if ((nameptr
= name
+ strlen(name
) - 8) <= name
|| strcmp(nameptr
, "-default"))
1907 if (ippGetValueTag(attr
) == IPP_TAG_BEGIN_COLLECTION
)
1909 if (cups_collection_string(attr
, value
, sizeof(value
)) >= sizeof(value
))
1912 else if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
1915 dinfo
->num_defaults
= cupsAddOption(name
, value
, dinfo
->num_defaults
, &dinfo
->defaults
);
1921 * 'cups_create_media_db()' - Create the media database.
1925 cups_create_media_db(
1926 cups_dinfo_t
*dinfo
, /* I - Destination information */
1927 unsigned flags
) /* I - Media flags */
1929 int i
; /* Looping var */
1930 _ipp_value_t
*val
; /* Current value */
1931 ipp_attribute_t
*media_col_db
, /* media-col-database */
1932 *media_attr
, /* media-xxx */
1933 *x_dimension
, /* x-dimension */
1934 *y_dimension
; /* y-dimension */
1935 pwg_media_t
*pwg
; /* PWG media info */
1936 cups_array_t
*db
; /* New media database array */
1937 _cups_media_db_t mdb
; /* Media entry */
1938 char media_key
[256]; /* Synthesized media-key value */
1941 db
= cupsArrayNew3((cups_array_func_t
)cups_compare_media_db
,
1943 (cups_acopy_func_t
)cups_copy_media_db
,
1944 (cups_afree_func_t
)cups_free_media_db
);
1946 if (flags
== CUPS_MEDIA_FLAGS_READY
)
1948 dinfo
->ready_db
= db
;
1950 media_col_db
= ippFindAttribute(dinfo
->ready_attrs
, "media-col-ready",
1951 IPP_TAG_BEGIN_COLLECTION
);
1952 media_attr
= ippFindAttribute(dinfo
->ready_attrs
, "media-ready",
1957 dinfo
->media_db
= db
;
1958 dinfo
->min_size
.width
= INT_MAX
;
1959 dinfo
->min_size
.length
= INT_MAX
;
1960 dinfo
->max_size
.width
= 0;
1961 dinfo
->max_size
.length
= 0;
1963 media_col_db
= ippFindAttribute(dinfo
->attrs
, "media-col-database",
1964 IPP_TAG_BEGIN_COLLECTION
);
1965 media_attr
= ippFindAttribute(dinfo
->attrs
, "media-supported",
1971 _ipp_value_t
*custom
= NULL
; /* Custom size range value */
1973 for (i
= media_col_db
->num_values
, val
= media_col_db
->values
;
1977 memset(&mdb
, 0, sizeof(mdb
));
1979 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size",
1980 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1982 ipp_t
*media_size
= media_attr
->values
[0].collection
;
1983 /* media-size collection value */
1985 if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1986 IPP_TAG_INTEGER
)) != NULL
&&
1987 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1988 IPP_TAG_INTEGER
)) != NULL
)
1994 mdb
.width
= x_dimension
->values
[0].integer
;
1995 mdb
.length
= y_dimension
->values
[0].integer
;
1997 else if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1998 IPP_TAG_INTEGER
)) != NULL
&&
1999 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
2000 IPP_TAG_RANGE
)) != NULL
)
2006 mdb
.width
= x_dimension
->values
[0].integer
;
2007 mdb
.length
= y_dimension
->values
[0].range
.upper
;
2009 else if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
2010 (x_dimension
= ippFindAttribute(media_size
, "x-dimension",
2011 IPP_TAG_RANGE
)) != NULL
&&
2012 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
2013 IPP_TAG_RANGE
)) != NULL
)
2016 * Custom size range; save this as the custom size value with default
2017 * margins, then continue; we'll capture the real margins below...
2022 dinfo
->min_size
.width
= x_dimension
->values
[0].range
.lower
;
2023 dinfo
->min_size
.length
= y_dimension
->values
[0].range
.lower
;
2024 dinfo
->min_size
.left
=
2025 dinfo
->min_size
.right
= 635; /* Default 1/4" side margins */
2026 dinfo
->min_size
.top
=
2027 dinfo
->min_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
2029 dinfo
->max_size
.width
= x_dimension
->values
[0].range
.upper
;
2030 dinfo
->max_size
.length
= y_dimension
->values
[0].range
.upper
;
2031 dinfo
->max_size
.left
=
2032 dinfo
->max_size
.right
= 635; /* Default 1/4" side margins */
2033 dinfo
->max_size
.top
=
2034 dinfo
->max_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
2039 if ((media_attr
= ippFindAttribute(val
->collection
, "media-color", IPP_TAG_ZERO
)) != NULL
&& (media_attr
->value_tag
== IPP_TAG_NAME
|| media_attr
->value_tag
== IPP_TAG_NAMELANG
|| media_attr
->value_tag
== IPP_TAG_KEYWORD
))
2040 mdb
.color
= media_attr
->values
[0].string
.text
;
2042 if ((media_attr
= ippFindAttribute(val
->collection
, "media-info", IPP_TAG_TEXT
)) != NULL
)
2043 mdb
.info
= media_attr
->values
[0].string
.text
;
2045 if ((media_attr
= ippFindAttribute(val
->collection
, "media-key", IPP_TAG_ZERO
)) != NULL
&& (media_attr
->value_tag
== IPP_TAG_NAME
|| media_attr
->value_tag
== IPP_TAG_NAMELANG
|| media_attr
->value_tag
== IPP_TAG_KEYWORD
))
2046 mdb
.key
= media_attr
->values
[0].string
.text
;
2048 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size-name", IPP_TAG_ZERO
)) != NULL
&& (media_attr
->value_tag
== IPP_TAG_NAME
|| media_attr
->value_tag
== IPP_TAG_NAMELANG
|| media_attr
->value_tag
== IPP_TAG_KEYWORD
))
2049 mdb
.size_name
= media_attr
->values
[0].string
.text
;
2051 if ((media_attr
= ippFindAttribute(val
->collection
, "media-source", IPP_TAG_ZERO
)) != NULL
&& (media_attr
->value_tag
== IPP_TAG_NAME
|| media_attr
->value_tag
== IPP_TAG_NAMELANG
|| media_attr
->value_tag
== IPP_TAG_KEYWORD
))
2052 mdb
.source
= media_attr
->values
[0].string
.text
;
2054 if ((media_attr
= ippFindAttribute(val
->collection
, "media-type", IPP_TAG_ZERO
)) != NULL
&& (media_attr
->value_tag
== IPP_TAG_NAME
|| media_attr
->value_tag
== IPP_TAG_NAMELANG
|| media_attr
->value_tag
== IPP_TAG_KEYWORD
))
2055 mdb
.type
= media_attr
->values
[0].string
.text
;
2057 if ((media_attr
= ippFindAttribute(val
->collection
, "media-bottom-margin", IPP_TAG_INTEGER
)) != NULL
)
2058 mdb
.bottom
= media_attr
->values
[0].integer
;
2060 if ((media_attr
= ippFindAttribute(val
->collection
, "media-left-margin", IPP_TAG_INTEGER
)) != NULL
)
2061 mdb
.left
= media_attr
->values
[0].integer
;
2063 if ((media_attr
= ippFindAttribute(val
->collection
, "media-right-margin", IPP_TAG_INTEGER
)) != NULL
)
2064 mdb
.right
= media_attr
->values
[0].integer
;
2066 if ((media_attr
= ippFindAttribute(val
->collection
, "media-top-margin", IPP_TAG_INTEGER
)) != NULL
)
2067 mdb
.top
= media_attr
->values
[0].integer
;
2071 if (!mdb
.size_name
&& (pwg
= pwgMediaForSize(mdb
.width
, mdb
.length
)) != NULL
)
2072 mdb
.size_name
= (char *)pwg
->pwg
;
2077 * Use a CUPS-specific identifier if we don't have a size name...
2080 if (flags
& CUPS_MEDIA_FLAGS_READY
)
2081 snprintf(media_key
, sizeof(media_key
), "cups-media-ready-%d", i
+ 1);
2083 snprintf(media_key
, sizeof(media_key
), "cups-media-%d", i
+ 1);
2085 else if (mdb
.source
)
2088 * Generate key using size name, source, and type (if set)...
2092 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s", mdb
.size_name
, mdb
.source
, mdb
.type
);
2094 snprintf(media_key
, sizeof(media_key
), "%s_%s", mdb
.size_name
, mdb
.source
);
2099 * Generate key using size name and type...
2102 snprintf(media_key
, sizeof(media_key
), "%s_%s", mdb
.size_name
, mdb
.type
);
2107 * Key is just the size name...
2110 strlcpy(media_key
, mdb
.size_name
, sizeof(media_key
));
2114 * Append "_borderless" for borderless media...
2117 if (!mdb
.bottom
&& !mdb
.left
&& !mdb
.right
&& !mdb
.top
)
2118 strlcat(media_key
, "_borderless", sizeof(media_key
));
2120 mdb
.key
= media_key
;
2123 DEBUG_printf(("1cups_create_media_db: Adding media: key=\"%s\", width=%d, length=%d, source=\"%s\", type=\"%s\".", mdb
.key
, mdb
.width
, mdb
.length
, mdb
.source
, mdb
.type
));
2125 cupsArrayAdd(db
, &mdb
);
2130 if ((media_attr
= ippFindAttribute(custom
->collection
,
2131 "media-bottom-margin",
2132 IPP_TAG_INTEGER
)) != NULL
)
2134 dinfo
->min_size
.top
=
2135 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
2138 if ((media_attr
= ippFindAttribute(custom
->collection
,
2139 "media-left-margin",
2140 IPP_TAG_INTEGER
)) != NULL
)
2142 dinfo
->min_size
.left
=
2143 dinfo
->max_size
.left
= media_attr
->values
[0].integer
;
2146 if ((media_attr
= ippFindAttribute(custom
->collection
,
2147 "media-right-margin",
2148 IPP_TAG_INTEGER
)) != NULL
)
2150 dinfo
->min_size
.right
=
2151 dinfo
->max_size
.right
= media_attr
->values
[0].integer
;
2154 if ((media_attr
= ippFindAttribute(custom
->collection
,
2156 IPP_TAG_INTEGER
)) != NULL
)
2158 dinfo
->min_size
.top
=
2159 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
2163 else if (media_attr
&&
2164 (media_attr
->value_tag
== IPP_TAG_NAME
||
2165 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
2166 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
2168 memset(&mdb
, 0, sizeof(mdb
));
2171 mdb
.right
= 635; /* Default 1/4" side margins */
2173 mdb
.bottom
= 1270; /* Default 1/2" top/bottom margins */
2175 for (i
= media_attr
->num_values
, val
= media_attr
->values
;
2179 if ((pwg
= pwgMediaForPWG(val
->string
.text
)) == NULL
)
2180 if ((pwg
= pwgMediaForLegacy(val
->string
.text
)) == NULL
)
2182 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
2187 mdb
.width
= pwg
->width
;
2188 mdb
.length
= pwg
->length
;
2190 if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
2191 !strncmp(val
->string
.text
, "custom_min_", 11))
2193 mdb
.size_name
= NULL
;
2194 dinfo
->min_size
= mdb
;
2196 else if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
2197 !strncmp(val
->string
.text
, "custom_max_", 11))
2199 mdb
.size_name
= NULL
;
2200 dinfo
->max_size
= mdb
;
2204 mdb
.size_name
= val
->string
.text
;
2206 cupsArrayAdd(db
, &mdb
);
2214 * 'cups_free_media_cb()' - Free a media entry.
2219 _cups_media_db_t
*mdb
) /* I - Media entry to free */
2222 _cupsStrFree(mdb
->color
);
2224 _cupsStrFree(mdb
->key
);
2226 _cupsStrFree(mdb
->info
);
2228 _cupsStrFree(mdb
->size_name
);
2230 _cupsStrFree(mdb
->source
);
2232 _cupsStrFree(mdb
->type
);
2239 * 'cups_get_media_db()' - Lookup the media entry for a given size.
2242 static int /* O - 1 on match, 0 on failure */
2243 cups_get_media_db(http_t
*http
, /* I - Connection to destination */
2244 cups_dinfo_t
*dinfo
, /* I - Destination information */
2245 pwg_media_t
*pwg
, /* I - PWG media info */
2246 unsigned flags
, /* I - Media matching flags */
2247 cups_size_t
*size
) /* O - Media size/margin/name info */
2249 cups_array_t
*db
; /* Which media database to query */
2250 _cups_media_db_t
*mdb
, /* Current media database entry */
2251 *best
= NULL
, /* Best matching entry */
2252 key
; /* Search key */
2256 * Create the media database as needed...
2259 if (flags
& CUPS_MEDIA_FLAGS_READY
)
2261 cups_update_ready(http
, dinfo
);
2262 db
= dinfo
->ready_db
;
2266 if (!dinfo
->media_db
)
2267 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
);
2269 db
= dinfo
->media_db
;
2276 memset(&key
, 0, sizeof(key
));
2277 key
.width
= pwg
->width
;
2278 key
.length
= pwg
->length
;
2280 if ((mdb
= cupsArrayFind(db
, &key
)) != NULL
)
2283 * Found an exact match, let's figure out the best margins for the flags
2289 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
2292 * Look for the smallest margins...
2295 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 || best
->bottom
!= 0)
2297 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2298 mdb
&& !cups_compare_media_db(mdb
, &key
);
2299 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2301 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
2302 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
)
2305 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
2313 * If we need an exact match, return no-match if the size is not
2317 if ((flags
& CUPS_MEDIA_FLAGS_EXACT
) &&
2318 (best
->left
|| best
->right
|| best
->top
|| best
->bottom
))
2321 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
2324 * Look for the largest margins...
2327 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2328 mdb
&& !cups_compare_media_db(mdb
, &key
);
2329 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2331 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
2332 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
&&
2333 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2340 * Look for the smallest non-zero margins...
2343 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2344 mdb
&& !cups_compare_media_db(mdb
, &key
);
2345 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2347 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
2348 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) || best
->right
== 0) &&
2349 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
2350 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) || best
->bottom
== 0) &&
2351 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2356 else if (flags
& CUPS_MEDIA_FLAGS_EXACT
)
2359 * See if we can do this as a custom size...
2362 if (pwg
->width
< dinfo
->min_size
.width
||
2363 pwg
->width
> dinfo
->max_size
.width
||
2364 pwg
->length
< dinfo
->min_size
.length
||
2365 pwg
->length
> dinfo
->max_size
.length
)
2366 return (0); /* Out of range */
2368 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) &&
2369 (dinfo
->min_size
.left
> 0 || dinfo
->min_size
.right
> 0 ||
2370 dinfo
->min_size
.top
> 0 || dinfo
->min_size
.bottom
> 0))
2371 return (0); /* Not borderless */
2373 key
.size_name
= (char *)pwg
->pwg
;
2374 key
.bottom
= dinfo
->min_size
.bottom
;
2375 key
.left
= dinfo
->min_size
.left
;
2376 key
.right
= dinfo
->min_size
.right
;
2377 key
.top
= dinfo
->min_size
.top
;
2381 else if (pwg
->width
>= dinfo
->min_size
.width
&&
2382 pwg
->width
<= dinfo
->max_size
.width
&&
2383 pwg
->length
>= dinfo
->min_size
.length
&&
2384 pwg
->length
<= dinfo
->max_size
.length
)
2387 * Map to custom size...
2390 key
.size_name
= (char *)pwg
->pwg
;
2391 key
.bottom
= dinfo
->min_size
.bottom
;
2392 key
.left
= dinfo
->min_size
.left
;
2393 key
.right
= dinfo
->min_size
.right
;
2394 key
.top
= dinfo
->min_size
.top
;
2401 * Find a close size...
2404 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
);
2406 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2407 if (cups_is_close_media_db(mdb
, &key
))
2415 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
2418 * Look for the smallest margins...
2421 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 ||
2424 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2425 mdb
&& cups_is_close_media_db(mdb
, &key
);
2426 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2428 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
2429 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
&&
2430 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2433 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
2440 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
2443 * Look for the largest margins...
2446 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2447 mdb
&& cups_is_close_media_db(mdb
, &key
);
2448 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2450 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
2451 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
&&
2452 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2459 * Look for the smallest non-zero margins...
2462 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2463 mdb
&& cups_is_close_media_db(mdb
, &key
);
2464 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2466 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
2467 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) ||
2468 best
->right
== 0) &&
2469 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
2470 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) ||
2471 best
->bottom
== 0) &&
2472 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2481 * Return the matching size...
2485 strlcpy(size
->media
, best
->key
, sizeof(size
->media
));
2486 else if (best
->size_name
)
2487 strlcpy(size
->media
, best
->size_name
, sizeof(size
->media
));
2489 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
2491 size
->width
= best
->width
;
2492 size
->length
= best
->length
;
2493 size
->bottom
= best
->bottom
;
2494 size
->left
= best
->left
;
2495 size
->right
= best
->right
;
2496 size
->top
= best
->top
;
2506 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
2507 * close to the same size.
2509 * Currently we use 5 points (from PostScript) as the matching range...
2512 static int /* O - 1 if the sizes are close */
2513 cups_is_close_media_db(
2514 _cups_media_db_t
*a
, /* I - First media entries */
2515 _cups_media_db_t
*b
) /* I - Second media entries */
2517 int dwidth
, /* Difference in width */
2518 dlength
; /* Difference in length */
2521 dwidth
= a
->width
- b
->width
;
2522 dlength
= a
->length
- b
->length
;
2524 return (dwidth
>= -176 && dwidth
<= 176 &&
2525 dlength
>= -176 && dlength
<= 176);
2530 * 'cups_test_constraints()' - Test constraints.
2533 static cups_array_t
* /* O - Active constraints */
2534 cups_test_constraints(
2535 cups_dinfo_t
*dinfo
, /* I - Destination information */
2536 const char *new_option
, /* I - Newly selected option */
2537 const char *new_value
, /* I - Newly selected value */
2538 int num_options
, /* I - Number of options */
2539 cups_option_t
*options
, /* I - Options */
2540 int *num_conflicts
, /* O - Number of conflicting options */
2541 cups_option_t
**conflicts
) /* O - Conflicting options */
2543 int i
, /* Looping var */
2544 count
, /* Number of values */
2545 match
; /* Value matches? */
2546 int num_matching
; /* Number of matching options */
2547 cups_option_t
*matching
; /* Matching options */
2548 _cups_dconstres_t
*c
; /* Current constraint */
2549 cups_array_t
*active
= NULL
; /* Active constraints */
2550 ipp_t
*col
; /* Collection value */
2551 ipp_attribute_t
*attr
; /* Current attribute */
2552 _ipp_value_t
*attrval
; /* Current attribute value */
2553 const char *value
; /* Current value */
2554 char temp
[1024]; /* Temporary string */
2555 int int_value
; /* Integer value */
2556 int xres_value
, /* Horizontal resolution */
2557 yres_value
; /* Vertical resolution */
2558 ipp_res_t units_value
; /* Resolution units */
2561 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(dinfo
->constraints
);
2563 c
= (_cups_dconstres_t
*)cupsArrayNext(dinfo
->constraints
))
2568 for (attr
= ippFirstAttribute(c
->collection
);
2570 attr
= ippNextAttribute(c
->collection
))
2573 * Get the value for the current attribute in the constraint...
2576 if (new_option
&& new_value
&& !strcmp(attr
->name
, new_option
))
2578 else if ((value
= cupsGetOption(attr
->name
, num_options
, options
)) == NULL
)
2579 value
= cupsGetOption(attr
->name
, dinfo
->num_defaults
, dinfo
->defaults
);
2584 * Not set so this constraint does not apply...
2592 switch (attr
->value_tag
)
2594 case IPP_TAG_INTEGER
:
2596 int_value
= atoi(value
);
2598 for (i
= attr
->num_values
, attrval
= attr
->values
;
2602 if (attrval
->integer
== int_value
)
2610 case IPP_TAG_BOOLEAN
:
2611 int_value
= !strcmp(value
, "true");
2613 for (i
= attr
->num_values
, attrval
= attr
->values
;
2617 if (attrval
->boolean
== int_value
)
2625 case IPP_TAG_RANGE
:
2626 int_value
= atoi(value
);
2628 for (i
= attr
->num_values
, attrval
= attr
->values
;
2632 if (int_value
>= attrval
->range
.lower
&&
2633 int_value
<= attrval
->range
.upper
)
2641 case IPP_TAG_RESOLUTION
:
2642 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
2644 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
2647 yres_value
= xres_value
;
2650 if (!strcmp(temp
, "dpi"))
2651 units_value
= IPP_RES_PER_INCH
;
2652 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
2653 units_value
= IPP_RES_PER_CM
;
2657 for (i
= attr
->num_values
, attrval
= attr
->values
;
2661 if (attrval
->resolution
.xres
== xres_value
&&
2662 attrval
->resolution
.yres
== yres_value
&&
2663 attrval
->resolution
.units
== units_value
)
2673 case IPP_TAG_KEYWORD
:
2674 case IPP_TAG_CHARSET
:
2676 case IPP_TAG_URISCHEME
:
2677 case IPP_TAG_MIMETYPE
:
2678 case IPP_TAG_LANGUAGE
:
2679 case IPP_TAG_TEXTLANG
:
2680 case IPP_TAG_NAMELANG
:
2681 for (i
= attr
->num_values
, attrval
= attr
->values
;
2685 if (!strcmp(attrval
->string
.text
, value
))
2693 case IPP_TAG_BEGIN_COLLECTION
:
2695 _cupsEncodeOption(col
, IPP_TAG_ZERO
, NULL
, ippGetName(attr
), value
);
2697 for (i
= 0, count
= ippGetCount(attr
); i
< count
; i
++)
2699 if (cups_collection_contains(col
, ippGetCollection(attr
, i
)))
2716 num_matching
= cupsAddOption(attr
->name
, value
, num_matching
, &matching
);
2722 active
= cupsArrayNew(NULL
, NULL
);
2724 cupsArrayAdd(active
, c
);
2726 if (num_conflicts
&& conflicts
)
2728 cups_option_t
*moption
; /* Matching option */
2730 for (i
= num_matching
, moption
= matching
; i
> 0; i
--, moption
++)
2731 *num_conflicts
= cupsAddOption(moption
->name
, moption
->value
, *num_conflicts
, conflicts
);
2735 cupsFreeOptions(num_matching
, matching
);
2743 * 'cups_update_ready()' - Update xxx-ready attributes for the printer.
2747 cups_update_ready(http_t
*http
, /* I - Connection to destination */
2748 cups_dinfo_t
*dinfo
) /* I - Destination information */
2750 ipp_t
*request
; /* Get-Printer-Attributes request */
2751 static const char * const pattrs
[] = /* Printer attributes we want */
2753 "finishings-col-ready",
2755 "job-finishings-col-ready",
2756 "job-finishings-ready",
2763 * Don't update more than once every 30 seconds...
2766 if ((time(NULL
) - dinfo
->ready_time
) < _CUPS_MEDIA_READY_TTL
)
2770 * Free any previous results...
2773 if (dinfo
->cached_flags
& CUPS_MEDIA_FLAGS_READY
)
2775 cupsArrayDelete(dinfo
->cached_db
);
2776 dinfo
->cached_db
= NULL
;
2777 dinfo
->cached_flags
= CUPS_MEDIA_FLAGS_DEFAULT
;
2780 ippDelete(dinfo
->ready_attrs
);
2781 dinfo
->ready_attrs
= NULL
;
2783 cupsArrayDelete(dinfo
->ready_db
);
2784 dinfo
->ready_db
= NULL
;
2787 * Query the xxx-ready values...
2790 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
2791 ippSetVersion(request
, dinfo
->version
/ 10, dinfo
->version
% 10);
2793 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
2795 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
2797 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "requested-attributes", (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
2799 dinfo
->ready_attrs
= cupsDoRequest(http
, request
, dinfo
->resource
);
2802 * Update the ready media database...
2805 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_READY
);
2808 * Update last lookup time and return...
2811 dinfo
->ready_time
= time(NULL
);