]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/dest-options.c
Merge changes from CUPS 1.6svn-r10188, including changes for <rdar://problem/10127258...
[thirdparty/cups.git] / cups / dest-options.c
1 /*
2 * "$Id$"
3 *
4 * Destination option/media support for CUPS.
5 *
6 * Copyright 2012 by Apple Inc.
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 *
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_compare_media_db() - Compare two media entries.
29 * cups_copy_media_db() - Copy a media entry.
30 * cups_create_media_db() - Create the media database.
31 * cups_free_media_cb() - Free a media entry.
32 * cups_get_media_db() - Lookup the media entry for a given size.
33 * cups_is_close_media_db() - Compare two media entries to see if they are
34 * close to the same size.
35 */
36
37 /*
38 * Include necessary headers...
39 */
40
41 #include "cups-private.h"
42
43
44 /*
45 * Local functions...
46 */
47
48 static int cups_compare_media_db(_cups_media_db_t *a,
49 _cups_media_db_t *b);
50 static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb);
51 static void cups_create_media_db(cups_dinfo_t *dinfo);
52 static void cups_free_media_db(_cups_media_db_t *mdb);
53 static int cups_get_media_db(cups_dinfo_t *dinfo,
54 _pwg_media_t *pwg, unsigned flags,
55 cups_size_t *size);
56 static int cups_is_close_media_db(_cups_media_db_t *a,
57 _cups_media_db_t *b);
58
59 /*
60 * 'cupsCheckDestSupported()' - Check that the option and value are supported
61 * by the destination.
62 *
63 * Returns 1 if supported, 0 otherwise.
64 *
65 * @since CUPS 1.6@
66 */
67
68 int /* O - 1 if supported, 0 otherwise */
69 cupsCheckDestSupported(
70 http_t *http, /* I - Connection to destination */
71 cups_dest_t *dest, /* I - Destination */
72 cups_dinfo_t *dinfo, /* I - Destination information */
73 const char *option, /* I - Option */
74 const char *value) /* I - Value */
75 {
76 int i; /* Looping var */
77 char temp[1024]; /* Temporary string */
78 int int_value; /* Integer value */
79 int xres_value, /* Horizontal resolution */
80 yres_value; /* Vertical resolution */
81 ipp_res_t units_value; /* Resolution units */
82 ipp_attribute_t *attr; /* Attribute */
83 _ipp_value_t *attrval; /* Current attribute value */
84
85
86 /*
87 * Range check input...
88 */
89
90 if (!http || !dest || !dinfo || !option || !value)
91 return (0);
92
93 /*
94 * Lookup the attribute...
95 */
96
97 if (strstr(option, "-supported"))
98 attr = ippFindAttribute(dinfo->attrs, option, IPP_TAG_ZERO);
99 else
100 {
101 snprintf(temp, sizeof(temp), "%s-supported", option);
102 attr = ippFindAttribute(dinfo->attrs, temp, IPP_TAG_ZERO);
103 }
104
105 if (!attr)
106 return (0);
107
108 /*
109 * Compare values...
110 */
111
112 if (!strcmp(option, "media") && !strncmp(value, "custom_", 7))
113 {
114 /*
115 * Check range of custom media sizes...
116 */
117
118 _pwg_media_t *pwg; /* Current PWG media size info */
119 int min_width, /* Minimum width */
120 min_length, /* Minimum length */
121 max_width, /* Maximum width */
122 max_length; /* Maximum length */
123
124 /*
125 * Get the minimum and maximum size...
126 */
127
128 min_width = min_length = INT_MAX;
129 max_width = max_length = 0;
130
131 for (i = attr->num_values, attrval = attr->values;
132 i > 0;
133 i --, attrval ++)
134 {
135 if (!strncmp(attrval->string.text, "custom_min_", 11) &&
136 (pwg = _pwgMediaForPWG(attrval->string.text)) != NULL)
137 {
138 min_width = pwg->width;
139 min_length = pwg->length;
140 }
141 else if (!strncmp(attrval->string.text, "custom_max_", 11) &&
142 (pwg = _pwgMediaForPWG(attrval->string.text)) != NULL)
143 {
144 max_width = pwg->width;
145 max_length = pwg->length;
146 }
147 }
148
149 /*
150 * Check the range...
151 */
152
153 if (min_width < INT_MAX && max_width > 0 &&
154 (pwg = _pwgMediaForPWG(value)) != NULL &&
155 pwg->width >= min_width && pwg->width <= max_width &&
156 pwg->length >= min_length && pwg->length <= max_length)
157 return (1);
158 }
159 else
160 {
161 /*
162 * Check literal values...
163 */
164
165 switch (attr->value_tag)
166 {
167 case IPP_TAG_INTEGER :
168 case IPP_TAG_ENUM :
169 int_value = atoi(value);
170
171 for (i = 0; i < attr->num_values; i ++)
172 if (attr->values[i].integer == int_value)
173 return (1);
174 break;
175
176 case IPP_TAG_BOOLEAN :
177 return (attr->values[0].boolean);
178
179 case IPP_TAG_RESOLUTION :
180 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
181 {
182 if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
183 return (0);
184
185 yres_value = xres_value;
186 }
187
188 if (!strcmp(temp, "dpi"))
189 units_value = IPP_RES_PER_INCH;
190 else if (!strcmp(temp, "dpc"))
191 units_value = IPP_RES_PER_CM;
192 else
193 return (0);
194
195 for (i = attr->num_values, attrval = attr->values;
196 i > 0;
197 i --, attrval ++)
198 {
199 if (attrval->resolution.xres == xres_value &&
200 attrval->resolution.yres == yres_value &&
201 attrval->resolution.units == units_value)
202 return (1);
203 }
204 break;
205
206 case IPP_TAG_TEXT :
207 case IPP_TAG_NAME :
208 case IPP_TAG_KEYWORD :
209 case IPP_TAG_CHARSET :
210 case IPP_TAG_URI :
211 case IPP_TAG_URISCHEME :
212 case IPP_TAG_MIMETYPE :
213 case IPP_TAG_LANGUAGE :
214 case IPP_TAG_TEXTLANG :
215 case IPP_TAG_NAMELANG :
216 for (i = 0; i < attr->num_values; i ++)
217 if (!strcmp(attr->values[i].string.text, value))
218 return (1);
219 break;
220
221 default :
222 break;
223 }
224 }
225
226 /*
227 * If we get there the option+value is not supported...
228 */
229
230 return (0);
231 }
232
233
234 /*
235 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
236 * option/value pair.
237 *
238 * "num_options" and "options" represent the currently selected options by the
239 * user. "new_option" and "new_value" are the setting the user has just
240 * changed.
241 *
242 * Returns 1 if there is a conflict and 0 otherwise.
243 *
244 * If "num_conflicts" and "conflicts" are not NULL, they are set to contain the
245 * list of conflicting option/value pairs. Similarly, if "num_resolved" and
246 * "resolved" are not NULL they will be set to the list of changes needed to
247 * resolve the conflict.
248 *
249 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
250 * to 0 and NULL, respectively, then the conflict cannot be resolved.
251 *
252 * @since CUPS 1.6@
253 */
254
255 int /* O - 1 if there is a conflict */
256 cupsCopyDestConflicts(
257 http_t *http, /* I - Connection to destination */
258 cups_dest_t *dest, /* I - Destination */
259 cups_dinfo_t *dinfo, /* I - Destination information */
260 int num_options, /* I - Number of current options */
261 cups_option_t *options, /* I - Current options */
262 const char *new_option, /* I - New option */
263 const char *new_value, /* I - New value */
264 int *num_conflicts, /* O - Number of conflicting options */
265 cups_option_t **conflicts, /* O - Conflicting options */
266 int *num_resolved, /* O - Number of options to resolve */
267 cups_option_t **resolved) /* O - Resolved options */
268 {
269 /*
270 * Clear returned values...
271 */
272
273 if (num_conflicts)
274 *num_conflicts = 0;
275
276 if (conflicts)
277 *conflicts = NULL;
278
279 if (num_resolved)
280 *num_resolved = 0;
281
282 if (resolved)
283 *resolved = NULL;
284
285 /*
286 * Range check input...
287 */
288
289 if (!http || !dest || !dinfo || !new_option || !new_value ||
290 (num_conflicts != NULL) != (conflicts != NULL) ||
291 (num_resolved != NULL) != (resolved != NULL))
292 return (0);
293
294 /*
295 * Check for an resolve any conflicts...
296 */
297
298 /* TODO: implement me! */
299
300 return (0);
301 }
302
303
304 /*
305 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
306 * destination.
307 *
308 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
309 * value. @code NULL@ is returned on error.
310 *
311 * @since CUPS 1.6@
312 */
313
314 cups_dinfo_t * /* O - Destination information */
315 cupsCopyDestInfo(
316 http_t *http, /* I - Connection to destination */
317 cups_dest_t *dest) /* I - Destination */
318 {
319 cups_dinfo_t *dinfo; /* Destination information */
320 ipp_t *request, /* Get-Printer-Attributes request */
321 *response; /* Supported attributes */
322 const char *uri; /* Printer URI */
323 char resource[1024]; /* Resource path */
324 int version; /* IPP version */
325 ipp_status_t status; /* Status of request */
326 static const char * const requested_attrs[] =
327 { /* Requested attributes */
328 "job-template",
329 "media-col-database",
330 "printer-description"
331 };
332
333
334 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", http, dest,
335 dest ? dest->name : ""));
336
337 /*
338 * Range check input...
339 */
340
341 if (!http || !dest)
342 return (NULL);
343
344 /*
345 * Get the printer URI and resource path...
346 */
347
348 if ((uri = _cupsGetDestResource(dest, resource, sizeof(resource))) == NULL)
349 return (NULL);
350
351 /*
352 * Get the supported attributes...
353 */
354
355 version = 20;
356
357 do
358 {
359 /*
360 * Send a Get-Printer-Attributes request...
361 */
362
363 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
364 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
365 uri);
366 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
367 NULL, cupsUser());
368 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
369 "requested-attributes",
370 (int)(sizeof(requested_attrs) / sizeof(requested_attrs[0])),
371 NULL, requested_attrs);
372 response = cupsDoRequest(http, request, resource);
373 status = cupsLastError();
374
375 if (status > IPP_OK_SUBST)
376 {
377 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
378 "returned %s (%s)", dest->name, ippErrorString(status),
379 cupsLastErrorString()));
380
381 ippDelete(response);
382 response = NULL;
383
384 if (status == IPP_VERSION_NOT_SUPPORTED && version > 11)
385 version = 11;
386 else
387 return (NULL);
388 }
389 }
390 while (!response);
391
392 /*
393 * Allocate a cups_dinfo_t structure and return it...
394 */
395
396 if ((dinfo = calloc(1, sizeof(cups_dinfo_t))) == NULL)
397 {
398 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
399 ippDelete(response);
400 return (NULL);
401 }
402
403 dinfo->uri = uri;
404 dinfo->resource = _cupsStrAlloc(resource);
405 dinfo->attrs = response;
406
407 return (dinfo);
408 }
409
410
411 /*
412 * 'cupsFreeDestInfo()' - Free destination information obtained using
413 * @link cupsCopyDestInfo@.
414 */
415
416 void
417 cupsFreeDestInfo(cups_dinfo_t *dinfo) /* I - Destination information */
418 {
419 /*
420 * Range check input...
421 */
422
423 if (!dinfo)
424 return;
425
426 /*
427 * Free memory and return...
428 */
429
430 _cupsStrFree(dinfo->resource);
431
432 ippDelete(dinfo->attrs);
433
434 cupsArrayDelete(dinfo->constraints);
435
436 cupsArrayDelete(dinfo->localizations);
437
438 cupsArrayDelete(dinfo->media_db);
439
440 free(dinfo);
441 }
442
443
444 /*
445 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
446 *
447 * The "media" string is a PWG media name, while "width" and "length" are the
448 * dimensions in hundredths of millimeters. "flags" provides some matching
449 * guidance (multiple flags can be combined):
450 *
451 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer
452 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size
453 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing
454 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size
455 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
456 * size amongst the "ready" media.
457 *
458 * The matching result (if any) is returned in the "cups_size_t" structure.
459 *
460 * Returns 1 when there is a match and 0 if there is not a match.
461 *
462 * @since CUPS 1.6@
463 */
464
465 int /* O - 1 on match, 0 on failure */
466 cupsGetDestMediaByName(
467 http_t *http, /* I - Connection to destination */
468 cups_dest_t *dest, /* I - Destination */
469 cups_dinfo_t *dinfo, /* I - Destination information */
470 const char *media, /* I - Media name */
471 unsigned flags, /* I - Media matching flags */
472 cups_size_t *size) /* O - Media size information */
473 {
474 _pwg_media_t *pwg; /* PWG media info */
475
476
477 /*
478 * Range check input...
479 */
480
481 if (size)
482 memset(size, 0, sizeof(cups_size_t));
483
484 if (!http || !dest || !dinfo || !media || !size)
485 {
486 _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
487 return (0);
488 }
489
490 /*
491 * Lookup the media size name...
492 */
493
494 if ((pwg = _pwgMediaForPWG(media)) == NULL)
495 if ((pwg = _pwgMediaForLegacy(media)) == NULL)
496 {
497 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media));
498 _cupsSetError(IPP_INTERNAL_ERROR, _("Unknown media size name."), 1);
499 return (0);
500 }
501
502 /*
503 * Lookup the size...
504 */
505
506 return (cups_get_media_db(dinfo, pwg, flags, size));
507 }
508
509
510 /*
511 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
512 *
513 * The "media" string is a PWG media name, while "width" and "length" are the
514 * dimensions in hundredths of millimeters. "flags" provides some matching
515 * guidance (multiple flags can be combined):
516 *
517 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer
518 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size
519 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing
520 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size
521 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
522 * size amongst the "ready" media.
523 *
524 * The matching result (if any) is returned in the "cups_size_t" structure.
525 *
526 * Returns 1 when there is a match and 0 if there is not a match.
527 *
528 * @since CUPS 1.6@
529 */
530
531 int /* O - 1 on match, 0 on failure */
532 cupsGetDestMediaBySize(
533 http_t *http, /* I - Connection to destination */
534 cups_dest_t *dest, /* I - Destination */
535 cups_dinfo_t *dinfo, /* I - Destination information */
536 int width, /* I - Media width in hundredths of
537 * of millimeters */
538 int length, /* I - Media length in hundredths of
539 * of millimeters */
540 unsigned flags, /* I - Media matching flags */
541 cups_size_t *size) /* O - Media size information */
542 {
543 _pwg_media_t *pwg; /* PWG media info */
544
545
546 /*
547 * Range check input...
548 */
549
550 if (size)
551 memset(size, 0, sizeof(cups_size_t));
552
553 if (!http || !dest || !dinfo || width <= 0 || length <= 0 || !size)
554 {
555 _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
556 return (0);
557 }
558
559 /*
560 * Lookup the media size name...
561 */
562
563 if ((pwg = _pwgMediaForSize(width, length)) == NULL)
564 {
565 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width,
566 length));
567 _cupsSetError(IPP_INTERNAL_ERROR, _("Invalid media size."), 1);
568 return (0);
569 }
570
571 /*
572 * Lookup the size...
573 */
574
575 return (cups_get_media_db(dinfo, pwg, flags, size));
576 }
577
578
579 /*
580 * 'cups_compare_media_db()' - Compare two media entries.
581 */
582
583 static int /* O - Result of comparison */
584 cups_compare_media_db(
585 _cups_media_db_t *a, /* I - First media entries */
586 _cups_media_db_t *b) /* I - Second media entries */
587 {
588 int result; /* Result of comparison */
589
590
591 if ((result = a->width - b->width) == 0)
592 result = a->length - b->length;
593
594 return (result);
595 }
596
597
598 /*
599 * 'cups_copy_media_db()' - Copy a media entry.
600 */
601
602 static _cups_media_db_t * /* O - New media entry */
603 cups_copy_media_db(
604 _cups_media_db_t *mdb) /* I - Media entry to copy */
605 {
606 _cups_media_db_t *temp; /* New media entry */
607
608
609 if ((temp = calloc(1, sizeof(_cups_media_db_t))) == NULL)
610 return (NULL);
611
612 if (mdb->color)
613 temp->color = _cupsStrAlloc(mdb->color);
614 if (mdb->key)
615 temp->key = _cupsStrAlloc(mdb->key);
616 if (mdb->info)
617 temp->info = _cupsStrAlloc(mdb->info);
618 if (mdb->size_name)
619 temp->size_name = _cupsStrAlloc(mdb->size_name);
620 if (mdb->source)
621 temp->source = _cupsStrAlloc(mdb->source);
622 if (mdb->type)
623 temp->type = _cupsStrAlloc(mdb->type);
624
625 temp->width = mdb->width;
626 temp->length = mdb->length;
627 temp->bottom = mdb->bottom;
628 temp->left = mdb->left;
629 temp->right = mdb->right;
630 temp->top = mdb->top;
631
632 return (temp);
633 }
634
635
636 /*
637 * 'cups_create_media_db()' - Create the media database.
638 */
639
640 static void
641 cups_create_media_db(
642 cups_dinfo_t *dinfo) /* I - Destination information */
643 {
644 int i; /* Looping var */
645 _ipp_value_t *val; /* Current value */
646 ipp_attribute_t *media_col_db, /* media-col-database */
647 *media_attr, /* media-xxx */
648 *x_dimension, /* x-dimension */
649 *y_dimension; /* y-dimension */
650 _pwg_media_t *pwg; /* PWG media info */
651 _cups_media_db_t mdb; /* Media entry */
652
653
654 dinfo->media_db = cupsArrayNew3((cups_array_func_t)cups_compare_media_db,
655 NULL, NULL, 0,
656 (cups_acopy_func_t)cups_copy_media_db,
657 (cups_afree_func_t)cups_free_media_db);
658 dinfo->min_size.width = INT_MAX;
659 dinfo->min_size.length = INT_MAX;
660 dinfo->max_size.width = 0;
661 dinfo->max_size.length = 0;
662
663 if ((media_col_db = ippFindAttribute(dinfo->attrs, "media-col-database",
664 IPP_TAG_BEGIN_COLLECTION)) != NULL)
665 {
666 _ipp_value_t *custom = NULL; /* Custom size range value */
667
668 for (i = media_col_db->num_values, val = media_col_db->values;
669 i > 0;
670 i --, val ++)
671 {
672 memset(&mdb, 0, sizeof(mdb));
673
674 if ((media_attr = ippFindAttribute(val->collection, "media-size",
675 IPP_TAG_BEGIN_COLLECTION)) != NULL)
676 {
677 ipp_t *media_size = media_attr->values[0].collection;
678 /* media-size collection value */
679
680 if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
681 IPP_TAG_INTEGER)) != NULL &&
682 (y_dimension = ippFindAttribute(media_size, "y-dimension",
683 IPP_TAG_INTEGER)) != NULL)
684 {
685 mdb.width = x_dimension->values[0].integer;
686 mdb.length = y_dimension->values[0].integer;
687 }
688 else if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
689 IPP_TAG_RANGE)) != NULL &&
690 (y_dimension = ippFindAttribute(media_size, "y-dimension",
691 IPP_TAG_RANGE)) != NULL)
692 {
693 /*
694 * Custom size range; save this as the custom size value with default
695 * margins, then continue; we'll capture the real margins below...
696 */
697
698 custom = val;
699
700 dinfo->min_size.width = x_dimension->values[0].range.lower;
701 dinfo->min_size.length = y_dimension->values[0].range.lower;
702 dinfo->min_size.left =
703 dinfo->min_size.right = 635; /* Default 1/4" side margins */
704 dinfo->min_size.top =
705 dinfo->min_size.bottom = 1270; /* Default 1/2" top/bottom margins */
706
707 dinfo->max_size.width = x_dimension->values[0].range.upper;
708 dinfo->max_size.length = y_dimension->values[0].range.upper;
709 dinfo->max_size.left =
710 dinfo->max_size.right = 635; /* Default 1/4" side margins */
711 dinfo->max_size.top =
712 dinfo->max_size.bottom = 1270; /* Default 1/2" top/bottom margins */
713
714 continue;
715 }
716 }
717
718 if ((media_attr = ippFindAttribute(val->collection, "media-color",
719 IPP_TAG_ZERO)) != NULL &&
720 (media_attr->value_tag == IPP_TAG_NAME ||
721 media_attr->value_tag == IPP_TAG_NAMELANG ||
722 media_attr->value_tag == IPP_TAG_KEYWORD))
723 mdb.color = media_attr->values[0].string.text;
724
725 if ((media_attr = ippFindAttribute(val->collection, "media-info",
726 IPP_TAG_TEXT)) != NULL)
727 mdb.info = media_attr->values[0].string.text;
728
729 if ((media_attr = ippFindAttribute(val->collection, "media-key",
730 IPP_TAG_ZERO)) != NULL &&
731 (media_attr->value_tag == IPP_TAG_NAME ||
732 media_attr->value_tag == IPP_TAG_NAMELANG ||
733 media_attr->value_tag == IPP_TAG_KEYWORD))
734 mdb.key = media_attr->values[0].string.text;
735
736 if ((media_attr = ippFindAttribute(val->collection, "media-size-name",
737 IPP_TAG_ZERO)) != NULL &&
738 (media_attr->value_tag == IPP_TAG_NAME ||
739 media_attr->value_tag == IPP_TAG_NAMELANG ||
740 media_attr->value_tag == IPP_TAG_KEYWORD))
741 mdb.size_name = media_attr->values[0].string.text;
742
743 if ((media_attr = ippFindAttribute(val->collection, "media-source",
744 IPP_TAG_ZERO)) != NULL &&
745 (media_attr->value_tag == IPP_TAG_NAME ||
746 media_attr->value_tag == IPP_TAG_NAMELANG ||
747 media_attr->value_tag == IPP_TAG_KEYWORD))
748 mdb.source = media_attr->values[0].string.text;
749
750 if ((media_attr = ippFindAttribute(val->collection, "media-type",
751 IPP_TAG_ZERO)) != NULL &&
752 (media_attr->value_tag == IPP_TAG_NAME ||
753 media_attr->value_tag == IPP_TAG_NAMELANG ||
754 media_attr->value_tag == IPP_TAG_KEYWORD))
755 mdb.type = media_attr->values[0].string.text;
756
757 if ((media_attr = ippFindAttribute(val->collection, "media-bottom-margin",
758 IPP_TAG_INTEGER)) != NULL)
759 mdb.bottom = media_attr->values[0].integer;
760
761 if ((media_attr = ippFindAttribute(val->collection, "media-left-margin",
762 IPP_TAG_INTEGER)) != NULL)
763 mdb.left = media_attr->values[0].integer;
764
765 if ((media_attr = ippFindAttribute(val->collection, "media-right-margin",
766 IPP_TAG_INTEGER)) != NULL)
767 mdb.right = media_attr->values[0].integer;
768
769 if ((media_attr = ippFindAttribute(val->collection, "media-top-margin",
770 IPP_TAG_INTEGER)) != NULL)
771 mdb.top = media_attr->values[0].integer;
772
773 cupsArrayAdd(dinfo->media_db, &mdb);
774 }
775
776 if (custom)
777 {
778 if ((media_attr = ippFindAttribute(custom->collection,
779 "media-bottom-margin",
780 IPP_TAG_INTEGER)) != NULL)
781 {
782 dinfo->min_size.top =
783 dinfo->max_size.top = media_attr->values[0].integer;
784 }
785
786 if ((media_attr = ippFindAttribute(custom->collection,
787 "media-left-margin",
788 IPP_TAG_INTEGER)) != NULL)
789 {
790 dinfo->min_size.left =
791 dinfo->max_size.left = media_attr->values[0].integer;
792 }
793
794 if ((media_attr = ippFindAttribute(custom->collection,
795 "media-right-margin",
796 IPP_TAG_INTEGER)) != NULL)
797 {
798 dinfo->min_size.right =
799 dinfo->max_size.right = media_attr->values[0].integer;
800 }
801
802 if ((media_attr = ippFindAttribute(custom->collection,
803 "media-top-margin",
804 IPP_TAG_INTEGER)) != NULL)
805 {
806 dinfo->min_size.top =
807 dinfo->max_size.top = media_attr->values[0].integer;
808 }
809 }
810 }
811 else if ((media_attr = ippFindAttribute(dinfo->attrs, "media-supported",
812 IPP_TAG_ZERO)) != NULL &&
813 (media_attr->value_tag == IPP_TAG_NAME ||
814 media_attr->value_tag == IPP_TAG_NAMELANG ||
815 media_attr->value_tag == IPP_TAG_KEYWORD))
816 {
817 memset(&mdb, 0, sizeof(mdb));
818
819 mdb.left =
820 mdb.right = 635; /* Default 1/4" side margins */
821 mdb.top =
822 mdb.bottom = 1270; /* Default 1/2" top/bottom margins */
823
824 for (i = media_col_db->num_values, val = media_col_db->values;
825 i > 0;
826 i --, val ++)
827 {
828 if ((pwg = _pwgMediaForPWG(val->string.text)) == NULL)
829 if ((pwg = _pwgMediaForLegacy(val->string.text)) == NULL)
830 {
831 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
832 val->string.text));
833 continue;
834 }
835
836 mdb.width = pwg->width;
837 mdb.length = pwg->length;
838
839 if (!strncmp(val->string.text, "custom_min_", 11))
840 {
841 mdb.size_name = NULL;
842 dinfo->min_size = mdb;
843 }
844 else if (!strncmp(val->string.text, "custom_max_", 11))
845 {
846 mdb.size_name = NULL;
847 dinfo->max_size = mdb;
848 }
849 else
850 {
851 mdb.size_name = val->string.text;
852
853 cupsArrayAdd(dinfo->media_db, &mdb);
854 }
855 }
856 }
857 }
858
859
860 /*
861 * 'cups_free_media_cb()' - Free a media entry.
862 */
863
864 static void
865 cups_free_media_db(
866 _cups_media_db_t *mdb) /* I - Media entry to free */
867 {
868 if (mdb->color)
869 _cupsStrFree(mdb->color);
870 if (mdb->key)
871 _cupsStrFree(mdb->key);
872 if (mdb->info)
873 _cupsStrFree(mdb->info);
874 if (mdb->size_name)
875 _cupsStrFree(mdb->size_name);
876 if (mdb->source)
877 _cupsStrFree(mdb->source);
878 if (mdb->type)
879 _cupsStrFree(mdb->type);
880
881 free(mdb);
882 }
883
884
885 /*
886 * 'cups_get_media_db()' - Lookup the media entry for a given size.
887 */
888
889 static int /* O - 1 on match, 0 on failure */
890 cups_get_media_db(cups_dinfo_t *dinfo, /* I - Destination information */
891 _pwg_media_t *pwg, /* I - PWG media info */
892 unsigned flags, /* I - Media matching flags */
893 cups_size_t *size) /* O - Media size/margin/name info */
894 {
895 _cups_media_db_t *mdb, /* Current media database entry */
896 *best = NULL, /* Best matching entry */
897 key; /* Search key */
898
899
900 /*
901 * Create the media database as needed...
902 */
903
904 if (!dinfo->media_db)
905 cups_create_media_db(dinfo);
906
907 /*
908 * Find a match...
909 */
910
911 memset(&key, 0, sizeof(key));
912 key.width = pwg->width;
913 key.length = pwg->length;
914
915 if ((mdb = cupsArrayFind(dinfo->media_db, &key)) != NULL)
916 {
917 /*
918 * Found an exact match, let's figure out the best margins for the flags
919 * supplied...
920 */
921
922 best = mdb;
923
924 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
925 {
926 /*
927 * Look for the smallest margins...
928 */
929
930 if (best->left != 0 || best->right != 0 || best->top != 0 ||
931 best->bottom != 0)
932 {
933 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
934 mdb && !cups_compare_media_db(mdb, &key);
935 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
936 {
937 if (mdb->left <= best->left && mdb->right <= best->right &&
938 mdb->top <= best->top && mdb->bottom <= best->bottom)
939 {
940 best = mdb;
941 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
942 mdb->top == 0)
943 break;
944 }
945 }
946 }
947
948 /*
949 * If we need an exact match, return no-match if the size is not
950 * borderless.
951 */
952
953 if ((flags & CUPS_MEDIA_FLAGS_EXACT) &&
954 (best->left || best->right || best->top || best->bottom))
955 return (0);
956 }
957 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
958 {
959 /*
960 * Look for the largest margins...
961 */
962
963 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
964 mdb && !cups_compare_media_db(mdb, &key);
965 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
966 {
967 if (mdb->left >= best->left && mdb->right >= best->right &&
968 mdb->top >= best->top && mdb->bottom >= best->bottom)
969 best = mdb;
970 }
971 }
972 else
973 {
974 /*
975 * Look for the smallest non-zero margins...
976 */
977
978 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
979 mdb && !cups_compare_media_db(mdb, &key);
980 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
981 {
982 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
983 ((mdb->right > 0 && mdb->right <= best->right) ||
984 best->right == 0) &&
985 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
986 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
987 best->bottom == 0))
988 best = mdb;
989 }
990 }
991 }
992 else if (flags & CUPS_MEDIA_FLAGS_EXACT)
993 {
994 /*
995 * See if we can do this as a custom size...
996 */
997
998 if (pwg->width < dinfo->min_size.width ||
999 pwg->width > dinfo->max_size.width ||
1000 pwg->length < dinfo->min_size.length ||
1001 pwg->length > dinfo->max_size.length)
1002 return (0); /* Out of range */
1003
1004 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
1005 (dinfo->min_size.left > 0 || dinfo->min_size.right > 0 ||
1006 dinfo->min_size.top > 0 || dinfo->min_size.bottom > 0))
1007 return (0); /* Not borderless */
1008
1009 key.size_name = (char *)pwg->pwg;
1010 key.bottom = dinfo->min_size.bottom;
1011 key.left = dinfo->min_size.left;
1012 key.right = dinfo->min_size.right;
1013 key.top = dinfo->min_size.top;
1014
1015 best = &key;
1016 }
1017 else if (pwg->width >= dinfo->min_size.width &&
1018 pwg->width <= dinfo->max_size.width &&
1019 pwg->length >= dinfo->min_size.length &&
1020 pwg->length <= dinfo->max_size.length)
1021 {
1022 /*
1023 * Map to custom size...
1024 */
1025
1026 key.size_name = (char *)pwg->pwg;
1027 key.bottom = dinfo->min_size.bottom;
1028 key.left = dinfo->min_size.left;
1029 key.right = dinfo->min_size.right;
1030 key.top = dinfo->min_size.top;
1031
1032 best = &key;
1033 }
1034 else
1035 {
1036 /*
1037 * Find a close size...
1038 */
1039
1040 for (mdb = (_cups_media_db_t *)cupsArrayFirst(dinfo->media_db);
1041 mdb;
1042 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1043 if (cups_is_close_media_db(mdb, &key))
1044 break;
1045
1046 if (!mdb)
1047 return (0);
1048
1049 best = mdb;
1050
1051 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1052 {
1053 /*
1054 * Look for the smallest margins...
1055 */
1056
1057 if (best->left != 0 || best->right != 0 || best->top != 0 ||
1058 best->bottom != 0)
1059 {
1060 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
1061 mdb && cups_is_close_media_db(mdb, &key);
1062 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1063 {
1064 if (mdb->left <= best->left && mdb->right <= best->right &&
1065 mdb->top <= best->top && mdb->bottom <= best->bottom)
1066 {
1067 best = mdb;
1068 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
1069 mdb->top == 0)
1070 break;
1071 }
1072 }
1073 }
1074 }
1075 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1076 {
1077 /*
1078 * Look for the largest margins...
1079 */
1080
1081 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
1082 mdb && cups_is_close_media_db(mdb, &key);
1083 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1084 {
1085 if (mdb->left >= best->left && mdb->right >= best->right &&
1086 mdb->top >= best->top && mdb->bottom >= best->bottom)
1087 best = mdb;
1088 }
1089 }
1090 else
1091 {
1092 /*
1093 * Look for the smallest non-zero margins...
1094 */
1095
1096 for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db);
1097 mdb && cups_is_close_media_db(mdb, &key);
1098 mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db))
1099 {
1100 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
1101 ((mdb->right > 0 && mdb->right <= best->right) ||
1102 best->right == 0) &&
1103 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
1104 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
1105 best->bottom == 0))
1106 best = mdb;
1107 }
1108 }
1109 }
1110
1111 if (best)
1112 {
1113 /*
1114 * Return the matching size...
1115 */
1116
1117 if (best->size_name)
1118 strlcpy(size->media, best->size_name, sizeof(size->media));
1119 else if (best->key)
1120 strlcpy(size->media, best->key, sizeof(size->media));
1121 else
1122 strlcpy(size->media, pwg->pwg, sizeof(size->media));
1123
1124 size->width = best->width;
1125 size->length = best->length;
1126 size->bottom = best->bottom;
1127 size->left = best->left;
1128 size->right = best->right;
1129 size->top = best->top;
1130
1131 return (1);
1132 }
1133
1134 return (0);
1135 }
1136
1137
1138 /*
1139 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
1140 * close to the same size.
1141 *
1142 * Currently we use 5 points (from PostScript) as the matching range...
1143 */
1144
1145 static int /* O - 1 if the sizes are close */
1146 cups_is_close_media_db(
1147 _cups_media_db_t *a, /* I - First media entries */
1148 _cups_media_db_t *b) /* I - Second media entries */
1149 {
1150 int dwidth, /* Difference in width */
1151 dlength; /* Difference in length */
1152
1153
1154 dwidth = a->width - b->width;
1155 dlength = a->length - b->length;
1156
1157 return (dwidth >= -176 && dwidth <= 176 &&
1158 dlength >= -176 && dlength <= 176);
1159 }
1160
1161
1162 /*
1163 * End of "$Id$".
1164 */