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 @code NULL@, they are set to
273 * contain the list of conflicting option/value pairs. Similarly, if
274 * "num_resolved" and "resolved" are not @code NULL@ they will be set to the
275 * list of changes needed to resolve the conflict.
277 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
278 * to 0 and @code 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. "Flags" provides some matching
720 * guidance (multiple flags can be combined):
722 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
723 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
724 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
725 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
726 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
727 * size amongst the "ready" media.
729 * The matching result (if any) is returned in the "cups_size_t" structure.
731 * Returns 1 when there is a match and 0 if there is not a match.
733 * @since CUPS 1.6/OS X 10.8@
736 int /* O - 1 on match, 0 on failure */
737 cupsGetDestMediaByName(
738 http_t
*http
, /* I - Connection to destination */
739 cups_dest_t
*dest
, /* I - Destination */
740 cups_dinfo_t
*dinfo
, /* I - Destination information */
741 const char *media
, /* I - Media name */
742 unsigned flags
, /* I - Media matching flags */
743 cups_size_t
*size
) /* O - Media size information */
745 _pwg_media_t
*pwg
; /* PWG media info */
749 * Range check input...
753 memset(size
, 0, sizeof(cups_size_t
));
755 if (!http
|| !dest
|| !dinfo
|| !media
|| !size
)
757 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(EINVAL
), 0);
762 * Lookup the media size name...
765 if ((pwg
= _pwgMediaForPWG(media
)) == NULL
)
766 if ((pwg
= _pwgMediaForLegacy(media
)) == NULL
)
768 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media
));
769 _cupsSetError(IPP_INTERNAL_ERROR
, _("Unknown media size name."), 1);
777 return (cups_get_media_db(dinfo
, pwg
, flags
, size
));
782 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
784 * "Width" and "length" are the dimensions in hundredths of millimeters.
785 * "Flags" provides some matching guidance (multiple flags can be combined):
787 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
788 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
789 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
790 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
791 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
792 * size amongst the "ready" media.
794 * The matching result (if any) is returned in the "cups_size_t" structure.
796 * Returns 1 when there is a match and 0 if there is not a match.
798 * @since CUPS 1.6/OS X 10.8@
801 int /* O - 1 on match, 0 on failure */
802 cupsGetDestMediaBySize(
803 http_t
*http
, /* I - Connection to destination */
804 cups_dest_t
*dest
, /* I - Destination */
805 cups_dinfo_t
*dinfo
, /* I - Destination information */
806 int width
, /* I - Media width in hundredths of
808 int length
, /* I - Media length in hundredths of
810 unsigned flags
, /* I - Media matching flags */
811 cups_size_t
*size
) /* O - Media size information */
813 _pwg_media_t
*pwg
; /* PWG media info */
817 * Range check input...
821 memset(size
, 0, sizeof(cups_size_t
));
823 if (!http
|| !dest
|| !dinfo
|| width
<= 0 || length
<= 0 || !size
)
825 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(EINVAL
), 0);
830 * Lookup the media size name...
833 if ((pwg
= _pwgMediaForSize(width
, length
)) == NULL
)
835 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width
,
837 _cupsSetError(IPP_INTERNAL_ERROR
, _("Invalid media size."), 1);
845 return (cups_get_media_db(dinfo
, pwg
, flags
, size
));
850 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
855 cups_array_t
*a
, /* I - Array */
856 ipp_t
*collection
) /* I - Collection value */
858 ipp_attribute_t
*attr
; /* Attribute */
859 _cups_dconstres_t
*temp
; /* Current constraint/resolver */
862 if ((attr
= ippFindAttribute(collection
, "resolver-name",
863 IPP_TAG_NAME
)) == NULL
)
866 if ((temp
= calloc(1, sizeof(_cups_dconstres_t
))) == NULL
)
869 temp
->name
= attr
->values
[0].string
.text
;
870 temp
->collection
= collection
;
872 cupsArrayAdd(a
, temp
);
877 * 'cups_compare_dconstres()' - Compare to resolver entries.
880 static int /* O - Result of comparison */
881 cups_compare_dconstres(
882 _cups_dconstres_t
*a
, /* I - First resolver */
883 _cups_dconstres_t
*b
) /* I - Second resolver */
885 return (strcmp(a
->name
, b
->name
));
890 * 'cups_compare_media_db()' - Compare two media entries.
893 static int /* O - Result of comparison */
894 cups_compare_media_db(
895 _cups_media_db_t
*a
, /* I - First media entries */
896 _cups_media_db_t
*b
) /* I - Second media entries */
898 int result
; /* Result of comparison */
901 if ((result
= a
->width
- b
->width
) == 0)
902 result
= a
->length
- b
->length
;
909 * 'cups_copy_media_db()' - Copy a media entry.
912 static _cups_media_db_t
* /* O - New media entry */
914 _cups_media_db_t
*mdb
) /* I - Media entry to copy */
916 _cups_media_db_t
*temp
; /* New media entry */
919 if ((temp
= calloc(1, sizeof(_cups_media_db_t
))) == NULL
)
923 temp
->color
= _cupsStrAlloc(mdb
->color
);
925 temp
->key
= _cupsStrAlloc(mdb
->key
);
927 temp
->info
= _cupsStrAlloc(mdb
->info
);
929 temp
->size_name
= _cupsStrAlloc(mdb
->size_name
);
931 temp
->source
= _cupsStrAlloc(mdb
->source
);
933 temp
->type
= _cupsStrAlloc(mdb
->type
);
935 temp
->width
= mdb
->width
;
936 temp
->length
= mdb
->length
;
937 temp
->bottom
= mdb
->bottom
;
938 temp
->left
= mdb
->left
;
939 temp
->right
= mdb
->right
;
940 temp
->top
= mdb
->top
;
947 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
951 cups_create_constraints(
952 cups_dinfo_t
*dinfo
) /* I - Destination information */
954 int i
; /* Looping var */
955 ipp_attribute_t
*attr
; /* Attribute */
956 _ipp_value_t
*val
; /* Current value */
959 dinfo
->constraints
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, NULL
,
960 (cups_afree_func_t
)free
);
961 dinfo
->resolvers
= cupsArrayNew3((cups_array_func_t
)cups_compare_dconstres
,
963 (cups_afree_func_t
)free
);
965 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-constraints-supported",
966 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
968 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
969 cups_add_dconstres(dinfo
->constraints
, val
->collection
);
972 if ((attr
= ippFindAttribute(dinfo
->attrs
, "job-resolvers-supported",
973 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
975 for (i
= attr
->num_values
, val
= attr
->values
; i
> 0; i
--, val
++)
976 cups_add_dconstres(dinfo
->resolvers
, val
->collection
);
982 * 'cups_create_defaults()' - Create the -default option array.
984 * TODO: Need to support collection defaults...
988 cups_create_defaults(
989 cups_dinfo_t
*dinfo
) /* I - Destination information */
991 ipp_attribute_t
*attr
; /* Current attribute */
992 char name
[IPP_MAX_NAME
+ 1],
994 *nameptr
, /* Pointer into current name */
995 value
[2048]; /* Current value */
999 * Iterate through the printer attributes looking for xxx-default and adding
1000 * xxx=value to the defaults option array.
1003 for (attr
= ippFirstAttribute(dinfo
->attrs
);
1005 attr
= ippNextAttribute(dinfo
->attrs
))
1007 if (!attr
->name
|| attr
->group_tag
!= IPP_TAG_PRINTER
)
1010 if (attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
)
1011 continue; /* TODO: STR #4096 */
1013 if ((nameptr
= attr
->name
+ strlen(attr
->name
) - 8) <= attr
->name
||
1014 strcmp(nameptr
, "-default"))
1017 strlcpy(name
, attr
->name
, sizeof(name
));
1018 if ((nameptr
= name
+ strlen(name
) - 8) <= name
||
1019 strcmp(nameptr
, "-default"))
1024 if (ippAttributeString(attr
, value
, sizeof(value
)) >= sizeof(value
))
1027 dinfo
->num_defaults
= cupsAddOption(name
, value
, dinfo
->num_defaults
,
1034 * 'cups_create_media_db()' - Create the media database.
1038 cups_create_media_db(
1039 cups_dinfo_t
*dinfo
) /* I - Destination information */
1041 int i
; /* Looping var */
1042 _ipp_value_t
*val
; /* Current value */
1043 ipp_attribute_t
*media_col_db
, /* media-col-database */
1044 *media_attr
, /* media-xxx */
1045 *x_dimension
, /* x-dimension */
1046 *y_dimension
; /* y-dimension */
1047 _pwg_media_t
*pwg
; /* PWG media info */
1048 _cups_media_db_t mdb
; /* Media entry */
1051 dinfo
->media_db
= cupsArrayNew3((cups_array_func_t
)cups_compare_media_db
,
1053 (cups_acopy_func_t
)cups_copy_media_db
,
1054 (cups_afree_func_t
)cups_free_media_db
);
1055 dinfo
->min_size
.width
= INT_MAX
;
1056 dinfo
->min_size
.length
= INT_MAX
;
1057 dinfo
->max_size
.width
= 0;
1058 dinfo
->max_size
.length
= 0;
1060 if ((media_col_db
= ippFindAttribute(dinfo
->attrs
, "media-col-database",
1061 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1063 _ipp_value_t
*custom
= NULL
; /* Custom size range value */
1065 for (i
= media_col_db
->num_values
, val
= media_col_db
->values
;
1069 memset(&mdb
, 0, sizeof(mdb
));
1071 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size",
1072 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1074 ipp_t
*media_size
= media_attr
->values
[0].collection
;
1075 /* media-size collection value */
1077 if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1078 IPP_TAG_INTEGER
)) != NULL
&&
1079 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1080 IPP_TAG_INTEGER
)) != NULL
)
1082 mdb
.width
= x_dimension
->values
[0].integer
;
1083 mdb
.length
= y_dimension
->values
[0].integer
;
1085 else if ((x_dimension
= ippFindAttribute(media_size
, "x-dimension",
1086 IPP_TAG_RANGE
)) != NULL
&&
1087 (y_dimension
= ippFindAttribute(media_size
, "y-dimension",
1088 IPP_TAG_RANGE
)) != NULL
)
1091 * Custom size range; save this as the custom size value with default
1092 * margins, then continue; we'll capture the real margins below...
1097 dinfo
->min_size
.width
= x_dimension
->values
[0].range
.lower
;
1098 dinfo
->min_size
.length
= y_dimension
->values
[0].range
.lower
;
1099 dinfo
->min_size
.left
=
1100 dinfo
->min_size
.right
= 635; /* Default 1/4" side margins */
1101 dinfo
->min_size
.top
=
1102 dinfo
->min_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1104 dinfo
->max_size
.width
= x_dimension
->values
[0].range
.upper
;
1105 dinfo
->max_size
.length
= y_dimension
->values
[0].range
.upper
;
1106 dinfo
->max_size
.left
=
1107 dinfo
->max_size
.right
= 635; /* Default 1/4" side margins */
1108 dinfo
->max_size
.top
=
1109 dinfo
->max_size
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1115 if ((media_attr
= ippFindAttribute(val
->collection
, "media-color",
1116 IPP_TAG_ZERO
)) != NULL
&&
1117 (media_attr
->value_tag
== IPP_TAG_NAME
||
1118 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1119 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1120 mdb
.color
= media_attr
->values
[0].string
.text
;
1122 if ((media_attr
= ippFindAttribute(val
->collection
, "media-info",
1123 IPP_TAG_TEXT
)) != NULL
)
1124 mdb
.info
= media_attr
->values
[0].string
.text
;
1126 if ((media_attr
= ippFindAttribute(val
->collection
, "media-key",
1127 IPP_TAG_ZERO
)) != NULL
&&
1128 (media_attr
->value_tag
== IPP_TAG_NAME
||
1129 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1130 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1131 mdb
.key
= media_attr
->values
[0].string
.text
;
1133 if ((media_attr
= ippFindAttribute(val
->collection
, "media-size-name",
1134 IPP_TAG_ZERO
)) != NULL
&&
1135 (media_attr
->value_tag
== IPP_TAG_NAME
||
1136 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1137 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1138 mdb
.size_name
= media_attr
->values
[0].string
.text
;
1140 if ((media_attr
= ippFindAttribute(val
->collection
, "media-source",
1141 IPP_TAG_ZERO
)) != NULL
&&
1142 (media_attr
->value_tag
== IPP_TAG_NAME
||
1143 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1144 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1145 mdb
.source
= media_attr
->values
[0].string
.text
;
1147 if ((media_attr
= ippFindAttribute(val
->collection
, "media-type",
1148 IPP_TAG_ZERO
)) != NULL
&&
1149 (media_attr
->value_tag
== IPP_TAG_NAME
||
1150 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1151 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1152 mdb
.type
= media_attr
->values
[0].string
.text
;
1154 if ((media_attr
= ippFindAttribute(val
->collection
, "media-bottom-margin",
1155 IPP_TAG_INTEGER
)) != NULL
)
1156 mdb
.bottom
= media_attr
->values
[0].integer
;
1158 if ((media_attr
= ippFindAttribute(val
->collection
, "media-left-margin",
1159 IPP_TAG_INTEGER
)) != NULL
)
1160 mdb
.left
= media_attr
->values
[0].integer
;
1162 if ((media_attr
= ippFindAttribute(val
->collection
, "media-right-margin",
1163 IPP_TAG_INTEGER
)) != NULL
)
1164 mdb
.right
= media_attr
->values
[0].integer
;
1166 if ((media_attr
= ippFindAttribute(val
->collection
, "media-top-margin",
1167 IPP_TAG_INTEGER
)) != NULL
)
1168 mdb
.top
= media_attr
->values
[0].integer
;
1170 cupsArrayAdd(dinfo
->media_db
, &mdb
);
1175 if ((media_attr
= ippFindAttribute(custom
->collection
,
1176 "media-bottom-margin",
1177 IPP_TAG_INTEGER
)) != NULL
)
1179 dinfo
->min_size
.top
=
1180 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
1183 if ((media_attr
= ippFindAttribute(custom
->collection
,
1184 "media-left-margin",
1185 IPP_TAG_INTEGER
)) != NULL
)
1187 dinfo
->min_size
.left
=
1188 dinfo
->max_size
.left
= media_attr
->values
[0].integer
;
1191 if ((media_attr
= ippFindAttribute(custom
->collection
,
1192 "media-right-margin",
1193 IPP_TAG_INTEGER
)) != NULL
)
1195 dinfo
->min_size
.right
=
1196 dinfo
->max_size
.right
= media_attr
->values
[0].integer
;
1199 if ((media_attr
= ippFindAttribute(custom
->collection
,
1201 IPP_TAG_INTEGER
)) != NULL
)
1203 dinfo
->min_size
.top
=
1204 dinfo
->max_size
.top
= media_attr
->values
[0].integer
;
1208 else if ((media_attr
= ippFindAttribute(dinfo
->attrs
, "media-supported",
1209 IPP_TAG_ZERO
)) != NULL
&&
1210 (media_attr
->value_tag
== IPP_TAG_NAME
||
1211 media_attr
->value_tag
== IPP_TAG_NAMELANG
||
1212 media_attr
->value_tag
== IPP_TAG_KEYWORD
))
1214 memset(&mdb
, 0, sizeof(mdb
));
1217 mdb
.right
= 635; /* Default 1/4" side margins */
1219 mdb
.bottom
= 1270; /* Default 1/2" top/bottom margins */
1221 for (i
= media_attr
->num_values
, val
= media_attr
->values
;
1225 if ((pwg
= _pwgMediaForPWG(val
->string
.text
)) == NULL
)
1226 if ((pwg
= _pwgMediaForLegacy(val
->string
.text
)) == NULL
)
1228 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
1233 mdb
.width
= pwg
->width
;
1234 mdb
.length
= pwg
->length
;
1236 if (!strncmp(val
->string
.text
, "custom_min_", 11))
1238 mdb
.size_name
= NULL
;
1239 dinfo
->min_size
= mdb
;
1241 else if (!strncmp(val
->string
.text
, "custom_max_", 11))
1243 mdb
.size_name
= NULL
;
1244 dinfo
->max_size
= mdb
;
1248 mdb
.size_name
= val
->string
.text
;
1250 cupsArrayAdd(dinfo
->media_db
, &mdb
);
1258 * 'cups_free_media_cb()' - Free a media entry.
1263 _cups_media_db_t
*mdb
) /* I - Media entry to free */
1266 _cupsStrFree(mdb
->color
);
1268 _cupsStrFree(mdb
->key
);
1270 _cupsStrFree(mdb
->info
);
1272 _cupsStrFree(mdb
->size_name
);
1274 _cupsStrFree(mdb
->source
);
1276 _cupsStrFree(mdb
->type
);
1283 * 'cups_get_media_db()' - Lookup the media entry for a given size.
1286 static int /* O - 1 on match, 0 on failure */
1287 cups_get_media_db(cups_dinfo_t
*dinfo
, /* I - Destination information */
1288 _pwg_media_t
*pwg
, /* I - PWG media info */
1289 unsigned flags
, /* I - Media matching flags */
1290 cups_size_t
*size
) /* O - Media size/margin/name info */
1292 _cups_media_db_t
*mdb
, /* Current media database entry */
1293 *best
= NULL
, /* Best matching entry */
1294 key
; /* Search key */
1298 * Create the media database as needed...
1301 if (!dinfo
->media_db
)
1302 cups_create_media_db(dinfo
);
1308 memset(&key
, 0, sizeof(key
));
1309 key
.width
= pwg
->width
;
1310 key
.length
= pwg
->length
;
1312 if ((mdb
= cupsArrayFind(dinfo
->media_db
, &key
)) != NULL
)
1315 * Found an exact match, let's figure out the best margins for the flags
1321 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1324 * Look for the smallest margins...
1327 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 ||
1330 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1331 mdb
&& !cups_compare_media_db(mdb
, &key
);
1332 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1334 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
1335 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
)
1338 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
1346 * If we need an exact match, return no-match if the size is not
1350 if ((flags
& CUPS_MEDIA_FLAGS_EXACT
) &&
1351 (best
->left
|| best
->right
|| best
->top
|| best
->bottom
))
1354 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1357 * Look for the largest margins...
1360 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1361 mdb
&& !cups_compare_media_db(mdb
, &key
);
1362 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1364 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
1365 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
)
1372 * Look for the smallest non-zero margins...
1375 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1376 mdb
&& !cups_compare_media_db(mdb
, &key
);
1377 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1379 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
1380 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) ||
1381 best
->right
== 0) &&
1382 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
1383 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) ||
1389 else if (flags
& CUPS_MEDIA_FLAGS_EXACT
)
1392 * See if we can do this as a custom size...
1395 if (pwg
->width
< dinfo
->min_size
.width
||
1396 pwg
->width
> dinfo
->max_size
.width
||
1397 pwg
->length
< dinfo
->min_size
.length
||
1398 pwg
->length
> dinfo
->max_size
.length
)
1399 return (0); /* Out of range */
1401 if ((flags
& CUPS_MEDIA_FLAGS_BORDERLESS
) &&
1402 (dinfo
->min_size
.left
> 0 || dinfo
->min_size
.right
> 0 ||
1403 dinfo
->min_size
.top
> 0 || dinfo
->min_size
.bottom
> 0))
1404 return (0); /* Not borderless */
1406 key
.size_name
= (char *)pwg
->pwg
;
1407 key
.bottom
= dinfo
->min_size
.bottom
;
1408 key
.left
= dinfo
->min_size
.left
;
1409 key
.right
= dinfo
->min_size
.right
;
1410 key
.top
= dinfo
->min_size
.top
;
1414 else if (pwg
->width
>= dinfo
->min_size
.width
&&
1415 pwg
->width
<= dinfo
->max_size
.width
&&
1416 pwg
->length
>= dinfo
->min_size
.length
&&
1417 pwg
->length
<= dinfo
->max_size
.length
)
1420 * Map to custom size...
1423 key
.size_name
= (char *)pwg
->pwg
;
1424 key
.bottom
= dinfo
->min_size
.bottom
;
1425 key
.left
= dinfo
->min_size
.left
;
1426 key
.right
= dinfo
->min_size
.right
;
1427 key
.top
= dinfo
->min_size
.top
;
1434 * Find a close size...
1437 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(dinfo
->media_db
);
1439 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1440 if (cups_is_close_media_db(mdb
, &key
))
1448 if (flags
& CUPS_MEDIA_FLAGS_BORDERLESS
)
1451 * Look for the smallest margins...
1454 if (best
->left
!= 0 || best
->right
!= 0 || best
->top
!= 0 ||
1457 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1458 mdb
&& cups_is_close_media_db(mdb
, &key
);
1459 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1461 if (mdb
->left
<= best
->left
&& mdb
->right
<= best
->right
&&
1462 mdb
->top
<= best
->top
&& mdb
->bottom
<= best
->bottom
)
1465 if (mdb
->left
== 0 && mdb
->right
== 0 && mdb
->bottom
== 0 &&
1472 else if (flags
& CUPS_MEDIA_FLAGS_DUPLEX
)
1475 * Look for the largest margins...
1478 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1479 mdb
&& cups_is_close_media_db(mdb
, &key
);
1480 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1482 if (mdb
->left
>= best
->left
&& mdb
->right
>= best
->right
&&
1483 mdb
->top
>= best
->top
&& mdb
->bottom
>= best
->bottom
)
1490 * Look for the smallest non-zero margins...
1493 for (mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
);
1494 mdb
&& cups_is_close_media_db(mdb
, &key
);
1495 mdb
= (_cups_media_db_t
*)cupsArrayNext(dinfo
->media_db
))
1497 if (((mdb
->left
> 0 && mdb
->left
<= best
->left
) || best
->left
== 0) &&
1498 ((mdb
->right
> 0 && mdb
->right
<= best
->right
) ||
1499 best
->right
== 0) &&
1500 ((mdb
->top
> 0 && mdb
->top
<= best
->top
) || best
->top
== 0) &&
1501 ((mdb
->bottom
> 0 && mdb
->bottom
<= best
->bottom
) ||
1511 * Return the matching size...
1514 if (best
->size_name
)
1515 strlcpy(size
->media
, best
->size_name
, sizeof(size
->media
));
1517 strlcpy(size
->media
, best
->key
, sizeof(size
->media
));
1519 strlcpy(size
->media
, pwg
->pwg
, sizeof(size
->media
));
1521 size
->width
= best
->width
;
1522 size
->length
= best
->length
;
1523 size
->bottom
= best
->bottom
;
1524 size
->left
= best
->left
;
1525 size
->right
= best
->right
;
1526 size
->top
= best
->top
;
1536 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
1537 * close to the same size.
1539 * Currently we use 5 points (from PostScript) as the matching range...
1542 static int /* O - 1 if the sizes are close */
1543 cups_is_close_media_db(
1544 _cups_media_db_t
*a
, /* I - First media entries */
1545 _cups_media_db_t
*b
) /* I - Second media entries */
1547 int dwidth
, /* Difference in width */
1548 dlength
; /* Difference in length */
1551 dwidth
= a
->width
- b
->width
;
1552 dlength
= a
->length
- b
->length
;
1554 return (dwidth
>= -176 && dwidth
<= 176 &&
1555 dlength
>= -176 && dlength
<= 176);
1560 * 'cups_test_constraints()' - Test constraints.
1562 * TODO: STR #4096 - Need to properly support media-col contraints...
1565 static cups_array_t
* /* O - Active constraints */
1566 cups_test_constraints(
1567 cups_dinfo_t
*dinfo
, /* I - Destination information */
1568 const char *new_option
, /* I - Newly selected option */
1569 const char *new_value
, /* I - Newly selected value */
1570 int num_options
, /* I - Number of options */
1571 cups_option_t
*options
, /* I - Options */
1572 int *num_conflicts
, /* O - Number of conflicting options */
1573 cups_option_t
**conflicts
) /* O - Conflicting options */
1575 int i
, /* Looping var */
1576 match
; /* Value matches? */
1577 int num_matching
; /* Number of matching options */
1578 cups_option_t
*matching
; /* Matching options */
1579 _cups_dconstres_t
*c
; /* Current constraint */
1580 cups_array_t
*active
= NULL
; /* Active constraints */
1581 ipp_attribute_t
*attr
; /* Current attribute */
1582 _ipp_value_t
*attrval
; /* Current attribute value */
1583 const char *value
; /* Current value */
1584 char temp
[1024]; /* Temporary string */
1585 int int_value
; /* Integer value */
1586 int xres_value
, /* Horizontal resolution */
1587 yres_value
; /* Vertical resolution */
1588 ipp_res_t units_value
; /* Resolution units */
1591 for (c
= (_cups_dconstres_t
*)cupsArrayFirst(dinfo
->constraints
);
1593 c
= (_cups_dconstres_t
*)cupsArrayNext(dinfo
->constraints
))
1598 for (attr
= ippFirstAttribute(c
->collection
);
1600 attr
= ippNextAttribute(c
->collection
))
1602 if (attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
)
1603 break; /* TODO: STR #4096 */
1606 * Get the value for the current attribute in the constraint...
1609 if (new_option
&& new_value
&& !strcmp(attr
->name
, new_option
))
1611 else if ((value
= cupsGetOption(attr
->name
, num_options
,
1613 value
= cupsGetOption(attr
->name
, dinfo
->num_defaults
, dinfo
->defaults
);
1618 * Not set so this constraint does not apply...
1626 switch (attr
->value_tag
)
1628 case IPP_TAG_INTEGER
:
1630 int_value
= atoi(value
);
1632 for (i
= attr
->num_values
, attrval
= attr
->values
;
1636 if (attrval
->integer
== int_value
)
1644 case IPP_TAG_BOOLEAN
:
1645 int_value
= !strcmp(value
, "true");
1647 for (i
= attr
->num_values
, attrval
= attr
->values
;
1651 if (attrval
->boolean
== int_value
)
1659 case IPP_TAG_RANGE
:
1660 int_value
= atoi(value
);
1662 for (i
= attr
->num_values
, attrval
= attr
->values
;
1666 if (int_value
>= attrval
->range
.lower
&&
1667 int_value
<= attrval
->range
.upper
)
1675 case IPP_TAG_RESOLUTION
:
1676 if (sscanf(value
, "%dx%d%15s", &xres_value
, &yres_value
, temp
) != 3)
1678 if (sscanf(value
, "%d%15s", &xres_value
, temp
) != 2)
1681 yres_value
= xres_value
;
1684 if (!strcmp(temp
, "dpi"))
1685 units_value
= IPP_RES_PER_INCH
;
1686 else if (!strcmp(temp
, "dpc") || !strcmp(temp
, "dpcm"))
1687 units_value
= IPP_RES_PER_CM
;
1691 for (i
= attr
->num_values
, attrval
= attr
->values
;
1695 if (attrval
->resolution
.xres
== xres_value
&&
1696 attrval
->resolution
.yres
== yres_value
&&
1697 attrval
->resolution
.units
== units_value
)
1707 case IPP_TAG_KEYWORD
:
1708 case IPP_TAG_CHARSET
:
1710 case IPP_TAG_URISCHEME
:
1711 case IPP_TAG_MIMETYPE
:
1712 case IPP_TAG_LANGUAGE
:
1713 case IPP_TAG_TEXTLANG
:
1714 case IPP_TAG_NAMELANG
:
1715 for (i
= attr
->num_values
, attrval
= attr
->values
;
1719 if (!strcmp(attrval
->string
.text
, value
))
1734 num_matching
= cupsAddOption(attr
->name
, value
, num_matching
, &matching
);
1740 active
= cupsArrayNew(NULL
, NULL
);
1742 cupsArrayAdd(active
, c
);
1744 if (num_conflicts
&& conflicts
)
1746 cups_option_t
*moption
; /* Matching option */
1748 for (i
= num_matching
, moption
= matching
; i
> 0; i
--, moption
++)
1749 *num_conflicts
= cupsAddOption(moption
->name
, moption
->value
,
1750 *num_conflicts
, conflicts
);
1754 cupsFreeOptions(num_matching
, matching
);