2 * Destination option/media support for CUPS.
4 * Copyright © 2012-2019 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
11 * Include necessary headers...
14 #include "cups-private.h"
15 #include "debug-internal.h"
22 #define _CUPS_MEDIA_READY_TTL 30 /* Life of xxx-ready values */
29 static void cups_add_dconstres(cups_array_t
*a
, ipp_t
*collection
);
30 static int cups_collection_contains(ipp_t
*test
, ipp_t
*match
);
31 static size_t cups_collection_string(ipp_attribute_t
*attr
, char *buffer
, size_t bufsize
) _CUPS_NONNULL((1,2));
32 static int cups_compare_dconstres(_cups_dconstres_t
*a
,
33 _cups_dconstres_t
*b
);
34 static int cups_compare_media_db(_cups_media_db_t
*a
,
36 static _cups_media_db_t
*cups_copy_media_db(_cups_media_db_t
*mdb
);
37 static void cups_create_cached(http_t
*http
, cups_dinfo_t
*dinfo
,
39 static void cups_create_constraints(cups_dinfo_t
*dinfo
);
40 static void cups_create_defaults(cups_dinfo_t
*dinfo
);
41 static void cups_create_media_db(cups_dinfo_t
*dinfo
,
43 static void cups_free_media_db(_cups_media_db_t
*mdb
);
44 static int cups_get_media_db(http_t
*http
, cups_dinfo_t
*dinfo
,
45 pwg_media_t
*pwg
, unsigned flags
,
47 static int cups_is_close_media_db(_cups_media_db_t
*a
,
49 static cups_array_t
*cups_test_constraints(cups_dinfo_t
*dinfo
,
50 const char *new_option
,
51 const char *new_value
,
53 cups_option_t
*options
,
55 cups_option_t
**conflicts
);
56 static void cups_update_ready(http_t
*http
, cups_dinfo_t
*dinfo
);
60 * 'cupsAddDestMediaOptions()' - Add the option corresponding to the specified media size.
62 * @since CUPS 2.3/macOS 10.14@
65 int /* O - New number of options */
66 cupsAddDestMediaOptions(
67 http_t
*http
, /* I - Connection to destination */
68 cups_dest_t
*dest
, /* I - Destination */
69 cups_dinfo_t
*dinfo
, /* I - Destination information */
70 unsigned flags
, /* I - Media matching flags */
71 cups_size_t
*size
, /* I - Media size */
72 int num_options
, /* I - Current number of options */
73 cups_option_t
**options
) /* IO - Options */
75 cups_array_t
*db
; /* Media database */
76 _cups_media_db_t
*mdb
; /* Media database entry */
77 char value
[2048]; /* Option value */
81 * Range check input...
84 if (!http
|| !dest
|| !dinfo
|| !size
|| !options
)
86 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
91 * Find the matching media size...
94 if (flags
& CUPS_MEDIA_FLAGS_READY
)
99 DEBUG_printf(("1cupsAddDestMediaOptions: size->media=\"%s\"", size
->media
));
101 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
); mdb
; mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
103 if (mdb
->key
&& !strcmp(mdb
->key
, size
->media
))
105 else if (mdb
->size_name
&& !strcmp(mdb
->size_name
, size
->media
))
111 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
); mdb
; mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
113 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
)
120 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
); mdb
; mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
122 if (mdb
->width
== size
->width
&& mdb
->length
== size
->length
)
129 DEBUG_puts("1cupsAddDestMediaOptions: Unable to find matching size.");
130 return (num_options
);
133 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
));
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\" media-type=\"%s\"}", mdb
->width
, mdb
->length
, mdb
->bottom
, mdb
->left
, mdb
->right
, mdb
->top
, mdb
->source
, mdb
->type
);
140 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
);
144 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
);
148 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
);
151 num_options
= cupsAddOption("media-col", value
, num_options
, options
);
153 return (num_options
);
158 * 'cupsCheckDestSupported()' - Check that the option and value are supported
159 * by the destination.
161 * Returns 1 if supported, 0 otherwise.
163 * @since CUPS 1.6/macOS 10.8@
166 int /* O - 1 if supported, 0 otherwise */
167 cupsCheckDestSupported(
168 http_t
*http
, /* I - Connection to destination */
169 cups_dest_t
*dest
, /* I - Destination */
170 cups_dinfo_t
*dinfo
, /* I - Destination information */
171 const char *option
, /* I - Option */
172 const char *value
) /* I - Value or @code NULL@ */
174 int i
; /* Looping var */
175 char temp
[1024]; /* Temporary string */
176 int int_value
; /* Integer value */
177 int xres_value
, /* Horizontal resolution */
178 yres_value
; /* Vertical resolution */
179 ipp_res_t units_value
; /* Resolution units */
180 ipp_attribute_t
*attr
; /* Attribute */
181 _ipp_value_t
*attrval
; /* Current attribute value */
182 _ipp_option_t
*map
; /* Option mapping information */
186 * Get the default connection as needed...
190 http
= _cupsConnect();
193 * Range check input...
196 if (!http
|| !dest
|| !dinfo
|| !option
)
200 * Lookup the attribute...
203 if (strstr(option
, "-supported"))
204 attr
= ippFindAttribute(dinfo
->attrs
, option
, IPP_TAG_ZERO
);
207 snprintf(temp
, sizeof(temp
), "%s-supported", option
);
208 attr
= ippFindAttribute(dinfo
->attrs
, temp
, IPP_TAG_ZERO
);
221 if (!strcmp(option
, "media") && !strncmp(value
, "custom_", 7))
224 * Check range of custom media sizes...
227 pwg_media_t
*pwg
; /* Current PWG media size info */
228 int min_width
, /* Minimum width */
229 min_length
, /* Minimum length */
230 max_width
, /* Maximum width */
231 max_length
; /* Maximum length */
234 * Get the minimum and maximum size...
237 min_width
= min_length
= INT_MAX
;
238 max_width
= max_length
= 0;
240 for (i
= attr
->num_values
, attrval
= attr
->values
;
244 if (!strncmp(attrval
->string
.text
, "custom_min_", 11) &&
245 (pwg
= pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
247 min_width
= pwg
->width
;
248 min_length
= pwg
->length
;
250 else if (!strncmp(attrval
->string
.text
, "custom_max_", 11) &&
251 (pwg
= pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
253 max_width
= pwg
->width
;
254 max_length
= pwg
->length
;
262 if (min_width
< INT_MAX
&& max_width
> 0 &&
263 (pwg
= pwgMediaForPWG(value
)) != NULL
&&
264 pwg
->width
>= min_width
&& pwg
->width
<= max_width
&&
265 pwg
->length
>= min_length
&& pwg
->length
<= max_length
)
271 * Check literal values...
274 map
= _ippFindOption(option
);
276 switch (attr
->value_tag
)
278 case IPP_TAG_INTEGER
:
279 if (map
&& map
->value_tag
== IPP_TAG_STRING
)
280 return (strlen(value
) <= (size_t)attr
->values
[0].integer
);
283 int_value
= atoi(value
);
285 for (i
= 0; i
< attr
->num_values
; i
++)
286 if (attr
->values
[i
].integer
== int_value
)
290 case IPP_TAG_BOOLEAN
:
291 return (attr
->values
[0].boolean
);
294 if (map
&& map
->value_tag
== IPP_TAG_STRING
)
295 int_value
= (int)strlen(value
);
297 int_value
= atoi(value
);
299 for (i
= 0; i
< attr
->num_values
; i
++)
300 if (int_value
>= attr
->values
[i
].range
.lower
&&
301 int_value
<= attr
->values
[i
].range
.upper
)
305 case IPP_TAG_RESOLUTION
:
306 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
308 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
311 yres_value
= xres_value
;
314 if (!strcmp(temp
, "dpi"))
315 units_value
= IPP_RES_PER_INCH
;
316 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
317 units_value
= IPP_RES_PER_CM
;
321 for (i
= attr
->num_values
, attrval
= attr
->values
;
325 if (attrval
->resolution
.xres
== xres_value
&&
326 attrval
->resolution
.yres
== yres_value
&&
327 attrval
->resolution
.units
== units_value
)
334 case IPP_TAG_KEYWORD
:
335 case IPP_TAG_CHARSET
:
337 case IPP_TAG_URISCHEME
:
338 case IPP_TAG_MIMETYPE
:
339 case IPP_TAG_LANGUAGE
:
340 case IPP_TAG_TEXTLANG
:
341 case IPP_TAG_NAMELANG
:
342 for (i
= 0; i
< attr
->num_values
; i
++)
343 if (!strcmp(attr
->values
[i
].string
.text
, value
))
353 * If we get there the option+value is not supported...
361 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
364 * "num_options" and "options" represent the currently selected options by the
365 * user. "new_option" and "new_value" are the setting the user has just
368 * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
369 * there was an unrecoverable error such as a resolver loop.
371 * If "num_conflicts" and "conflicts" are not @code NULL@, they are set to
372 * contain the list of conflicting option/value pairs. Similarly, if
373 * "num_resolved" and "resolved" are not @code NULL@ they will be set to the
374 * list of changes needed to resolve the conflict.
376 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
377 * to 0 and @code NULL@, respectively, then the conflict cannot be resolved.
379 * @since CUPS 1.6/macOS 10.8@
382 int /* O - 1 if there is a conflict, 0 if none, -1 on error */
383 cupsCopyDestConflicts(
384 http_t
*http
, /* I - Connection to destination */
385 cups_dest_t
*dest
, /* I - Destination */
386 cups_dinfo_t
*dinfo
, /* I - Destination information */
387 int num_options
, /* I - Number of current options */
388 cups_option_t
*options
, /* I - Current options */
389 const char *new_option
, /* I - New option */
390 const char *new_value
, /* I - New value */
391 int *num_conflicts
, /* O - Number of conflicting options */
392 cups_option_t
**conflicts
, /* O - Conflicting options */
393 int *num_resolved
, /* O - Number of options to resolve */
394 cups_option_t
**resolved
) /* O - Resolved options */
396 int i
, /* Looping var */
397 have_conflicts
= 0, /* Do we have conflicts? */
398 changed
, /* Did we change something? */
399 tries
, /* Number of tries for resolution */
400 num_myconf
= 0, /* My number of conflicting options */
401 num_myres
= 0; /* My number of resolved options */
402 cups_option_t
*myconf
= NULL
, /* My conflicting options */
403 *myres
= NULL
, /* My resolved options */
404 *myoption
, /* My current option */
405 *option
; /* Current option */
406 cups_array_t
*active
= NULL
, /* Active conflicts */
407 *pass
= NULL
, /* Resolvers for this pass */
408 *resolvers
= NULL
, /* Resolvers we have used */
409 *test
; /* Test array for conflicts */
410 _cups_dconstres_t
*c
, /* Current constraint */
411 *r
; /* Current resolver */
412 ipp_attribute_t
*attr
; /* Current attribute */
413 char value
[2048]; /* Current attribute value as string */
414 const char *myvalue
; /* Current value of an option */
418 * Clear returned values...
434 * Get the default connection as needed...
438 http
= _cupsConnect();
441 * Range check input...
444 if (!http
|| !dest
|| !dinfo
||
445 (num_conflicts
!= NULL
) != (conflicts
!= NULL
) ||
446 (num_resolved
!= NULL
) != (resolved
!= NULL
))
450 * Load constraints as needed...
453 if (!dinfo
->constraints
)
454 cups_create_constraints(dinfo
);
456 if (cupsArrayCount(dinfo
->constraints
) == 0)
459 if (!dinfo
->num_defaults
)
460 cups_create_defaults(dinfo
);
463 * If we are resolving, create a shadow array...
468 for (i
= num_options
, option
= options
; i
> 0; i
--, option
++)
469 num_myres
= cupsAddOption(option
->name
, option
->value
, num_myres
, &myres
);
471 if (new_option
&& new_value
)
472 num_myres
= cupsAddOption(new_option
, new_value
, num_myres
, &myres
);
476 num_myres
= num_options
;
481 * Check for any conflicts...
485 pass
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
487 for (tries
= 0; tries
< 100; tries
++)
490 * Check for any conflicts...
493 if (num_conflicts
|| num_resolved
)
495 cupsFreeOptions(num_myconf
, myconf
);
499 active
= cups_test_constraints(dinfo
, new_option
, new_value
,
500 num_myres
, myres
, &num_myconf
,
504 active
= cups_test_constraints(dinfo
, new_option
, new_value
, num_myres
,
507 have_conflicts
= (active
!= NULL
);
509 if (!active
|| !num_resolved
)
510 break; /* All done */
513 * Scan the constraints that were triggered to apply resolvers...
517 resolvers
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
519 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(active
), changed
= 0;
521 c
= (_cups_dconstres_t
*)cupsArrayNext(active
))
523 if (cupsArrayFind(pass
, c
))
524 continue; /* Already applied this resolver... */
526 if (cupsArrayFind(resolvers
, c
))
528 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
534 if ((r
= cupsArrayFind(dinfo
->resolvers
, c
)) == NULL
)
536 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
543 * Add the options from the resolver...
546 cupsArrayAdd(pass
, r
);
547 cupsArrayAdd(resolvers
, r
);
549 for (attr
= ippFirstAttribute(r
->collection
);
551 attr
= ippNextAttribute(r
->collection
))
553 if (new_option
&& !strcmp(attr
->name
, new_option
))
554 continue; /* Ignore this if we just changed it */
556 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
557 continue; /* Ignore if the value is too long */
559 if ((test
= cups_test_constraints(dinfo
, attr
->name
, value
, num_myres
,
560 myres
, NULL
, NULL
)) == NULL
)
563 * That worked, flag it...
569 cupsArrayDelete(test
);
572 * Add the option/value from the resolver regardless of whether it
573 * worked; this makes sure that we can cascade several changes to
574 * make things resolve...
577 num_myres
= cupsAddOption(attr
->name
, value
, num_myres
, &myres
);
583 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
588 cupsArrayClear(pass
);
590 cupsArrayDelete(active
);
596 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
602 * Copy resolved options as needed...
607 for (i
= num_myres
, myoption
= myres
; i
> 0; i
--, myoption
++)
609 if ((myvalue
= cupsGetOption(myoption
->name
, num_options
,
611 strcmp(myvalue
, myoption
->value
))
613 if (new_option
&& !strcmp(new_option
, myoption
->name
) &&
614 new_value
&& !strcmp(new_value
, myoption
->value
))
617 *num_resolved
= cupsAddOption(myoption
->name
, myoption
->value
,
618 *num_resolved
, resolved
);
629 cupsArrayDelete(active
);
630 cupsArrayDelete(pass
);
631 cupsArrayDelete(resolvers
);
636 * Free shadow copy of options...
639 cupsFreeOptions(num_myres
, myres
);
645 * Return conflicting options to caller...
648 *num_conflicts
= num_myconf
;
654 * Free conflicting options...
657 cupsFreeOptions(num_myconf
, myconf
);
660 return (have_conflicts
);
665 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
668 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
669 * value. @code NULL@ is returned on error.
671 * @since CUPS 1.6/macOS 10.8@
674 cups_dinfo_t
* /* O - Destination information */
676 http_t
*http
, /* I - Connection to destination */
677 cups_dest_t
*dest
) /* I - Destination */
679 cups_dinfo_t
*dinfo
; /* Destination information */
680 unsigned dflags
; /* Destination flags */
681 ipp_t
*request
, /* Get-Printer-Attributes request */
682 *response
; /* Supported attributes */
683 int tries
, /* Number of tries so far */
684 delay
, /* Current retry delay */
685 prev_delay
; /* Next retry delay */
686 const char *uri
; /* Printer URI */
687 char resource
[1024]; /* Resource path */
688 int version
; /* IPP version */
689 ipp_status_t status
; /* Status of request */
690 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
691 static const char * const requested_attrs
[] =
692 { /* Requested attributes */
694 "media-col-database",
695 "printer-description"
699 DEBUG_printf(("cupsCopyDestInfo(http=%p, dest=%p(%s))", (void *)http
, (void *)dest
, dest
? dest
->name
: ""));
702 * Get the default connection as needed...
707 DEBUG_puts("1cupsCopyDestInfo: Default server connection.");
708 http
= _cupsConnect();
709 dflags
= CUPS_DEST_FLAGS_NONE
;
712 else if (httpAddrFamily(http
->hostaddr
) == AF_LOCAL
)
714 DEBUG_puts("1cupsCopyDestInfo: Connection to server (domain socket).");
715 dflags
= CUPS_DEST_FLAGS_NONE
;
717 #endif /* AF_LOCAL */
718 else if ((strcmp(http
->hostname
, cg
->server
) && cg
->server
[0] != '/') || cg
->ipp_port
!= httpAddrPort(http
->hostaddr
))
720 DEBUG_printf(("1cupsCopyDestInfo: Connection to device (%s).", http
->hostname
));
721 dflags
= CUPS_DEST_FLAGS_DEVICE
;
725 DEBUG_printf(("1cupsCopyDestInfo: Connection to server (%s).", http
->hostname
));
726 dflags
= CUPS_DEST_FLAGS_NONE
;
730 * Range check input...
737 * Get the printer URI and resource path...
740 if ((uri
= _cupsGetDestResource(dest
, dflags
, resource
, sizeof(resource
))) == NULL
)
742 DEBUG_puts("1cupsCopyDestInfo: Unable to get resource.");
747 * Get the supported attributes...
758 * Send a Get-Printer-Attributes request...
761 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
763 ippSetVersion(request
, version
/ 10, version
% 10);
764 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, uri
);
765 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
766 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "requested-attributes", (int)(sizeof(requested_attrs
) / sizeof(requested_attrs
[0])), NULL
, requested_attrs
);
767 response
= cupsDoRequest(http
, request
, resource
);
768 status
= cupsLastError();
770 if (status
> IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED
)
772 DEBUG_printf(("1cupsCopyDestInfo: Get-Printer-Attributes for '%s' returned %s (%s)", dest
->name
, ippErrorString(status
), cupsLastErrorString()));
777 if ((status
== IPP_STATUS_ERROR_BAD_REQUEST
|| status
== IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
) && version
> 11)
781 else if (status
== IPP_STATUS_ERROR_BUSY
)
783 sleep((unsigned)delay
);
785 delay
= _cupsNextDelay(delay
, &prev_delay
);
793 while (!response
&& tries
< 10);
797 DEBUG_puts("1cupsCopyDestInfo: Unable to get printer attributes.");
802 * Allocate a cups_dinfo_t structure and return it...
805 if ((dinfo
= calloc(1, sizeof(cups_dinfo_t
))) == NULL
)
807 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
812 DEBUG_printf(("1cupsCopyDestInfo: version=%d, uri=\"%s\", resource=\"%s\".", version
, uri
, resource
));
814 dinfo
->version
= version
;
816 dinfo
->resource
= _cupsStrAlloc(resource
);
817 dinfo
->attrs
= response
;
824 * 'cupsFindDestDefault()' - Find the default value(s) for the given option.
826 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
827 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
828 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
829 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
830 * functions to inspect the default value(s) as needed.
832 * @since CUPS 1.7/macOS 10.9@
835 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
837 http_t
*http
, /* I - Connection to destination */
838 cups_dest_t
*dest
, /* I - Destination */
839 cups_dinfo_t
*dinfo
, /* I - Destination information */
840 const char *option
) /* I - Option/attribute name */
842 char name
[IPP_MAX_NAME
]; /* Attribute name */
846 * Get the default connection as needed...
850 http
= _cupsConnect();
853 * Range check input...
856 if (!http
|| !dest
|| !dinfo
|| !option
)
858 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
863 * Find and return the attribute...
866 snprintf(name
, sizeof(name
), "%s-default", option
);
867 return (ippFindAttribute(dinfo
->attrs
, name
, IPP_TAG_ZERO
));
872 * 'cupsFindDestReady()' - Find the default value(s) for the given option.
874 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
875 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
876 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
877 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
878 * functions to inspect the default value(s) as needed.
880 * @since CUPS 1.7/macOS 10.9@
883 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
885 http_t
*http
, /* I - Connection to destination */
886 cups_dest_t
*dest
, /* I - Destination */
887 cups_dinfo_t
*dinfo
, /* I - Destination information */
888 const char *option
) /* I - Option/attribute name */
890 char name
[IPP_MAX_NAME
]; /* Attribute name */
894 * Get the default connection as needed...
898 http
= _cupsConnect();
901 * Range check input...
904 if (!http
|| !dest
|| !dinfo
|| !option
)
906 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
911 * Find and return the attribute...
914 cups_update_ready(http
, dinfo
);
916 snprintf(name
, sizeof(name
), "%s-ready", option
);
917 return (ippFindAttribute(dinfo
->ready_attrs
, name
, IPP_TAG_ZERO
));
922 * 'cupsFindDestSupported()' - Find the default value(s) for the given option.
924 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
925 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
926 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
927 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
928 * functions to inspect the default value(s) as needed.
930 * @since CUPS 1.7/macOS 10.9@
933 ipp_attribute_t
* /* O - Default attribute or @code NULL@ for none */
934 cupsFindDestSupported(
935 http_t
*http
, /* I - Connection to destination */
936 cups_dest_t
*dest
, /* I - Destination */
937 cups_dinfo_t
*dinfo
, /* I - Destination information */
938 const char *option
) /* I - Option/attribute name */
940 char name
[IPP_MAX_NAME
]; /* Attribute name */
944 * Get the default connection as needed...
948 http
= _cupsConnect();
951 * Range check input...
954 if (!http
|| !dest
|| !dinfo
|| !option
)
956 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
961 * Find and return the attribute...
964 snprintf(name
, sizeof(name
), "%s-supported", option
);
965 return (ippFindAttribute(dinfo
->attrs
, name
, IPP_TAG_ZERO
));
970 * 'cupsFreeDestInfo()' - Free destination information obtained using
971 * @link cupsCopyDestInfo@.
973 * @since CUPS 1.6/macOS 10.8@
977 cupsFreeDestInfo(cups_dinfo_t
*dinfo
) /* I - Destination information */
980 * Range check input...
987 * Free memory and return...
990 _cupsStrFree(dinfo
->resource
);
992 cupsArrayDelete(dinfo
->constraints
);
993 cupsArrayDelete(dinfo
->resolvers
);
995 cupsArrayDelete(dinfo
->localizations
);
997 cupsArrayDelete(dinfo
->media_db
);
999 cupsArrayDelete(dinfo
->cached_db
);
1001 ippDelete(dinfo
->ready_attrs
);
1002 cupsArrayDelete(dinfo
->ready_db
);
1004 ippDelete(dinfo
->attrs
);
1011 * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a
1014 * The @code flags@ parameter determines which set of media are indexed. For
1015 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth
1016 * borderless size supported by the printer.
1018 * @since CUPS 1.7/macOS 10.9@
1021 int /* O - 1 on success, 0 on failure */
1022 cupsGetDestMediaByIndex(
1023 http_t
*http
, /* I - Connection to destination */
1024 cups_dest_t
*dest
, /* I - Destination */
1025 cups_dinfo_t
*dinfo
, /* I - Destination information */
1026 int n
, /* I - Media size number (0-based) */
1027 unsigned flags
, /* I - Media flags */
1028 cups_size_t
*size
) /* O - Media size information */
1030 _cups_media_db_t
*nsize
; /* Size for N */
1031 pwg_media_t
*pwg
; /* PWG media name for size */
1035 * Get the default connection as needed...
1039 http
= _cupsConnect();
1042 * Range check input...
1046 memset(size
, 0, sizeof(cups_size_t
));
1048 if (!http
|| !dest
|| !dinfo
|| n
< 0 || !size
)
1050 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1055 * Load media list as needed...
1058 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1059 cups_update_ready(http
, dinfo
);
1061 if (!dinfo
->cached_db
|| dinfo
->cached_flags
!= flags
)
1062 cups_create_cached(http
, dinfo
, flags
);
1065 * Copy the size over and return...
1068 if ((nsize
= (_cups_media_db_t
*)cupsArrayIndex(dinfo
->cached_db
, n
)) == NULL
)
1070 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1075 strlcpy(size
->media
, nsize
->key
, sizeof(size
->media
));
1076 else if (nsize
->size_name
)
1077 strlcpy(size
->media
, nsize
->size_name
, sizeof(size
->media
));
1078 else if ((pwg
= pwgMediaForSize(nsize
->width
, nsize
->length
)) != NULL
)
1079 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
1082 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1086 size
->width
= nsize
->width
;
1087 size
->length
= nsize
->length
;
1088 size
->bottom
= nsize
->bottom
;
1089 size
->left
= nsize
->left
;
1090 size
->right
= nsize
->right
;
1091 size
->top
= nsize
->top
;
1098 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
1100 * The "media" string is a PWG media name. "Flags" provides some matching
1101 * guidance (multiple flags can be combined):
1103 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
1104 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
1105 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
1106 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
1107 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
1108 * size amongst the "ready" media.
1110 * The matching result (if any) is returned in the "cups_size_t" structure.
1112 * Returns 1 when there is a match and 0 if there is not a match.
1114 * @since CUPS 1.6/macOS 10.8@
1117 int /* O - 1 on match, 0 on failure */
1118 cupsGetDestMediaByName(
1119 http_t
*http
, /* I - Connection to destination */
1120 cups_dest_t
*dest
, /* I - Destination */
1121 cups_dinfo_t
*dinfo
, /* I - Destination information */
1122 const char *media
, /* I - Media name */
1123 unsigned flags
, /* I - Media matching flags */
1124 cups_size_t
*size
) /* O - Media size information */
1126 pwg_media_t
*pwg
; /* PWG media info */
1130 * Get the default connection as needed...
1134 http
= _cupsConnect();
1137 * Range check input...
1141 memset(size
, 0, sizeof(cups_size_t
));
1143 if (!http
|| !dest
|| !dinfo
|| !media
|| !size
)
1145 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1150 * Lookup the media size name...
1153 if ((pwg
= pwgMediaForPWG(media
)) == NULL
)
1154 if ((pwg
= pwgMediaForLegacy(media
)) == NULL
)
1156 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media
));
1157 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unknown media size name."), 1);
1162 * Lookup the size...
1165 return (cups_get_media_db(http
, dinfo
, pwg
, flags
, size
));
1170 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
1172 * "Width" and "length" are the dimensions in hundredths of millimeters.
1173 * "Flags" provides some matching guidance (multiple flags can be combined):
1175 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
1176 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
1177 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
1178 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
1179 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
1180 * size amongst the "ready" media.
1182 * The matching result (if any) is returned in the "cups_size_t" structure.
1184 * Returns 1 when there is a match and 0 if there is not a match.
1186 * @since CUPS 1.6/macOS 10.8@
1189 int /* O - 1 on match, 0 on failure */
1190 cupsGetDestMediaBySize(
1191 http_t
*http
, /* I - Connection to destination */
1192 cups_dest_t
*dest
, /* I - Destination */
1193 cups_dinfo_t
*dinfo
, /* I - Destination information */
1194 int width
, /* I - Media width in hundredths of
1196 int length
, /* I - Media length in hundredths of
1198 unsigned flags
, /* I - Media matching flags */
1199 cups_size_t
*size
) /* O - Media size information */
1201 pwg_media_t
*pwg
; /* PWG media info */
1205 * Get the default connection as needed...
1209 http
= _cupsConnect();
1212 * Range check input...
1216 memset(size
, 0, sizeof(cups_size_t
));
1218 if (!http
|| !dest
|| !dinfo
|| width
<= 0 || length
<= 0 || !size
)
1220 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1225 * Lookup the media size name...
1228 if ((pwg
= pwgMediaForSize(width
, length
)) == NULL
)
1230 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width
,
1232 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Invalid media size."), 1);
1237 * Lookup the size...
1240 return (cups_get_media_db(http
, dinfo
, pwg
, flags
, size
));
1245 * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a
1248 * The @code flags@ parameter determines the set of media sizes that are
1249 * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return
1250 * the number of borderless sizes.
1252 * @since CUPS 1.7/macOS 10.9@
1255 int /* O - Number of sizes */
1256 cupsGetDestMediaCount(
1257 http_t
*http
, /* I - Connection to destination */
1258 cups_dest_t
*dest
, /* I - Destination */
1259 cups_dinfo_t
*dinfo
, /* I - Destination information */
1260 unsigned flags
) /* I - Media flags */
1263 * Get the default connection as needed...
1267 http
= _cupsConnect();
1270 * Range check input...
1273 if (!http
|| !dest
|| !dinfo
)
1275 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1280 * Load media list as needed...
1283 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1284 cups_update_ready(http
, dinfo
);
1286 if (!dinfo
->cached_db
|| dinfo
->cached_flags
!= flags
)
1287 cups_create_cached(http
, dinfo
, flags
);
1289 return (cupsArrayCount(dinfo
->cached_db
));
1294 * 'cupsGetDestMediaDefault()' - Get the default size for a destination.
1296 * The @code flags@ parameter determines which default size is returned. For
1297 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default
1298 * borderless size, typically US Letter or A4, but sometimes 4x6 photo media.
1300 * @since CUPS 1.7/macOS 10.9@
1303 int /* O - 1 on success, 0 on failure */
1304 cupsGetDestMediaDefault(
1305 http_t
*http
, /* I - Connection to destination */
1306 cups_dest_t
*dest
, /* I - Destination */
1307 cups_dinfo_t
*dinfo
, /* I - Destination information */
1308 unsigned flags
, /* I - Media flags */
1309 cups_size_t
*size
) /* O - Media size information */
1311 const char *media
; /* Default media size */
1315 * Get the default connection as needed...
1319 http
= _cupsConnect();
1322 * Range check input...
1326 memset(size
, 0, sizeof(cups_size_t
));
1328 if (!http
|| !dest
|| !dinfo
|| !size
)
1330 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
1335 * Get the default media size, if any...
1338 if ((media
= cupsGetOption("media", dest
->num_options
, dest
->options
)) == NULL
)
1339 media
= "na_letter_8.5x11in";
1341 if (cupsGetDestMediaByName(http
, dest
, dinfo
, media
, flags
, size
))
1344 if (strcmp(media
, "na_letter_8.5x11in") && cupsGetDestMediaByName(http
, dest
, dinfo
, "iso_a4_210x297mm", flags
, size
))
1347 if (strcmp(media
, "iso_a4_210x297mm") && cupsGetDestMediaByName(http
, dest
, dinfo
, "na_letter_8.5x11in", flags
, size
))
1350 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) && cupsGetDestMediaByName(http
, dest
, dinfo
, "na_index_4x6in", flags
, size
))
1354 * Fall back to the first matching media size...
1357 return (cupsGetDestMediaByIndex(http
, dest
, dinfo
, 0, flags
, size
));
1362 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
1367 cups_array_t
*a
, /* I - Array */
1368 ipp_t
*collection
) /* I - Collection value */
1370 ipp_attribute_t
*attr
; /* Attribute */
1371 _cups_dconstres_t
*temp
; /* Current constraint/resolver */
1374 if ((attr
= ippFindAttribute(collection
, "resolver-name",
1375 IPP_TAG_NAME
)) == NULL
)
1378 if ((temp
= calloc(1, sizeof(_cups_dconstres_t
))) == NULL
)
1381 temp
->name
= attr
->values
[0].string
.text
;
1382 temp
->collection
= collection
;
1384 cupsArrayAdd(a
, temp
);
1389 * 'cups_collection_contains()' - Check whether test collection is contained in the matching collection.
1392 static int /* O - 1 on a match, 0 on a non-match */
1393 cups_collection_contains(ipp_t
*test
, /* I - Collection to test */
1394 ipp_t
*match
) /* I - Matching values */
1396 int i
, j
, /* Looping vars */
1397 mcount
, /* Number of match values */
1398 tcount
; /* Number of test values */
1399 ipp_attribute_t
*tattr
, /* Testing attribute */
1400 *mattr
; /* Matching attribute */
1401 const char *tval
; /* Testing string value */
1404 for (mattr
= ippFirstAttribute(match
); mattr
; mattr
= ippNextAttribute(match
))
1406 if ((tattr
= ippFindAttribute(test
, ippGetName(mattr
), IPP_TAG_ZERO
)) == NULL
)
1409 tcount
= ippGetCount(tattr
);
1411 switch (ippGetValueTag(mattr
))
1413 case IPP_TAG_INTEGER
:
1415 if (ippGetValueTag(tattr
) != ippGetValueTag(mattr
))
1418 for (i
= 0; i
< tcount
; i
++)
1420 if (!ippContainsInteger(mattr
, ippGetInteger(tattr
, i
)))
1425 case IPP_TAG_RANGE
:
1426 if (ippGetValueTag(tattr
) != IPP_TAG_INTEGER
)
1429 for (i
= 0; i
< tcount
; i
++)
1431 if (!ippContainsInteger(mattr
, ippGetInteger(tattr
, i
)))
1436 case IPP_TAG_BOOLEAN
:
1437 if (ippGetValueTag(tattr
) != IPP_TAG_BOOLEAN
|| ippGetBoolean(tattr
, 0) != ippGetBoolean(mattr
, 0))
1441 case IPP_TAG_TEXTLANG
:
1442 case IPP_TAG_NAMELANG
:
1445 case IPP_TAG_KEYWORD
:
1447 case IPP_TAG_URISCHEME
:
1448 case IPP_TAG_CHARSET
:
1449 case IPP_TAG_LANGUAGE
:
1450 case IPP_TAG_MIMETYPE
:
1451 for (i
= 0; i
< tcount
; i
++)
1453 if ((tval
= ippGetString(tattr
, i
, NULL
)) == NULL
|| !ippContainsString(mattr
, tval
))
1458 case IPP_TAG_BEGIN_COLLECTION
:
1459 for (i
= 0; i
< tcount
; i
++)
1461 ipp_t
*tcol
= ippGetCollection(tattr
, i
);
1462 /* Testing collection */
1464 for (j
= 0, mcount
= ippGetCount(mattr
); j
< mcount
; j
++)
1465 if (!cups_collection_contains(tcol
, ippGetCollection(mattr
, j
)))
1480 * 'cups_collection_string()' - Convert an IPP collection to an option string.
1483 static size_t /* O - Number of bytes needed */
1484 cups_collection_string(
1485 ipp_attribute_t
*attr
, /* I - Collection attribute */
1486 char *buffer
, /* I - String buffer */
1487 size_t bufsize
) /* I - Size of buffer */
1489 int i
, j
, /* Looping vars */
1490 count
, /* Number of collection values */
1491 mcount
; /* Number of member values */
1492 ipp_t
*col
; /* Collection */
1493 ipp_attribute_t
*first
, /* First member attribute */
1494 *member
; /* Member attribute */
1495 char *bufptr
, /* Pointer into buffer */
1496 *bufend
, /* End of buffer */
1497 temp
[100]; /* Temporary string */
1498 const char *mptr
; /* Pointer into member value */
1499 int mlen
; /* Length of octetString */
1503 bufend
= buffer
+ bufsize
- 1;
1505 for (i
= 0, count
= ippGetCount(attr
); i
< count
; i
++)
1507 col
= ippGetCollection(attr
, i
);
1511 if (bufptr
< bufend
)
1517 if (bufptr
< bufend
)
1522 for (member
= first
= ippFirstAttribute(col
); member
; member
= ippNextAttribute(col
))
1524 const char *mname
= ippGetName(member
);
1526 if (member
!= first
)
1528 if (bufptr
< bufend
)
1534 if (ippGetValueTag(member
) == IPP_TAG_BOOLEAN
)
1536 if (!ippGetBoolean(member
, 0))
1538 if (bufptr
< bufend
)
1539 strlcpy(bufptr
, "no", (size_t)(bufend
- bufptr
+ 1));
1543 if (bufptr
< bufend
)
1544 strlcpy(bufptr
, mname
, (size_t)(bufend
- bufptr
+ 1));
1545 bufptr
+= strlen(mname
);
1549 if (bufptr
< bufend
)
1550 strlcpy(bufptr
, mname
, (size_t)(bufend
- bufptr
+ 1));
1551 bufptr
+= strlen(mname
);
1553 if (bufptr
< bufend
)
1558 if (ippGetValueTag(member
) == IPP_TAG_BEGIN_COLLECTION
)
1561 * Convert sub-collection...
1564 bufptr
+= cups_collection_string(member
, bufptr
, bufptr
< bufend
? (size_t)(bufend
- bufptr
+ 1) : 0);
1569 * Convert simple type...
1572 for (j
= 0, mcount
= ippGetCount(member
); j
< mcount
; j
++)
1576 if (bufptr
< bufend
)
1582 switch (ippGetValueTag(member
))
1584 case IPP_TAG_INTEGER
:
1586 bufptr
+= snprintf(bufptr
, bufptr
< bufend
? (size_t)(bufend
- bufptr
+ 1) : 0, "%d", ippGetInteger(member
, j
));
1589 case IPP_TAG_STRING
:
1590 if (bufptr
< bufend
)
1595 for (mptr
= (const char *)ippGetOctetString(member
, j
, &mlen
); mlen
> 0; mlen
--, mptr
++)
1597 if (*mptr
== '\"' || *mptr
== '\\')
1599 if (bufptr
< bufend
)
1605 if (bufptr
< bufend
)
1611 if (bufptr
< bufend
)
1619 unsigned year
; /* Year */
1620 const ipp_uchar_t
*date
= ippGetDate(member
, j
);
1623 year
= ((unsigned)date
[0] << 8) + (unsigned)date
[1];
1625 if (date
[9] == 0 && date
[10] == 0)
1626 snprintf(temp
, sizeof(temp
), "%04u-%02u-%02uT%02u:%02u:%02uZ", year
, date
[2], date
[3], date
[4], date
[5], date
[6]);
1628 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]);
1630 if (bufptr
< bufend
)
1631 strlcpy(bufptr
, temp
, (size_t)(bufend
- bufptr
+ 1));
1633 bufptr
+= strlen(temp
);
1637 case IPP_TAG_RESOLUTION
:
1639 int xres
, /* Horizontal resolution */
1640 yres
; /* Vertical resolution */
1641 ipp_res_t units
; /* Resolution units */
1643 xres
= ippGetResolution(member
, j
, &yres
, &units
);
1646 snprintf(temp
, sizeof(temp
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1648 snprintf(temp
, sizeof(temp
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1650 if (bufptr
< bufend
)
1651 strlcpy(bufptr
, temp
, (size_t)(bufend
- bufptr
+ 1));
1653 bufptr
+= strlen(temp
);
1657 case IPP_TAG_RANGE
:
1659 int lower
, /* Lower bound */
1660 upper
; /* Upper bound */
1662 lower
= ippGetRange(member
, j
, &upper
);
1664 snprintf(temp
, sizeof(temp
), "%d-%d", lower
, upper
);
1666 if (bufptr
< bufend
)
1667 strlcpy(bufptr
, temp
, (size_t)(bufend
- bufptr
+ 1));
1669 bufptr
+= strlen(temp
);
1673 case IPP_TAG_TEXTLANG
:
1674 case IPP_TAG_NAMELANG
:
1677 case IPP_TAG_KEYWORD
:
1679 case IPP_TAG_URISCHEME
:
1680 case IPP_TAG_CHARSET
:
1681 case IPP_TAG_LANGUAGE
:
1682 case IPP_TAG_MIMETYPE
:
1683 if (bufptr
< bufend
)
1688 for (mptr
= ippGetString(member
, j
, NULL
); *mptr
; mptr
++)
1690 if (*mptr
== '\"' || *mptr
== '\\')
1692 if (bufptr
< bufend
)
1698 if (bufptr
< bufend
)
1704 if (bufptr
< bufend
)
1717 if (bufptr
< bufend
)
1724 return ((size_t)(bufptr
- buffer
+ 1));
1729 * 'cups_compare_dconstres()' - Compare to resolver entries.
1732 static int /* O - Result of comparison */
1733 cups_compare_dconstres(
1734 _cups_dconstres_t
*a
, /* I - First resolver */
1735 _cups_dconstres_t
*b
) /* I - Second resolver */
1737 return (strcmp(a
->name
, b
->name
));
1742 * 'cups_compare_media_db()' - Compare two media entries.
1745 static int /* O - Result of comparison */
1746 cups_compare_media_db(
1747 _cups_media_db_t
*a
, /* I - First media entries */
1748 _cups_media_db_t
*b
) /* I - Second media entries */
1750 int result
; /* Result of comparison */
1753 if ((result
= a
->width
- b
->width
) == 0)
1754 result
= a
->length
- b
->length
;
1761 * 'cups_copy_media_db()' - Copy a media entry.
1764 static _cups_media_db_t
* /* O - New media entry */
1766 _cups_media_db_t
*mdb
) /* I - Media entry to copy */
1768 _cups_media_db_t
*temp
; /* New media entry */
1771 if ((temp
= calloc(1, sizeof(_cups_media_db_t
))) == NULL
)
1775 temp
->color
= _cupsStrAlloc(mdb
->color
);
1777 temp
->key
= _cupsStrAlloc(mdb
->key
);
1779 temp
->info
= _cupsStrAlloc(mdb
->info
);
1781 temp
->size_name
= _cupsStrAlloc(mdb
->size_name
);
1783 temp
->source
= _cupsStrAlloc(mdb
->source
);
1785 temp
->type
= _cupsStrAlloc(mdb
->type
);
1787 temp
->width
= mdb
->width
;
1788 temp
->length
= mdb
->length
;
1789 temp
->bottom
= mdb
->bottom
;
1790 temp
->left
= mdb
->left
;
1791 temp
->right
= mdb
->right
;
1792 temp
->top
= mdb
->top
;
1799 * 'cups_create_cached()' - Create the media selection cache.
1803 cups_create_cached(http_t
*http
, /* I - Connection to destination */
1804 cups_dinfo_t
*dinfo
, /* I - Destination information */
1805 unsigned flags
) /* I - Media selection flags */
1807 cups_array_t
*db
; /* Media database array to use */
1808 _cups_media_db_t
*mdb
, /* Media database entry */
1809 *first
; /* First entry this size */
1812 DEBUG_printf(("3cups_create_cached(http=%p, dinfo=%p, flags=%u)", (void *)http
, (void *)dinfo
, flags
));
1814 if (dinfo
->cached_db
)
1815 cupsArrayDelete(dinfo
->cached_db
);
1817 dinfo
->cached_db
= cupsArrayNew(NULL
, NULL
);
1818 dinfo
->cached_flags
= flags
;
1820 if (flags
& CUPS_MEDIA_FLAGS_READY
)
1822 DEBUG_puts("4cups_create_cached: ready media");
1824 cups_update_ready(http
, dinfo
);
1825 db
= dinfo
->ready_db
;
1829 DEBUG_puts("4cups_create_cached: supported media");
1831 if (!dinfo
->media_db
)
1832 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
);
1834 db
= dinfo
->media_db
;
1837 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
), first
= mdb
;
1839 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
1841 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
));
1843 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1845 if (!mdb
->left
&& !mdb
->right
&& !mdb
->top
&& !mdb
->bottom
)
1847 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb
));
1848 cupsArrayAdd(dinfo
->cached_db
, mdb
);
1851 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1853 if (first
->width
!= mdb
->width
|| first
->length
!= mdb
->length
)
1855 DEBUG_printf(("4cups_create_cached: add %p", (void *)first
));
1856 cupsArrayAdd(dinfo
->cached_db
, first
);
1859 else if (mdb
->left
>= first
->left
&& mdb
->right
>= first
->right
&& mdb
->top
>= first
->top
&& mdb
->bottom
>= first
->bottom
&&
1860 (mdb
->left
!= first
->left
|| mdb
->right
!= first
->right
|| mdb
->top
!= first
->top
|| mdb
->bottom
!= first
->bottom
))
1865 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb
));
1866 cupsArrayAdd(dinfo
->cached_db
, mdb
);
1870 if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1872 DEBUG_printf(("4cups_create_cached: add %p", (void *)first
));
1873 cupsArrayAdd(dinfo
->cached_db
, first
);
1879 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
1883 cups_create_constraints(
1884 cups_dinfo_t
*dinfo
) /* I - Destination information */
1886 int i
; /* Looping var */
1887 ipp_attribute_t
*attr
; /* Attribute */
1888 _ipp_value_t
*val
; /* Current value */
1891 dinfo
->constraints
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, NULL
,
1892 (cups_afree_func_t
)free
);
1893 dinfo
->resolvers
= cupsArrayNew3((cups_array_func_t
)cups_compare_dconstres
,
1894 NULL
, NULL
, 0, NULL
,
1895 (cups_afree_func_t
)free
);
1897 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-constraints-supported",
1898 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1900 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
1901 cups_add_dconstres(dinfo
->constraints
, val
->collection
);
1904 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-resolvers-supported",
1905 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1907 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
1908 cups_add_dconstres(dinfo
->resolvers
, val
->collection
);
1914 * 'cups_create_defaults()' - Create the -default option array.
1918 cups_create_defaults(
1919 cups_dinfo_t
*dinfo
) /* I - Destination information */
1921 ipp_attribute_t
*attr
; /* Current attribute */
1922 char name
[IPP_MAX_NAME
+ 1],
1924 *nameptr
, /* Pointer into current name */
1925 value
[2048]; /* Current value */
1929 * Iterate through the printer attributes looking for xxx-default and adding
1930 * xxx=value to the defaults option array.
1933 for (attr
= ippFirstAttribute(dinfo
->attrs
); attr
; attr
= ippNextAttribute(dinfo
->attrs
))
1935 if (!ippGetName(attr
) || ippGetGroupTag(attr
) != IPP_TAG_PRINTER
)
1938 strlcpy(name
, ippGetName(attr
), sizeof(name
));
1939 if ((nameptr
= name
+ strlen(name
) - 8) <= name
|| strcmp(nameptr
, "-default"))
1944 if (ippGetValueTag(attr
) == IPP_TAG_BEGIN_COLLECTION
)
1946 if (cups_collection_string(attr
, value
, sizeof(value
)) >= sizeof(value
))
1949 else if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
1952 dinfo
->num_defaults
= cupsAddOption(name
, value
, dinfo
->num_defaults
, &dinfo
->defaults
);
1958 * 'cups_create_media_db()' - Create the media database.
1962 cups_create_media_db(
1963 cups_dinfo_t
*dinfo
, /* I - Destination information */
1964 unsigned flags
) /* I - Media flags */
1966 int i
; /* Looping var */
1967 _ipp_value_t
*val
; /* Current value */
1968 ipp_attribute_t
*media_col_db
, /* media-col-database */
1969 *media_attr
, /* media-xxx */
1970 *x_dimension
, /* x-dimension */
1971 *y_dimension
; /* y-dimension */
1972 pwg_media_t
*pwg
; /* PWG media info */
1973 cups_array_t
*db
; /* New media database array */
1974 _cups_media_db_t mdb
; /* Media entry */
1975 char media_key
[256]; /* Synthesized media-key value */
1978 db
= cupsArrayNew3((cups_array_func_t
)cups_compare_media_db
,
1980 (cups_acopy_func_t
)cups_copy_media_db
,
1981 (cups_afree_func_t
)cups_free_media_db
);
1983 if (flags
== CUPS_MEDIA_FLAGS_READY
)
1985 dinfo
->ready_db
= db
;
1987 media_col_db
= ippFindAttribute(dinfo
->ready_attrs
, "media-col-ready",
1988 IPP_TAG_BEGIN_COLLECTION
);
1989 media_attr
= ippFindAttribute(dinfo
->ready_attrs
, "media-ready",
1994 dinfo
->media_db
= db
;
1995 dinfo
->min_size
.width
= INT_MAX
;
1996 dinfo
->min_size
.length
= INT_MAX
;
1997 dinfo
->max_size
.width
= 0;
1998 dinfo
->max_size
.length
= 0;
2000 media_col_db
= ippFindAttribute(dinfo
->attrs
, "media-col-database",
2001 IPP_TAG_BEGIN_COLLECTION
);
2002 media_attr
= ippFindAttribute(dinfo
->attrs
, "media-supported",
2008 _ipp_value_t
*custom
= NULL
; /* Custom size range value */
2010 for (i
= media_col_db
->num_values
, val
= media_col_db
->values
;
2014 memset(&mdb
, 0, sizeof(mdb
));
2016 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size",
2017 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
2019 ipp_t
*media_size
= media_attr
->values
[0].collection
;
2020 /* media-size collection value */
2022 if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
2023 IPP_TAG_INTEGER
)) != NULL
&&
2024 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
2025 IPP_TAG_INTEGER
)) != NULL
)
2031 mdb
.width
= x_dimension
->values
[0].integer
;
2032 mdb
.length
= y_dimension
->values
[0].integer
;
2034 else if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
2035 IPP_TAG_INTEGER
)) != NULL
&&
2036 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
2037 IPP_TAG_RANGE
)) != NULL
)
2043 mdb
.width
= x_dimension
->values
[0].integer
;
2044 mdb
.length
= y_dimension
->values
[0].range
.upper
;
2046 else if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
2047 (x_dimension
= ippFindAttribute(media_size
, "x-dimension",
2048 IPP_TAG_RANGE
)) != NULL
&&
2049 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
2050 IPP_TAG_RANGE
)) != NULL
)
2053 * Custom size range; save this as the custom size value with default
2054 * margins, then continue; we'll capture the real margins below...
2059 dinfo
->min_size
.width
= x_dimension
->values
[0].range
.lower
;
2060 dinfo
->min_size
.length
= y_dimension
->values
[0].range
.lower
;
2061 dinfo
->min_size
.left
=
2062 dinfo
->min_size
.right
= 635; /* Default 1/4" side margins */
2063 dinfo
->min_size
.top
=
2064 dinfo
->min_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
2066 dinfo
->max_size
.width
= x_dimension
->values
[0].range
.upper
;
2067 dinfo
->max_size
.length
= y_dimension
->values
[0].range
.upper
;
2068 dinfo
->max_size
.left
=
2069 dinfo
->max_size
.right
= 635; /* Default 1/4" side margins */
2070 dinfo
->max_size
.top
=
2071 dinfo
->max_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
2076 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
))
2077 mdb
.color
= media_attr
->values
[0].string
.text
;
2079 if ((media_attr
= ippFindAttribute(val
->collection
, "media-info", IPP_TAG_TEXT
)) != NULL
)
2080 mdb
.info
= media_attr
->values
[0].string
.text
;
2082 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
))
2083 mdb
.key
= media_attr
->values
[0].string
.text
;
2085 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
))
2086 mdb
.size_name
= media_attr
->values
[0].string
.text
;
2088 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
))
2089 mdb
.source
= media_attr
->values
[0].string
.text
;
2091 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
))
2092 mdb
.type
= media_attr
->values
[0].string
.text
;
2094 if ((media_attr
= ippFindAttribute(val
->collection
, "media-bottom-margin", IPP_TAG_INTEGER
)) != NULL
)
2095 mdb
.bottom
= media_attr
->values
[0].integer
;
2097 if ((media_attr
= ippFindAttribute(val
->collection
, "media-left-margin", IPP_TAG_INTEGER
)) != NULL
)
2098 mdb
.left
= media_attr
->values
[0].integer
;
2100 if ((media_attr
= ippFindAttribute(val
->collection
, "media-right-margin", IPP_TAG_INTEGER
)) != NULL
)
2101 mdb
.right
= media_attr
->values
[0].integer
;
2103 if ((media_attr
= ippFindAttribute(val
->collection
, "media-top-margin", IPP_TAG_INTEGER
)) != NULL
)
2104 mdb
.top
= media_attr
->values
[0].integer
;
2108 if (!mdb
.size_name
&& (pwg
= pwgMediaForSize(mdb
.width
, mdb
.length
)) != NULL
)
2109 mdb
.size_name
= (char *)pwg
->pwg
;
2114 * Use a CUPS-specific identifier if we don't have a size name...
2117 if (flags
& CUPS_MEDIA_FLAGS_READY
)
2118 snprintf(media_key
, sizeof(media_key
), "cups-media-ready-%d", i
+ 1);
2120 snprintf(media_key
, sizeof(media_key
), "cups-media-%d", i
+ 1);
2122 else if (mdb
.source
)
2125 * Generate key using size name, source, and type (if set)...
2129 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s", mdb
.size_name
, mdb
.source
, mdb
.type
);
2131 snprintf(media_key
, sizeof(media_key
), "%s_%s", mdb
.size_name
, mdb
.source
);
2136 * Generate key using size name and type...
2139 snprintf(media_key
, sizeof(media_key
), "%s_%s", mdb
.size_name
, mdb
.type
);
2144 * Key is just the size name...
2147 strlcpy(media_key
, mdb
.size_name
, sizeof(media_key
));
2151 * Append "_borderless" for borderless media...
2154 if (!mdb
.bottom
&& !mdb
.left
&& !mdb
.right
&& !mdb
.top
)
2155 strlcat(media_key
, "_borderless", sizeof(media_key
));
2157 mdb
.key
= media_key
;
2160 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
));
2162 cupsArrayAdd(db
, &mdb
);
2167 if ((media_attr
= ippFindAttribute(custom
->collection
,
2168 "media-bottom-margin",
2169 IPP_TAG_INTEGER
)) != NULL
)
2171 dinfo
->min_size
.top
=
2172 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
2175 if ((media_attr
= ippFindAttribute(custom
->collection
,
2176 "media-left-margin",
2177 IPP_TAG_INTEGER
)) != NULL
)
2179 dinfo
->min_size
.left
=
2180 dinfo
->max_size
.left
= media_attr
->values
[0].integer
;
2183 if ((media_attr
= ippFindAttribute(custom
->collection
,
2184 "media-right-margin",
2185 IPP_TAG_INTEGER
)) != NULL
)
2187 dinfo
->min_size
.right
=
2188 dinfo
->max_size
.right
= media_attr
->values
[0].integer
;
2191 if ((media_attr
= ippFindAttribute(custom
->collection
,
2193 IPP_TAG_INTEGER
)) != NULL
)
2195 dinfo
->min_size
.top
=
2196 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
2200 else if (media_attr
&&
2201 (media_attr
->value_tag
== IPP_TAG_NAME
||
2202 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
2203 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
2205 memset(&mdb
, 0, sizeof(mdb
));
2208 mdb
.right
= 635; /* Default 1/4" side margins */
2210 mdb
.bottom
= 1270; /* Default 1/2" top/bottom margins */
2212 for (i
= media_attr
->num_values
, val
= media_attr
->values
;
2216 if ((pwg
= pwgMediaForPWG(val
->string
.text
)) == NULL
)
2217 if ((pwg
= pwgMediaForLegacy(val
->string
.text
)) == NULL
)
2219 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
2224 mdb
.width
= pwg
->width
;
2225 mdb
.length
= pwg
->length
;
2227 if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
2228 !strncmp(val
->string
.text
, "custom_min_", 11))
2230 mdb
.size_name
= NULL
;
2231 dinfo
->min_size
= mdb
;
2233 else if (flags
!= CUPS_MEDIA_FLAGS_READY
&&
2234 !strncmp(val
->string
.text
, "custom_max_", 11))
2236 mdb
.size_name
= NULL
;
2237 dinfo
->max_size
= mdb
;
2241 mdb
.size_name
= val
->string
.text
;
2243 cupsArrayAdd(db
, &mdb
);
2251 * 'cups_free_media_cb()' - Free a media entry.
2256 _cups_media_db_t
*mdb
) /* I - Media entry to free */
2259 _cupsStrFree(mdb
->color
);
2261 _cupsStrFree(mdb
->key
);
2263 _cupsStrFree(mdb
->info
);
2265 _cupsStrFree(mdb
->size_name
);
2267 _cupsStrFree(mdb
->source
);
2269 _cupsStrFree(mdb
->type
);
2276 * 'cups_get_media_db()' - Lookup the media entry for a given size.
2279 static int /* O - 1 on match, 0 on failure */
2280 cups_get_media_db(http_t
*http
, /* I - Connection to destination */
2281 cups_dinfo_t
*dinfo
, /* I - Destination information */
2282 pwg_media_t
*pwg
, /* I - PWG media info */
2283 unsigned flags
, /* I - Media matching flags */
2284 cups_size_t
*size
) /* O - Media size/margin/name info */
2286 cups_array_t
*db
; /* Which media database to query */
2287 _cups_media_db_t
*mdb
, /* Current media database entry */
2288 *best
= NULL
, /* Best matching entry */
2289 key
; /* Search key */
2293 * Create the media database as needed...
2296 if (flags
& CUPS_MEDIA_FLAGS_READY
)
2298 cups_update_ready(http
, dinfo
);
2299 db
= dinfo
->ready_db
;
2303 if (!dinfo
->media_db
)
2304 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
);
2306 db
= dinfo
->media_db
;
2313 memset(&key
, 0, sizeof(key
));
2314 key
.width
= pwg
->width
;
2315 key
.length
= pwg
->length
;
2317 if ((mdb
= cupsArrayFind(db
, &key
)) != NULL
)
2320 * Found an exact match, let's figure out the best margins for the flags
2326 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
2329 * Look for the smallest margins...
2332 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 || best
->bottom
!= 0)
2334 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2335 mdb
&& !cups_compare_media_db(mdb
, &key
);
2336 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2338 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
2339 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
)
2342 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
2350 * If we need an exact match, return no-match if the size is not
2354 if ((flags
& CUPS_MEDIA_FLAGS_EXACT
) &&
2355 (best
->left
|| best
->right
|| best
->top
|| best
->bottom
))
2358 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
2361 * Look for the largest margins...
2364 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2365 mdb
&& !cups_compare_media_db(mdb
, &key
);
2366 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2368 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
2369 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
&&
2370 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2377 * Look for the smallest non-zero margins...
2380 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2381 mdb
&& !cups_compare_media_db(mdb
, &key
);
2382 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2384 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
2385 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) || best
->right
== 0) &&
2386 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
2387 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) || best
->bottom
== 0) &&
2388 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2393 else if (flags
& CUPS_MEDIA_FLAGS_EXACT
)
2396 * See if we can do this as a custom size...
2399 if (pwg
->width
< dinfo
->min_size
.width
||
2400 pwg
->width
> dinfo
->max_size
.width
||
2401 pwg
->length
< dinfo
->min_size
.length
||
2402 pwg
->length
> dinfo
->max_size
.length
)
2403 return (0); /* Out of range */
2405 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) &&
2406 (dinfo
->min_size
.left
> 0 || dinfo
->min_size
.right
> 0 ||
2407 dinfo
->min_size
.top
> 0 || dinfo
->min_size
.bottom
> 0))
2408 return (0); /* Not borderless */
2410 key
.size_name
= (char *)pwg
->pwg
;
2411 key
.bottom
= dinfo
->min_size
.bottom
;
2412 key
.left
= dinfo
->min_size
.left
;
2413 key
.right
= dinfo
->min_size
.right
;
2414 key
.top
= dinfo
->min_size
.top
;
2418 else if (pwg
->width
>= dinfo
->min_size
.width
&&
2419 pwg
->width
<= dinfo
->max_size
.width
&&
2420 pwg
->length
>= dinfo
->min_size
.length
&&
2421 pwg
->length
<= dinfo
->max_size
.length
)
2424 * Map to custom size...
2427 key
.size_name
= (char *)pwg
->pwg
;
2428 key
.bottom
= dinfo
->min_size
.bottom
;
2429 key
.left
= dinfo
->min_size
.left
;
2430 key
.right
= dinfo
->min_size
.right
;
2431 key
.top
= dinfo
->min_size
.top
;
2438 * Find a close size...
2441 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
);
2443 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2444 if (cups_is_close_media_db(mdb
, &key
))
2452 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
2455 * Look for the smallest margins...
2458 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 ||
2461 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2462 mdb
&& cups_is_close_media_db(mdb
, &key
);
2463 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2465 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
2466 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
&&
2467 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2470 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
2477 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
2480 * Look for the largest margins...
2483 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2484 mdb
&& cups_is_close_media_db(mdb
, &key
);
2485 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2487 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
2488 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
&&
2489 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2496 * Look for the smallest non-zero margins...
2499 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(db
);
2500 mdb
&& cups_is_close_media_db(mdb
, &key
);
2501 mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
2503 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
2504 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) ||
2505 best
->right
== 0) &&
2506 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
2507 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) ||
2508 best
->bottom
== 0) &&
2509 (mdb
->bottom
!= best
->bottom
|| mdb
->left
!= best
->left
|| mdb
->right
!= best
->right
|| mdb
->top
!= best
->top
))
2518 * Return the matching size...
2522 strlcpy(size
->media
, best
->key
, sizeof(size
->media
));
2523 else if (best
->size_name
)
2524 strlcpy(size
->media
, best
->size_name
, sizeof(size
->media
));
2525 else if (pwg
&& pwg
->pwg
)
2526 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
2528 strlcpy(size
->media
, "unknown", sizeof(size
->media
));
2530 size
->width
= best
->width
;
2531 size
->length
= best
->length
;
2532 size
->bottom
= best
->bottom
;
2533 size
->left
= best
->left
;
2534 size
->right
= best
->right
;
2535 size
->top
= best
->top
;
2545 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
2546 * close to the same size.
2548 * Currently we use 5 points (from PostScript) as the matching range...
2551 static int /* O - 1 if the sizes are close */
2552 cups_is_close_media_db(
2553 _cups_media_db_t
*a
, /* I - First media entries */
2554 _cups_media_db_t
*b
) /* I - Second media entries */
2556 int dwidth
, /* Difference in width */
2557 dlength
; /* Difference in length */
2560 dwidth
= a
->width
- b
->width
;
2561 dlength
= a
->length
- b
->length
;
2563 return (dwidth
>= -176 && dwidth
<= 176 &&
2564 dlength
>= -176 && dlength
<= 176);
2569 * 'cups_test_constraints()' - Test constraints.
2572 static cups_array_t
* /* O - Active constraints */
2573 cups_test_constraints(
2574 cups_dinfo_t
*dinfo
, /* I - Destination information */
2575 const char *new_option
, /* I - Newly selected option */
2576 const char *new_value
, /* I - Newly selected value */
2577 int num_options
, /* I - Number of options */
2578 cups_option_t
*options
, /* I - Options */
2579 int *num_conflicts
, /* O - Number of conflicting options */
2580 cups_option_t
**conflicts
) /* O - Conflicting options */
2582 int i
, /* Looping var */
2583 count
, /* Number of values */
2584 match
; /* Value matches? */
2585 int num_matching
; /* Number of matching options */
2586 cups_option_t
*matching
; /* Matching options */
2587 _cups_dconstres_t
*c
; /* Current constraint */
2588 cups_array_t
*active
= NULL
; /* Active constraints */
2589 ipp_t
*col
; /* Collection value */
2590 ipp_attribute_t
*attr
; /* Current attribute */
2591 _ipp_value_t
*attrval
; /* Current attribute value */
2592 const char *value
; /* Current value */
2593 char temp
[1024]; /* Temporary string */
2594 int int_value
; /* Integer value */
2595 int xres_value
, /* Horizontal resolution */
2596 yres_value
; /* Vertical resolution */
2597 ipp_res_t units_value
; /* Resolution units */
2600 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(dinfo
->constraints
);
2602 c
= (_cups_dconstres_t
*)cupsArrayNext(dinfo
->constraints
))
2607 for (attr
= ippFirstAttribute(c
->collection
);
2609 attr
= ippNextAttribute(c
->collection
))
2612 * Get the value for the current attribute in the constraint...
2615 if (new_option
&& new_value
&& !strcmp(attr
->name
, new_option
))
2617 else if ((value
= cupsGetOption(attr
->name
, num_options
, options
)) == NULL
)
2618 value
= cupsGetOption(attr
->name
, dinfo
->num_defaults
, dinfo
->defaults
);
2623 * Not set so this constraint does not apply...
2631 switch (attr
->value_tag
)
2633 case IPP_TAG_INTEGER
:
2635 int_value
= atoi(value
);
2637 for (i
= attr
->num_values
, attrval
= attr
->values
;
2641 if (attrval
->integer
== int_value
)
2649 case IPP_TAG_BOOLEAN
:
2650 int_value
= !strcmp(value
, "true");
2652 for (i
= attr
->num_values
, attrval
= attr
->values
;
2656 if (attrval
->boolean
== int_value
)
2664 case IPP_TAG_RANGE
:
2665 int_value
= atoi(value
);
2667 for (i
= attr
->num_values
, attrval
= attr
->values
;
2671 if (int_value
>= attrval
->range
.lower
&&
2672 int_value
<= attrval
->range
.upper
)
2680 case IPP_TAG_RESOLUTION
:
2681 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
2683 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
2686 yres_value
= xres_value
;
2689 if (!strcmp(temp
, "dpi"))
2690 units_value
= IPP_RES_PER_INCH
;
2691 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
2692 units_value
= IPP_RES_PER_CM
;
2696 for (i
= attr
->num_values
, attrval
= attr
->values
;
2700 if (attrval
->resolution
.xres
== xres_value
&&
2701 attrval
->resolution
.yres
== yres_value
&&
2702 attrval
->resolution
.units
== units_value
)
2712 case IPP_TAG_KEYWORD
:
2713 case IPP_TAG_CHARSET
:
2715 case IPP_TAG_URISCHEME
:
2716 case IPP_TAG_MIMETYPE
:
2717 case IPP_TAG_LANGUAGE
:
2718 case IPP_TAG_TEXTLANG
:
2719 case IPP_TAG_NAMELANG
:
2720 for (i
= attr
->num_values
, attrval
= attr
->values
;
2724 if (!strcmp(attrval
->string
.text
, value
))
2732 case IPP_TAG_BEGIN_COLLECTION
:
2734 _cupsEncodeOption(col
, IPP_TAG_ZERO
, NULL
, ippGetName(attr
), value
);
2736 for (i
= 0, count
= ippGetCount(attr
); i
< count
; i
++)
2738 if (cups_collection_contains(col
, ippGetCollection(attr
, i
)))
2755 num_matching
= cupsAddOption(attr
->name
, value
, num_matching
, &matching
);
2761 active
= cupsArrayNew(NULL
, NULL
);
2763 cupsArrayAdd(active
, c
);
2765 if (num_conflicts
&& conflicts
)
2767 cups_option_t
*moption
; /* Matching option */
2769 for (i
= num_matching
, moption
= matching
; i
> 0; i
--, moption
++)
2770 *num_conflicts
= cupsAddOption(moption
->name
, moption
->value
, *num_conflicts
, conflicts
);
2774 cupsFreeOptions(num_matching
, matching
);
2782 * 'cups_update_ready()' - Update xxx-ready attributes for the printer.
2786 cups_update_ready(http_t
*http
, /* I - Connection to destination */
2787 cups_dinfo_t
*dinfo
) /* I - Destination information */
2789 ipp_t
*request
; /* Get-Printer-Attributes request */
2790 static const char * const pattrs
[] = /* Printer attributes we want */
2792 "finishings-col-ready",
2794 "job-finishings-col-ready",
2795 "job-finishings-ready",
2802 * Don't update more than once every 30 seconds...
2805 if ((time(NULL
) - dinfo
->ready_time
) < _CUPS_MEDIA_READY_TTL
)
2809 * Free any previous results...
2812 if (dinfo
->cached_flags
& CUPS_MEDIA_FLAGS_READY
)
2814 cupsArrayDelete(dinfo
->cached_db
);
2815 dinfo
->cached_db
= NULL
;
2816 dinfo
->cached_flags
= CUPS_MEDIA_FLAGS_DEFAULT
;
2819 ippDelete(dinfo
->ready_attrs
);
2820 dinfo
->ready_attrs
= NULL
;
2822 cupsArrayDelete(dinfo
->ready_db
);
2823 dinfo
->ready_db
= NULL
;
2826 * Query the xxx-ready values...
2829 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
2830 ippSetVersion(request
, dinfo
->version
/ 10, dinfo
->version
% 10);
2832 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
2834 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
2836 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "requested-attributes", (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
2838 dinfo
->ready_attrs
= cupsDoRequest(http
, request
, dinfo
->resource
);
2841 * Update the ready media database...
2844 cups_create_media_db(dinfo
, CUPS_MEDIA_FLAGS_READY
);
2847 * Update last lookup time and return...
2850 dinfo
->ready_time
= time(NULL
);