]>
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 | * | |
a29fd7dd MS |
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. | |
dcb445bc MS |
40 | */ |
41 | ||
42 | /* | |
43 | * Include necessary headers... | |
44 | */ | |
45 | ||
46 | #include "cups-private.h" | |
47 | ||
48 | ||
49 | /* | |
50 | * Local functions... | |
51 | */ | |
52 | ||
a29fd7dd MS |
53 | static void cups_add_dconstres(cups_array_t *a, ipp_t *collection); |
54 | static int cups_compare_dconstres(_cups_dconstres_t *a, | |
55 | _cups_dconstres_t *b); | |
dcb445bc MS |
56 | static int cups_compare_media_db(_cups_media_db_t *a, |
57 | _cups_media_db_t *b); | |
58 | static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb); | |
a29fd7dd MS |
59 | static void cups_create_constraints(cups_dinfo_t *dinfo); |
60 | static void cups_create_defaults(cups_dinfo_t *dinfo); | |
dcb445bc MS |
61 | static void cups_create_media_db(cups_dinfo_t *dinfo); |
62 | static void cups_free_media_db(_cups_media_db_t *mdb); | |
63 | static int cups_get_media_db(cups_dinfo_t *dinfo, | |
64 | _pwg_media_t *pwg, unsigned flags, | |
65 | cups_size_t *size); | |
66 | static int cups_is_close_media_db(_cups_media_db_t *a, | |
67 | _cups_media_db_t *b); | |
a29fd7dd MS |
68 | static 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 | ||
dcb445bc MS |
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 | * | |
f3c17241 | 83 | * @since CUPS 1.6/OS X 10.8@ |
dcb445bc MS |
84 | */ |
85 | ||
86 | int /* O - 1 if supported, 0 otherwise */ | |
87 | cupsCheckDestSupported( | |
88 | http_t *http, /* I - Connection to destination */ | |
89 | cups_dest_t *dest, /* I - Destination */ | |
90 | cups_dinfo_t *dinfo, /* I - Destination information */ | |
91 | const char *option, /* I - Option */ | |
92 | const char *value) /* I - Value */ | |
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 | ||
136 | _pwg_media_t *pwg; /* Current PWG media size info */ | |
137 | int min_width, /* Minimum width */ | |
138 | min_length, /* Minimum length */ | |
139 | max_width, /* Maximum width */ | |
140 | max_length; /* Maximum length */ | |
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) && | |
154 | (pwg = _pwgMediaForPWG(attrval->string.text)) != NULL) | |
155 | { | |
156 | min_width = pwg->width; | |
157 | min_length = pwg->length; | |
158 | } | |
159 | else if (!strncmp(attrval->string.text, "custom_max_", 11) && | |
160 | (pwg = _pwgMediaForPWG(attrval->string.text)) != NULL) | |
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 && | |
172 | (pwg = _pwgMediaForPWG(value)) != NULL && | |
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 | ||
a29fd7dd MS |
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 | ||
dcb445bc MS |
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; | |
3e7fe0ca | 217 | else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm")) |
dcb445bc MS |
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 | * | |
a29fd7dd MS |
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. | |
dcb445bc | 271 | * |
5a9febac MS |
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. | |
dcb445bc MS |
276 | * |
277 | * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set | |
5a9febac | 278 | * to 0 and @code NULL@, respectively, then the conflict cannot be resolved. |
dcb445bc | 279 | * |
f3c17241 | 280 | * @since CUPS 1.6/OS X 10.8@ |
dcb445bc MS |
281 | */ |
282 | ||
a29fd7dd | 283 | int /* O - 1 if there is a conflict, 0 if none, -1 on error */ |
dcb445bc MS |
284 | cupsCopyDestConflicts( |
285 | http_t *http, /* I - Connection to destination */ | |
286 | cups_dest_t *dest, /* I - Destination */ | |
287 | cups_dinfo_t *dinfo, /* I - Destination information */ | |
288 | int num_options, /* I - Number of current options */ | |
289 | cups_option_t *options, /* I - Current options */ | |
290 | const char *new_option, /* I - New option */ | |
291 | const char *new_value, /* I - New value */ | |
292 | int *num_conflicts, /* O - Number of conflicting options */ | |
293 | cups_option_t **conflicts, /* O - Conflicting options */ | |
294 | int *num_resolved, /* O - Number of options to resolve */ | |
295 | cups_option_t **resolved) /* O - Resolved options */ | |
296 | { | |
a29fd7dd MS |
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 | ||
dcb445bc MS |
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 | ||
a29fd7dd | 338 | if (!http || !dest || !dinfo || |
dcb445bc MS |
339 | (num_conflicts != NULL) != (conflicts != NULL) || |
340 | (num_resolved != NULL) != (resolved != NULL)) | |
341 | return (0); | |
342 | ||
343 | /* | |
a29fd7dd | 344 | * Load constraints as needed... |
dcb445bc MS |
345 | */ |
346 | ||
a29fd7dd MS |
347 | if (!dinfo->constraints) |
348 | cups_create_constraints(dinfo); | |
dcb445bc | 349 | |
a29fd7dd MS |
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 | ||
488 | if (tries >= 0) | |
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); | |
dcb445bc MS |
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 | * | |
f3c17241 | 565 | * @since CUPS 1.6/OS X 10.8@ |
dcb445bc MS |
566 | */ |
567 | ||
568 | cups_dinfo_t * /* O - Destination information */ | |
569 | cupsCopyDestInfo( | |
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 */ | |
a29fd7dd MS |
576 | int tries, /* Number of tries so far */ |
577 | delay, /* Current retry delay */ | |
578 | prev_delay; /* Next retry delay */ | |
dcb445bc MS |
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 | ||
a29fd7dd MS |
612 | delay = 1; |
613 | prev_delay = 1; | |
614 | tries = 0; | |
615 | version = 20; | |
dcb445bc MS |
616 | |
617 | do | |
618 | { | |
619 | /* | |
620 | * Send a Get-Printer-Attributes request... | |
621 | */ | |
622 | ||
623 | request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); | |
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 | ||
635 | if (status > IPP_OK_SUBST) | |
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 | ||
644 | if (status == IPP_VERSION_NOT_SUPPORTED && version > 11) | |
645 | version = 11; | |
a29fd7dd MS |
646 | else if (status == IPP_PRINTER_BUSY) |
647 | { | |
648 | sleep(delay); | |
649 | ||
650 | delay = _cupsNextDelay(delay, &prev_delay); | |
651 | } | |
dcb445bc MS |
652 | else |
653 | return (NULL); | |
654 | } | |
a29fd7dd MS |
655 | |
656 | tries ++; | |
dcb445bc | 657 | } |
a29fd7dd MS |
658 | while (!response && tries < 10); |
659 | ||
660 | if (!response) | |
661 | return (NULL); | |
dcb445bc MS |
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 | { | |
669 | _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0); | |
670 | ippDelete(response); | |
671 | return (NULL); | |
672 | } | |
673 | ||
674 | dinfo->uri = uri; | |
675 | dinfo->resource = _cupsStrAlloc(resource); | |
676 | dinfo->attrs = response; | |
677 | ||
678 | return (dinfo); | |
679 | } | |
680 | ||
681 | ||
682 | /* | |
683 | * 'cupsFreeDestInfo()' - Free destination information obtained using | |
684 | * @link cupsCopyDestInfo@. | |
685 | */ | |
686 | ||
687 | void | |
688 | cupsFreeDestInfo(cups_dinfo_t *dinfo) /* I - Destination information */ | |
689 | { | |
690 | /* | |
691 | * Range check input... | |
692 | */ | |
693 | ||
694 | if (!dinfo) | |
695 | return; | |
696 | ||
697 | /* | |
698 | * Free memory and return... | |
699 | */ | |
700 | ||
701 | _cupsStrFree(dinfo->resource); | |
702 | ||
dcb445bc | 703 | cupsArrayDelete(dinfo->constraints); |
a29fd7dd | 704 | cupsArrayDelete(dinfo->resolvers); |
dcb445bc MS |
705 | |
706 | cupsArrayDelete(dinfo->localizations); | |
707 | ||
708 | cupsArrayDelete(dinfo->media_db); | |
709 | ||
a29fd7dd MS |
710 | ippDelete(dinfo->attrs); |
711 | ||
dcb445bc MS |
712 | free(dinfo); |
713 | } | |
714 | ||
715 | ||
716 | /* | |
717 | * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins. | |
718 | * | |
5a9febac | 719 | * The "media" string is a PWG media name. "Flags" provides some matching |
dcb445bc MS |
720 | * guidance (multiple flags can be combined): |
721 | * | |
5a9febac MS |
722 | * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer, |
723 | * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size, | |
724 | * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing, | |
725 | * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and | |
dcb445bc MS |
726 | * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the |
727 | * size amongst the "ready" media. | |
728 | * | |
729 | * The matching result (if any) is returned in the "cups_size_t" structure. | |
730 | * | |
731 | * Returns 1 when there is a match and 0 if there is not a match. | |
732 | * | |
f3c17241 | 733 | * @since CUPS 1.6/OS X 10.8@ |
dcb445bc MS |
734 | */ |
735 | ||
736 | int /* O - 1 on match, 0 on failure */ | |
737 | cupsGetDestMediaByName( | |
738 | http_t *http, /* I - Connection to destination */ | |
739 | cups_dest_t *dest, /* I - Destination */ | |
740 | cups_dinfo_t *dinfo, /* I - Destination information */ | |
741 | const char *media, /* I - Media name */ | |
742 | unsigned flags, /* I - Media matching flags */ | |
743 | cups_size_t *size) /* O - Media size information */ | |
744 | { | |
745 | _pwg_media_t *pwg; /* PWG media info */ | |
746 | ||
747 | ||
748 | /* | |
749 | * Range check input... | |
750 | */ | |
751 | ||
752 | if (size) | |
753 | memset(size, 0, sizeof(cups_size_t)); | |
754 | ||
755 | if (!http || !dest || !dinfo || !media || !size) | |
756 | { | |
757 | _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0); | |
758 | return (0); | |
759 | } | |
760 | ||
761 | /* | |
762 | * Lookup the media size name... | |
763 | */ | |
764 | ||
765 | if ((pwg = _pwgMediaForPWG(media)) == NULL) | |
766 | if ((pwg = _pwgMediaForLegacy(media)) == NULL) | |
767 | { | |
768 | DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media)); | |
769 | _cupsSetError(IPP_INTERNAL_ERROR, _("Unknown media size name."), 1); | |
770 | return (0); | |
771 | } | |
772 | ||
773 | /* | |
774 | * Lookup the size... | |
775 | */ | |
776 | ||
777 | return (cups_get_media_db(dinfo, pwg, flags, size)); | |
778 | } | |
779 | ||
780 | ||
781 | /* | |
782 | * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins. | |
783 | * | |
5a9febac MS |
784 | * "Width" and "length" are the dimensions in hundredths of millimeters. |
785 | * "Flags" provides some matching guidance (multiple flags can be combined): | |
dcb445bc | 786 | * |
5a9febac MS |
787 | * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer, |
788 | * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size, | |
789 | * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing, | |
790 | * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and | |
dcb445bc MS |
791 | * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the |
792 | * size amongst the "ready" media. | |
793 | * | |
794 | * The matching result (if any) is returned in the "cups_size_t" structure. | |
795 | * | |
796 | * Returns 1 when there is a match and 0 if there is not a match. | |
797 | * | |
f3c17241 | 798 | * @since CUPS 1.6/OS X 10.8@ |
dcb445bc MS |
799 | */ |
800 | ||
801 | int /* O - 1 on match, 0 on failure */ | |
802 | cupsGetDestMediaBySize( | |
803 | http_t *http, /* I - Connection to destination */ | |
804 | cups_dest_t *dest, /* I - Destination */ | |
805 | cups_dinfo_t *dinfo, /* I - Destination information */ | |
806 | int width, /* I - Media width in hundredths of | |
807 | * of millimeters */ | |
808 | int length, /* I - Media length in hundredths of | |
809 | * of millimeters */ | |
810 | unsigned flags, /* I - Media matching flags */ | |
811 | cups_size_t *size) /* O - Media size information */ | |
812 | { | |
813 | _pwg_media_t *pwg; /* PWG media info */ | |
814 | ||
815 | ||
816 | /* | |
817 | * Range check input... | |
818 | */ | |
819 | ||
820 | if (size) | |
821 | memset(size, 0, sizeof(cups_size_t)); | |
822 | ||
823 | if (!http || !dest || !dinfo || width <= 0 || length <= 0 || !size) | |
824 | { | |
825 | _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0); | |
826 | return (0); | |
827 | } | |
828 | ||
829 | /* | |
830 | * Lookup the media size name... | |
831 | */ | |
832 | ||
833 | if ((pwg = _pwgMediaForSize(width, length)) == NULL) | |
834 | { | |
835 | DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width, | |
836 | length)); | |
837 | _cupsSetError(IPP_INTERNAL_ERROR, _("Invalid media size."), 1); | |
838 | return (0); | |
839 | } | |
840 | ||
841 | /* | |
842 | * Lookup the size... | |
843 | */ | |
844 | ||
845 | return (cups_get_media_db(dinfo, pwg, flags, size)); | |
846 | } | |
847 | ||
848 | ||
a29fd7dd MS |
849 | /* |
850 | * 'cups_add_dconstres()' - Add a constraint or resolver to an array. | |
851 | */ | |
852 | ||
853 | static void | |
854 | cups_add_dconstres( | |
855 | cups_array_t *a, /* I - Array */ | |
856 | ipp_t *collection) /* I - Collection value */ | |
857 | { | |
858 | ipp_attribute_t *attr; /* Attribute */ | |
859 | _cups_dconstres_t *temp; /* Current constraint/resolver */ | |
860 | ||
861 | ||
862 | if ((attr = ippFindAttribute(collection, "resolver-name", | |
863 | IPP_TAG_NAME)) == NULL) | |
864 | return; | |
865 | ||
866 | if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL) | |
867 | return; | |
868 | ||
869 | temp->name = attr->values[0].string.text; | |
870 | temp->collection = collection; | |
871 | ||
872 | cupsArrayAdd(a, temp); | |
873 | } | |
874 | ||
875 | ||
876 | /* | |
877 | * 'cups_compare_dconstres()' - Compare to resolver entries. | |
878 | */ | |
879 | ||
880 | static int /* O - Result of comparison */ | |
881 | cups_compare_dconstres( | |
882 | _cups_dconstres_t *a, /* I - First resolver */ | |
883 | _cups_dconstres_t *b) /* I - Second resolver */ | |
884 | { | |
885 | return (strcmp(a->name, b->name)); | |
886 | } | |
887 | ||
888 | ||
dcb445bc MS |
889 | /* |
890 | * 'cups_compare_media_db()' - Compare two media entries. | |
891 | */ | |
892 | ||
893 | static int /* O - Result of comparison */ | |
894 | cups_compare_media_db( | |
895 | _cups_media_db_t *a, /* I - First media entries */ | |
896 | _cups_media_db_t *b) /* I - Second media entries */ | |
897 | { | |
898 | int result; /* Result of comparison */ | |
899 | ||
900 | ||
901 | if ((result = a->width - b->width) == 0) | |
902 | result = a->length - b->length; | |
903 | ||
904 | return (result); | |
905 | } | |
906 | ||
907 | ||
908 | /* | |
909 | * 'cups_copy_media_db()' - Copy a media entry. | |
910 | */ | |
911 | ||
912 | static _cups_media_db_t * /* O - New media entry */ | |
913 | cups_copy_media_db( | |
914 | _cups_media_db_t *mdb) /* I - Media entry to copy */ | |
915 | { | |
916 | _cups_media_db_t *temp; /* New media entry */ | |
917 | ||
918 | ||
919 | if ((temp = calloc(1, sizeof(_cups_media_db_t))) == NULL) | |
920 | return (NULL); | |
921 | ||
922 | if (mdb->color) | |
923 | temp->color = _cupsStrAlloc(mdb->color); | |
924 | if (mdb->key) | |
925 | temp->key = _cupsStrAlloc(mdb->key); | |
926 | if (mdb->info) | |
927 | temp->info = _cupsStrAlloc(mdb->info); | |
928 | if (mdb->size_name) | |
929 | temp->size_name = _cupsStrAlloc(mdb->size_name); | |
930 | if (mdb->source) | |
931 | temp->source = _cupsStrAlloc(mdb->source); | |
932 | if (mdb->type) | |
933 | temp->type = _cupsStrAlloc(mdb->type); | |
934 | ||
935 | temp->width = mdb->width; | |
936 | temp->length = mdb->length; | |
937 | temp->bottom = mdb->bottom; | |
938 | temp->left = mdb->left; | |
939 | temp->right = mdb->right; | |
940 | temp->top = mdb->top; | |
941 | ||
942 | return (temp); | |
943 | } | |
944 | ||
945 | ||
a29fd7dd MS |
946 | /* |
947 | * 'cups_create_constraints()' - Create the constraints and resolvers arrays. | |
948 | */ | |
949 | ||
950 | static void | |
951 | cups_create_constraints( | |
952 | cups_dinfo_t *dinfo) /* I - Destination information */ | |
953 | { | |
954 | int i; /* Looping var */ | |
955 | ipp_attribute_t *attr; /* Attribute */ | |
956 | _ipp_value_t *val; /* Current value */ | |
957 | ||
958 | ||
959 | dinfo->constraints = cupsArrayNew3(NULL, NULL, NULL, 0, NULL, | |
960 | (cups_afree_func_t)free); | |
961 | dinfo->resolvers = cupsArrayNew3((cups_array_func_t)cups_compare_dconstres, | |
962 | NULL, NULL, 0, NULL, | |
963 | (cups_afree_func_t)free); | |
964 | ||
965 | if ((attr = ippFindAttribute(dinfo->attrs, "job-constraints-supported", | |
966 | IPP_TAG_BEGIN_COLLECTION)) != NULL) | |
967 | { | |
968 | for (i = attr->num_values, val = attr->values; i > 0; i --, val ++) | |
969 | cups_add_dconstres(dinfo->constraints, val->collection); | |
970 | } | |
971 | ||
972 | if ((attr = ippFindAttribute(dinfo->attrs, "job-resolvers-supported", | |
973 | IPP_TAG_BEGIN_COLLECTION)) != NULL) | |
974 | { | |
975 | for (i = attr->num_values, val = attr->values; i > 0; i --, val ++) | |
976 | cups_add_dconstres(dinfo->resolvers, val->collection); | |
977 | } | |
978 | } | |
979 | ||
980 | ||
981 | /* | |
982 | * 'cups_create_defaults()' - Create the -default option array. | |
983 | * | |
984 | * TODO: Need to support collection defaults... | |
985 | */ | |
986 | ||
987 | static void | |
988 | cups_create_defaults( | |
989 | cups_dinfo_t *dinfo) /* I - Destination information */ | |
990 | { | |
991 | ipp_attribute_t *attr; /* Current attribute */ | |
992 | char name[IPP_MAX_NAME + 1], | |
993 | /* Current name */ | |
994 | *nameptr, /* Pointer into current name */ | |
995 | value[2048]; /* Current value */ | |
996 | ||
997 | ||
998 | /* | |
999 | * Iterate through the printer attributes looking for xxx-default and adding | |
1000 | * xxx=value to the defaults option array. | |
1001 | */ | |
1002 | ||
1003 | for (attr = ippFirstAttribute(dinfo->attrs); | |
1004 | attr; | |
1005 | attr = ippNextAttribute(dinfo->attrs)) | |
1006 | { | |
1007 | if (!attr->name || attr->group_tag != IPP_TAG_PRINTER) | |
1008 | continue; | |
1009 | ||
1010 | if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION) | |
1011 | continue; /* TODO: STR #4096 */ | |
1012 | ||
1013 | if ((nameptr = attr->name + strlen(attr->name) - 8) <= attr->name || | |
1014 | strcmp(nameptr, "-default")) | |
1015 | continue; | |
1016 | ||
1017 | strlcpy(name, attr->name, sizeof(name)); | |
1018 | if ((nameptr = name + strlen(name) - 8) <= name || | |
1019 | strcmp(nameptr, "-default")) | |
1020 | continue; | |
1021 | ||
1022 | *nameptr = '\0'; | |
1023 | ||
1024 | if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value)) | |
1025 | continue; | |
1026 | ||
1027 | dinfo->num_defaults = cupsAddOption(name, value, dinfo->num_defaults, | |
1028 | &dinfo->defaults); | |
1029 | } | |
1030 | } | |
1031 | ||
1032 | ||
dcb445bc MS |
1033 | /* |
1034 | * 'cups_create_media_db()' - Create the media database. | |
1035 | */ | |
1036 | ||
1037 | static void | |
1038 | cups_create_media_db( | |
1039 | cups_dinfo_t *dinfo) /* I - Destination information */ | |
1040 | { | |
1041 | int i; /* Looping var */ | |
1042 | _ipp_value_t *val; /* Current value */ | |
1043 | ipp_attribute_t *media_col_db, /* media-col-database */ | |
1044 | *media_attr, /* media-xxx */ | |
1045 | *x_dimension, /* x-dimension */ | |
1046 | *y_dimension; /* y-dimension */ | |
1047 | _pwg_media_t *pwg; /* PWG media info */ | |
1048 | _cups_media_db_t mdb; /* Media entry */ | |
1049 | ||
1050 | ||
1051 | dinfo->media_db = cupsArrayNew3((cups_array_func_t)cups_compare_media_db, | |
1052 | NULL, NULL, 0, | |
1053 | (cups_acopy_func_t)cups_copy_media_db, | |
1054 | (cups_afree_func_t)cups_free_media_db); | |
1055 | dinfo->min_size.width = INT_MAX; | |
1056 | dinfo->min_size.length = INT_MAX; | |
1057 | dinfo->max_size.width = 0; | |
1058 | dinfo->max_size.length = 0; | |
1059 | ||
1060 | if ((media_col_db = ippFindAttribute(dinfo->attrs, "media-col-database", | |
1061 | IPP_TAG_BEGIN_COLLECTION)) != NULL) | |
1062 | { | |
1063 | _ipp_value_t *custom = NULL; /* Custom size range value */ | |
1064 | ||
1065 | for (i = media_col_db->num_values, val = media_col_db->values; | |
1066 | i > 0; | |
1067 | i --, val ++) | |
1068 | { | |
1069 | memset(&mdb, 0, sizeof(mdb)); | |
1070 | ||
1071 | if ((media_attr = ippFindAttribute(val->collection, "media-size", | |
1072 | IPP_TAG_BEGIN_COLLECTION)) != NULL) | |
1073 | { | |
1074 | ipp_t *media_size = media_attr->values[0].collection; | |
1075 | /* media-size collection value */ | |
1076 | ||
1077 | if ((x_dimension = ippFindAttribute(media_size, "x-dimension", | |
1078 | IPP_TAG_INTEGER)) != NULL && | |
1079 | (y_dimension = ippFindAttribute(media_size, "y-dimension", | |
1080 | IPP_TAG_INTEGER)) != NULL) | |
1081 | { | |
1082 | mdb.width = x_dimension->values[0].integer; | |
1083 | mdb.length = y_dimension->values[0].integer; | |
1084 | } | |
1085 | else if ((x_dimension = ippFindAttribute(media_size, "x-dimension", | |
1086 | IPP_TAG_RANGE)) != NULL && | |
1087 | (y_dimension = ippFindAttribute(media_size, "y-dimension", | |
1088 | IPP_TAG_RANGE)) != NULL) | |
1089 | { | |
1090 | /* | |
1091 | * Custom size range; save this as the custom size value with default | |
1092 | * margins, then continue; we'll capture the real margins below... | |
1093 | */ | |
1094 | ||
1095 | custom = val; | |
1096 | ||
1097 | dinfo->min_size.width = x_dimension->values[0].range.lower; | |
1098 | dinfo->min_size.length = y_dimension->values[0].range.lower; | |
1099 | dinfo->min_size.left = | |
1100 | dinfo->min_size.right = 635; /* Default 1/4" side margins */ | |
1101 | dinfo->min_size.top = | |
1102 | dinfo->min_size.bottom = 1270; /* Default 1/2" top/bottom margins */ | |
1103 | ||
1104 | dinfo->max_size.width = x_dimension->values[0].range.upper; | |
1105 | dinfo->max_size.length = y_dimension->values[0].range.upper; | |
1106 | dinfo->max_size.left = | |
1107 | dinfo->max_size.right = 635; /* Default 1/4" side margins */ | |
1108 | dinfo->max_size.top = | |
1109 | dinfo->max_size.bottom = 1270; /* Default 1/2" top/bottom margins */ | |
1110 | ||
1111 | continue; | |
1112 | } | |
1113 | } | |
1114 | ||
1115 | if ((media_attr = ippFindAttribute(val->collection, "media-color", | |
1116 | IPP_TAG_ZERO)) != NULL && | |
1117 | (media_attr->value_tag == IPP_TAG_NAME || | |
1118 | media_attr->value_tag == IPP_TAG_NAMELANG || | |
1119 | media_attr->value_tag == IPP_TAG_KEYWORD)) | |
1120 | mdb.color = media_attr->values[0].string.text; | |
1121 | ||
1122 | if ((media_attr = ippFindAttribute(val->collection, "media-info", | |
1123 | IPP_TAG_TEXT)) != NULL) | |
1124 | mdb.info = media_attr->values[0].string.text; | |
1125 | ||
1126 | if ((media_attr = ippFindAttribute(val->collection, "media-key", | |
1127 | IPP_TAG_ZERO)) != NULL && | |
1128 | (media_attr->value_tag == IPP_TAG_NAME || | |
1129 | media_attr->value_tag == IPP_TAG_NAMELANG || | |
1130 | media_attr->value_tag == IPP_TAG_KEYWORD)) | |
1131 | mdb.key = media_attr->values[0].string.text; | |
1132 | ||
1133 | if ((media_attr = ippFindAttribute(val->collection, "media-size-name", | |
1134 | IPP_TAG_ZERO)) != NULL && | |
1135 | (media_attr->value_tag == IPP_TAG_NAME || | |
1136 | media_attr->value_tag == IPP_TAG_NAMELANG || | |
1137 | media_attr->value_tag == IPP_TAG_KEYWORD)) | |
1138 | mdb.size_name = media_attr->values[0].string.text; | |
1139 | ||
1140 | if ((media_attr = ippFindAttribute(val->collection, "media-source", | |
1141 | IPP_TAG_ZERO)) != NULL && | |
1142 | (media_attr->value_tag == IPP_TAG_NAME || | |
1143 | media_attr->value_tag == IPP_TAG_NAMELANG || | |
1144 | media_attr->value_tag == IPP_TAG_KEYWORD)) | |
1145 | mdb.source = media_attr->values[0].string.text; | |
1146 | ||
1147 | if ((media_attr = ippFindAttribute(val->collection, "media-type", | |
1148 | IPP_TAG_ZERO)) != NULL && | |
1149 | (media_attr->value_tag == IPP_TAG_NAME || | |
1150 | media_attr->value_tag == IPP_TAG_NAMELANG || | |
1151 | media_attr->value_tag == IPP_TAG_KEYWORD)) | |
1152 | mdb.type = media_attr->values[0].string.text; | |
1153 | ||
1154 | if ((media_attr = ippFindAttribute(val->collection, "media-bottom-margin", | |
1155 | IPP_TAG_INTEGER)) != NULL) | |
1156 | mdb.bottom = media_attr->values[0].integer; | |
1157 | ||
1158 | if ((media_attr = ippFindAttribute(val->collection, "media-left-margin", | |
1159 | IPP_TAG_INTEGER)) != NULL) | |
1160 | mdb.left = media_attr->values[0].integer; | |
1161 | ||
1162 | if ((media_attr = ippFindAttribute(val->collection, "media-right-margin", | |
1163 | IPP_TAG_INTEGER)) != NULL) | |
1164 | mdb.right = media_attr->values[0].integer; | |
1165 | ||
1166 | if ((media_attr = ippFindAttribute(val->collection, "media-top-margin", | |
1167 | IPP_TAG_INTEGER)) != NULL) | |
1168 | mdb.top = media_attr->values[0].integer; | |
1169 | ||
1170 | cupsArrayAdd(dinfo->media_db, &mdb); | |
1171 | } | |
1172 | ||
1173 | if (custom) | |
1174 | { | |
1175 | if ((media_attr = ippFindAttribute(custom->collection, | |
1176 | "media-bottom-margin", | |
1177 | IPP_TAG_INTEGER)) != NULL) | |
1178 | { | |
1179 | dinfo->min_size.top = | |
1180 | dinfo->max_size.top = media_attr->values[0].integer; | |
1181 | } | |
1182 | ||
1183 | if ((media_attr = ippFindAttribute(custom->collection, | |
1184 | "media-left-margin", | |
1185 | IPP_TAG_INTEGER)) != NULL) | |
1186 | { | |
1187 | dinfo->min_size.left = | |
1188 | dinfo->max_size.left = media_attr->values[0].integer; | |
1189 | } | |
1190 | ||
1191 | if ((media_attr = ippFindAttribute(custom->collection, | |
1192 | "media-right-margin", | |
1193 | IPP_TAG_INTEGER)) != NULL) | |
1194 | { | |
1195 | dinfo->min_size.right = | |
1196 | dinfo->max_size.right = media_attr->values[0].integer; | |
1197 | } | |
1198 | ||
1199 | if ((media_attr = ippFindAttribute(custom->collection, | |
1200 | "media-top-margin", | |
1201 | IPP_TAG_INTEGER)) != NULL) | |
1202 | { | |
1203 | dinfo->min_size.top = | |
1204 | dinfo->max_size.top = media_attr->values[0].integer; | |
1205 | } | |
1206 | } | |
1207 | } | |
1208 | else if ((media_attr = ippFindAttribute(dinfo->attrs, "media-supported", | |
1209 | IPP_TAG_ZERO)) != NULL && | |
1210 | (media_attr->value_tag == IPP_TAG_NAME || | |
1211 | media_attr->value_tag == IPP_TAG_NAMELANG || | |
1212 | media_attr->value_tag == IPP_TAG_KEYWORD)) | |
1213 | { | |
1214 | memset(&mdb, 0, sizeof(mdb)); | |
1215 | ||
1216 | mdb.left = | |
1217 | mdb.right = 635; /* Default 1/4" side margins */ | |
1218 | mdb.top = | |
1219 | mdb.bottom = 1270; /* Default 1/2" top/bottom margins */ | |
1220 | ||
82cc1f9a | 1221 | for (i = media_attr->num_values, val = media_attr->values; |
dcb445bc MS |
1222 | i > 0; |
1223 | i --, val ++) | |
1224 | { | |
1225 | if ((pwg = _pwgMediaForPWG(val->string.text)) == NULL) | |
1226 | if ((pwg = _pwgMediaForLegacy(val->string.text)) == NULL) | |
1227 | { | |
1228 | DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.", | |
1229 | val->string.text)); | |
1230 | continue; | |
1231 | } | |
1232 | ||
1233 | mdb.width = pwg->width; | |
1234 | mdb.length = pwg->length; | |
1235 | ||
1236 | if (!strncmp(val->string.text, "custom_min_", 11)) | |
1237 | { | |
1238 | mdb.size_name = NULL; | |
1239 | dinfo->min_size = mdb; | |
1240 | } | |
1241 | else if (!strncmp(val->string.text, "custom_max_", 11)) | |
1242 | { | |
1243 | mdb.size_name = NULL; | |
1244 | dinfo->max_size = mdb; | |
1245 | } | |
1246 | else | |
1247 | { | |
1248 | mdb.size_name = val->string.text; | |
1249 | ||
1250 | cupsArrayAdd(dinfo->media_db, &mdb); | |
1251 | } | |
1252 | } | |
1253 | } | |
1254 | } | |
1255 | ||
1256 | ||
1257 | /* | |
1258 | * 'cups_free_media_cb()' - Free a media entry. | |
1259 | */ | |
1260 | ||
1261 | static void | |
1262 | cups_free_media_db( | |
1263 | _cups_media_db_t *mdb) /* I - Media entry to free */ | |
1264 | { | |
1265 | if (mdb->color) | |
1266 | _cupsStrFree(mdb->color); | |
1267 | if (mdb->key) | |
1268 | _cupsStrFree(mdb->key); | |
1269 | if (mdb->info) | |
1270 | _cupsStrFree(mdb->info); | |
1271 | if (mdb->size_name) | |
1272 | _cupsStrFree(mdb->size_name); | |
1273 | if (mdb->source) | |
1274 | _cupsStrFree(mdb->source); | |
1275 | if (mdb->type) | |
1276 | _cupsStrFree(mdb->type); | |
1277 | ||
1278 | free(mdb); | |
1279 | } | |
1280 | ||
1281 | ||
1282 | /* | |
1283 | * 'cups_get_media_db()' - Lookup the media entry for a given size. | |
1284 | */ | |
1285 | ||
1286 | static int /* O - 1 on match, 0 on failure */ | |
1287 | cups_get_media_db(cups_dinfo_t *dinfo, /* I - Destination information */ | |
1288 | _pwg_media_t *pwg, /* I - PWG media info */ | |
1289 | unsigned flags, /* I - Media matching flags */ | |
1290 | cups_size_t *size) /* O - Media size/margin/name info */ | |
1291 | { | |
1292 | _cups_media_db_t *mdb, /* Current media database entry */ | |
1293 | *best = NULL, /* Best matching entry */ | |
1294 | key; /* Search key */ | |
1295 | ||
1296 | ||
1297 | /* | |
1298 | * Create the media database as needed... | |
1299 | */ | |
1300 | ||
1301 | if (!dinfo->media_db) | |
1302 | cups_create_media_db(dinfo); | |
1303 | ||
1304 | /* | |
1305 | * Find a match... | |
1306 | */ | |
1307 | ||
1308 | memset(&key, 0, sizeof(key)); | |
1309 | key.width = pwg->width; | |
1310 | key.length = pwg->length; | |
1311 | ||
1312 | if ((mdb = cupsArrayFind(dinfo->media_db, &key)) != NULL) | |
1313 | { | |
1314 | /* | |
1315 | * Found an exact match, let's figure out the best margins for the flags | |
1316 | * supplied... | |
1317 | */ | |
1318 | ||
1319 | best = mdb; | |
1320 | ||
1321 | if (flags & CUPS_MEDIA_FLAGS_BORDERLESS) | |
1322 | { | |
1323 | /* | |
1324 | * Look for the smallest margins... | |
1325 | */ | |
1326 | ||
1327 | if (best->left != 0 || best->right != 0 || best->top != 0 || | |
1328 | best->bottom != 0) | |
1329 | { | |
1330 | for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db); | |
1331 | mdb && !cups_compare_media_db(mdb, &key); | |
1332 | mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db)) | |
1333 | { | |
1334 | if (mdb->left <= best->left && mdb->right <= best->right && | |
1335 | mdb->top <= best->top && mdb->bottom <= best->bottom) | |
1336 | { | |
1337 | best = mdb; | |
1338 | if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 && | |
1339 | mdb->top == 0) | |
1340 | break; | |
1341 | } | |
1342 | } | |
1343 | } | |
1344 | ||
1345 | /* | |
1346 | * If we need an exact match, return no-match if the size is not | |
1347 | * borderless. | |
1348 | */ | |
1349 | ||
1350 | if ((flags & CUPS_MEDIA_FLAGS_EXACT) && | |
1351 | (best->left || best->right || best->top || best->bottom)) | |
1352 | return (0); | |
1353 | } | |
1354 | else if (flags & CUPS_MEDIA_FLAGS_DUPLEX) | |
1355 | { | |
1356 | /* | |
1357 | * Look for the largest margins... | |
1358 | */ | |
1359 | ||
1360 | for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db); | |
1361 | mdb && !cups_compare_media_db(mdb, &key); | |
1362 | mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db)) | |
1363 | { | |
1364 | if (mdb->left >= best->left && mdb->right >= best->right && | |
1365 | mdb->top >= best->top && mdb->bottom >= best->bottom) | |
1366 | best = mdb; | |
1367 | } | |
1368 | } | |
1369 | else | |
1370 | { | |
1371 | /* | |
1372 | * Look for the smallest non-zero margins... | |
1373 | */ | |
1374 | ||
1375 | for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db); | |
1376 | mdb && !cups_compare_media_db(mdb, &key); | |
1377 | mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db)) | |
1378 | { | |
1379 | if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) && | |
1380 | ((mdb->right > 0 && mdb->right <= best->right) || | |
1381 | best->right == 0) && | |
1382 | ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) && | |
1383 | ((mdb->bottom > 0 && mdb->bottom <= best->bottom) || | |
1384 | best->bottom == 0)) | |
1385 | best = mdb; | |
1386 | } | |
1387 | } | |
1388 | } | |
1389 | else if (flags & CUPS_MEDIA_FLAGS_EXACT) | |
1390 | { | |
1391 | /* | |
1392 | * See if we can do this as a custom size... | |
1393 | */ | |
1394 | ||
1395 | if (pwg->width < dinfo->min_size.width || | |
1396 | pwg->width > dinfo->max_size.width || | |
1397 | pwg->length < dinfo->min_size.length || | |
1398 | pwg->length > dinfo->max_size.length) | |
1399 | return (0); /* Out of range */ | |
1400 | ||
1401 | if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) && | |
1402 | (dinfo->min_size.left > 0 || dinfo->min_size.right > 0 || | |
1403 | dinfo->min_size.top > 0 || dinfo->min_size.bottom > 0)) | |
1404 | return (0); /* Not borderless */ | |
1405 | ||
1406 | key.size_name = (char *)pwg->pwg; | |
1407 | key.bottom = dinfo->min_size.bottom; | |
1408 | key.left = dinfo->min_size.left; | |
1409 | key.right = dinfo->min_size.right; | |
1410 | key.top = dinfo->min_size.top; | |
1411 | ||
1412 | best = &key; | |
1413 | } | |
1414 | else if (pwg->width >= dinfo->min_size.width && | |
1415 | pwg->width <= dinfo->max_size.width && | |
1416 | pwg->length >= dinfo->min_size.length && | |
1417 | pwg->length <= dinfo->max_size.length) | |
1418 | { | |
1419 | /* | |
1420 | * Map to custom size... | |
1421 | */ | |
1422 | ||
1423 | key.size_name = (char *)pwg->pwg; | |
1424 | key.bottom = dinfo->min_size.bottom; | |
1425 | key.left = dinfo->min_size.left; | |
1426 | key.right = dinfo->min_size.right; | |
1427 | key.top = dinfo->min_size.top; | |
1428 | ||
1429 | best = &key; | |
1430 | } | |
1431 | else | |
1432 | { | |
1433 | /* | |
1434 | * Find a close size... | |
1435 | */ | |
1436 | ||
1437 | for (mdb = (_cups_media_db_t *)cupsArrayFirst(dinfo->media_db); | |
1438 | mdb; | |
1439 | mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db)) | |
1440 | if (cups_is_close_media_db(mdb, &key)) | |
1441 | break; | |
1442 | ||
1443 | if (!mdb) | |
1444 | return (0); | |
1445 | ||
1446 | best = mdb; | |
1447 | ||
1448 | if (flags & CUPS_MEDIA_FLAGS_BORDERLESS) | |
1449 | { | |
1450 | /* | |
1451 | * Look for the smallest margins... | |
1452 | */ | |
1453 | ||
1454 | if (best->left != 0 || best->right != 0 || best->top != 0 || | |
1455 | best->bottom != 0) | |
1456 | { | |
1457 | for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db); | |
1458 | mdb && cups_is_close_media_db(mdb, &key); | |
1459 | mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db)) | |
1460 | { | |
1461 | if (mdb->left <= best->left && mdb->right <= best->right && | |
1462 | mdb->top <= best->top && mdb->bottom <= best->bottom) | |
1463 | { | |
1464 | best = mdb; | |
1465 | if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 && | |
1466 | mdb->top == 0) | |
1467 | break; | |
1468 | } | |
1469 | } | |
1470 | } | |
1471 | } | |
1472 | else if (flags & CUPS_MEDIA_FLAGS_DUPLEX) | |
1473 | { | |
1474 | /* | |
1475 | * Look for the largest margins... | |
1476 | */ | |
1477 | ||
1478 | for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db); | |
1479 | mdb && cups_is_close_media_db(mdb, &key); | |
1480 | mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db)) | |
1481 | { | |
1482 | if (mdb->left >= best->left && mdb->right >= best->right && | |
1483 | mdb->top >= best->top && mdb->bottom >= best->bottom) | |
1484 | best = mdb; | |
1485 | } | |
1486 | } | |
1487 | else | |
1488 | { | |
1489 | /* | |
1490 | * Look for the smallest non-zero margins... | |
1491 | */ | |
1492 | ||
1493 | for (mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db); | |
1494 | mdb && cups_is_close_media_db(mdb, &key); | |
1495 | mdb = (_cups_media_db_t *)cupsArrayNext(dinfo->media_db)) | |
1496 | { | |
1497 | if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) && | |
1498 | ((mdb->right > 0 && mdb->right <= best->right) || | |
1499 | best->right == 0) && | |
1500 | ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) && | |
1501 | ((mdb->bottom > 0 && mdb->bottom <= best->bottom) || | |
1502 | best->bottom == 0)) | |
1503 | best = mdb; | |
1504 | } | |
1505 | } | |
1506 | } | |
1507 | ||
1508 | if (best) | |
1509 | { | |
1510 | /* | |
1511 | * Return the matching size... | |
1512 | */ | |
1513 | ||
1514 | if (best->size_name) | |
1515 | strlcpy(size->media, best->size_name, sizeof(size->media)); | |
1516 | else if (best->key) | |
1517 | strlcpy(size->media, best->key, sizeof(size->media)); | |
1518 | else | |
1519 | strlcpy(size->media, pwg->pwg, sizeof(size->media)); | |
1520 | ||
1521 | size->width = best->width; | |
1522 | size->length = best->length; | |
1523 | size->bottom = best->bottom; | |
1524 | size->left = best->left; | |
1525 | size->right = best->right; | |
1526 | size->top = best->top; | |
1527 | ||
1528 | return (1); | |
1529 | } | |
1530 | ||
1531 | return (0); | |
1532 | } | |
1533 | ||
1534 | ||
1535 | /* | |
1536 | * 'cups_is_close_media_db()' - Compare two media entries to see if they are | |
1537 | * close to the same size. | |
1538 | * | |
1539 | * Currently we use 5 points (from PostScript) as the matching range... | |
1540 | */ | |
1541 | ||
1542 | static int /* O - 1 if the sizes are close */ | |
1543 | cups_is_close_media_db( | |
1544 | _cups_media_db_t *a, /* I - First media entries */ | |
1545 | _cups_media_db_t *b) /* I - Second media entries */ | |
1546 | { | |
1547 | int dwidth, /* Difference in width */ | |
1548 | dlength; /* Difference in length */ | |
1549 | ||
1550 | ||
1551 | dwidth = a->width - b->width; | |
1552 | dlength = a->length - b->length; | |
1553 | ||
1554 | return (dwidth >= -176 && dwidth <= 176 && | |
1555 | dlength >= -176 && dlength <= 176); | |
1556 | } | |
1557 | ||
1558 | ||
a29fd7dd MS |
1559 | /* |
1560 | * 'cups_test_constraints()' - Test constraints. | |
1561 | * | |
1562 | * TODO: STR #4096 - Need to properly support media-col contraints... | |
1563 | */ | |
1564 | ||
1565 | static cups_array_t * /* O - Active constraints */ | |
1566 | cups_test_constraints( | |
1567 | cups_dinfo_t *dinfo, /* I - Destination information */ | |
1568 | const char *new_option, /* I - Newly selected option */ | |
1569 | const char *new_value, /* I - Newly selected value */ | |
1570 | int num_options, /* I - Number of options */ | |
1571 | cups_option_t *options, /* I - Options */ | |
1572 | int *num_conflicts, /* O - Number of conflicting options */ | |
1573 | cups_option_t **conflicts) /* O - Conflicting options */ | |
1574 | { | |
1575 | int i, /* Looping var */ | |
1576 | match; /* Value matches? */ | |
1577 | int num_matching; /* Number of matching options */ | |
1578 | cups_option_t *matching; /* Matching options */ | |
1579 | _cups_dconstres_t *c; /* Current constraint */ | |
1580 | cups_array_t *active = NULL; /* Active constraints */ | |
1581 | ipp_attribute_t *attr; /* Current attribute */ | |
1582 | _ipp_value_t *attrval; /* Current attribute value */ | |
1583 | const char *value; /* Current value */ | |
1584 | char temp[1024]; /* Temporary string */ | |
1585 | int int_value; /* Integer value */ | |
1586 | int xres_value, /* Horizontal resolution */ | |
1587 | yres_value; /* Vertical resolution */ | |
1588 | ipp_res_t units_value; /* Resolution units */ | |
1589 | ||
1590 | ||
1591 | for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints); | |
1592 | c; | |
1593 | c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints)) | |
1594 | { | |
1595 | num_matching = 0; | |
1596 | matching = NULL; | |
1597 | ||
1598 | for (attr = ippFirstAttribute(c->collection); | |
1599 | attr; | |
1600 | attr = ippNextAttribute(c->collection)) | |
1601 | { | |
1602 | if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION) | |
1603 | break; /* TODO: STR #4096 */ | |
1604 | ||
1605 | /* | |
1606 | * Get the value for the current attribute in the constraint... | |
1607 | */ | |
1608 | ||
1609 | if (new_option && new_value && !strcmp(attr->name, new_option)) | |
1610 | value = new_value; | |
1611 | else if ((value = cupsGetOption(attr->name, num_options, | |
1612 | options)) == NULL) | |
1613 | value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults); | |
1614 | ||
1615 | if (!value) | |
1616 | { | |
1617 | /* | |
1618 | * Not set so this constraint does not apply... | |
1619 | */ | |
1620 | ||
1621 | break; | |
1622 | } | |
1623 | ||
1624 | match = 0; | |
1625 | ||
1626 | switch (attr->value_tag) | |
1627 | { | |
1628 | case IPP_TAG_INTEGER : | |
1629 | case IPP_TAG_ENUM : | |
1630 | int_value = atoi(value); | |
1631 | ||
1632 | for (i = attr->num_values, attrval = attr->values; | |
1633 | i > 0; | |
1634 | i --, attrval ++) | |
1635 | { | |
1636 | if (attrval->integer == int_value) | |
1637 | { | |
1638 | match = 1; | |
1639 | break; | |
1640 | } | |
1641 | } | |
1642 | break; | |
1643 | ||
1644 | case IPP_TAG_BOOLEAN : | |
1645 | int_value = !strcmp(value, "true"); | |
1646 | ||
1647 | for (i = attr->num_values, attrval = attr->values; | |
1648 | i > 0; | |
1649 | i --, attrval ++) | |
1650 | { | |
1651 | if (attrval->boolean == int_value) | |
1652 | { | |
1653 | match = 1; | |
1654 | break; | |
1655 | } | |
1656 | } | |
1657 | break; | |
1658 | ||
1659 | case IPP_TAG_RANGE : | |
1660 | int_value = atoi(value); | |
1661 | ||
1662 | for (i = attr->num_values, attrval = attr->values; | |
1663 | i > 0; | |
1664 | i --, attrval ++) | |
1665 | { | |
1666 | if (int_value >= attrval->range.lower && | |
1667 | int_value <= attrval->range.upper) | |
1668 | { | |
1669 | match = 1; | |
1670 | break; | |
1671 | } | |
1672 | } | |
1673 | break; | |
1674 | ||
1675 | case IPP_TAG_RESOLUTION : | |
1676 | if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3) | |
1677 | { | |
1678 | if (sscanf(value, "%d%15s", &xres_value, temp) != 2) | |
1679 | break; | |
1680 | ||
1681 | yres_value = xres_value; | |
1682 | } | |
1683 | ||
1684 | if (!strcmp(temp, "dpi")) | |
1685 | units_value = IPP_RES_PER_INCH; | |
1686 | else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm")) | |
1687 | units_value = IPP_RES_PER_CM; | |
1688 | else | |
1689 | break; | |
1690 | ||
1691 | for (i = attr->num_values, attrval = attr->values; | |
1692 | i > 0; | |
1693 | i --, attrval ++) | |
1694 | { | |
1695 | if (attrval->resolution.xres == xres_value && | |
1696 | attrval->resolution.yres == yres_value && | |
1697 | attrval->resolution.units == units_value) | |
1698 | { | |
1699 | match = 1; | |
1700 | break; | |
1701 | } | |
1702 | } | |
1703 | break; | |
1704 | ||
1705 | case IPP_TAG_TEXT : | |
1706 | case IPP_TAG_NAME : | |
1707 | case IPP_TAG_KEYWORD : | |
1708 | case IPP_TAG_CHARSET : | |
1709 | case IPP_TAG_URI : | |
1710 | case IPP_TAG_URISCHEME : | |
1711 | case IPP_TAG_MIMETYPE : | |
1712 | case IPP_TAG_LANGUAGE : | |
1713 | case IPP_TAG_TEXTLANG : | |
1714 | case IPP_TAG_NAMELANG : | |
1715 | for (i = attr->num_values, attrval = attr->values; | |
1716 | i > 0; | |
1717 | i --, attrval ++) | |
1718 | { | |
1719 | if (!strcmp(attrval->string.text, value)) | |
1720 | { | |
1721 | match = 1; | |
1722 | break; | |
1723 | } | |
1724 | } | |
1725 | break; | |
1726 | ||
1727 | default : | |
1728 | break; | |
1729 | } | |
1730 | ||
1731 | if (!match) | |
1732 | break; | |
1733 | ||
1734 | num_matching = cupsAddOption(attr->name, value, num_matching, &matching); | |
1735 | } | |
1736 | ||
1737 | if (!attr) | |
1738 | { | |
1739 | if (!active) | |
1740 | active = cupsArrayNew(NULL, NULL); | |
1741 | ||
1742 | cupsArrayAdd(active, c); | |
1743 | ||
1744 | if (num_conflicts && conflicts) | |
1745 | { | |
1746 | cups_option_t *moption; /* Matching option */ | |
1747 | ||
1748 | for (i = num_matching, moption = matching; i > 0; i --, moption ++) | |
1749 | *num_conflicts = cupsAddOption(moption->name, moption->value, | |
1750 | *num_conflicts, conflicts); | |
1751 | } | |
1752 | } | |
1753 | ||
1754 | cupsFreeOptions(num_matching, matching); | |
1755 | } | |
1756 | ||
1757 | return (active); | |
1758 | } | |
1759 | ||
1760 | ||
dcb445bc MS |
1761 | /* |
1762 | * End of "$Id$". | |
1763 | */ |