]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/dest-options.c
SAve and use the IPP version from Get-Printer-Attributes (STR #4292)
[thirdparty/cups.git] / cups / dest-options.c
CommitLineData
fb5d8bf6 1/*
2 * "$Id$"
3 *
4 * Destination option/media support for CUPS.
5 *
d6ff282a 6 * Copyright 2012-2013 by Apple Inc.
fb5d8bf6 7 *
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/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 *
16 * Contents:
17 *
9973a393 18 * cupsCheckDestSupported() - Check that the option and value are supported
19 * by the destination.
20 * cupsCopyDestConflicts() - Get conflicts and resolutions for a new
21 * option/value pair.
22 * cupsCopyDestInfo() - Get the supported values/capabilities for the
23 * destination.
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.
fb5d8bf6 40 */
41
42/*
43 * Include necessary headers...
44 */
45
46#include "cups-private.h"
47
48
49/*
50 * Local functions...
51 */
52
a98cf586 53static void cups_add_dconstres(cups_array_t *a, ipp_t *collection);
54static int cups_compare_dconstres(_cups_dconstres_t *a,
55 _cups_dconstres_t *b);
fb5d8bf6 56static int cups_compare_media_db(_cups_media_db_t *a,
57 _cups_media_db_t *b);
58static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb);
a98cf586 59static void cups_create_constraints(cups_dinfo_t *dinfo);
60static void cups_create_defaults(cups_dinfo_t *dinfo);
fb5d8bf6 61static void cups_create_media_db(cups_dinfo_t *dinfo);
62static void cups_free_media_db(_cups_media_db_t *mdb);
63static int cups_get_media_db(cups_dinfo_t *dinfo,
e106c1dc 64 pwg_media_t *pwg, unsigned flags,
fb5d8bf6 65 cups_size_t *size);
66static int cups_is_close_media_db(_cups_media_db_t *a,
67 _cups_media_db_t *b);
a98cf586 68static cups_array_t *cups_test_constraints(cups_dinfo_t *dinfo,
69 const char *new_option,
70 const char *new_value,
71 int num_options,
72 cups_option_t *options,
73 int *num_conflicts,
74 cups_option_t **conflicts);
75
fb5d8bf6 76
77/*
78 * 'cupsCheckDestSupported()' - Check that the option and value are supported
79 * by the destination.
80 *
81 * Returns 1 if supported, 0 otherwise.
82 *
c45401bb 83 * @since CUPS 1.6/OS X 10.8@
fb5d8bf6 84 */
85
86int /* O - 1 if supported, 0 otherwise */
87cupsCheckDestSupported(
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 */
93{
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 */
102
103
104 /*
105 * Range check input...
106 */
107
108 if (!http || !dest || !dinfo || !option || !value)
109 return (0);
110
111 /*
112 * Lookup the attribute...
113 */
114
115 if (strstr(option, "-supported"))
116 attr = ippFindAttribute(dinfo->attrs, option, IPP_TAG_ZERO);
117 else
118 {
119 snprintf(temp, sizeof(temp), "%s-supported", option);
120 attr = ippFindAttribute(dinfo->attrs, temp, IPP_TAG_ZERO);
121 }
122
123 if (!attr)
124 return (0);
125
126 /*
127 * Compare values...
128 */
129
130 if (!strcmp(option, "media") && !strncmp(value, "custom_", 7))
131 {
132 /*
133 * Check range of custom media sizes...
134 */
135
e106c1dc 136 pwg_media_t *pwg; /* Current PWG media size info */
fb5d8bf6 137 int min_width, /* Minimum width */
138 min_length, /* Minimum length */
139 max_width, /* Maximum width */
140 max_length; /* Maximum length */
141
142 /*
143 * Get the minimum and maximum size...
144 */
145
146 min_width = min_length = INT_MAX;
147 max_width = max_length = 0;
148
149 for (i = attr->num_values, attrval = attr->values;
150 i > 0;
151 i --, attrval ++)
152 {
153 if (!strncmp(attrval->string.text, "custom_min_", 11) &&
e106c1dc 154 (pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
fb5d8bf6 155 {
156 min_width = pwg->width;
157 min_length = pwg->length;
158 }
159 else if (!strncmp(attrval->string.text, "custom_max_", 11) &&
e106c1dc 160 (pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
fb5d8bf6 161 {
162 max_width = pwg->width;
163 max_length = pwg->length;
164 }
165 }
166
167 /*
168 * Check the range...
169 */
170
171 if (min_width < INT_MAX && max_width > 0 &&
e106c1dc 172 (pwg = pwgMediaForPWG(value)) != NULL &&
fb5d8bf6 173 pwg->width >= min_width && pwg->width <= max_width &&
174 pwg->length >= min_length && pwg->length <= max_length)
175 return (1);
176 }
177 else
178 {
179 /*
180 * Check literal values...
181 */
182
183 switch (attr->value_tag)
184 {
185 case IPP_TAG_INTEGER :
186 case IPP_TAG_ENUM :
187 int_value = atoi(value);
188
189 for (i = 0; i < attr->num_values; i ++)
190 if (attr->values[i].integer == int_value)
191 return (1);
192 break;
193
194 case IPP_TAG_BOOLEAN :
195 return (attr->values[0].boolean);
196
a98cf586 197 case IPP_TAG_RANGE :
198 int_value = atoi(value);
199
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)
203 return (1);
204 break;
205
fb5d8bf6 206 case IPP_TAG_RESOLUTION :
207 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
208 {
209 if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
210 return (0);
211
212 yres_value = xres_value;
213 }
214
215 if (!strcmp(temp, "dpi"))
216 units_value = IPP_RES_PER_INCH;
1c8f86e4 217 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
fb5d8bf6 218 units_value = IPP_RES_PER_CM;
219 else
220 return (0);
221
222 for (i = attr->num_values, attrval = attr->values;
223 i > 0;
224 i --, attrval ++)
225 {
226 if (attrval->resolution.xres == xres_value &&
227 attrval->resolution.yres == yres_value &&
228 attrval->resolution.units == units_value)
229 return (1);
230 }
231 break;
232
233 case IPP_TAG_TEXT :
234 case IPP_TAG_NAME :
235 case IPP_TAG_KEYWORD :
236 case IPP_TAG_CHARSET :
237 case IPP_TAG_URI :
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))
245 return (1);
246 break;
247
248 default :
249 break;
250 }
251 }
252
253 /*
254 * If we get there the option+value is not supported...
255 */
256
257 return (0);
258}
259
260
261/*
262 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
263 * option/value pair.
264 *
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
267 * changed.
268 *
a98cf586 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.
fb5d8bf6 271 *
305b3fd5 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.
fb5d8bf6 276 *
277 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
305b3fd5 278 * to 0 and @code NULL@, respectively, then the conflict cannot be resolved.
fb5d8bf6 279 *
c45401bb 280 * @since CUPS 1.6/OS X 10.8@
fb5d8bf6 281 */
282
a98cf586 283int /* O - 1 if there is a conflict, 0 if none, -1 on error */
fb5d8bf6 284cupsCopyDestConflicts(
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 */
296{
a98cf586 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 */
316
317
fb5d8bf6 318 /*
319 * Clear returned values...
320 */
321
322 if (num_conflicts)
323 *num_conflicts = 0;
324
325 if (conflicts)
326 *conflicts = NULL;
327
328 if (num_resolved)
329 *num_resolved = 0;
330
331 if (resolved)
332 *resolved = NULL;
333
334 /*
335 * Range check input...
336 */
337
a98cf586 338 if (!http || !dest || !dinfo ||
fb5d8bf6 339 (num_conflicts != NULL) != (conflicts != NULL) ||
340 (num_resolved != NULL) != (resolved != NULL))
341 return (0);
342
343 /*
a98cf586 344 * Load constraints as needed...
fb5d8bf6 345 */
346
a98cf586 347 if (!dinfo->constraints)
348 cups_create_constraints(dinfo);
fb5d8bf6 349
a98cf586 350 if (cupsArrayCount(dinfo->constraints) == 0)
351 return (0);
352
353 if (!dinfo->num_defaults)
354 cups_create_defaults(dinfo);
355
356 /*
357 * If we are resolving, create a shadow array...
358 */
359
360 if (num_resolved)
361 {
362 for (i = num_options, option = options; i > 0; i --, option ++)
363 num_myres = cupsAddOption(option->name, option->value, num_myres, &myres);
364
365 if (new_option && new_value)
366 num_myres = cupsAddOption(new_option, new_value, num_myres, &myres);
367 }
368 else
369 {
370 num_myres = num_options;
371 myres = options;
372 }
373
374 /*
375 * Check for any conflicts...
376 */
377
378 if (num_resolved)
379 pass = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
380
381 for (tries = 0; tries < 100; tries ++)
382 {
383 /*
384 * Check for any conflicts...
385 */
386
387 if (num_conflicts || num_resolved)
388 {
389 cupsFreeOptions(num_myconf, myconf);
390
391 num_myconf = 0;
392 myconf = NULL;
393 active = cups_test_constraints(dinfo, new_option, new_value,
394 num_myres, myres, &num_myconf,
395 &myconf);
396 }
397 else
398 active = cups_test_constraints(dinfo, new_option, new_value, num_myres,
399 myres, NULL, NULL);
400
401 have_conflicts = (active != NULL);
402
403 if (!active || !num_resolved)
404 break; /* All done */
405
406 /*
407 * Scan the constraints that were triggered to apply resolvers...
408 */
409
410 if (!resolvers)
411 resolvers = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
412
413 for (c = (_cups_dconstres_t *)cupsArrayFirst(active), changed = 0;
414 c;
415 c = (_cups_dconstres_t *)cupsArrayNext(active))
416 {
417 if (cupsArrayFind(pass, c))
418 continue; /* Already applied this resolver... */
419
420 if (cupsArrayFind(resolvers, c))
421 {
422 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
423 c->name));
424 have_conflicts = -1;
425 goto cleanup;
426 }
427
428 if ((r = cupsArrayFind(dinfo->resolvers, c)) == NULL)
429 {
430 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
431 c->name));
432 have_conflicts = -1;
433 goto cleanup;
434 }
435
436 /*
437 * Add the options from the resolver...
438 */
439
440 cupsArrayAdd(pass, r);
441 cupsArrayAdd(resolvers, r);
442
443 for (attr = ippFirstAttribute(r->collection);
444 attr;
445 attr = ippNextAttribute(r->collection))
446 {
447 if (new_option && !strcmp(attr->name, new_option))
448 continue; /* Ignore this if we just changed it */
449
450 if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
451 continue; /* Ignore if the value is too long */
452
453 if ((test = cups_test_constraints(dinfo, attr->name, value, num_myres,
454 myres, NULL, NULL)) == NULL)
455 {
456 /*
457 * That worked, flag it...
458 */
459
460 changed = 1;
461 }
462 else
463 cupsArrayDelete(test);
464
465 /*
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...
469 */
470
471 num_myres = cupsAddOption(attr->name, value, num_myres, &myres);
472 }
473 }
474
475 if (!changed)
476 {
477 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
478 have_conflicts = -1;
479 goto cleanup;
480 }
481
482 cupsArrayClear(pass);
483
484 cupsArrayDelete(active);
485 active = NULL;
486 }
487
a674809b 488 if (tries >= 100)
a98cf586 489 {
490 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
491 have_conflicts = -1;
492 goto cleanup;
493 }
494
495 /*
496 * Copy resolved options as needed...
497 */
498
499 if (num_resolved)
500 {
501 for (i = num_myres, myoption = myres; i > 0; i --, myoption ++)
502 {
503 if ((myvalue = cupsGetOption(myoption->name, num_options,
504 options)) == NULL ||
505 strcmp(myvalue, myoption->value))
506 {
507 if (new_option && !strcmp(new_option, myoption->name) &&
508 new_value && !strcmp(new_value, myoption->value))
509 continue;
510
511 *num_resolved = cupsAddOption(myoption->name, myoption->value,
512 *num_resolved, resolved);
513 }
514 }
515 }
516
517 /*
518 * Clean up...
519 */
520
521 cleanup:
522
523 cupsArrayDelete(active);
524 cupsArrayDelete(pass);
525 cupsArrayDelete(resolvers);
526
527 if (num_resolved)
528 {
529 /*
530 * Free shadow copy of options...
531 */
532
533 cupsFreeOptions(num_myres, myres);
534 }
535
536 if (num_conflicts)
537 {
538 /*
539 * Return conflicting options to caller...
540 */
541
542 *num_conflicts = num_myconf;
543 *conflicts = myconf;
544 }
545 else
546 {
547 /*
548 * Free conflicting options...
549 */
550
551 cupsFreeOptions(num_myconf, myconf);
552 }
553
554 return (have_conflicts);
fb5d8bf6 555}
556
557
558/*
559 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
560 * destination.
561 *
562 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
563 * value. @code NULL@ is returned on error.
564 *
c45401bb 565 * @since CUPS 1.6/OS X 10.8@
fb5d8bf6 566 */
567
568cups_dinfo_t * /* O - Destination information */
569cupsCopyDestInfo(
570 http_t *http, /* I - Connection to destination */
571 cups_dest_t *dest) /* I - Destination */
572{
573 cups_dinfo_t *dinfo; /* Destination information */
574 ipp_t *request, /* Get-Printer-Attributes request */
575 *response; /* Supported attributes */
a98cf586 576 int tries, /* Number of tries so far */
577 delay, /* Current retry delay */
578 prev_delay; /* Next retry delay */
fb5d8bf6 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 */
585 "job-template",
586 "media-col-database",
587 "printer-description"
588 };
589
590
591 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", http, dest,
592 dest ? dest->name : ""));
593
594 /*
595 * Range check input...
596 */
597
598 if (!http || !dest)
599 return (NULL);
600
601 /*
602 * Get the printer URI and resource path...
603 */
604
605 if ((uri = _cupsGetDestResource(dest, resource, sizeof(resource))) == NULL)
606 return (NULL);
607
608 /*
609 * Get the supported attributes...
610 */
611
a98cf586 612 delay = 1;
613 prev_delay = 1;
614 tries = 0;
615 version = 20;
fb5d8bf6 616
617 do
618 {
619 /*
620 * Send a Get-Printer-Attributes request...
621 */
622
d6ff282a 623 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
fb5d8bf6 624 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
625 uri);
626 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
627 NULL, cupsUser());
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();
634
d6ff282a 635 if (status > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
fb5d8bf6 636 {
637 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
638 "returned %s (%s)", dest->name, ippErrorString(status),
639 cupsLastErrorString()));
640
641 ippDelete(response);
642 response = NULL;
643
d6ff282a 644 if (status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED && version > 11)
fb5d8bf6 645 version = 11;
d6ff282a 646 else if (status == IPP_STATUS_ERROR_BUSY)
a98cf586 647 {
648 sleep(delay);
649
650 delay = _cupsNextDelay(delay, &prev_delay);
651 }
fb5d8bf6 652 else
653 return (NULL);
654 }
a98cf586 655
656 tries ++;
fb5d8bf6 657 }
a98cf586 658 while (!response && tries < 10);
659
660 if (!response)
661 return (NULL);
fb5d8bf6 662
663 /*
664 * Allocate a cups_dinfo_t structure and return it...
665 */
666
667 if ((dinfo = calloc(1, sizeof(cups_dinfo_t))) == NULL)
668 {
d6ff282a 669 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
fb5d8bf6 670 ippDelete(response);
671 return (NULL);
672 }
673
3945ef67 674 dinfo->version = version;
fb5d8bf6 675 dinfo->uri = uri;
676 dinfo->resource = _cupsStrAlloc(resource);
677 dinfo->attrs = response;
678
679 return (dinfo);
680}
681
682
683/*
684 * 'cupsFreeDestInfo()' - Free destination information obtained using
685 * @link cupsCopyDestInfo@.
686 */
687
688void
689cupsFreeDestInfo(cups_dinfo_t *dinfo) /* I - Destination information */
690{
691 /*
692 * Range check input...
693 */
694
695 if (!dinfo)
696 return;
697
698 /*
699 * Free memory and return...
700 */
701
702 _cupsStrFree(dinfo->resource);
703
fb5d8bf6 704 cupsArrayDelete(dinfo->constraints);
a98cf586 705 cupsArrayDelete(dinfo->resolvers);
fb5d8bf6 706
707 cupsArrayDelete(dinfo->localizations);
708
709 cupsArrayDelete(dinfo->media_db);
710
a98cf586 711 ippDelete(dinfo->attrs);
712
fb5d8bf6 713 free(dinfo);
714}
715
716
717/*
718 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
719 *
305b3fd5 720 * The "media" string is a PWG media name. "Flags" provides some matching
fb5d8bf6 721 * guidance (multiple flags can be combined):
722 *
305b3fd5 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, and
fb5d8bf6 727 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
728 * size amongst the "ready" media.
729 *
730 * The matching result (if any) is returned in the "cups_size_t" structure.
731 *
732 * Returns 1 when there is a match and 0 if there is not a match.
733 *
c45401bb 734 * @since CUPS 1.6/OS X 10.8@
fb5d8bf6 735 */
736
737int /* O - 1 on match, 0 on failure */
738cupsGetDestMediaByName(
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 */
745{
e106c1dc 746 pwg_media_t *pwg; /* PWG media info */
fb5d8bf6 747
748
749 /*
750 * Range check input...
751 */
752
753 if (size)
754 memset(size, 0, sizeof(cups_size_t));
755
756 if (!http || !dest || !dinfo || !media || !size)
757 {
d6ff282a 758 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
fb5d8bf6 759 return (0);
760 }
761
762 /*
763 * Lookup the media size name...
764 */
765
e106c1dc 766 if ((pwg = pwgMediaForPWG(media)) == NULL)
767 if ((pwg = pwgMediaForLegacy(media)) == NULL)
fb5d8bf6 768 {
769 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media));
d6ff282a 770 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown media size name."), 1);
fb5d8bf6 771 return (0);
772 }
773
774 /*
775 * Lookup the size...
776 */
777
778 return (cups_get_media_db(dinfo, pwg, flags, size));
779}
780
781
782/*
783 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
784 *
305b3fd5 785 * "Width" and "length" are the dimensions in hundredths of millimeters.
786 * "Flags" provides some matching guidance (multiple flags can be combined):
fb5d8bf6 787 *
305b3fd5 788 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
789 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
790 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
791 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
fb5d8bf6 792 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
793 * size amongst the "ready" media.
794 *
795 * The matching result (if any) is returned in the "cups_size_t" structure.
796 *
797 * Returns 1 when there is a match and 0 if there is not a match.
798 *
c45401bb 799 * @since CUPS 1.6/OS X 10.8@
fb5d8bf6 800 */
801
802int /* O - 1 on match, 0 on failure */
803cupsGetDestMediaBySize(
804 http_t *http, /* I - Connection to destination */
805 cups_dest_t *dest, /* I - Destination */
806 cups_dinfo_t *dinfo, /* I - Destination information */
807 int width, /* I - Media width in hundredths of
808 * of millimeters */
809 int length, /* I - Media length in hundredths of
810 * of millimeters */
811 unsigned flags, /* I - Media matching flags */
812 cups_size_t *size) /* O - Media size information */
813{
e106c1dc 814 pwg_media_t *pwg; /* PWG media info */
fb5d8bf6 815
816
817 /*
818 * Range check input...
819 */
820
821 if (size)
822 memset(size, 0, sizeof(cups_size_t));
823
824 if (!http || !dest || !dinfo || width <= 0 || length <= 0 || !size)
825 {
d6ff282a 826 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
fb5d8bf6 827 return (0);
828 }
829
830 /*
831 * Lookup the media size name...
832 */
833
e106c1dc 834 if ((pwg = pwgMediaForSize(width, length)) == NULL)
fb5d8bf6 835 {
836 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width,
837 length));
d6ff282a 838 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid media size."), 1);
fb5d8bf6 839 return (0);
840 }
841
842 /*
843 * Lookup the size...
844 */
845
846 return (cups_get_media_db(dinfo, pwg, flags, size));
847}
848
849
a98cf586 850/*
851 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
852 */
853
854static void
855cups_add_dconstres(
856 cups_array_t *a, /* I - Array */
857 ipp_t *collection) /* I - Collection value */
858{
859 ipp_attribute_t *attr; /* Attribute */
860 _cups_dconstres_t *temp; /* Current constraint/resolver */
861
862
863 if ((attr = ippFindAttribute(collection, "resolver-name",
864 IPP_TAG_NAME)) == NULL)
865 return;
866
867 if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL)
868 return;
869
870 temp->name = attr->values[0].string.text;
871 temp->collection = collection;
872
873 cupsArrayAdd(a, temp);
874}
875
876
877/*
878 * 'cups_compare_dconstres()' - Compare to resolver entries.
879 */
880
881static int /* O - Result of comparison */
882cups_compare_dconstres(
883 _cups_dconstres_t *a, /* I - First resolver */
884 _cups_dconstres_t *b) /* I - Second resolver */
885{
886 return (strcmp(a->name, b->name));
887}
888
889
fb5d8bf6 890/*
891 * 'cups_compare_media_db()' - Compare two media entries.
892 */
893
894static int /* O - Result of comparison */
895cups_compare_media_db(
896 _cups_media_db_t *a, /* I - First media entries */
897 _cups_media_db_t *b) /* I - Second media entries */
898{
899 int result; /* Result of comparison */
900
901
902 if ((result = a->width - b->width) == 0)
903 result = a->length - b->length;
904
905 return (result);
906}
907
908
909/*
910 * 'cups_copy_media_db()' - Copy a media entry.
911 */
912
913static _cups_media_db_t * /* O - New media entry */
914cups_copy_media_db(
915 _cups_media_db_t *mdb) /* I - Media entry to copy */
916{
917 _cups_media_db_t *temp; /* New media entry */
918
919
920 if ((temp = calloc(1, sizeof(_cups_media_db_t))) == NULL)
921 return (NULL);
922
923 if (mdb->color)
924 temp->color = _cupsStrAlloc(mdb->color);
925 if (mdb->key)
926 temp->key = _cupsStrAlloc(mdb->key);
927 if (mdb->info)
928 temp->info = _cupsStrAlloc(mdb->info);
929 if (mdb->size_name)
930 temp->size_name = _cupsStrAlloc(mdb->size_name);
931 if (mdb->source)
932 temp->source = _cupsStrAlloc(mdb->source);
933 if (mdb->type)
934 temp->type = _cupsStrAlloc(mdb->type);
935
936 temp->width = mdb->width;
937 temp->length = mdb->length;
938 temp->bottom = mdb->bottom;
939 temp->left = mdb->left;
940 temp->right = mdb->right;
941 temp->top = mdb->top;
942
943 return (temp);
944}
945
946
a98cf586 947/*
948 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
949 */
950
951static void
952cups_create_constraints(
953 cups_dinfo_t *dinfo) /* I - Destination information */
954{
955 int i; /* Looping var */
956 ipp_attribute_t *attr; /* Attribute */
957 _ipp_value_t *val; /* Current value */
958
959
960 dinfo->constraints = cupsArrayNew3(NULL, NULL, NULL, 0, NULL,
961 (cups_afree_func_t)free);
962 dinfo->resolvers = cupsArrayNew3((cups_array_func_t)cups_compare_dconstres,
963 NULL, NULL, 0, NULL,
964 (cups_afree_func_t)free);
965
966 if ((attr = ippFindAttribute(dinfo->attrs, "job-constraints-supported",
967 IPP_TAG_BEGIN_COLLECTION)) != NULL)
968 {
969 for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
970 cups_add_dconstres(dinfo->constraints, val->collection);
971 }
972
973 if ((attr = ippFindAttribute(dinfo->attrs, "job-resolvers-supported",
974 IPP_TAG_BEGIN_COLLECTION)) != NULL)
975 {
976 for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
977 cups_add_dconstres(dinfo->resolvers, val->collection);
978 }
979}
980
981
982/*
983 * 'cups_create_defaults()' - Create the -default option array.
984 *
985 * TODO: Need to support collection defaults...
986 */
987
988static void
989cups_create_defaults(
990 cups_dinfo_t *dinfo) /* I - Destination information */
991{
992 ipp_attribute_t *attr; /* Current attribute */
993 char name[IPP_MAX_NAME + 1],
994 /* Current name */
995 *nameptr, /* Pointer into current name */
996 value[2048]; /* Current value */
997
998
999 /*
1000 * Iterate through the printer attributes looking for xxx-default and adding
1001 * xxx=value to the defaults option array.
1002 */
1003
1004 for (attr = ippFirstAttribute(dinfo->attrs);
1005 attr;
1006 attr = ippNextAttribute(dinfo->attrs))
1007 {
1008 if (!attr->name || attr->group_tag != IPP_TAG_PRINTER)
1009 continue;
1010
1011 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
1012 continue; /* TODO: STR #4096 */
1013
1014 if ((nameptr = attr->name + strlen(attr->name) - 8) <= attr->name ||
1015 strcmp(nameptr, "-default"))
1016 continue;
1017
1018 strlcpy(name, attr->name, sizeof(name));
1019 if ((nameptr = name + strlen(name) - 8) <= name ||
1020 strcmp(nameptr, "-default"))
1021 continue;
1022
1023 *nameptr = '\0';
1024
1025 if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
1026 continue;
1027
1028 dinfo->num_defaults = cupsAddOption(name, value, dinfo->num_defaults,
1029 &dinfo->defaults);
1030 }
1031}
1032
1033
fb5d8bf6 1034/*
1035 * 'cups_create_media_db()' - Create the media database.
1036 */
1037
1038static void
1039cups_create_media_db(
1040 cups_dinfo_t *dinfo) /* I - Destination information */
1041{
1042 int i; /* Looping var */
1043 _ipp_value_t *val; /* Current value */
1044 ipp_attribute_t *media_col_db, /* media-col-database */
1045 *media_attr, /* media-xxx */
1046 *x_dimension, /* x-dimension */
1047 *y_dimension; /* y-dimension */
e106c1dc 1048 pwg_media_t *pwg; /* PWG media info */
fb5d8bf6 1049 _cups_media_db_t mdb; /* Media entry */
1050
1051
1052 dinfo->media_db = cupsArrayNew3((cups_array_func_t)cups_compare_media_db,
1053 NULL, NULL, 0,
1054 (cups_acopy_func_t)cups_copy_media_db,
1055 (cups_afree_func_t)cups_free_media_db);
1056 dinfo->min_size.width = INT_MAX;
1057 dinfo->min_size.length = INT_MAX;
1058 dinfo->max_size.width = 0;
1059 dinfo->max_size.length = 0;
1060
1061 if ((media_col_db = ippFindAttribute(dinfo->attrs, "media-col-database",
1062 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1063 {
1064 _ipp_value_t *custom = NULL; /* Custom size range value */
1065
1066 for (i = media_col_db->num_values, val = media_col_db->values;
1067 i > 0;
1068 i --, val ++)
1069 {
1070 memset(&mdb, 0, sizeof(mdb));
1071
1072 if ((media_attr = ippFindAttribute(val->collection, "media-size",
1073 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1074 {
1075 ipp_t *media_size = media_attr->values[0].collection;
1076 /* media-size collection value */
1077
1078 if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
1079 IPP_TAG_INTEGER)) != NULL &&
1080 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1081 IPP_TAG_INTEGER)) != NULL)
1082 {
1083 mdb.width = x_dimension->values[0].integer;
1084 mdb.length = y_dimension->values[0].integer;
1085 }
1086 else if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
1087 IPP_TAG_RANGE)) != NULL &&
1088 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1089 IPP_TAG_RANGE)) != NULL)
1090 {
1091 /*
1092 * Custom size range; save this as the custom size value with default
1093 * margins, then continue; we'll capture the real margins below...
1094 */
1095
1096 custom = val;
1097
1098 dinfo->min_size.width = x_dimension->values[0].range.lower;
1099 dinfo->min_size.length = y_dimension->values[0].range.lower;
1100 dinfo->min_size.left =
1101 dinfo->min_size.right = 635; /* Default 1/4" side margins */
1102 dinfo->min_size.top =
1103 dinfo->min_size.bottom = 1270; /* Default 1/2" top/bottom margins */
1104
1105 dinfo->max_size.width = x_dimension->values[0].range.upper;
1106 dinfo->max_size.length = y_dimension->values[0].range.upper;
1107 dinfo->max_size.left =
1108 dinfo->max_size.right = 635; /* Default 1/4" side margins */
1109 dinfo->max_size.top =
1110 dinfo->max_size.bottom = 1270; /* Default 1/2" top/bottom margins */
1111
1112 continue;
1113 }
1114 }
1115
1116 if ((media_attr = ippFindAttribute(val->collection, "media-color",
1117 IPP_TAG_ZERO)) != NULL &&
1118 (media_attr->value_tag == IPP_TAG_NAME ||
1119 media_attr->value_tag == IPP_TAG_NAMELANG ||
1120 media_attr->value_tag == IPP_TAG_KEYWORD))
1121 mdb.color = media_attr->values[0].string.text;
1122
1123 if ((media_attr = ippFindAttribute(val->collection, "media-info",
1124 IPP_TAG_TEXT)) != NULL)
1125 mdb.info = media_attr->values[0].string.text;
1126
1127 if ((media_attr = ippFindAttribute(val->collection, "media-key",
1128 IPP_TAG_ZERO)) != NULL &&
1129 (media_attr->value_tag == IPP_TAG_NAME ||
1130 media_attr->value_tag == IPP_TAG_NAMELANG ||
1131 media_attr->value_tag == IPP_TAG_KEYWORD))
1132 mdb.key = media_attr->values[0].string.text;
1133
1134 if ((media_attr = ippFindAttribute(val->collection, "media-size-name",
1135 IPP_TAG_ZERO)) != NULL &&
1136 (media_attr->value_tag == IPP_TAG_NAME ||
1137 media_attr->value_tag == IPP_TAG_NAMELANG ||
1138 media_attr->value_tag == IPP_TAG_KEYWORD))
1139 mdb.size_name = media_attr->values[0].string.text;
1140
1141 if ((media_attr = ippFindAttribute(val->collection, "media-source",
1142 IPP_TAG_ZERO)) != NULL &&
1143 (media_attr->value_tag == IPP_TAG_NAME ||
1144 media_attr->value_tag == IPP_TAG_NAMELANG ||
1145 media_attr->value_tag == IPP_TAG_KEYWORD))
1146 mdb.source = media_attr->values[0].string.text;
1147
1148 if ((media_attr = ippFindAttribute(val->collection, "media-type",
1149 IPP_TAG_ZERO)) != NULL &&
1150 (media_attr->value_tag == IPP_TAG_NAME ||
1151 media_attr->value_tag == IPP_TAG_NAMELANG ||
1152 media_attr->value_tag == IPP_TAG_KEYWORD))
1153 mdb.type = media_attr->values[0].string.text;
1154
1155 if ((media_attr = ippFindAttribute(val->collection, "media-bottom-margin",
1156 IPP_TAG_INTEGER)) != NULL)
1157 mdb.bottom = media_attr->values[0].integer;
1158
1159 if ((media_attr = ippFindAttribute(val->collection, "media-left-margin",
1160 IPP_TAG_INTEGER)) != NULL)
1161 mdb.left = media_attr->values[0].integer;
1162
1163 if ((media_attr = ippFindAttribute(val->collection, "media-right-margin",
1164 IPP_TAG_INTEGER)) != NULL)
1165 mdb.right = media_attr->values[0].integer;
1166
1167 if ((media_attr = ippFindAttribute(val->collection, "media-top-margin",
1168 IPP_TAG_INTEGER)) != NULL)
1169 mdb.top = media_attr->values[0].integer;
1170
1171 cupsArrayAdd(dinfo->media_db, &mdb);
1172 }
1173
1174 if (custom)
1175 {
1176 if ((media_attr = ippFindAttribute(custom->collection,
1177 "media-bottom-margin",
1178 IPP_TAG_INTEGER)) != NULL)
1179 {
1180 dinfo->min_size.top =
1181 dinfo->max_size.top = media_attr->values[0].integer;
1182 }
1183
1184 if ((media_attr = ippFindAttribute(custom->collection,
1185 "media-left-margin",
1186 IPP_TAG_INTEGER)) != NULL)
1187 {
1188 dinfo->min_size.left =
1189 dinfo->max_size.left = media_attr->values[0].integer;
1190 }
1191
1192 if ((media_attr = ippFindAttribute(custom->collection,
1193 "media-right-margin",
1194 IPP_TAG_INTEGER)) != NULL)
1195 {
1196 dinfo->min_size.right =
1197 dinfo->max_size.right = media_attr->values[0].integer;
1198 }
1199
1200 if ((media_attr = ippFindAttribute(custom->collection,
1201 "media-top-margin",
1202 IPP_TAG_INTEGER)) != NULL)
1203 {
1204 dinfo->min_size.top =
1205 dinfo->max_size.top = media_attr->values[0].integer;
1206 }
1207 }
1208 }
1209 else if ((media_attr = ippFindAttribute(dinfo->attrs, "media-supported",
1210 IPP_TAG_ZERO)) != NULL &&
1211 (media_attr->value_tag == IPP_TAG_NAME ||
1212 media_attr->value_tag == IPP_TAG_NAMELANG ||
1213 media_attr->value_tag == IPP_TAG_KEYWORD))
1214 {
1215 memset(&mdb, 0, sizeof(mdb));
1216
1217 mdb.left =
1218 mdb.right = 635; /* Default 1/4" side margins */
1219 mdb.top =
1220 mdb.bottom = 1270; /* Default 1/2" top/bottom margins */
1221
75d1b92c 1222 for (i = media_attr->num_values, val = media_attr->values;
fb5d8bf6 1223 i > 0;
1224 i --, val ++)
1225 {
e106c1dc 1226 if ((pwg = pwgMediaForPWG(val->string.text)) == NULL)
1227 if ((pwg = pwgMediaForLegacy(val->string.text)) == NULL)
fb5d8bf6 1228 {
1229 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
1230 val->string.text));
1231 continue;
1232 }
1233
1234 mdb.width = pwg->width;
1235 mdb.length = pwg->length;
1236
1237 if (!strncmp(val->string.text, "custom_min_", 11))
1238 {
1239 mdb.size_name = NULL;
1240 dinfo->min_size = mdb;
1241 }
1242 else if (!strncmp(val->string.text, "custom_max_", 11))
1243 {
1244 mdb.size_name = NULL;
1245 dinfo->max_size = mdb;
1246 }
1247 else
1248 {
1249 mdb.size_name = val->string.text;
1250
1251 cupsArrayAdd(dinfo->media_db, &mdb);
1252 }
1253 }
1254 }
1255}
1256
1257
1258/*
1259 * 'cups_free_media_cb()' - Free a media entry.
1260 */
1261
1262static void
1263cups_free_media_db(
1264 _cups_media_db_t *mdb) /* I - Media entry to free */
1265{
1266 if (mdb->color)
1267 _cupsStrFree(mdb->color);
1268 if (mdb->key)
1269 _cupsStrFree(mdb->key);
1270 if (mdb->info)
1271 _cupsStrFree(mdb->info);
1272 if (mdb->size_name)
1273 _cupsStrFree(mdb->size_name);
1274 if (mdb->source)
1275 _cupsStrFree(mdb->source);
1276 if (mdb->type)
1277 _cupsStrFree(mdb->type);
1278
1279 free(mdb);
1280}
1281
1282
1283/*
1284 * 'cups_get_media_db()' - Lookup the media entry for a given size.
1285 */
1286
1287static int /* O - 1 on match, 0 on failure */
1288cups_get_media_db(cups_dinfo_t *dinfo, /* I - Destination information */
e106c1dc 1289 pwg_media_t *pwg, /* I - PWG media info */
fb5d8bf6 1290 unsigned flags, /* I - Media matching flags */
1291 cups_size_t *size) /* O - Media size/margin/name info */
1292{
1293 _cups_media_db_t *mdb, /* Current media database entry */
1294 *best = NULL, /* Best matching entry */
1295 key; /* Search key */
1296
1297
1298 /*
1299 * Create the media database as needed...
1300 */
1301
1302 if (!dinfo->media_db)
1303 cups_create_media_db(dinfo);
1304
1305 /*
1306 * Find a match...
1307 */
1308
1309 memset(&key, 0, sizeof(key));
1310 key.width = pwg->width;
1311 key.length = pwg->length;
1312
1313 if ((mdb = cupsArrayFind(dinfo->media_db, &key)) != NULL)
1314 {
1315 /*
1316 * Found an exact match, let's figure out the best margins for the flags
1317 * supplied...
1318 */
1319
1320 best = mdb;
1321
1322 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1323 {
1324 /*
1325 * Look for the smallest margins...
1326 */
1327
1328 if (best->left != 0 || best->right != 0 || best->top != 0 ||
1329 best->bottom != 0)
1330 {
1331 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
1332 mdb && !cups_compare_media_db(mdb, &key);
1333 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1334 {
1335 if (mdb->left <= best->left && mdb->right <= best->right &&
1336 mdb->top <= best->top && mdb->bottom <= best->bottom)
1337 {
1338 best = mdb;
1339 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
1340 mdb->top == 0)
1341 break;
1342 }
1343 }
1344 }
1345
1346 /*
1347 * If we need an exact match, return no-match if the size is not
1348 * borderless.
1349 */
1350
1351 if ((flags & CUPS_MEDIA_FLAGS_EXACT) &&
1352 (best->left || best->right || best->top || best->bottom))
1353 return (0);
1354 }
1355 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1356 {
1357 /*
1358 * Look for the largest margins...
1359 */
1360
1361 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
1362 mdb && !cups_compare_media_db(mdb, &key);
1363 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1364 {
1365 if (mdb->left >= best->left && mdb->right >= best->right &&
1366 mdb->top >= best->top && mdb->bottom >= best->bottom)
1367 best = mdb;
1368 }
1369 }
1370 else
1371 {
1372 /*
1373 * Look for the smallest non-zero margins...
1374 */
1375
1376 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
1377 mdb && !cups_compare_media_db(mdb, &key);
1378 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1379 {
1380 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
1381 ((mdb->right > 0 && mdb->right <= best->right) ||
1382 best->right == 0) &&
1383 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
1384 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
1385 best->bottom == 0))
1386 best = mdb;
1387 }
1388 }
1389 }
1390 else if (flags & CUPS_MEDIA_FLAGS_EXACT)
1391 {
1392 /*
1393 * See if we can do this as a custom size...
1394 */
1395
1396 if (pwg->width < dinfo->min_size.width ||
1397 pwg->width > dinfo->max_size.width ||
1398 pwg->length < dinfo->min_size.length ||
1399 pwg->length > dinfo->max_size.length)
1400 return (0); /* Out of range */
1401
1402 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
1403 (dinfo->min_size.left > 0 || dinfo->min_size.right > 0 ||
1404 dinfo->min_size.top > 0 || dinfo->min_size.bottom > 0))
1405 return (0); /* Not borderless */
1406
1407 key.size_name = (char *)pwg->pwg;
1408 key.bottom = dinfo->min_size.bottom;
1409 key.left = dinfo->min_size.left;
1410 key.right = dinfo->min_size.right;
1411 key.top = dinfo->min_size.top;
1412
1413 best = &key;
1414 }
1415 else if (pwg->width >= dinfo->min_size.width &&
1416 pwg->width <= dinfo->max_size.width &&
1417 pwg->length >= dinfo->min_size.length &&
1418 pwg->length <= dinfo->max_size.length)
1419 {
1420 /*
1421 * Map to custom size...
1422 */
1423
1424 key.size_name = (char *)pwg->pwg;
1425 key.bottom = dinfo->min_size.bottom;
1426 key.left = dinfo->min_size.left;
1427 key.right = dinfo->min_size.right;
1428 key.top = dinfo->min_size.top;
1429
1430 best = &key;
1431 }
1432 else
1433 {
1434 /*
1435 * Find a close size...
1436 */
1437
1438 for (mdb = (_cups_media_db_t *)cupsArrayFirst(dinfo->media_db);
1439 mdb;
1440 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1441 if (cups_is_close_media_db(mdb, &key))
1442 break;
1443
1444 if (!mdb)
1445 return (0);
1446
1447 best = mdb;
1448
1449 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1450 {
1451 /*
1452 * Look for the smallest margins...
1453 */
1454
1455 if (best->left != 0 || best->right != 0 || best->top != 0 ||
1456 best->bottom != 0)
1457 {
1458 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
1459 mdb && cups_is_close_media_db(mdb, &key);
1460 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1461 {
1462 if (mdb->left <= best->left && mdb->right <= best->right &&
1463 mdb->top <= best->top && mdb->bottom <= best->bottom)
1464 {
1465 best = mdb;
1466 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
1467 mdb->top == 0)
1468 break;
1469 }
1470 }
1471 }
1472 }
1473 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1474 {
1475 /*
1476 * Look for the largest margins...
1477 */
1478
1479 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
1480 mdb && cups_is_close_media_db(mdb, &key);
1481 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1482 {
1483 if (mdb->left >= best->left && mdb->right >= best->right &&
1484 mdb->top >= best->top && mdb->bottom >= best->bottom)
1485 best = mdb;
1486 }
1487 }
1488 else
1489 {
1490 /*
1491 * Look for the smallest non-zero margins...
1492 */
1493
1494 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
1495 mdb && cups_is_close_media_db(mdb, &key);
1496 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1497 {
1498 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
1499 ((mdb->right > 0 && mdb->right <= best->right) ||
1500 best->right == 0) &&
1501 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
1502 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
1503 best->bottom == 0))
1504 best = mdb;
1505 }
1506 }
1507 }
1508
1509 if (best)
1510 {
1511 /*
1512 * Return the matching size...
1513 */
1514
1515 if (best->size_name)
1516 strlcpy(size->media, best->size_name, sizeof(size->media));
1517 else if (best->key)
1518 strlcpy(size->media, best->key, sizeof(size->media));
1519 else
1520 strlcpy(size->media, pwg->pwg, sizeof(size->media));
1521
1522 size->width = best->width;
1523 size->length = best->length;
1524 size->bottom = best->bottom;
1525 size->left = best->left;
1526 size->right = best->right;
1527 size->top = best->top;
1528
1529 return (1);
1530 }
1531
1532 return (0);
1533}
1534
1535
1536/*
1537 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
1538 * close to the same size.
1539 *
1540 * Currently we use 5 points (from PostScript) as the matching range...
1541 */
1542
1543static int /* O - 1 if the sizes are close */
1544cups_is_close_media_db(
1545 _cups_media_db_t *a, /* I - First media entries */
1546 _cups_media_db_t *b) /* I - Second media entries */
1547{
1548 int dwidth, /* Difference in width */
1549 dlength; /* Difference in length */
1550
1551
1552 dwidth = a->width - b->width;
1553 dlength = a->length - b->length;
1554
1555 return (dwidth >= -176 && dwidth <= 176 &&
1556 dlength >= -176 && dlength <= 176);
1557}
1558
1559
a98cf586 1560/*
1561 * 'cups_test_constraints()' - Test constraints.
1562 *
1563 * TODO: STR #4096 - Need to properly support media-col contraints...
1564 */
1565
1566static cups_array_t * /* O - Active constraints */
1567cups_test_constraints(
1568 cups_dinfo_t *dinfo, /* I - Destination information */
1569 const char *new_option, /* I - Newly selected option */
1570 const char *new_value, /* I - Newly selected value */
1571 int num_options, /* I - Number of options */
1572 cups_option_t *options, /* I - Options */
1573 int *num_conflicts, /* O - Number of conflicting options */
1574 cups_option_t **conflicts) /* O - Conflicting options */
1575{
1576 int i, /* Looping var */
1577 match; /* Value matches? */
1578 int num_matching; /* Number of matching options */
1579 cups_option_t *matching; /* Matching options */
1580 _cups_dconstres_t *c; /* Current constraint */
1581 cups_array_t *active = NULL; /* Active constraints */
1582 ipp_attribute_t *attr; /* Current attribute */
1583 _ipp_value_t *attrval; /* Current attribute value */
1584 const char *value; /* Current value */
1585 char temp[1024]; /* Temporary string */
1586 int int_value; /* Integer value */
1587 int xres_value, /* Horizontal resolution */
1588 yres_value; /* Vertical resolution */
1589 ipp_res_t units_value; /* Resolution units */
1590
1591
1592 for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints);
1593 c;
1594 c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints))
1595 {
1596 num_matching = 0;
1597 matching = NULL;
1598
1599 for (attr = ippFirstAttribute(c->collection);
1600 attr;
1601 attr = ippNextAttribute(c->collection))
1602 {
1603 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
1604 break; /* TODO: STR #4096 */
1605
1606 /*
1607 * Get the value for the current attribute in the constraint...
1608 */
1609
1610 if (new_option && new_value && !strcmp(attr->name, new_option))
1611 value = new_value;
1612 else if ((value = cupsGetOption(attr->name, num_options,
1613 options)) == NULL)
1614 value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults);
1615
1616 if (!value)
1617 {
1618 /*
1619 * Not set so this constraint does not apply...
1620 */
1621
1622 break;
1623 }
1624
1625 match = 0;
1626
1627 switch (attr->value_tag)
1628 {
1629 case IPP_TAG_INTEGER :
1630 case IPP_TAG_ENUM :
1631 int_value = atoi(value);
1632
1633 for (i = attr->num_values, attrval = attr->values;
1634 i > 0;
1635 i --, attrval ++)
1636 {
1637 if (attrval->integer == int_value)
1638 {
1639 match = 1;
1640 break;
1641 }
1642 }
1643 break;
1644
1645 case IPP_TAG_BOOLEAN :
1646 int_value = !strcmp(value, "true");
1647
1648 for (i = attr->num_values, attrval = attr->values;
1649 i > 0;
1650 i --, attrval ++)
1651 {
1652 if (attrval->boolean == int_value)
1653 {
1654 match = 1;
1655 break;
1656 }
1657 }
1658 break;
1659
1660 case IPP_TAG_RANGE :
1661 int_value = atoi(value);
1662
1663 for (i = attr->num_values, attrval = attr->values;
1664 i > 0;
1665 i --, attrval ++)
1666 {
1667 if (int_value >= attrval->range.lower &&
1668 int_value <= attrval->range.upper)
1669 {
1670 match = 1;
1671 break;
1672 }
1673 }
1674 break;
1675
1676 case IPP_TAG_RESOLUTION :
1677 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
1678 {
1679 if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
1680 break;
1681
1682 yres_value = xres_value;
1683 }
1684
1685 if (!strcmp(temp, "dpi"))
1686 units_value = IPP_RES_PER_INCH;
1687 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
1688 units_value = IPP_RES_PER_CM;
1689 else
1690 break;
1691
1692 for (i = attr->num_values, attrval = attr->values;
1693 i > 0;
1694 i --, attrval ++)
1695 {
1696 if (attrval->resolution.xres == xres_value &&
1697 attrval->resolution.yres == yres_value &&
1698 attrval->resolution.units == units_value)
1699 {
1700 match = 1;
1701 break;
1702 }
1703 }
1704 break;
1705
1706 case IPP_TAG_TEXT :
1707 case IPP_TAG_NAME :
1708 case IPP_TAG_KEYWORD :
1709 case IPP_TAG_CHARSET :
1710 case IPP_TAG_URI :
1711 case IPP_TAG_URISCHEME :
1712 case IPP_TAG_MIMETYPE :
1713 case IPP_TAG_LANGUAGE :
1714 case IPP_TAG_TEXTLANG :
1715 case IPP_TAG_NAMELANG :
1716 for (i = attr->num_values, attrval = attr->values;
1717 i > 0;
1718 i --, attrval ++)
1719 {
1720 if (!strcmp(attrval->string.text, value))
1721 {
1722 match = 1;
1723 break;
1724 }
1725 }
1726 break;
1727
1728 default :
1729 break;
1730 }
1731
1732 if (!match)
1733 break;
1734
1735 num_matching = cupsAddOption(attr->name, value, num_matching, &matching);
1736 }
1737
1738 if (!attr)
1739 {
1740 if (!active)
1741 active = cupsArrayNew(NULL, NULL);
1742
1743 cupsArrayAdd(active, c);
1744
1745 if (num_conflicts && conflicts)
1746 {
1747 cups_option_t *moption; /* Matching option */
1748
1749 for (i = num_matching, moption = matching; i > 0; i --, moption ++)
1750 *num_conflicts = cupsAddOption(moption->name, moption->value,
1751 *num_conflicts, conflicts);
1752 }
1753 }
1754
1755 cupsFreeOptions(num_matching, matching);
1756 }
1757
1758 return (active);
1759}
1760
1761
fb5d8bf6 1762/*
1763 * End of "$Id$".
1764 */