4 * Destination option/media support for CUPS.
6 * Copyright 2012 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * cupsCheckDestSupported() - Check that the option and value are supported
20 * cupsCopyDestConflicts() - Get conflicts and resolutions for a new
22 * cupsCopyDestInfo() - Get the supported values/capabilities for the
24 * cupsFreeDestInfo() - Free destination information obtained using
25 * @link cupsCopyDestInfo@.
26 * cupsGetDestMediaByName() - Get media names, dimensions, and margins.
27 * cupsGetDestMediaBySize() - Get media names, dimensions, and margins.
28 * cups_add_dconstres() - Add a constraint or resolver to an array.
29 * cups_compare_dconstres() - Compare to resolver entries.
30 * cups_compare_media_db() - Compare two media entries.
31 * cups_copy_media_db() - Copy a media entry.
32 * cups_create_constraints() - Create the constraints and resolvers arrays.
33 * cups_create_defaults() - Create the -default option array.
34 * cups_create_media_db() - Create the media database.
35 * cups_free_media_cb() - Free a media entry.
36 * cups_get_media_db() - Lookup the media entry for a given size.
37 * cups_is_close_media_db() - Compare two media entries to see if they are
38 * close to the same size.
39 * cups_test_constraints() - Test constraints.
43 * Include necessary headers...
46 #include "cups-private.h"
53 static void cups_add_dconstres(cups_array_t
*a
, ipp_t
*collection
);
54 static int cups_compare_dconstres(_cups_dconstres_t
*a
,
55 _cups_dconstres_t
*b
);
56 static int cups_compare_media_db(_cups_media_db_t
*a
,
58 static _cups_media_db_t
*cups_copy_media_db(_cups_media_db_t
*mdb
);
59 static void cups_create_constraints(cups_dinfo_t
*dinfo
);
60 static void cups_create_defaults(cups_dinfo_t
*dinfo
);
61 static void cups_create_media_db(cups_dinfo_t
*dinfo
);
62 static void cups_free_media_db(_cups_media_db_t
*mdb
);
63 static int cups_get_media_db(cups_dinfo_t
*dinfo
,
64 _pwg_media_t
*pwg
, unsigned flags
,
66 static int cups_is_close_media_db(_cups_media_db_t
*a
,
68 static cups_array_t
*cups_test_constraints(cups_dinfo_t
*dinfo
,
69 const char *new_option
,
70 const char *new_value
,
72 cups_option_t
*options
,
74 cups_option_t
**conflicts
);
78 * 'cupsCheckDestSupported()' - Check that the option and value are supported
81 * Returns 1 if supported, 0 otherwise.
83 * @since CUPS 1.6/OS X 10.8@
86 int /* O - 1 if supported, 0 otherwise */
87 cupsCheckDestSupported(
88 http_t
*http
, /* I - Connection to destination */
89 cups_dest_t
*dest
, /* I - Destination */
90 cups_dinfo_t
*dinfo
, /* I - Destination information */
91 const char *option
, /* I - Option */
92 const char *value
) /* I - Value */
94 int i
; /* Looping var */
95 char temp
[1024]; /* Temporary string */
96 int int_value
; /* Integer value */
97 int xres_value
, /* Horizontal resolution */
98 yres_value
; /* Vertical resolution */
99 ipp_res_t units_value
; /* Resolution units */
100 ipp_attribute_t
*attr
; /* Attribute */
101 _ipp_value_t
*attrval
; /* Current attribute value */
105 * Range check input...
108 if (!http
|| !dest
|| !dinfo
|| !option
|| !value
)
112 * Lookup the attribute...
115 if (strstr(option
, "-supported"))
116 attr
= ippFindAttribute(dinfo
->attrs
, option
, IPP_TAG_ZERO
);
119 snprintf(temp
, sizeof(temp
), "%s-supported", option
);
120 attr
= ippFindAttribute(dinfo
->attrs
, temp
, IPP_TAG_ZERO
);
130 if (!strcmp(option
, "media") && !strncmp(value
, "custom_", 7))
133 * Check range of custom media sizes...
136 _pwg_media_t
*pwg
; /* Current PWG media size info */
137 int min_width
, /* Minimum width */
138 min_length
, /* Minimum length */
139 max_width
, /* Maximum width */
140 max_length
; /* Maximum length */
143 * Get the minimum and maximum size...
146 min_width
= min_length
= INT_MAX
;
147 max_width
= max_length
= 0;
149 for (i
= attr
->num_values
, attrval
= attr
->values
;
153 if (!strncmp(attrval
->string
.text
, "custom_min_", 11) &&
154 (pwg
= _pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
156 min_width
= pwg
->width
;
157 min_length
= pwg
->length
;
159 else if (!strncmp(attrval
->string
.text
, "custom_max_", 11) &&
160 (pwg
= _pwgMediaForPWG(attrval
->string
.text
)) != NULL
)
162 max_width
= pwg
->width
;
163 max_length
= pwg
->length
;
171 if (min_width
< INT_MAX
&& max_width
> 0 &&
172 (pwg
= _pwgMediaForPWG(value
)) != NULL
&&
173 pwg
->width
>= min_width
&& pwg
->width
<= max_width
&&
174 pwg
->length
>= min_length
&& pwg
->length
<= max_length
)
180 * Check literal values...
183 switch (attr
->value_tag
)
185 case IPP_TAG_INTEGER
:
187 int_value
= atoi(value
);
189 for (i
= 0; i
< attr
->num_values
; i
++)
190 if (attr
->values
[i
].integer
== int_value
)
194 case IPP_TAG_BOOLEAN
:
195 return (attr
->values
[0].boolean
);
198 int_value
= atoi(value
);
200 for (i
= 0; i
< attr
->num_values
; i
++)
201 if (int_value
>= attr
->values
[i
].range
.lower
&&
202 int_value
<= attr
->values
[i
].range
.upper
)
206 case IPP_TAG_RESOLUTION
:
207 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
209 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
212 yres_value
= xres_value
;
215 if (!strcmp(temp
, "dpi"))
216 units_value
= IPP_RES_PER_INCH
;
217 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
218 units_value
= IPP_RES_PER_CM
;
222 for (i
= attr
->num_values
, attrval
= attr
->values
;
226 if (attrval
->resolution
.xres
== xres_value
&&
227 attrval
->resolution
.yres
== yres_value
&&
228 attrval
->resolution
.units
== units_value
)
235 case IPP_TAG_KEYWORD
:
236 case IPP_TAG_CHARSET
:
238 case IPP_TAG_URISCHEME
:
239 case IPP_TAG_MIMETYPE
:
240 case IPP_TAG_LANGUAGE
:
241 case IPP_TAG_TEXTLANG
:
242 case IPP_TAG_NAMELANG
:
243 for (i
= 0; i
< attr
->num_values
; i
++)
244 if (!strcmp(attr
->values
[i
].string
.text
, value
))
254 * If we get there the option+value is not supported...
262 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
265 * "num_options" and "options" represent the currently selected options by the
266 * user. "new_option" and "new_value" are the setting the user has just
269 * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
270 * there was an unrecoverable error such as a resolver loop.
272 * If "num_conflicts" and "conflicts" are not NULL, they are set to contain the
273 * list of conflicting option/value pairs. Similarly, if "num_resolved" and
274 * "resolved" are not NULL they will be set to the list of changes needed to
275 * resolve the conflict.
277 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
278 * to 0 and NULL, respectively, then the conflict cannot be resolved.
280 * @since CUPS 1.6/OS X 10.8@
283 int /* O - 1 if there is a conflict, 0 if none, -1 on error */
284 cupsCopyDestConflicts(
285 http_t
*http
, /* I - Connection to destination */
286 cups_dest_t
*dest
, /* I - Destination */
287 cups_dinfo_t
*dinfo
, /* I - Destination information */
288 int num_options
, /* I - Number of current options */
289 cups_option_t
*options
, /* I - Current options */
290 const char *new_option
, /* I - New option */
291 const char *new_value
, /* I - New value */
292 int *num_conflicts
, /* O - Number of conflicting options */
293 cups_option_t
**conflicts
, /* O - Conflicting options */
294 int *num_resolved
, /* O - Number of options to resolve */
295 cups_option_t
**resolved
) /* O - Resolved options */
297 int i
, /* Looping var */
298 have_conflicts
= 0, /* Do we have conflicts? */
299 changed
, /* Did we change something? */
300 tries
, /* Number of tries for resolution */
301 num_myconf
= 0, /* My number of conflicting options */
302 num_myres
= 0; /* My number of resolved options */
303 cups_option_t
*myconf
= NULL
, /* My conflicting options */
304 *myres
= NULL
, /* My resolved options */
305 *myoption
, /* My current option */
306 *option
; /* Current option */
307 cups_array_t
*active
, /* Active conflicts */
308 *pass
= NULL
, /* Resolvers for this pass */
309 *resolvers
= NULL
, /* Resolvers we have used */
310 *test
; /* Test array for conflicts */
311 _cups_dconstres_t
*c
, /* Current constraint */
312 *r
; /* Current resolver */
313 ipp_attribute_t
*attr
; /* Current attribute */
314 char value
[2048]; /* Current attribute value as string */
315 const char *myvalue
; /* Current value of an option */
319 * Clear returned values...
335 * Range check input...
338 if (!http
|| !dest
|| !dinfo
||
339 (num_conflicts
!= NULL
) != (conflicts
!= NULL
) ||
340 (num_resolved
!= NULL
) != (resolved
!= NULL
))
344 * Load constraints as needed...
347 if (!dinfo
->constraints
)
348 cups_create_constraints(dinfo
);
350 if (cupsArrayCount(dinfo
->constraints
) == 0)
353 if (!dinfo
->num_defaults
)
354 cups_create_defaults(dinfo
);
357 * If we are resolving, create a shadow array...
362 for (i
= num_options
, option
= options
; i
> 0; i
--, option
++)
363 num_myres
= cupsAddOption(option
->name
, option
->value
, num_myres
, &myres
);
365 if (new_option
&& new_value
)
366 num_myres
= cupsAddOption(new_option
, new_value
, num_myres
, &myres
);
370 num_myres
= num_options
;
375 * Check for any conflicts...
379 pass
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
381 for (tries
= 0; tries
< 100; tries
++)
384 * Check for any conflicts...
387 if (num_conflicts
|| num_resolved
)
389 cupsFreeOptions(num_myconf
, myconf
);
393 active
= cups_test_constraints(dinfo
, new_option
, new_value
,
394 num_myres
, myres
, &num_myconf
,
398 active
= cups_test_constraints(dinfo
, new_option
, new_value
, num_myres
,
401 have_conflicts
= (active
!= NULL
);
403 if (!active
|| !num_resolved
)
404 break; /* All done */
407 * Scan the constraints that were triggered to apply resolvers...
411 resolvers
= cupsArrayNew((cups_array_func_t
)cups_compare_dconstres
, NULL
);
413 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(active
), changed
= 0;
415 c
= (_cups_dconstres_t
*)cupsArrayNext(active
))
417 if (cupsArrayFind(pass
, c
))
418 continue; /* Already applied this resolver... */
420 if (cupsArrayFind(resolvers
, c
))
422 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
428 if ((r
= cupsArrayFind(dinfo
->resolvers
, c
)) == NULL
)
430 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
437 * Add the options from the resolver...
440 cupsArrayAdd(pass
, r
);
441 cupsArrayAdd(resolvers
, r
);
443 for (attr
= ippFirstAttribute(r
->collection
);
445 attr
= ippNextAttribute(r
->collection
))
447 if (new_option
&& !strcmp(attr
->name
, new_option
))
448 continue; /* Ignore this if we just changed it */
450 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
451 continue; /* Ignore if the value is too long */
453 if ((test
= cups_test_constraints(dinfo
, attr
->name
, value
, num_myres
,
454 myres
, NULL
, NULL
)) == NULL
)
457 * That worked, flag it...
463 cupsArrayDelete(test
);
466 * Add the option/value from the resolver regardless of whether it
467 * worked; this makes sure that we can cascade several changes to
468 * make things resolve...
471 num_myres
= cupsAddOption(attr
->name
, value
, num_myres
, &myres
);
477 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
482 cupsArrayClear(pass
);
484 cupsArrayDelete(active
);
490 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
496 * Copy resolved options as needed...
501 for (i
= num_myres
, myoption
= myres
; i
> 0; i
--, myoption
++)
503 if ((myvalue
= cupsGetOption(myoption
->name
, num_options
,
505 strcmp(myvalue
, myoption
->value
))
507 if (new_option
&& !strcmp(new_option
, myoption
->name
) &&
508 new_value
&& !strcmp(new_value
, myoption
->value
))
511 *num_resolved
= cupsAddOption(myoption
->name
, myoption
->value
,
512 *num_resolved
, resolved
);
523 cupsArrayDelete(active
);
524 cupsArrayDelete(pass
);
525 cupsArrayDelete(resolvers
);
530 * Free shadow copy of options...
533 cupsFreeOptions(num_myres
, myres
);
539 * Return conflicting options to caller...
542 *num_conflicts
= num_myconf
;
548 * Free conflicting options...
551 cupsFreeOptions(num_myconf
, myconf
);
554 return (have_conflicts
);
559 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
562 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
563 * value. @code NULL@ is returned on error.
565 * @since CUPS 1.6/OS X 10.8@
568 cups_dinfo_t
* /* O - Destination information */
570 http_t
*http
, /* I - Connection to destination */
571 cups_dest_t
*dest
) /* I - Destination */
573 cups_dinfo_t
*dinfo
; /* Destination information */
574 ipp_t
*request
, /* Get-Printer-Attributes request */
575 *response
; /* Supported attributes */
576 int tries
, /* Number of tries so far */
577 delay
, /* Current retry delay */
578 prev_delay
; /* Next retry delay */
579 const char *uri
; /* Printer URI */
580 char resource
[1024]; /* Resource path */
581 int version
; /* IPP version */
582 ipp_status_t status
; /* Status of request */
583 static const char * const requested_attrs
[] =
584 { /* Requested attributes */
586 "media-col-database",
587 "printer-description"
591 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", http
, dest
,
592 dest
? dest
->name
: ""));
595 * Range check input...
602 * Get the printer URI and resource path...
605 if ((uri
= _cupsGetDestResource(dest
, resource
, sizeof(resource
))) == NULL
)
609 * Get the supported attributes...
620 * Send a Get-Printer-Attributes request...
623 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
624 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
626 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
628 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
629 "requested-attributes",
630 (int)(sizeof(requested_attrs
) / sizeof(requested_attrs
[0])),
631 NULL
, requested_attrs
);
632 response
= cupsDoRequest(http
, request
, resource
);
633 status
= cupsLastError();
635 if (status
> IPP_OK_SUBST
)
637 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
638 "returned %s (%s)", dest
->name
, ippErrorString(status
),
639 cupsLastErrorString()));
644 if (status
== IPP_VERSION_NOT_SUPPORTED
&& version
> 11)
646 else if (status
== IPP_PRINTER_BUSY
)
650 delay
= _cupsNextDelay(delay
, &prev_delay
);
658 while (!response
&& tries
< 10);
664 * Allocate a cups_dinfo_t structure and return it...
667 if ((dinfo
= calloc(1, sizeof(cups_dinfo_t
))) == NULL
)
669 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
), 0);
675 dinfo
->resource
= _cupsStrAlloc(resource
);
676 dinfo
->attrs
= response
;
683 * 'cupsFreeDestInfo()' - Free destination information obtained using
684 * @link cupsCopyDestInfo@.
688 cupsFreeDestInfo(cups_dinfo_t
*dinfo
) /* I - Destination information */
691 * Range check input...
698 * Free memory and return...
701 _cupsStrFree(dinfo
->resource
);
703 cupsArrayDelete(dinfo
->constraints
);
704 cupsArrayDelete(dinfo
->resolvers
);
706 cupsArrayDelete(dinfo
->localizations
);
708 cupsArrayDelete(dinfo
->media_db
);
710 ippDelete(dinfo
->attrs
);
717 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
719 * The "media" string is a PWG media name, while "width" and "length" are the
720 * dimensions in hundredths of millimeters. "flags" provides some matching
721 * guidance (multiple flags can be combined):
723 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer
724 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size
725 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing
726 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size
727 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
728 * size amongst the "ready" media.
730 * The matching result (if any) is returned in the "cups_size_t" structure.
732 * Returns 1 when there is a match and 0 if there is not a match.
734 * @since CUPS 1.6/OS X 10.8@
737 int /* O - 1 on match, 0 on failure */
738 cupsGetDestMediaByName(
739 http_t
*http
, /* I - Connection to destination */
740 cups_dest_t
*dest
, /* I - Destination */
741 cups_dinfo_t
*dinfo
, /* I - Destination information */
742 const char *media
, /* I - Media name */
743 unsigned flags
, /* I - Media matching flags */
744 cups_size_t
*size
) /* O - Media size information */
746 _pwg_media_t
*pwg
; /* PWG media info */
750 * Range check input...
754 memset(size
, 0, sizeof(cups_size_t
));
756 if (!http
|| !dest
|| !dinfo
|| !media
|| !size
)
758 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(EINVAL
), 0);
763 * Lookup the media size name...
766 if ((pwg
= _pwgMediaForPWG(media
)) == NULL
)
767 if ((pwg
= _pwgMediaForLegacy(media
)) == NULL
)
769 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media
));
770 _cupsSetError(IPP_INTERNAL_ERROR
, _("Unknown media size name."), 1);
778 return (cups_get_media_db(dinfo
, pwg
, flags
, size
));
783 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
785 * The "media" string is a PWG media name, while "width" and "length" are the
786 * dimensions in hundredths of millimeters. "flags" provides some matching
787 * guidance (multiple flags can be combined):
789 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer
790 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size
791 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing
792 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size
793 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
794 * size amongst the "ready" media.
796 * The matching result (if any) is returned in the "cups_size_t" structure.
798 * Returns 1 when there is a match and 0 if there is not a match.
800 * @since CUPS 1.6/OS X 10.8@
803 int /* O - 1 on match, 0 on failure */
804 cupsGetDestMediaBySize(
805 http_t
*http
, /* I - Connection to destination */
806 cups_dest_t
*dest
, /* I - Destination */
807 cups_dinfo_t
*dinfo
, /* I - Destination information */
808 int width
, /* I - Media width in hundredths of
810 int length
, /* I - Media length in hundredths of
812 unsigned flags
, /* I - Media matching flags */
813 cups_size_t
*size
) /* O - Media size information */
815 _pwg_media_t
*pwg
; /* PWG media info */
819 * Range check input...
823 memset(size
, 0, sizeof(cups_size_t
));
825 if (!http
|| !dest
|| !dinfo
|| width
<= 0 || length
<= 0 || !size
)
827 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(EINVAL
), 0);
832 * Lookup the media size name...
835 if ((pwg
= _pwgMediaForSize(width
, length
)) == NULL
)
837 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width
,
839 _cupsSetError(IPP_INTERNAL_ERROR
, _("Invalid media size."), 1);
847 return (cups_get_media_db(dinfo
, pwg
, flags
, size
));
852 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
857 cups_array_t
*a
, /* I - Array */
858 ipp_t
*collection
) /* I - Collection value */
860 ipp_attribute_t
*attr
; /* Attribute */
861 _cups_dconstres_t
*temp
; /* Current constraint/resolver */
864 if ((attr
= ippFindAttribute(collection
, "resolver-name",
865 IPP_TAG_NAME
)) == NULL
)
868 if ((temp
= calloc(1, sizeof(_cups_dconstres_t
))) == NULL
)
871 temp
->name
= attr
->values
[0].string
.text
;
872 temp
->collection
= collection
;
874 cupsArrayAdd(a
, temp
);
879 * 'cups_compare_dconstres()' - Compare to resolver entries.
882 static int /* O - Result of comparison */
883 cups_compare_dconstres(
884 _cups_dconstres_t
*a
, /* I - First resolver */
885 _cups_dconstres_t
*b
) /* I - Second resolver */
887 return (strcmp(a
->name
, b
->name
));
892 * 'cups_compare_media_db()' - Compare two media entries.
895 static int /* O - Result of comparison */
896 cups_compare_media_db(
897 _cups_media_db_t
*a
, /* I - First media entries */
898 _cups_media_db_t
*b
) /* I - Second media entries */
900 int result
; /* Result of comparison */
903 if ((result
= a
->width
- b
->width
) == 0)
904 result
= a
->length
- b
->length
;
911 * 'cups_copy_media_db()' - Copy a media entry.
914 static _cups_media_db_t
* /* O - New media entry */
916 _cups_media_db_t
*mdb
) /* I - Media entry to copy */
918 _cups_media_db_t
*temp
; /* New media entry */
921 if ((temp
= calloc(1, sizeof(_cups_media_db_t
))) == NULL
)
925 temp
->color
= _cupsStrAlloc(mdb
->color
);
927 temp
->key
= _cupsStrAlloc(mdb
->key
);
929 temp
->info
= _cupsStrAlloc(mdb
->info
);
931 temp
->size_name
= _cupsStrAlloc(mdb
->size_name
);
933 temp
->source
= _cupsStrAlloc(mdb
->source
);
935 temp
->type
= _cupsStrAlloc(mdb
->type
);
937 temp
->width
= mdb
->width
;
938 temp
->length
= mdb
->length
;
939 temp
->bottom
= mdb
->bottom
;
940 temp
->left
= mdb
->left
;
941 temp
->right
= mdb
->right
;
942 temp
->top
= mdb
->top
;
949 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
953 cups_create_constraints(
954 cups_dinfo_t
*dinfo
) /* I - Destination information */
956 int i
; /* Looping var */
957 ipp_attribute_t
*attr
; /* Attribute */
958 _ipp_value_t
*val
; /* Current value */
961 dinfo
->constraints
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, NULL
,
962 (cups_afree_func_t
)free
);
963 dinfo
->resolvers
= cupsArrayNew3((cups_array_func_t
)cups_compare_dconstres
,
965 (cups_afree_func_t
)free
);
967 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-constraints-supported",
968 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
970 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
971 cups_add_dconstres(dinfo
->constraints
, val
->collection
);
974 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-resolvers-supported",
975 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
977 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
978 cups_add_dconstres(dinfo
->resolvers
, val
->collection
);
984 * 'cups_create_defaults()' - Create the -default option array.
986 * TODO: Need to support collection defaults...
990 cups_create_defaults(
991 cups_dinfo_t
*dinfo
) /* I - Destination information */
993 ipp_attribute_t
*attr
; /* Current attribute */
994 char name
[IPP_MAX_NAME
+ 1],
996 *nameptr
, /* Pointer into current name */
997 value
[2048]; /* Current value */
1001 * Iterate through the printer attributes looking for xxx-default and adding
1002 * xxx=value to the defaults option array.
1005 for (attr
= ippFirstAttribute(dinfo
->attrs
);
1007 attr
= ippNextAttribute(dinfo
->attrs
))
1009 if (!attr
->name
|| attr
->group_tag
!= IPP_TAG_PRINTER
)
1012 if (attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
)
1013 continue; /* TODO: STR #4096 */
1015 if ((nameptr
= attr
->name
+ strlen(attr
->name
) - 8) <= attr
->name
||
1016 strcmp(nameptr
, "-default"))
1019 strlcpy(name
, attr
->name
, sizeof(name
));
1020 if ((nameptr
= name
+ strlen(name
) - 8) <= name
||
1021 strcmp(nameptr
, "-default"))
1026 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
1029 dinfo
->num_defaults
= cupsAddOption(name
, value
, dinfo
->num_defaults
,
1036 * 'cups_create_media_db()' - Create the media database.
1040 cups_create_media_db(
1041 cups_dinfo_t
*dinfo
) /* I - Destination information */
1043 int i
; /* Looping var */
1044 _ipp_value_t
*val
; /* Current value */
1045 ipp_attribute_t
*media_col_db
, /* media-col-database */
1046 *media_attr
, /* media-xxx */
1047 *x_dimension
, /* x-dimension */
1048 *y_dimension
; /* y-dimension */
1049 _pwg_media_t
*pwg
; /* PWG media info */
1050 _cups_media_db_t mdb
; /* Media entry */
1053 dinfo
->media_db
= cupsArrayNew3((cups_array_func_t
)cups_compare_media_db
,
1055 (cups_acopy_func_t
)cups_copy_media_db
,
1056 (cups_afree_func_t
)cups_free_media_db
);
1057 dinfo
->min_size
.width
= INT_MAX
;
1058 dinfo
->min_size
.length
= INT_MAX
;
1059 dinfo
->max_size
.width
= 0;
1060 dinfo
->max_size
.length
= 0;
1062 if ((media_col_db
= ippFindAttribute(dinfo
->attrs
, "media-col-database",
1063 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1065 _ipp_value_t
*custom
= NULL
; /* Custom size range value */
1067 for (i
= media_col_db
->num_values
, val
= media_col_db
->values
;
1071 memset(&mdb
, 0, sizeof(mdb
));
1073 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size",
1074 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1076 ipp_t
*media_size
= media_attr
->values
[0].collection
;
1077 /* media-size collection value */
1079 if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1080 IPP_TAG_INTEGER
)) != NULL
&&
1081 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1082 IPP_TAG_INTEGER
)) != NULL
)
1084 mdb
.width
= x_dimension
->values
[0].integer
;
1085 mdb
.length
= y_dimension
->values
[0].integer
;
1087 else if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1088 IPP_TAG_RANGE
)) != NULL
&&
1089 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1090 IPP_TAG_RANGE
)) != NULL
)
1093 * Custom size range; save this as the custom size value with default
1094 * margins, then continue; we'll capture the real margins below...
1099 dinfo
->min_size
.width
= x_dimension
->values
[0].range
.lower
;
1100 dinfo
->min_size
.length
= y_dimension
->values
[0].range
.lower
;
1101 dinfo
->min_size
.left
=
1102 dinfo
->min_size
.right
= 635; /* Default 1/4" side margins */
1103 dinfo
->min_size
.top
=
1104 dinfo
->min_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1106 dinfo
->max_size
.width
= x_dimension
->values
[0].range
.upper
;
1107 dinfo
->max_size
.length
= y_dimension
->values
[0].range
.upper
;
1108 dinfo
->max_size
.left
=
1109 dinfo
->max_size
.right
= 635; /* Default 1/4" side margins */
1110 dinfo
->max_size
.top
=
1111 dinfo
->max_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1117 if ((media_attr
= ippFindAttribute(val
->collection
, "media-color",
1118 IPP_TAG_ZERO
)) != NULL
&&
1119 (media_attr
->value_tag
== IPP_TAG_NAME
||
1120 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1121 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1122 mdb
.color
= media_attr
->values
[0].string
.text
;
1124 if ((media_attr
= ippFindAttribute(val
->collection
, "media-info",
1125 IPP_TAG_TEXT
)) != NULL
)
1126 mdb
.info
= media_attr
->values
[0].string
.text
;
1128 if ((media_attr
= ippFindAttribute(val
->collection
, "media-key",
1129 IPP_TAG_ZERO
)) != NULL
&&
1130 (media_attr
->value_tag
== IPP_TAG_NAME
||
1131 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1132 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1133 mdb
.key
= media_attr
->values
[0].string
.text
;
1135 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size-name",
1136 IPP_TAG_ZERO
)) != NULL
&&
1137 (media_attr
->value_tag
== IPP_TAG_NAME
||
1138 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1139 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1140 mdb
.size_name
= media_attr
->values
[0].string
.text
;
1142 if ((media_attr
= ippFindAttribute(val
->collection
, "media-source",
1143 IPP_TAG_ZERO
)) != NULL
&&
1144 (media_attr
->value_tag
== IPP_TAG_NAME
||
1145 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1146 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1147 mdb
.source
= media_attr
->values
[0].string
.text
;
1149 if ((media_attr
= ippFindAttribute(val
->collection
, "media-type",
1150 IPP_TAG_ZERO
)) != NULL
&&
1151 (media_attr
->value_tag
== IPP_TAG_NAME
||
1152 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1153 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1154 mdb
.type
= media_attr
->values
[0].string
.text
;
1156 if ((media_attr
= ippFindAttribute(val
->collection
, "media-bottom-margin",
1157 IPP_TAG_INTEGER
)) != NULL
)
1158 mdb
.bottom
= media_attr
->values
[0].integer
;
1160 if ((media_attr
= ippFindAttribute(val
->collection
, "media-left-margin",
1161 IPP_TAG_INTEGER
)) != NULL
)
1162 mdb
.left
= media_attr
->values
[0].integer
;
1164 if ((media_attr
= ippFindAttribute(val
->collection
, "media-right-margin",
1165 IPP_TAG_INTEGER
)) != NULL
)
1166 mdb
.right
= media_attr
->values
[0].integer
;
1168 if ((media_attr
= ippFindAttribute(val
->collection
, "media-top-margin",
1169 IPP_TAG_INTEGER
)) != NULL
)
1170 mdb
.top
= media_attr
->values
[0].integer
;
1172 cupsArrayAdd(dinfo
->media_db
, &mdb
);
1177 if ((media_attr
= ippFindAttribute(custom
->collection
,
1178 "media-bottom-margin",
1179 IPP_TAG_INTEGER
)) != NULL
)
1181 dinfo
->min_size
.top
=
1182 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
1185 if ((media_attr
= ippFindAttribute(custom
->collection
,
1186 "media-left-margin",
1187 IPP_TAG_INTEGER
)) != NULL
)
1189 dinfo
->min_size
.left
=
1190 dinfo
->max_size
.left
= media_attr
->values
[0].integer
;
1193 if ((media_attr
= ippFindAttribute(custom
->collection
,
1194 "media-right-margin",
1195 IPP_TAG_INTEGER
)) != NULL
)
1197 dinfo
->min_size
.right
=
1198 dinfo
->max_size
.right
= media_attr
->values
[0].integer
;
1201 if ((media_attr
= ippFindAttribute(custom
->collection
,
1203 IPP_TAG_INTEGER
)) != NULL
)
1205 dinfo
->min_size
.top
=
1206 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
1210 else if ((media_attr
= ippFindAttribute(dinfo
->attrs
, "media-supported",
1211 IPP_TAG_ZERO
)) != NULL
&&
1212 (media_attr
->value_tag
== IPP_TAG_NAME
||
1213 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1214 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1216 memset(&mdb
, 0, sizeof(mdb
));
1219 mdb
.right
= 635; /* Default 1/4" side margins */
1221 mdb
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1223 for (i
= media_attr
->num_values
, val
= media_attr
->values
;
1227 if ((pwg
= _pwgMediaForPWG(val
->string
.text
)) == NULL
)
1228 if ((pwg
= _pwgMediaForLegacy(val
->string
.text
)) == NULL
)
1230 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
1235 mdb
.width
= pwg
->width
;
1236 mdb
.length
= pwg
->length
;
1238 if (!strncmp(val
->string
.text
, "custom_min_", 11))
1240 mdb
.size_name
= NULL
;
1241 dinfo
->min_size
= mdb
;
1243 else if (!strncmp(val
->string
.text
, "custom_max_", 11))
1245 mdb
.size_name
= NULL
;
1246 dinfo
->max_size
= mdb
;
1250 mdb
.size_name
= val
->string
.text
;
1252 cupsArrayAdd(dinfo
->media_db
, &mdb
);
1260 * 'cups_free_media_cb()' - Free a media entry.
1265 _cups_media_db_t
*mdb
) /* I - Media entry to free */
1268 _cupsStrFree(mdb
->color
);
1270 _cupsStrFree(mdb
->key
);
1272 _cupsStrFree(mdb
->info
);
1274 _cupsStrFree(mdb
->size_name
);
1276 _cupsStrFree(mdb
->source
);
1278 _cupsStrFree(mdb
->type
);
1285 * 'cups_get_media_db()' - Lookup the media entry for a given size.
1288 static int /* O - 1 on match, 0 on failure */
1289 cups_get_media_db(cups_dinfo_t
*dinfo
, /* I - Destination information */
1290 _pwg_media_t
*pwg
, /* I - PWG media info */
1291 unsigned flags
, /* I - Media matching flags */
1292 cups_size_t
*size
) /* O - Media size/margin/name info */
1294 _cups_media_db_t
*mdb
, /* Current media database entry */
1295 *best
= NULL
, /* Best matching entry */
1296 key
; /* Search key */
1300 * Create the media database as needed...
1303 if (!dinfo
->media_db
)
1304 cups_create_media_db(dinfo
);
1310 memset(&key
, 0, sizeof(key
));
1311 key
.width
= pwg
->width
;
1312 key
.length
= pwg
->length
;
1314 if ((mdb
= cupsArrayFind(dinfo
->media_db
, &key
)) != NULL
)
1317 * Found an exact match, let's figure out the best margins for the flags
1323 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1326 * Look for the smallest margins...
1329 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 ||
1332 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1333 mdb
&& !cups_compare_media_db(mdb
, &key
);
1334 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1336 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
1337 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
)
1340 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
1348 * If we need an exact match, return no-match if the size is not
1352 if ((flags
& CUPS_MEDIA_FLAGS_EXACT
) &&
1353 (best
->left
|| best
->right
|| best
->top
|| best
->bottom
))
1356 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1359 * Look for the largest margins...
1362 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1363 mdb
&& !cups_compare_media_db(mdb
, &key
);
1364 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1366 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
1367 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
)
1374 * Look for the smallest non-zero margins...
1377 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1378 mdb
&& !cups_compare_media_db(mdb
, &key
);
1379 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1381 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
1382 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) ||
1383 best
->right
== 0) &&
1384 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
1385 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) ||
1391 else if (flags
& CUPS_MEDIA_FLAGS_EXACT
)
1394 * See if we can do this as a custom size...
1397 if (pwg
->width
< dinfo
->min_size
.width
||
1398 pwg
->width
> dinfo
->max_size
.width
||
1399 pwg
->length
< dinfo
->min_size
.length
||
1400 pwg
->length
> dinfo
->max_size
.length
)
1401 return (0); /* Out of range */
1403 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) &&
1404 (dinfo
->min_size
.left
> 0 || dinfo
->min_size
.right
> 0 ||
1405 dinfo
->min_size
.top
> 0 || dinfo
->min_size
.bottom
> 0))
1406 return (0); /* Not borderless */
1408 key
.size_name
= (char *)pwg
->pwg
;
1409 key
.bottom
= dinfo
->min_size
.bottom
;
1410 key
.left
= dinfo
->min_size
.left
;
1411 key
.right
= dinfo
->min_size
.right
;
1412 key
.top
= dinfo
->min_size
.top
;
1416 else if (pwg
->width
>= dinfo
->min_size
.width
&&
1417 pwg
->width
<= dinfo
->max_size
.width
&&
1418 pwg
->length
>= dinfo
->min_size
.length
&&
1419 pwg
->length
<= dinfo
->max_size
.length
)
1422 * Map to custom size...
1425 key
.size_name
= (char *)pwg
->pwg
;
1426 key
.bottom
= dinfo
->min_size
.bottom
;
1427 key
.left
= dinfo
->min_size
.left
;
1428 key
.right
= dinfo
->min_size
.right
;
1429 key
.top
= dinfo
->min_size
.top
;
1436 * Find a close size...
1439 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(dinfo
->media_db
);
1441 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1442 if (cups_is_close_media_db(mdb
, &key
))
1450 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1453 * Look for the smallest margins...
1456 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 ||
1459 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1460 mdb
&& cups_is_close_media_db(mdb
, &key
);
1461 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1463 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
1464 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
)
1467 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
1474 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1477 * Look for the largest margins...
1480 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1481 mdb
&& cups_is_close_media_db(mdb
, &key
);
1482 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1484 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
1485 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
)
1492 * Look for the smallest non-zero margins...
1495 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1496 mdb
&& cups_is_close_media_db(mdb
, &key
);
1497 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1499 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
1500 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) ||
1501 best
->right
== 0) &&
1502 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
1503 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) ||
1513 * Return the matching size...
1516 if (best
->size_name
)
1517 strlcpy(size
->media
, best
->size_name
, sizeof(size
->media
));
1519 strlcpy(size
->media
, best
->key
, sizeof(size
->media
));
1521 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
1523 size
->width
= best
->width
;
1524 size
->length
= best
->length
;
1525 size
->bottom
= best
->bottom
;
1526 size
->left
= best
->left
;
1527 size
->right
= best
->right
;
1528 size
->top
= best
->top
;
1538 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
1539 * close to the same size.
1541 * Currently we use 5 points (from PostScript) as the matching range...
1544 static int /* O - 1 if the sizes are close */
1545 cups_is_close_media_db(
1546 _cups_media_db_t
*a
, /* I - First media entries */
1547 _cups_media_db_t
*b
) /* I - Second media entries */
1549 int dwidth
, /* Difference in width */
1550 dlength
; /* Difference in length */
1553 dwidth
= a
->width
- b
->width
;
1554 dlength
= a
->length
- b
->length
;
1556 return (dwidth
>= -176 && dwidth
<= 176 &&
1557 dlength
>= -176 && dlength
<= 176);
1562 * 'cups_test_constraints()' - Test constraints.
1564 * TODO: STR #4096 - Need to properly support media-col contraints...
1567 static cups_array_t
* /* O - Active constraints */
1568 cups_test_constraints(
1569 cups_dinfo_t
*dinfo
, /* I - Destination information */
1570 const char *new_option
, /* I - Newly selected option */
1571 const char *new_value
, /* I - Newly selected value */
1572 int num_options
, /* I - Number of options */
1573 cups_option_t
*options
, /* I - Options */
1574 int *num_conflicts
, /* O - Number of conflicting options */
1575 cups_option_t
**conflicts
) /* O - Conflicting options */
1577 int i
, /* Looping var */
1578 match
; /* Value matches? */
1579 int num_matching
; /* Number of matching options */
1580 cups_option_t
*matching
; /* Matching options */
1581 _cups_dconstres_t
*c
; /* Current constraint */
1582 cups_array_t
*active
= NULL
; /* Active constraints */
1583 ipp_attribute_t
*attr
; /* Current attribute */
1584 _ipp_value_t
*attrval
; /* Current attribute value */
1585 const char *value
; /* Current value */
1586 char temp
[1024]; /* Temporary string */
1587 int int_value
; /* Integer value */
1588 int xres_value
, /* Horizontal resolution */
1589 yres_value
; /* Vertical resolution */
1590 ipp_res_t units_value
; /* Resolution units */
1593 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(dinfo
->constraints
);
1595 c
= (_cups_dconstres_t
*)cupsArrayNext(dinfo
->constraints
))
1600 for (attr
= ippFirstAttribute(c
->collection
);
1602 attr
= ippNextAttribute(c
->collection
))
1604 if (attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
)
1605 break; /* TODO: STR #4096 */
1608 * Get the value for the current attribute in the constraint...
1611 if (new_option
&& new_value
&& !strcmp(attr
->name
, new_option
))
1613 else if ((value
= cupsGetOption(attr
->name
, num_options
,
1615 value
= cupsGetOption(attr
->name
, dinfo
->num_defaults
, dinfo
->defaults
);
1620 * Not set so this constraint does not apply...
1628 switch (attr
->value_tag
)
1630 case IPP_TAG_INTEGER
:
1632 int_value
= atoi(value
);
1634 for (i
= attr
->num_values
, attrval
= attr
->values
;
1638 if (attrval
->integer
== int_value
)
1646 case IPP_TAG_BOOLEAN
:
1647 int_value
= !strcmp(value
, "true");
1649 for (i
= attr
->num_values
, attrval
= attr
->values
;
1653 if (attrval
->boolean
== int_value
)
1661 case IPP_TAG_RANGE
:
1662 int_value
= atoi(value
);
1664 for (i
= attr
->num_values
, attrval
= attr
->values
;
1668 if (int_value
>= attrval
->range
.lower
&&
1669 int_value
<= attrval
->range
.upper
)
1677 case IPP_TAG_RESOLUTION
:
1678 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
1680 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
1683 yres_value
= xres_value
;
1686 if (!strcmp(temp
, "dpi"))
1687 units_value
= IPP_RES_PER_INCH
;
1688 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
1689 units_value
= IPP_RES_PER_CM
;
1693 for (i
= attr
->num_values
, attrval
= attr
->values
;
1697 if (attrval
->resolution
.xres
== xres_value
&&
1698 attrval
->resolution
.yres
== yres_value
&&
1699 attrval
->resolution
.units
== units_value
)
1709 case IPP_TAG_KEYWORD
:
1710 case IPP_TAG_CHARSET
:
1712 case IPP_TAG_URISCHEME
:
1713 case IPP_TAG_MIMETYPE
:
1714 case IPP_TAG_LANGUAGE
:
1715 case IPP_TAG_TEXTLANG
:
1716 case IPP_TAG_NAMELANG
:
1717 for (i
= attr
->num_values
, attrval
= attr
->values
;
1721 if (!strcmp(attrval
->string
.text
, value
))
1736 num_matching
= cupsAddOption(attr
->name
, value
, num_matching
, &matching
);
1742 active
= cupsArrayNew(NULL
, NULL
);
1744 cupsArrayAdd(active
, c
);
1746 if (num_conflicts
&& conflicts
)
1748 cups_option_t
*moption
; /* Matching option */
1750 for (i
= num_matching
, moption
= matching
; i
> 0; i
--, moption
++)
1751 *num_conflicts
= cupsAddOption(moption
->name
, moption
->value
,
1752 *num_conflicts
, conflicts
);
1756 cupsFreeOptions(num_matching
, matching
);