]>
Commit | Line | Data |
---|---|---|
dcb445bc MS |
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 | * | |
82cc1f9a | 18 | * cupsCheckDestSupported() - Check that the option and value are supported |
dcb445bc MS |
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; | |
3e7fe0ca | 190 | else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm")) |
dcb445bc MS |
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 | /* | |
82cc1f9a | 295 | * Check for and resolve any conflicts... |
dcb445bc MS |
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 | ||
82cc1f9a | 824 | for (i = media_attr->num_values, val = media_attr->values; |
dcb445bc MS |
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 | */ |