]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd-cache.c
Remove unused _PPD_NORMAL_CONSTRAINTS enum
[thirdparty/cups.git] / cups / ppd-cache.c
1 /*
2 * PPD cache implementation for CUPS.
3 *
4 * Copyright 2010-2017 by Apple Inc.
5 *
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
7 */
8
9 /*
10 * Include necessary headers...
11 */
12
13 #include "cups-private.h"
14 #include "ppd-private.h"
15 #include <math.h>
16
17
18 /*
19 * Macro to test for two almost-equal PWG measurements.
20 */
21
22 #define _PWG_EQUIVALENT(x, y) (abs((x)-(y)) < 2)
23
24
25 /*
26 * Local functions...
27 */
28
29 static int cups_get_url(http_t **http, const char *url, char *name, size_t namesize);
30 static void pwg_add_finishing(cups_array_t *finishings, ipp_finishings_t template, const char *name, const char *value);
31 static void pwg_add_message(cups_array_t *a, const char *msg, const char *str);
32 static int pwg_compare_finishings(_pwg_finishings_t *a, _pwg_finishings_t *b);
33 static int pwg_compare_sizes(cups_size_t *a, cups_size_t *b);
34 static cups_size_t *pwg_copy_size(cups_size_t *size);
35 static void pwg_free_finishings(_pwg_finishings_t *f);
36 static void pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
37 static void pwg_ppdize_resolution(ipp_attribute_t *attr, int element, int *xres, int *yres, char *name, size_t namesize);
38 static void pwg_unppdize_name(const char *ppd, char *name, size_t namesize,
39 const char *dashchars);
40
41
42 /*
43 * '_cupsConvertOptions()' - Convert printer options to standard IPP attributes.
44 *
45 * This functions converts PPD and CUPS-specific options to their standard IPP
46 * attributes and values and adds them to the specified IPP request.
47 */
48
49 int /* O - New number of copies */
50 _cupsConvertOptions(
51 ipp_t *request, /* I - IPP request */
52 ppd_file_t *ppd, /* I - PPD file */
53 _ppd_cache_t *pc, /* I - PPD cache info */
54 ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
55 ipp_attribute_t *doc_handling_sup, /* I - multiple-document-handling-supported values */
56 ipp_attribute_t *print_color_mode_sup,
57 /* I - Printer supports print-color-mode */
58 const char *user, /* I - User info */
59 const char *format, /* I - document-format value */
60 int copies, /* I - Number of copies */
61 int num_options, /* I - Number of options */
62 cups_option_t *options) /* I - Options */
63 {
64 int i; /* Looping var */
65 const char *keyword, /* PWG keyword */
66 *password; /* Password string */
67 pwg_size_t *size; /* PWG media size */
68 ipp_t *media_col, /* media-col value */
69 *media_size; /* media-size value */
70 const char *media_source, /* media-source value */
71 *media_type, /* media-type value */
72 *collate_str, /* multiple-document-handling value */
73 *color_attr_name, /* Supported color attribute */
74 *mandatory, /* Mandatory attributes */
75 *finishing_template; /* Finishing template */
76 int num_finishings = 0, /* Number of finishing values */
77 finishings[10]; /* Finishing enum values */
78 ppd_choice_t *choice; /* Marked choice */
79 int finishings_copies = copies;
80 /* Number of copies for finishings */
81
82
83 /*
84 * Send standard IPP attributes...
85 */
86
87 if (pc->password && (password = cupsGetOption("job-password", num_options, options)) != NULL && ippGetOperation(request) != IPP_OP_VALIDATE_JOB)
88 {
89 ipp_attribute_t *attr = NULL; /* job-password attribute */
90
91 if ((keyword = cupsGetOption("job-password-encryption", num_options, options)) == NULL)
92 keyword = "none";
93
94 if (!strcmp(keyword, "none"))
95 {
96 /*
97 * Add plain-text job-password...
98 */
99
100 attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", password, (int)strlen(password));
101 }
102 else
103 {
104 /*
105 * Add hashed job-password...
106 */
107
108 unsigned char hash[64]; /* Hash of password */
109 ssize_t hashlen; /* Length of hash */
110
111 if ((hashlen = cupsHashData(keyword, password, strlen(password), hash, sizeof(hash))) > 0)
112 attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", hash, (int)hashlen);
113 }
114
115 if (attr)
116 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "job-password-encryption", NULL, keyword);
117 }
118
119 if (pc->account_id)
120 {
121 if ((keyword = cupsGetOption("job-account-id", num_options, options)) == NULL)
122 keyword = cupsGetOption("job-billing", num_options, options);
123
124 if (keyword)
125 ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id", NULL, keyword);
126 }
127
128 if (pc->accounting_user_id)
129 {
130 if ((keyword = cupsGetOption("job-accounting-user-id", num_options, options)) == NULL)
131 keyword = user;
132
133 if (keyword)
134 ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-accounting-user-id", NULL, keyword);
135 }
136
137 for (mandatory = (const char *)cupsArrayFirst(pc->mandatory); mandatory; mandatory = (const char *)cupsArrayNext(pc->mandatory))
138 {
139 if (strcmp(mandatory, "copies") &&
140 strcmp(mandatory, "destination-uris") &&
141 strcmp(mandatory, "finishings") &&
142 strcmp(mandatory, "finishings-col") &&
143 strcmp(mandatory, "finishing-template") &&
144 strcmp(mandatory, "job-account-id") &&
145 strcmp(mandatory, "job-accounting-user-id") &&
146 strcmp(mandatory, "job-password") &&
147 strcmp(mandatory, "job-password-encryption") &&
148 strcmp(mandatory, "media") &&
149 strncmp(mandatory, "media-col", 9) &&
150 strcmp(mandatory, "multiple-document-handling") &&
151 strcmp(mandatory, "output-bin") &&
152 strcmp(mandatory, "print-color-mode") &&
153 strcmp(mandatory, "print-quality") &&
154 strcmp(mandatory, "sides") &&
155 (keyword = cupsGetOption(mandatory, num_options, options)) != NULL)
156 {
157 _ipp_option_t *opt = _ippFindOption(mandatory);
158 /* Option type */
159 ipp_tag_t value_tag = opt ? opt->value_tag : IPP_TAG_NAME;
160 /* Value type */
161
162 switch (value_tag)
163 {
164 case IPP_TAG_INTEGER :
165 case IPP_TAG_ENUM :
166 ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory, atoi(keyword));
167 break;
168 case IPP_TAG_BOOLEAN :
169 ippAddBoolean(request, IPP_TAG_JOB, mandatory, !_cups_strcasecmp(keyword, "true"));
170 break;
171 case IPP_TAG_RANGE :
172 {
173 int lower, upper; /* Range */
174
175 if (sscanf(keyword, "%d-%d", &lower, &upper) != 2)
176 lower = upper = atoi(keyword);
177
178 ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper);
179 }
180 break;
181 case IPP_TAG_STRING :
182 ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword, (int)strlen(keyword));
183 break;
184 default :
185 if (!strcmp(mandatory, "print-color-mode") && !strcmp(keyword, "monochrome"))
186 {
187 if (ippContainsString(print_color_mode_sup, "auto-monochrome"))
188 keyword = "auto-monochrome";
189 else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome"))
190 keyword = "process-monochrome";
191 }
192
193 ippAddString(request, IPP_TAG_JOB, value_tag, mandatory, NULL, keyword);
194 break;
195 }
196 }
197 }
198
199 if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
200 keyword = cupsGetOption("media", num_options, options);
201
202 media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot", num_options, options));
203 media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType", num_options, options));
204 size = _ppdCacheGetSize(pc, keyword);
205
206 if (size || media_source || media_type)
207 {
208 /*
209 * Add a media-col value...
210 */
211
212 media_col = ippNew();
213
214 if (size)
215 {
216 media_size = ippNew();
217 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
218 "x-dimension", size->width);
219 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
220 "y-dimension", size->length);
221
222 ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
223 }
224
225 for (i = 0; i < media_col_sup->num_values; i ++)
226 {
227 if (size && !strcmp(media_col_sup->values[i].string.text, "media-left-margin"))
228 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-left-margin", size->left);
229 else if (size && !strcmp(media_col_sup->values[i].string.text, "media-bottom-margin"))
230 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-bottom-margin", size->bottom);
231 else if (size && !strcmp(media_col_sup->values[i].string.text, "media-right-margin"))
232 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-right-margin", size->right);
233 else if (size && !strcmp(media_col_sup->values[i].string.text, "media-top-margin"))
234 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-top-margin", size->top);
235 else if (media_source && !strcmp(media_col_sup->values[i].string.text, "media-source"))
236 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-source", NULL, media_source);
237 else if (media_type && !strcmp(media_col_sup->values[i].string.text, "media-type"))
238 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-type", NULL, media_type);
239 }
240
241 ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
242 }
243
244 if ((keyword = cupsGetOption("output-bin", num_options, options)) == NULL)
245 {
246 if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL)
247 keyword = _ppdCacheGetBin(pc, choice->choice);
248 }
249
250 if (keyword)
251 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin", NULL, keyword);
252
253 color_attr_name = print_color_mode_sup ? "print-color-mode" : "output-mode";
254
255 if ((keyword = cupsGetOption("print-color-mode", num_options, options)) == NULL)
256 {
257 if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL)
258 {
259 if (!_cups_strcasecmp(choice->choice, "Gray"))
260 keyword = "monochrome";
261 else
262 keyword = "color";
263 }
264 }
265
266 if (keyword && !strcmp(keyword, "monochrome"))
267 {
268 if (ippContainsString(print_color_mode_sup, "auto-monochrome"))
269 keyword = "auto-monochrome";
270 else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome"))
271 keyword = "process-monochrome";
272 }
273
274 if (keyword)
275 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, color_attr_name, NULL, keyword);
276
277 if ((keyword = cupsGetOption("print-quality", num_options, options)) != NULL)
278 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", atoi(keyword));
279 else if ((choice = ppdFindMarkedChoice(ppd, "cupsPrintQuality")) != NULL)
280 {
281 if (!_cups_strcasecmp(choice->choice, "draft"))
282 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_DRAFT);
283 else if (!_cups_strcasecmp(choice->choice, "normal"))
284 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_NORMAL);
285 else if (!_cups_strcasecmp(choice->choice, "high"))
286 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_HIGH);
287 }
288
289 if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
290 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, keyword);
291 else if (pc->sides_option && (choice = ppdFindMarkedChoice(ppd, pc->sides_option)) != NULL)
292 {
293 if (!_cups_strcasecmp(choice->choice, pc->sides_1sided))
294 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "one-sided");
295 else if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_long))
296 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-long-edge");
297 if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_short))
298 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-short-edge");
299 }
300
301 /*
302 * Copies...
303 */
304
305 if ((keyword = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
306 {
307 if (strstr(keyword, "uncollated"))
308 keyword = "false";
309 else
310 keyword = "true";
311 }
312 else if ((keyword = cupsGetOption("collate", num_options, options)) == NULL)
313 keyword = "true";
314
315 if (format)
316 {
317 if (!_cups_strcasecmp(format, "image/gif") ||
318 !_cups_strcasecmp(format, "image/jp2") ||
319 !_cups_strcasecmp(format, "image/jpeg") ||
320 !_cups_strcasecmp(format, "image/png") ||
321 !_cups_strcasecmp(format, "image/tiff") ||
322 !_cups_strncasecmp(format, "image/x-", 8))
323 {
324 /*
325 * Collation makes no sense for single page image formats...
326 */
327
328 keyword = "false";
329 }
330 else if (!_cups_strncasecmp(format, "image/", 6) ||
331 !_cups_strcasecmp(format, "application/vnd.cups-raster"))
332 {
333 /*
334 * Multi-page image formats will have copies applied by the upstream
335 * filters...
336 */
337
338 copies = 1;
339 }
340 }
341
342 if (doc_handling_sup)
343 {
344 if (!_cups_strcasecmp(keyword, "true"))
345 collate_str = "separate-documents-collated-copies";
346 else
347 collate_str = "separate-documents-uncollated-copies";
348
349 for (i = 0; i < doc_handling_sup->num_values; i ++)
350 {
351 if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
352 {
353 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "multiple-document-handling", NULL, collate_str);
354 break;
355 }
356 }
357
358 if (i >= doc_handling_sup->num_values)
359 copies = 1;
360 }
361
362 /*
363 * Map finishing options...
364 */
365
366 if ((finishing_template = cupsGetOption("cupsFinishingTemplate", num_options, options)) == NULL)
367 finishing_template = cupsGetOption("finishing-template", num_options, options);
368
369 if (finishing_template)
370 {
371 ipp_t *fin_col = ippNew(); /* finishings-col value */
372
373 ippAddString(fin_col, IPP_TAG_JOB, IPP_TAG_KEYWORD, "finishing-template", NULL, finishing_template);
374 ippAddCollection(request, IPP_TAG_JOB, "finishings-col", fin_col);
375 ippDelete(fin_col);
376
377 if (copies != finishings_copies && (keyword = cupsGetOption("job-impressions", num_options, options)) != NULL)
378 {
379 /*
380 * Send job-pages-per-set attribute to apply finishings correctly...
381 */
382
383 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", atoi(keyword) / finishings_copies);
384 }
385 }
386 else
387 {
388 num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options, (int)(sizeof(finishings) / sizeof(finishings[0])), finishings);
389 if (num_finishings > 0)
390 {
391 ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", num_finishings, finishings);
392
393 if (copies != finishings_copies && (keyword = cupsGetOption("job-impressions", num_options, options)) != NULL)
394 {
395 /*
396 * Send job-pages-per-set attribute to apply finishings correctly...
397 */
398
399 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", atoi(keyword) / finishings_copies);
400 }
401 }
402 }
403
404 return (copies);
405 }
406
407
408 /*
409 * '_ppdCacheCreateWithFile()' - Create PPD cache and mapping data from a
410 * written file.
411 *
412 * Use the @link _ppdCacheWriteFile@ function to write PWG mapping data to a
413 * file.
414 */
415
416 _ppd_cache_t * /* O - PPD cache and mapping data */
417 _ppdCacheCreateWithFile(
418 const char *filename, /* I - File to read */
419 ipp_t **attrs) /* IO - IPP attributes, if any */
420 {
421 cups_file_t *fp; /* File */
422 _ppd_cache_t *pc; /* PWG mapping data */
423 pwg_size_t *size; /* Current size */
424 pwg_map_t *map; /* Current map */
425 _pwg_finishings_t *finishings; /* Current finishings option */
426 int linenum, /* Current line number */
427 num_bins, /* Number of bins in file */
428 num_sizes, /* Number of sizes in file */
429 num_sources, /* Number of sources in file */
430 num_types; /* Number of types in file */
431 char line[2048], /* Current line */
432 *value, /* Pointer to value in line */
433 *valueptr, /* Pointer into value */
434 pwg_keyword[128], /* PWG keyword */
435 ppd_keyword[PPD_MAX_NAME];
436 /* PPD keyword */
437 _pwg_print_color_mode_t print_color_mode;
438 /* Print color mode for preset */
439 _pwg_print_quality_t print_quality; /* Print quality for preset */
440
441
442 DEBUG_printf(("_ppdCacheCreateWithFile(filename=\"%s\")", filename));
443
444 /*
445 * Range check input...
446 */
447
448 if (attrs)
449 *attrs = NULL;
450
451 if (!filename)
452 {
453 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
454 return (NULL);
455 }
456
457 /*
458 * Open the file...
459 */
460
461 if ((fp = cupsFileOpen(filename, "r")) == NULL)
462 {
463 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
464 return (NULL);
465 }
466
467 /*
468 * Read the first line and make sure it has "#CUPS-PPD-CACHE-version" in it...
469 */
470
471 if (!cupsFileGets(fp, line, sizeof(line)))
472 {
473 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
474 DEBUG_puts("_ppdCacheCreateWithFile: Unable to read first line.");
475 cupsFileClose(fp);
476 return (NULL);
477 }
478
479 if (strncmp(line, "#CUPS-PPD-CACHE-", 16))
480 {
481 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
482 DEBUG_printf(("_ppdCacheCreateWithFile: Wrong first line \"%s\".", line));
483 cupsFileClose(fp);
484 return (NULL);
485 }
486
487 if (atoi(line + 16) != _PPD_CACHE_VERSION)
488 {
489 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of date PPD cache file."), 1);
490 DEBUG_printf(("_ppdCacheCreateWithFile: Cache file has version %s, "
491 "expected %d.", line + 16, _PPD_CACHE_VERSION));
492 cupsFileClose(fp);
493 return (NULL);
494 }
495
496 /*
497 * Allocate the mapping data structure...
498 */
499
500 if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
501 {
502 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
503 DEBUG_puts("_ppdCacheCreateWithFile: Unable to allocate _ppd_cache_t.");
504 goto create_error;
505 }
506
507 pc->max_copies = 9999;
508
509 /*
510 * Read the file...
511 */
512
513 linenum = 0;
514 num_bins = 0;
515 num_sizes = 0;
516 num_sources = 0;
517 num_types = 0;
518
519 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
520 {
521 DEBUG_printf(("_ppdCacheCreateWithFile: line=\"%s\", value=\"%s\", "
522 "linenum=%d", line, value, linenum));
523
524 if (!value)
525 {
526 DEBUG_printf(("_ppdCacheCreateWithFile: Missing value on line %d.",
527 linenum));
528 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
529 goto create_error;
530 }
531 else if (!_cups_strcasecmp(line, "Filter"))
532 {
533 if (!pc->filters)
534 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
535 (cups_acopy_func_t)_cupsStrAlloc,
536 (cups_afree_func_t)_cupsStrFree);
537
538 cupsArrayAdd(pc->filters, value);
539 }
540 else if (!_cups_strcasecmp(line, "PreFilter"))
541 {
542 if (!pc->prefilters)
543 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
544 (cups_acopy_func_t)_cupsStrAlloc,
545 (cups_afree_func_t)_cupsStrFree);
546
547 cupsArrayAdd(pc->prefilters, value);
548 }
549 else if (!_cups_strcasecmp(line, "Product"))
550 {
551 pc->product = _cupsStrAlloc(value);
552 }
553 else if (!_cups_strcasecmp(line, "SingleFile"))
554 {
555 pc->single_file = !_cups_strcasecmp(value, "true");
556 }
557 else if (!_cups_strcasecmp(line, "IPP"))
558 {
559 off_t pos = cupsFileTell(fp), /* Position in file */
560 length = strtol(value, NULL, 10);
561 /* Length of IPP attributes */
562
563 if (attrs && *attrs)
564 {
565 DEBUG_puts("_ppdCacheCreateWithFile: IPP listed multiple times.");
566 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
567 goto create_error;
568 }
569 else if (length <= 0)
570 {
571 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP length.");
572 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
573 goto create_error;
574 }
575
576 if (attrs)
577 {
578 /*
579 * Read IPP attributes into the provided variable...
580 */
581
582 *attrs = ippNew();
583
584 if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
585 *attrs) != IPP_STATE_DATA)
586 {
587 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
588 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
589 goto create_error;
590 }
591 }
592 else
593 {
594 /*
595 * Skip the IPP data entirely...
596 */
597
598 cupsFileSeek(fp, pos + length);
599 }
600
601 if (cupsFileTell(fp) != (pos + length))
602 {
603 DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
604 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
605 goto create_error;
606 }
607 }
608 else if (!_cups_strcasecmp(line, "NumBins"))
609 {
610 if (num_bins > 0)
611 {
612 DEBUG_puts("_ppdCacheCreateWithFile: NumBins listed multiple times.");
613 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
614 goto create_error;
615 }
616
617 if ((num_bins = atoi(value)) <= 0 || num_bins > 65536)
618 {
619 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumBins value %d on line "
620 "%d.", num_sizes, linenum));
621 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
622 goto create_error;
623 }
624
625 if ((pc->bins = calloc((size_t)num_bins, sizeof(pwg_map_t))) == NULL)
626 {
627 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d bins.",
628 num_sizes));
629 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
630 goto create_error;
631 }
632 }
633 else if (!_cups_strcasecmp(line, "Bin"))
634 {
635 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
636 {
637 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Bin on line %d.", linenum));
638 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
639 goto create_error;
640 }
641
642 if (pc->num_bins >= num_bins)
643 {
644 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Bin's on line %d.",
645 linenum));
646 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
647 goto create_error;
648 }
649
650 map = pc->bins + pc->num_bins;
651 map->pwg = _cupsStrAlloc(pwg_keyword);
652 map->ppd = _cupsStrAlloc(ppd_keyword);
653
654 pc->num_bins ++;
655 }
656 else if (!_cups_strcasecmp(line, "NumSizes"))
657 {
658 if (num_sizes > 0)
659 {
660 DEBUG_puts("_ppdCacheCreateWithFile: NumSizes listed multiple times.");
661 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
662 goto create_error;
663 }
664
665 if ((num_sizes = atoi(value)) < 0 || num_sizes > 65536)
666 {
667 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSizes value %d on line "
668 "%d.", num_sizes, linenum));
669 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
670 goto create_error;
671 }
672
673 if (num_sizes > 0)
674 {
675 if ((pc->sizes = calloc((size_t)num_sizes, sizeof(pwg_size_t))) == NULL)
676 {
677 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sizes.",
678 num_sizes));
679 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
680 goto create_error;
681 }
682 }
683 }
684 else if (!_cups_strcasecmp(line, "Size"))
685 {
686 if (pc->num_sizes >= num_sizes)
687 {
688 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Size's on line %d.",
689 linenum));
690 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
691 goto create_error;
692 }
693
694 size = pc->sizes + pc->num_sizes;
695
696 if (sscanf(value, "%127s%40s%d%d%d%d%d%d", pwg_keyword, ppd_keyword,
697 &(size->width), &(size->length), &(size->left),
698 &(size->bottom), &(size->right), &(size->top)) != 8)
699 {
700 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Size on line %d.",
701 linenum));
702 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
703 goto create_error;
704 }
705
706 size->map.pwg = _cupsStrAlloc(pwg_keyword);
707 size->map.ppd = _cupsStrAlloc(ppd_keyword);
708
709 pc->num_sizes ++;
710 }
711 else if (!_cups_strcasecmp(line, "CustomSize"))
712 {
713 if (pc->custom_max_width > 0)
714 {
715 DEBUG_printf(("_ppdCacheCreateWithFile: Too many CustomSize's on line "
716 "%d.", linenum));
717 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
718 goto create_error;
719 }
720
721 if (sscanf(value, "%d%d%d%d%d%d%d%d", &(pc->custom_max_width),
722 &(pc->custom_max_length), &(pc->custom_min_width),
723 &(pc->custom_min_length), &(pc->custom_size.left),
724 &(pc->custom_size.bottom), &(pc->custom_size.right),
725 &(pc->custom_size.top)) != 8)
726 {
727 DEBUG_printf(("_ppdCacheCreateWithFile: Bad CustomSize on line %d.",
728 linenum));
729 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
730 goto create_error;
731 }
732
733 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
734 pc->custom_max_width, pc->custom_max_length, NULL);
735 pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
736
737 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
738 pc->custom_min_width, pc->custom_min_length, NULL);
739 pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
740 }
741 else if (!_cups_strcasecmp(line, "SourceOption"))
742 {
743 pc->source_option = _cupsStrAlloc(value);
744 }
745 else if (!_cups_strcasecmp(line, "NumSources"))
746 {
747 if (num_sources > 0)
748 {
749 DEBUG_puts("_ppdCacheCreateWithFile: NumSources listed multiple "
750 "times.");
751 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
752 goto create_error;
753 }
754
755 if ((num_sources = atoi(value)) <= 0 || num_sources > 65536)
756 {
757 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSources value %d on "
758 "line %d.", num_sources, linenum));
759 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
760 goto create_error;
761 }
762
763 if ((pc->sources = calloc((size_t)num_sources, sizeof(pwg_map_t))) == NULL)
764 {
765 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sources.",
766 num_sources));
767 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
768 goto create_error;
769 }
770 }
771 else if (!_cups_strcasecmp(line, "Source"))
772 {
773 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
774 {
775 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Source on line %d.",
776 linenum));
777 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
778 goto create_error;
779 }
780
781 if (pc->num_sources >= num_sources)
782 {
783 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Source's on line %d.",
784 linenum));
785 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
786 goto create_error;
787 }
788
789 map = pc->sources + pc->num_sources;
790 map->pwg = _cupsStrAlloc(pwg_keyword);
791 map->ppd = _cupsStrAlloc(ppd_keyword);
792
793 pc->num_sources ++;
794 }
795 else if (!_cups_strcasecmp(line, "NumTypes"))
796 {
797 if (num_types > 0)
798 {
799 DEBUG_puts("_ppdCacheCreateWithFile: NumTypes listed multiple times.");
800 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
801 goto create_error;
802 }
803
804 if ((num_types = atoi(value)) <= 0 || num_types > 65536)
805 {
806 DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumTypes value %d on "
807 "line %d.", num_types, linenum));
808 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
809 goto create_error;
810 }
811
812 if ((pc->types = calloc((size_t)num_types, sizeof(pwg_map_t))) == NULL)
813 {
814 DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d types.",
815 num_types));
816 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
817 goto create_error;
818 }
819 }
820 else if (!_cups_strcasecmp(line, "Type"))
821 {
822 if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
823 {
824 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Type on line %d.",
825 linenum));
826 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
827 goto create_error;
828 }
829
830 if (pc->num_types >= num_types)
831 {
832 DEBUG_printf(("_ppdCacheCreateWithFile: Too many Type's on line %d.",
833 linenum));
834 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
835 goto create_error;
836 }
837
838 map = pc->types + pc->num_types;
839 map->pwg = _cupsStrAlloc(pwg_keyword);
840 map->ppd = _cupsStrAlloc(ppd_keyword);
841
842 pc->num_types ++;
843 }
844 else if (!_cups_strcasecmp(line, "Preset"))
845 {
846 /*
847 * Preset output-mode print-quality name=value ...
848 */
849
850 print_color_mode = (_pwg_print_color_mode_t)strtol(value, &valueptr, 10);
851 print_quality = (_pwg_print_quality_t)strtol(valueptr, &valueptr, 10);
852
853 if (print_color_mode < _PWG_PRINT_COLOR_MODE_MONOCHROME ||
854 print_color_mode >= _PWG_PRINT_COLOR_MODE_MAX ||
855 print_quality < _PWG_PRINT_QUALITY_DRAFT ||
856 print_quality >= _PWG_PRINT_QUALITY_MAX ||
857 valueptr == value || !*valueptr)
858 {
859 DEBUG_printf(("_ppdCacheCreateWithFile: Bad Preset on line %d.",
860 linenum));
861 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
862 goto create_error;
863 }
864
865 pc->num_presets[print_color_mode][print_quality] =
866 cupsParseOptions(valueptr, 0,
867 pc->presets[print_color_mode] + print_quality);
868 }
869 else if (!_cups_strcasecmp(line, "SidesOption"))
870 pc->sides_option = _cupsStrAlloc(value);
871 else if (!_cups_strcasecmp(line, "Sides1Sided"))
872 pc->sides_1sided = _cupsStrAlloc(value);
873 else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
874 pc->sides_2sided_long = _cupsStrAlloc(value);
875 else if (!_cups_strcasecmp(line, "Sides2SidedShort"))
876 pc->sides_2sided_short = _cupsStrAlloc(value);
877 else if (!_cups_strcasecmp(line, "Finishings"))
878 {
879 if (!pc->finishings)
880 pc->finishings =
881 cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
882 NULL, NULL, 0, NULL,
883 (cups_afree_func_t)pwg_free_finishings);
884
885 if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
886 goto create_error;
887
888 finishings->value = (ipp_finishings_t)strtol(value, &valueptr, 10);
889 finishings->num_options = cupsParseOptions(valueptr, 0,
890 &(finishings->options));
891
892 cupsArrayAdd(pc->finishings, finishings);
893 }
894 else if (!_cups_strcasecmp(line, "FinishingTemplate"))
895 {
896 if (!pc->templates)
897 pc->templates = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsStrAlloc, (cups_afree_func_t)_cupsStrFree);
898
899 cupsArrayAdd(pc->templates, value);
900 }
901 else if (!_cups_strcasecmp(line, "MaxCopies"))
902 pc->max_copies = atoi(value);
903 else if (!_cups_strcasecmp(line, "ChargeInfoURI"))
904 pc->charge_info_uri = _cupsStrAlloc(value);
905 else if (!_cups_strcasecmp(line, "JobAccountId"))
906 pc->account_id = !_cups_strcasecmp(value, "true");
907 else if (!_cups_strcasecmp(line, "JobAccountingUserId"))
908 pc->accounting_user_id = !_cups_strcasecmp(value, "true");
909 else if (!_cups_strcasecmp(line, "JobPassword"))
910 pc->password = _cupsStrAlloc(value);
911 else if (!_cups_strcasecmp(line, "Mandatory"))
912 {
913 if (pc->mandatory)
914 _cupsArrayAddStrings(pc->mandatory, value, ' ');
915 else
916 pc->mandatory = _cupsArrayNewStrings(value, ' ');
917 }
918 else if (!_cups_strcasecmp(line, "SupportFile"))
919 {
920 if (!pc->support_files)
921 pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
922 (cups_acopy_func_t)_cupsStrAlloc,
923 (cups_afree_func_t)_cupsStrFree);
924
925 cupsArrayAdd(pc->support_files, value);
926 }
927 else
928 {
929 DEBUG_printf(("_ppdCacheCreateWithFile: Unknown %s on line %d.", line,
930 linenum));
931 }
932 }
933
934 if (pc->num_sizes < num_sizes)
935 {
936 DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sizes (%d < %d).",
937 pc->num_sizes, num_sizes));
938 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
939 goto create_error;
940 }
941
942 if (pc->num_sources < num_sources)
943 {
944 DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sources (%d < %d).",
945 pc->num_sources, num_sources));
946 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
947 goto create_error;
948 }
949
950 if (pc->num_types < num_types)
951 {
952 DEBUG_printf(("_ppdCacheCreateWithFile: Not enough types (%d < %d).",
953 pc->num_types, num_types));
954 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
955 goto create_error;
956 }
957
958 cupsFileClose(fp);
959
960 return (pc);
961
962 /*
963 * If we get here the file was bad - free any data and return...
964 */
965
966 create_error:
967
968 cupsFileClose(fp);
969 _ppdCacheDestroy(pc);
970
971 if (attrs)
972 {
973 ippDelete(*attrs);
974 *attrs = NULL;
975 }
976
977 return (NULL);
978 }
979
980
981 /*
982 * '_ppdCacheCreateWithPPD()' - Create PWG mapping data from a PPD file.
983 */
984
985 _ppd_cache_t * /* O - PPD cache and mapping data */
986 _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
987 {
988 int i, j, k; /* Looping vars */
989 _ppd_cache_t *pc; /* PWG mapping data */
990 ppd_option_t *input_slot, /* InputSlot option */
991 *media_type, /* MediaType option */
992 *output_bin, /* OutputBin option */
993 *color_model, /* ColorModel option */
994 *duplex, /* Duplex option */
995 *ppd_option; /* Other PPD option */
996 ppd_choice_t *choice; /* Current InputSlot/MediaType */
997 pwg_map_t *map; /* Current source/type map */
998 ppd_attr_t *ppd_attr; /* Current PPD preset attribute */
999 int num_options; /* Number of preset options and props */
1000 cups_option_t *options; /* Preset options and properties */
1001 ppd_size_t *ppd_size; /* Current PPD size */
1002 pwg_size_t *pwg_size; /* Current PWG size */
1003 char pwg_keyword[3 + PPD_MAX_NAME + 1 + 12 + 1 + 12 + 3],
1004 /* PWG keyword string */
1005 ppd_name[PPD_MAX_NAME];
1006 /* Normalized PPD name */
1007 const char *pwg_name; /* Standard PWG media name */
1008 pwg_media_t *pwg_media; /* PWG media data */
1009 _pwg_print_color_mode_t pwg_print_color_mode;
1010 /* print-color-mode index */
1011 _pwg_print_quality_t pwg_print_quality;
1012 /* print-quality index */
1013 int similar; /* Are the old and new size similar? */
1014 pwg_size_t *old_size; /* Current old size */
1015 int old_imageable, /* Old imageable length in 2540ths */
1016 old_borderless, /* Old borderless state */
1017 old_known_pwg; /* Old PWG name is well-known */
1018 int new_width, /* New width in 2540ths */
1019 new_length, /* New length in 2540ths */
1020 new_left, /* New left margin in 2540ths */
1021 new_bottom, /* New bottom margin in 2540ths */
1022 new_right, /* New right margin in 2540ths */
1023 new_top, /* New top margin in 2540ths */
1024 new_imageable, /* New imageable length in 2540ths */
1025 new_borderless, /* New borderless state */
1026 new_known_pwg; /* New PWG name is well-known */
1027 pwg_size_t *new_size; /* New size to add, if any */
1028 const char *filter; /* Current filter */
1029 _pwg_finishings_t *finishings; /* Current finishings value */
1030 char msg_id[256]; /* Message identifier */
1031
1032
1033 DEBUG_printf(("_ppdCacheCreateWithPPD(ppd=%p)", ppd));
1034
1035 /*
1036 * Range check input...
1037 */
1038
1039 if (!ppd)
1040 return (NULL);
1041
1042 /*
1043 * Allocate memory...
1044 */
1045
1046 if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
1047 {
1048 DEBUG_puts("_ppdCacheCreateWithPPD: Unable to allocate _ppd_cache_t.");
1049 goto create_error;
1050 }
1051
1052 pc->strings = _cupsMessageNew(NULL);
1053
1054 /*
1055 * Copy and convert size data...
1056 */
1057
1058 if (ppd->num_sizes > 0)
1059 {
1060 if ((pc->sizes = calloc((size_t)ppd->num_sizes, sizeof(pwg_size_t))) == NULL)
1061 {
1062 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1063 "pwg_size_t's.", ppd->num_sizes));
1064 goto create_error;
1065 }
1066
1067 for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes;
1068 i > 0;
1069 i --, ppd_size ++)
1070 {
1071 /*
1072 * Don't copy over custom size...
1073 */
1074
1075 if (!_cups_strcasecmp(ppd_size->name, "Custom"))
1076 continue;
1077
1078 /*
1079 * Convert the PPD size name to the corresponding PWG keyword name.
1080 */
1081
1082 if ((pwg_media = pwgMediaForPPD(ppd_size->name)) != NULL)
1083 {
1084 /*
1085 * Standard name, do we have conflicts?
1086 */
1087
1088 for (j = 0; j < pc->num_sizes; j ++)
1089 if (!strcmp(pc->sizes[j].map.pwg, pwg_media->pwg))
1090 {
1091 pwg_media = NULL;
1092 break;
1093 }
1094 }
1095
1096 if (pwg_media)
1097 {
1098 /*
1099 * Standard name and no conflicts, use it!
1100 */
1101
1102 pwg_name = pwg_media->pwg;
1103 new_known_pwg = 1;
1104 }
1105 else
1106 {
1107 /*
1108 * Not a standard name; convert it to a PWG vendor name of the form:
1109 *
1110 * pp_lowerppd_WIDTHxHEIGHTuu
1111 */
1112
1113 pwg_name = pwg_keyword;
1114 new_known_pwg = 0;
1115
1116 pwg_unppdize_name(ppd_size->name, ppd_name, sizeof(ppd_name), "_.");
1117 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), NULL, ppd_name,
1118 PWG_FROM_POINTS(ppd_size->width),
1119 PWG_FROM_POINTS(ppd_size->length), NULL);
1120 }
1121
1122 /*
1123 * If we have a similar paper with non-zero margins then we only want to
1124 * keep it if it has a larger imageable area length. The NULL check is for
1125 * dimensions that are <= 0...
1126 */
1127
1128 if ((pwg_media = _pwgMediaNearSize(PWG_FROM_POINTS(ppd_size->width),
1129 PWG_FROM_POINTS(ppd_size->length),
1130 0)) == NULL)
1131 continue;
1132
1133 new_width = pwg_media->width;
1134 new_length = pwg_media->length;
1135 new_left = PWG_FROM_POINTS(ppd_size->left);
1136 new_bottom = PWG_FROM_POINTS(ppd_size->bottom);
1137 new_right = PWG_FROM_POINTS(ppd_size->width - ppd_size->right);
1138 new_top = PWG_FROM_POINTS(ppd_size->length - ppd_size->top);
1139 new_imageable = new_length - new_top - new_bottom;
1140 new_borderless = new_bottom == 0 && new_top == 0 &&
1141 new_left == 0 && new_right == 0;
1142
1143 for (k = pc->num_sizes, similar = 0, old_size = pc->sizes, new_size = NULL;
1144 k > 0 && !similar;
1145 k --, old_size ++)
1146 {
1147 old_imageable = old_size->length - old_size->top - old_size->bottom;
1148 old_borderless = old_size->left == 0 && old_size->bottom == 0 &&
1149 old_size->right == 0 && old_size->top == 0;
1150 old_known_pwg = strncmp(old_size->map.pwg, "oe_", 3) &&
1151 strncmp(old_size->map.pwg, "om_", 3);
1152
1153 similar = old_borderless == new_borderless &&
1154 _PWG_EQUIVALENT(old_size->width, new_width) &&
1155 _PWG_EQUIVALENT(old_size->length, new_length);
1156
1157 if (similar &&
1158 (new_known_pwg || (!old_known_pwg && new_imageable > old_imageable)))
1159 {
1160 /*
1161 * The new paper has a larger imageable area so it could replace
1162 * the older paper. Regardless of the imageable area, we always
1163 * prefer the size with a well-known PWG name.
1164 */
1165
1166 new_size = old_size;
1167 _cupsStrFree(old_size->map.ppd);
1168 _cupsStrFree(old_size->map.pwg);
1169 }
1170 }
1171
1172 if (!similar)
1173 {
1174 /*
1175 * The paper was unique enough to deserve its own entry so add it to the
1176 * end.
1177 */
1178
1179 new_size = pwg_size ++;
1180 pc->num_sizes ++;
1181 }
1182
1183 if (new_size)
1184 {
1185 /*
1186 * Save this size...
1187 */
1188
1189 new_size->map.ppd = _cupsStrAlloc(ppd_size->name);
1190 new_size->map.pwg = _cupsStrAlloc(pwg_name);
1191 new_size->width = new_width;
1192 new_size->length = new_length;
1193 new_size->left = new_left;
1194 new_size->bottom = new_bottom;
1195 new_size->right = new_right;
1196 new_size->top = new_top;
1197 }
1198 }
1199 }
1200
1201 if (ppd->variable_sizes)
1202 {
1203 /*
1204 * Generate custom size data...
1205 */
1206
1207 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
1208 PWG_FROM_POINTS(ppd->custom_max[0]),
1209 PWG_FROM_POINTS(ppd->custom_max[1]), NULL);
1210 pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
1211 pc->custom_max_width = PWG_FROM_POINTS(ppd->custom_max[0]);
1212 pc->custom_max_length = PWG_FROM_POINTS(ppd->custom_max[1]);
1213
1214 pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
1215 PWG_FROM_POINTS(ppd->custom_min[0]),
1216 PWG_FROM_POINTS(ppd->custom_min[1]), NULL);
1217 pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
1218 pc->custom_min_width = PWG_FROM_POINTS(ppd->custom_min[0]);
1219 pc->custom_min_length = PWG_FROM_POINTS(ppd->custom_min[1]);
1220
1221 pc->custom_size.left = PWG_FROM_POINTS(ppd->custom_margins[0]);
1222 pc->custom_size.bottom = PWG_FROM_POINTS(ppd->custom_margins[1]);
1223 pc->custom_size.right = PWG_FROM_POINTS(ppd->custom_margins[2]);
1224 pc->custom_size.top = PWG_FROM_POINTS(ppd->custom_margins[3]);
1225 }
1226
1227 /*
1228 * Copy and convert InputSlot data...
1229 */
1230
1231 if ((input_slot = ppdFindOption(ppd, "InputSlot")) == NULL)
1232 input_slot = ppdFindOption(ppd, "HPPaperSource");
1233
1234 if (input_slot)
1235 {
1236 pc->source_option = _cupsStrAlloc(input_slot->keyword);
1237
1238 if ((pc->sources = calloc((size_t)input_slot->num_choices, sizeof(pwg_map_t))) == NULL)
1239 {
1240 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1241 "pwg_map_t's for InputSlot.", input_slot->num_choices));
1242 goto create_error;
1243 }
1244
1245 pc->num_sources = input_slot->num_choices;
1246
1247 for (i = input_slot->num_choices, choice = input_slot->choices,
1248 map = pc->sources;
1249 i > 0;
1250 i --, choice ++, map ++)
1251 {
1252 if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
1253 !_cups_strcasecmp(choice->choice, "Default"))
1254 pwg_name = "auto";
1255 else if (!_cups_strcasecmp(choice->choice, "Cassette"))
1256 pwg_name = "main";
1257 else if (!_cups_strcasecmp(choice->choice, "PhotoTray"))
1258 pwg_name = "photo";
1259 else if (!_cups_strcasecmp(choice->choice, "CDTray"))
1260 pwg_name = "disc";
1261 else if (!_cups_strncasecmp(choice->choice, "Multipurpose", 12) ||
1262 !_cups_strcasecmp(choice->choice, "MP") ||
1263 !_cups_strcasecmp(choice->choice, "MPTray"))
1264 pwg_name = "by-pass-tray";
1265 else if (!_cups_strcasecmp(choice->choice, "LargeCapacity"))
1266 pwg_name = "large-capacity";
1267 else if (!_cups_strncasecmp(choice->choice, "Lower", 5))
1268 pwg_name = "bottom";
1269 else if (!_cups_strncasecmp(choice->choice, "Middle", 6))
1270 pwg_name = "middle";
1271 else if (!_cups_strncasecmp(choice->choice, "Upper", 5))
1272 pwg_name = "top";
1273 else if (!_cups_strncasecmp(choice->choice, "Side", 4))
1274 pwg_name = "side";
1275 else if (!_cups_strcasecmp(choice->choice, "Roll"))
1276 pwg_name = "main-roll";
1277 else
1278 {
1279 /*
1280 * Convert PPD name to lowercase...
1281 */
1282
1283 pwg_name = pwg_keyword;
1284 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1285 "_");
1286 }
1287
1288 map->pwg = _cupsStrAlloc(pwg_name);
1289 map->ppd = _cupsStrAlloc(choice->choice);
1290
1291 /*
1292 * Add localized text for PWG keyword to message catalog...
1293 */
1294
1295 snprintf(msg_id, sizeof(msg_id), "media-source.%s", pwg_name);
1296 pwg_add_message(pc->strings, msg_id, choice->text);
1297 }
1298 }
1299
1300 /*
1301 * Copy and convert MediaType data...
1302 */
1303
1304 if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
1305 {
1306 if ((pc->types = calloc((size_t)media_type->num_choices, sizeof(pwg_map_t))) == NULL)
1307 {
1308 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1309 "pwg_map_t's for MediaType.", media_type->num_choices));
1310 goto create_error;
1311 }
1312
1313 pc->num_types = media_type->num_choices;
1314
1315 for (i = media_type->num_choices, choice = media_type->choices,
1316 map = pc->types;
1317 i > 0;
1318 i --, choice ++, map ++)
1319 {
1320 if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
1321 !_cups_strcasecmp(choice->choice, "Any") ||
1322 !_cups_strcasecmp(choice->choice, "Default"))
1323 pwg_name = "auto";
1324 else if (!_cups_strncasecmp(choice->choice, "Card", 4))
1325 pwg_name = "cardstock";
1326 else if (!_cups_strncasecmp(choice->choice, "Env", 3))
1327 pwg_name = "envelope";
1328 else if (!_cups_strncasecmp(choice->choice, "Gloss", 5))
1329 pwg_name = "photographic-glossy";
1330 else if (!_cups_strcasecmp(choice->choice, "HighGloss"))
1331 pwg_name = "photographic-high-gloss";
1332 else if (!_cups_strcasecmp(choice->choice, "Matte"))
1333 pwg_name = "photographic-matte";
1334 else if (!_cups_strncasecmp(choice->choice, "Plain", 5))
1335 pwg_name = "stationery";
1336 else if (!_cups_strncasecmp(choice->choice, "Coated", 6))
1337 pwg_name = "stationery-coated";
1338 else if (!_cups_strcasecmp(choice->choice, "Inkjet"))
1339 pwg_name = "stationery-inkjet";
1340 else if (!_cups_strcasecmp(choice->choice, "Letterhead"))
1341 pwg_name = "stationery-letterhead";
1342 else if (!_cups_strncasecmp(choice->choice, "Preprint", 8))
1343 pwg_name = "stationery-preprinted";
1344 else if (!_cups_strcasecmp(choice->choice, "Recycled"))
1345 pwg_name = "stationery-recycled";
1346 else if (!_cups_strncasecmp(choice->choice, "Transparen", 10))
1347 pwg_name = "transparency";
1348 else
1349 {
1350 /*
1351 * Convert PPD name to lowercase...
1352 */
1353
1354 pwg_name = pwg_keyword;
1355 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1356 "_");
1357 }
1358
1359 map->pwg = _cupsStrAlloc(pwg_name);
1360 map->ppd = _cupsStrAlloc(choice->choice);
1361
1362 /*
1363 * Add localized text for PWG keyword to message catalog...
1364 */
1365
1366 snprintf(msg_id, sizeof(msg_id), "media-type.%s", pwg_name);
1367 pwg_add_message(pc->strings, msg_id, choice->text);
1368 }
1369 }
1370
1371 /*
1372 * Copy and convert OutputBin data...
1373 */
1374
1375 if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
1376 {
1377 if ((pc->bins = calloc((size_t)output_bin->num_choices, sizeof(pwg_map_t))) == NULL)
1378 {
1379 DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
1380 "pwg_map_t's for OutputBin.", output_bin->num_choices));
1381 goto create_error;
1382 }
1383
1384 pc->num_bins = output_bin->num_choices;
1385
1386 for (i = output_bin->num_choices, choice = output_bin->choices,
1387 map = pc->bins;
1388 i > 0;
1389 i --, choice ++, map ++)
1390 {
1391 pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
1392
1393 map->pwg = _cupsStrAlloc(pwg_keyword);
1394 map->ppd = _cupsStrAlloc(choice->choice);
1395
1396 /*
1397 * Add localized text for PWG keyword to message catalog...
1398 */
1399
1400 snprintf(msg_id, sizeof(msg_id), "output-bin.%s", pwg_name);
1401 pwg_add_message(pc->strings, msg_id, choice->text);
1402 }
1403 }
1404
1405 if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
1406 {
1407 /*
1408 * Copy and convert APPrinterPreset (output-mode + print-quality) data...
1409 */
1410
1411 const char *quality, /* com.apple.print.preset.quality value */
1412 *output_mode, /* com.apple.print.preset.output-mode value */
1413 *color_model_val, /* ColorModel choice */
1414 *graphicsType, /* com.apple.print.preset.graphicsType value */
1415 *media_front_coating; /* com.apple.print.preset.media-front-coating value */
1416
1417 do
1418 {
1419 /*
1420 * Add localized text for PWG keyword to message catalog...
1421 */
1422
1423 snprintf(msg_id, sizeof(msg_id), "preset-name.%s", ppd_attr->spec);
1424 pwg_add_message(pc->strings, msg_id, ppd_attr->text);
1425
1426 /*
1427 * Get the options for this preset...
1428 */
1429
1430 num_options = _ppdParseOptions(ppd_attr->value, 0, &options,
1431 _PPD_PARSE_ALL);
1432
1433 if ((quality = cupsGetOption("com.apple.print.preset.quality",
1434 num_options, options)) != NULL)
1435 {
1436 /*
1437 * Get the print-quality for this preset...
1438 */
1439
1440 if (!strcmp(quality, "low"))
1441 pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1442 else if (!strcmp(quality, "high"))
1443 pwg_print_quality = _PWG_PRINT_QUALITY_HIGH;
1444 else
1445 pwg_print_quality = _PWG_PRINT_QUALITY_NORMAL;
1446
1447 /*
1448 * Ignore graphicsType "Photo" presets that are not high quality.
1449 */
1450
1451 graphicsType = cupsGetOption("com.apple.print.preset.graphicsType",
1452 num_options, options);
1453
1454 if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH && graphicsType &&
1455 !strcmp(graphicsType, "Photo"))
1456 continue;
1457
1458 /*
1459 * Ignore presets for normal and draft quality where the coating
1460 * isn't "none" or "autodetect".
1461 */
1462
1463 media_front_coating = cupsGetOption(
1464 "com.apple.print.preset.media-front-coating",
1465 num_options, options);
1466
1467 if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH &&
1468 media_front_coating &&
1469 strcmp(media_front_coating, "none") &&
1470 strcmp(media_front_coating, "autodetect"))
1471 continue;
1472
1473 /*
1474 * Get the output mode for this preset...
1475 */
1476
1477 output_mode = cupsGetOption("com.apple.print.preset.output-mode",
1478 num_options, options);
1479 color_model_val = cupsGetOption("ColorModel", num_options, options);
1480
1481 if (output_mode)
1482 {
1483 if (!strcmp(output_mode, "monochrome"))
1484 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1485 else
1486 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1487 }
1488 else if (color_model_val)
1489 {
1490 if (!_cups_strcasecmp(color_model_val, "Gray"))
1491 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1492 else
1493 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1494 }
1495 else
1496 pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1497
1498 /*
1499 * Save the options for this combination as needed...
1500 */
1501
1502 if (!pc->num_presets[pwg_print_color_mode][pwg_print_quality])
1503 pc->num_presets[pwg_print_color_mode][pwg_print_quality] =
1504 _ppdParseOptions(ppd_attr->value, 0,
1505 pc->presets[pwg_print_color_mode] +
1506 pwg_print_quality, _PPD_PARSE_OPTIONS);
1507 }
1508
1509 cupsFreeOptions(num_options, options);
1510 }
1511 while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) != NULL);
1512 }
1513
1514 if (!pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_DRAFT] &&
1515 !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_NORMAL] &&
1516 !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_HIGH])
1517 {
1518 /*
1519 * Try adding some common color options to create grayscale presets. These
1520 * are listed in order of popularity...
1521 */
1522
1523 const char *color_option = NULL, /* Color control option */
1524 *gray_choice = NULL; /* Choice to select grayscale */
1525
1526 if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL &&
1527 ppdFindChoice(color_model, "Gray"))
1528 {
1529 color_option = "ColorModel";
1530 gray_choice = "Gray";
1531 }
1532 else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL &&
1533 ppdFindChoice(color_model, "grayscale"))
1534 {
1535 color_option = "HPColorMode";
1536 gray_choice = "grayscale";
1537 }
1538 else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL &&
1539 ppdFindChoice(color_model, "Mono"))
1540 {
1541 color_option = "BRMonoColor";
1542 gray_choice = "Mono";
1543 }
1544 else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL &&
1545 ppdFindChoice(color_model, "1"))
1546 {
1547 color_option = "CNIJSGrayScale";
1548 gray_choice = "1";
1549 }
1550 else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL &&
1551 ppdFindChoice(color_model, "True"))
1552 {
1553 color_option = "HPColorAsGray";
1554 gray_choice = "True";
1555 }
1556
1557 if (color_option && gray_choice)
1558 {
1559 /*
1560 * Copy and convert ColorModel (output-mode) data...
1561 */
1562
1563 cups_option_t *coption, /* Color option */
1564 *moption; /* Monochrome option */
1565
1566 for (pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1567 pwg_print_quality < _PWG_PRINT_QUALITY_MAX;
1568 pwg_print_quality ++)
1569 {
1570 if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality])
1571 {
1572 /*
1573 * Copy the color options...
1574 */
1575
1576 num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
1577 [pwg_print_quality];
1578 options = calloc(sizeof(cups_option_t), (size_t)num_options);
1579
1580 if (options)
1581 {
1582 for (i = num_options, moption = options,
1583 coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
1584 [pwg_print_quality];
1585 i > 0;
1586 i --, moption ++, coption ++)
1587 {
1588 moption->name = _cupsStrRetain(coption->name);
1589 moption->value = _cupsStrRetain(coption->value);
1590 }
1591
1592 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1593 num_options;
1594 pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1595 options;
1596 }
1597 }
1598 else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL)
1599 continue;
1600
1601 /*
1602 * Add the grayscale option to the preset...
1603 */
1604
1605 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1606 cupsAddOption(color_option, gray_choice,
1607 pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
1608 [pwg_print_quality],
1609 pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME] +
1610 pwg_print_quality);
1611 }
1612 }
1613 }
1614
1615 /*
1616 * Copy and convert Duplex (sides) data...
1617 */
1618
1619 if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
1620 if ((duplex = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1621 if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
1622 if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
1623 duplex = ppdFindOption(ppd, "KD03Duplex");
1624
1625 if (duplex)
1626 {
1627 pc->sides_option = _cupsStrAlloc(duplex->keyword);
1628
1629 for (i = duplex->num_choices, choice = duplex->choices;
1630 i > 0;
1631 i --, choice ++)
1632 {
1633 if ((!_cups_strcasecmp(choice->choice, "None") ||
1634 !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
1635 pc->sides_1sided = _cupsStrAlloc(choice->choice);
1636 else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
1637 !_cups_strcasecmp(choice->choice, "LongEdge") ||
1638 !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
1639 pc->sides_2sided_long = _cupsStrAlloc(choice->choice);
1640 else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
1641 !_cups_strcasecmp(choice->choice, "ShortEdge") ||
1642 !_cups_strcasecmp(choice->choice, "Bottom")) &&
1643 !pc->sides_2sided_short)
1644 pc->sides_2sided_short = _cupsStrAlloc(choice->choice);
1645 }
1646 }
1647
1648 /*
1649 * Copy filters and pre-filters...
1650 */
1651
1652 pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
1653 (cups_acopy_func_t)_cupsStrAlloc,
1654 (cups_afree_func_t)_cupsStrFree);
1655
1656 cupsArrayAdd(pc->filters,
1657 "application/vnd.cups-raw application/octet-stream 0 -");
1658
1659 if ((ppd_attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL)
1660 {
1661 do
1662 {
1663 cupsArrayAdd(pc->filters, ppd_attr->value);
1664 }
1665 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
1666 }
1667 else if (ppd->num_filters > 0)
1668 {
1669 for (i = 0; i < ppd->num_filters; i ++)
1670 cupsArrayAdd(pc->filters, ppd->filters[i]);
1671 }
1672 else
1673 cupsArrayAdd(pc->filters, "application/vnd.cups-postscript 0 -");
1674
1675 /*
1676 * See if we have a command filter...
1677 */
1678
1679 for (filter = (const char *)cupsArrayFirst(pc->filters);
1680 filter;
1681 filter = (const char *)cupsArrayNext(pc->filters))
1682 if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
1683 _cups_isspace(filter[28]))
1684 break;
1685
1686 if (!filter &&
1687 ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL ||
1688 _cups_strcasecmp(ppd_attr->value, "none")))
1689 {
1690 /*
1691 * No command filter and no cupsCommands keyword telling us not to use one.
1692 * See if this is a PostScript printer, and if so add a PostScript command
1693 * filter...
1694 */
1695
1696 for (filter = (const char *)cupsArrayFirst(pc->filters);
1697 filter;
1698 filter = (const char *)cupsArrayNext(pc->filters))
1699 if (!_cups_strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
1700 _cups_isspace(filter[31]))
1701 break;
1702
1703 if (filter)
1704 cupsArrayAdd(pc->filters,
1705 "application/vnd.cups-command application/postscript 100 "
1706 "commandtops");
1707 }
1708
1709 if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
1710 {
1711 pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
1712 (cups_acopy_func_t)_cupsStrAlloc,
1713 (cups_afree_func_t)_cupsStrFree);
1714
1715 do
1716 {
1717 cupsArrayAdd(pc->prefilters, ppd_attr->value);
1718 }
1719 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) != NULL);
1720 }
1721
1722 if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL)
1723 pc->single_file = !_cups_strcasecmp(ppd_attr->value, "true");
1724
1725 /*
1726 * Copy the product string, if any...
1727 */
1728
1729 if (ppd->product)
1730 pc->product = _cupsStrAlloc(ppd->product);
1731
1732 /*
1733 * Copy finishings mapping data...
1734 */
1735
1736 if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL)
1737 {
1738 /*
1739 * Have proper vendor mapping of IPP finishings values to PPD options...
1740 */
1741
1742 pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
1743 NULL, NULL, 0, NULL,
1744 (cups_afree_func_t)pwg_free_finishings);
1745
1746 do
1747 {
1748 if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
1749 goto create_error;
1750
1751 finishings->value = (ipp_finishings_t)atoi(ppd_attr->spec);
1752 finishings->num_options = _ppdParseOptions(ppd_attr->value, 0,
1753 &(finishings->options),
1754 _PPD_PARSE_OPTIONS);
1755
1756 cupsArrayAdd(pc->finishings, finishings);
1757 }
1758 while ((ppd_attr = ppdFindNextAttr(ppd, "cupsIPPFinishings",
1759 NULL)) != NULL);
1760 }
1761 else
1762 {
1763 /*
1764 * No IPP mapping data, try to map common/standard PPD keywords...
1765 */
1766
1767 pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings, NULL, NULL, 0, NULL, (cups_afree_func_t)pwg_free_finishings);
1768
1769 if ((ppd_option = ppdFindOption(ppd, "StapleLocation")) != NULL)
1770 {
1771 /*
1772 * Add staple finishings...
1773 */
1774
1775 if (ppdFindChoice(ppd_option, "SinglePortrait"))
1776 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, "StapleLocation", "SinglePortrait");
1777 if (ppdFindChoice(ppd_option, "UpperLeft")) /* Ricoh extension */
1778 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, "StapleLocation", "UpperLeft");
1779 if (ppdFindChoice(ppd_option, "UpperRight")) /* Ricoh extension */
1780 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_RIGHT, "StapleLocation", "UpperRight");
1781 if (ppdFindChoice(ppd_option, "SingleLandscape"))
1782 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_BOTTOM_LEFT, "StapleLocation", "SingleLandscape");
1783 if (ppdFindChoice(ppd_option, "DualLandscape"))
1784 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_DUAL_LEFT, "StapleLocation", "DualLandscape");
1785 }
1786
1787 if ((ppd_option = ppdFindOption(ppd, "RIPunch")) != NULL)
1788 {
1789 /*
1790 * Add (Ricoh) punch finishings...
1791 */
1792
1793 if (ppdFindChoice(ppd_option, "Left2"))
1794 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_LEFT, "RIPunch", "Left2");
1795 if (ppdFindChoice(ppd_option, "Left3"))
1796 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_LEFT, "RIPunch", "Left3");
1797 if (ppdFindChoice(ppd_option, "Left4"))
1798 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_LEFT, "RIPunch", "Left4");
1799 if (ppdFindChoice(ppd_option, "Right2"))
1800 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_RIGHT, "RIPunch", "Right2");
1801 if (ppdFindChoice(ppd_option, "Right3"))
1802 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_RIGHT, "RIPunch", "Right3");
1803 if (ppdFindChoice(ppd_option, "Right4"))
1804 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_RIGHT, "RIPunch", "Right4");
1805 if (ppdFindChoice(ppd_option, "Upper2"))
1806 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_TOP, "RIPunch", "Upper2");
1807 if (ppdFindChoice(ppd_option, "Upper3"))
1808 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_TOP, "RIPunch", "Upper3");
1809 if (ppdFindChoice(ppd_option, "Upper4"))
1810 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_TOP, "RIPunch", "Upper4");
1811 }
1812
1813 if ((ppd_option = ppdFindOption(ppd, "BindEdge")) != NULL)
1814 {
1815 /*
1816 * Add bind finishings...
1817 */
1818
1819 if (ppdFindChoice(ppd_option, "Left"))
1820 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_LEFT, "BindEdge", "Left");
1821 if (ppdFindChoice(ppd_option, "Right"))
1822 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_RIGHT, "BindEdge", "Right");
1823 if (ppdFindChoice(ppd_option, "Top"))
1824 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_TOP, "BindEdge", "Top");
1825 if (ppdFindChoice(ppd_option, "Bottom"))
1826 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_BOTTOM, "BindEdge", "Bottom");
1827 }
1828
1829 if ((ppd_option = ppdFindOption(ppd, "FoldType")) != NULL)
1830 {
1831 /*
1832 * Add (Adobe) fold finishings...
1833 */
1834
1835 if (ppdFindChoice(ppd_option, "ZFold"))
1836 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_Z, "FoldType", "ZFold");
1837 if (ppdFindChoice(ppd_option, "Saddle"))
1838 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_HALF, "FoldType", "Saddle");
1839 if (ppdFindChoice(ppd_option, "DoubleGate"))
1840 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_DOUBLE_GATE, "FoldType", "DoubleGate");
1841 if (ppdFindChoice(ppd_option, "LeftGate"))
1842 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LEFT_GATE, "FoldType", "LeftGate");
1843 if (ppdFindChoice(ppd_option, "RightGate"))
1844 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_RIGHT_GATE, "FoldType", "RightGate");
1845 if (ppdFindChoice(ppd_option, "Letter"))
1846 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, "FoldType", "Letter");
1847 if (ppdFindChoice(ppd_option, "XFold"))
1848 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_POSTER, "FoldType", "XFold");
1849 }
1850
1851 if ((ppd_option = ppdFindOption(ppd, "RIFoldType")) != NULL)
1852 {
1853 /*
1854 * Add (Ricoh) fold finishings...
1855 */
1856
1857 if (ppdFindChoice(ppd_option, "OutsideTwoFold"))
1858 pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, "RIFoldType", "OutsideTwoFold");
1859 }
1860
1861 if (cupsArrayCount(pc->finishings) == 0)
1862 {
1863 cupsArrayDelete(pc->finishings);
1864 pc->finishings = NULL;
1865 }
1866 }
1867
1868 if ((ppd_option = ppdFindOption(ppd, "cupsFinishingTemplate")) != NULL)
1869 {
1870 pc->templates = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsStrAlloc, (cups_afree_func_t)_cupsStrFree);
1871
1872 for (choice = ppd_option->choices, i = ppd_option->num_choices; i > 0; choice ++, i --)
1873 {
1874 cupsArrayAdd(pc->templates, (void *)choice->choice);
1875
1876 /*
1877 * Add localized text for PWG keyword to message catalog...
1878 */
1879
1880 snprintf(msg_id, sizeof(msg_id), "finishing-template.%s", choice->choice);
1881 pwg_add_message(pc->strings, msg_id, choice->text);
1882 }
1883 }
1884
1885 /*
1886 * Max copies...
1887 */
1888
1889 if ((ppd_attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
1890 pc->max_copies = atoi(ppd_attr->value);
1891 else if (ppd->manual_copies)
1892 pc->max_copies = 1;
1893 else
1894 pc->max_copies = 9999;
1895
1896 /*
1897 * cupsChargeInfoURI, cupsJobAccountId, cupsJobAccountingUserId,
1898 * cupsJobPassword, and cupsMandatory.
1899 */
1900
1901 if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL)
1902 pc->charge_info_uri = _cupsStrAlloc(ppd_attr->value);
1903
1904 if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountId", NULL)) != NULL)
1905 pc->account_id = !_cups_strcasecmp(ppd_attr->value, "true");
1906
1907 if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountingUserId", NULL)) != NULL)
1908 pc->accounting_user_id = !_cups_strcasecmp(ppd_attr->value, "true");
1909
1910 if ((ppd_attr = ppdFindAttr(ppd, "cupsJobPassword", NULL)) != NULL)
1911 pc->password = _cupsStrAlloc(ppd_attr->value);
1912
1913 if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
1914 pc->mandatory = _cupsArrayNewStrings(ppd_attr->value, ' ');
1915
1916 /*
1917 * Support files...
1918 */
1919
1920 pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
1921 (cups_acopy_func_t)_cupsStrAlloc,
1922 (cups_afree_func_t)_cupsStrFree);
1923
1924 for (ppd_attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
1925 ppd_attr;
1926 ppd_attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
1927 cupsArrayAdd(pc->support_files, ppd_attr->value);
1928
1929 if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
1930 cupsArrayAdd(pc->support_files, ppd_attr->value);
1931
1932 /*
1933 * Return the cache data...
1934 */
1935
1936 return (pc);
1937
1938 /*
1939 * If we get here we need to destroy the PWG mapping data and return NULL...
1940 */
1941
1942 create_error:
1943
1944 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of memory."), 1);
1945 _ppdCacheDestroy(pc);
1946
1947 return (NULL);
1948 }
1949
1950
1951 /*
1952 * '_ppdCacheDestroy()' - Free all memory used for PWG mapping data.
1953 */
1954
1955 void
1956 _ppdCacheDestroy(_ppd_cache_t *pc) /* I - PPD cache and mapping data */
1957 {
1958 int i; /* Looping var */
1959 pwg_map_t *map; /* Current map */
1960 pwg_size_t *size; /* Current size */
1961
1962
1963 /*
1964 * Range check input...
1965 */
1966
1967 if (!pc)
1968 return;
1969
1970 /*
1971 * Free memory as needed...
1972 */
1973
1974 if (pc->bins)
1975 {
1976 for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
1977 {
1978 _cupsStrFree(map->pwg);
1979 _cupsStrFree(map->ppd);
1980 }
1981
1982 free(pc->bins);
1983 }
1984
1985 if (pc->sizes)
1986 {
1987 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
1988 {
1989 _cupsStrFree(size->map.pwg);
1990 _cupsStrFree(size->map.ppd);
1991 }
1992
1993 free(pc->sizes);
1994 }
1995
1996 if (pc->source_option)
1997 _cupsStrFree(pc->source_option);
1998
1999 if (pc->sources)
2000 {
2001 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
2002 {
2003 _cupsStrFree(map->pwg);
2004 _cupsStrFree(map->ppd);
2005 }
2006
2007 free(pc->sources);
2008 }
2009
2010 if (pc->types)
2011 {
2012 for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
2013 {
2014 _cupsStrFree(map->pwg);
2015 _cupsStrFree(map->ppd);
2016 }
2017
2018 free(pc->types);
2019 }
2020
2021 if (pc->custom_max_keyword)
2022 _cupsStrFree(pc->custom_max_keyword);
2023
2024 if (pc->custom_min_keyword)
2025 _cupsStrFree(pc->custom_min_keyword);
2026
2027 _cupsStrFree(pc->product);
2028 cupsArrayDelete(pc->filters);
2029 cupsArrayDelete(pc->prefilters);
2030 cupsArrayDelete(pc->finishings);
2031
2032 _cupsStrFree(pc->charge_info_uri);
2033 _cupsStrFree(pc->password);
2034
2035 cupsArrayDelete(pc->mandatory);
2036
2037 cupsArrayDelete(pc->support_files);
2038
2039 cupsArrayDelete(pc->strings);
2040
2041 free(pc);
2042 }
2043
2044
2045 /*
2046 * '_ppdCacheGetBin()' - Get the PWG output-bin keyword associated with a PPD
2047 * OutputBin.
2048 */
2049
2050 const char * /* O - output-bin or NULL */
2051 _ppdCacheGetBin(
2052 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2053 const char *output_bin) /* I - PPD OutputBin string */
2054 {
2055 int i; /* Looping var */
2056
2057
2058 /*
2059 * Range check input...
2060 */
2061
2062 if (!pc || !output_bin)
2063 return (NULL);
2064
2065 /*
2066 * Look up the OutputBin string...
2067 */
2068
2069
2070 for (i = 0; i < pc->num_bins; i ++)
2071 if (!_cups_strcasecmp(output_bin, pc->bins[i].ppd))
2072 return (pc->bins[i].pwg);
2073
2074 return (NULL);
2075 }
2076
2077
2078 /*
2079 * '_ppdCacheGetFinishingOptions()' - Get PPD finishing options for the given
2080 * IPP finishings value(s).
2081 */
2082
2083 int /* O - New number of options */
2084 _ppdCacheGetFinishingOptions(
2085 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2086 ipp_t *job, /* I - Job attributes or NULL */
2087 ipp_finishings_t value, /* I - IPP finishings value of IPP_FINISHINGS_NONE */
2088 int num_options, /* I - Number of options */
2089 cups_option_t **options) /* IO - Options */
2090 {
2091 int i; /* Looping var */
2092 _pwg_finishings_t *f, /* PWG finishings options */
2093 key; /* Search key */
2094 ipp_attribute_t *attr; /* Finishings attribute */
2095 cups_option_t *option; /* Current finishings option */
2096
2097
2098 /*
2099 * Range check input...
2100 */
2101
2102 if (!pc || cupsArrayCount(pc->finishings) == 0 || !options ||
2103 (!job && value == IPP_FINISHINGS_NONE))
2104 return (num_options);
2105
2106 /*
2107 * Apply finishing options...
2108 */
2109
2110 if (job && (attr = ippFindAttribute(job, "finishings", IPP_TAG_ENUM)) != NULL)
2111 {
2112 int num_values = ippGetCount(attr); /* Number of values */
2113
2114 for (i = 0; i < num_values; i ++)
2115 {
2116 key.value = (ipp_finishings_t)ippGetInteger(attr, i);
2117
2118 if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
2119 {
2120 int j; /* Another looping var */
2121
2122 for (j = f->num_options, option = f->options; j > 0; j --, option ++)
2123 num_options = cupsAddOption(option->name, option->value,
2124 num_options, options);
2125 }
2126 }
2127 }
2128 else if (value != IPP_FINISHINGS_NONE)
2129 {
2130 key.value = value;
2131
2132 if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
2133 {
2134 int j; /* Another looping var */
2135
2136 for (j = f->num_options, option = f->options; j > 0; j --, option ++)
2137 num_options = cupsAddOption(option->name, option->value,
2138 num_options, options);
2139 }
2140 }
2141
2142 return (num_options);
2143 }
2144
2145
2146 /*
2147 * '_ppdCacheGetFinishingValues()' - Get IPP finishings value(s) from the given
2148 * PPD options.
2149 */
2150
2151 int /* O - Number of finishings values */
2152 _ppdCacheGetFinishingValues(
2153 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2154 int num_options, /* I - Number of options */
2155 cups_option_t *options, /* I - Options */
2156 int max_values, /* I - Maximum number of finishings values */
2157 int *values) /* O - Finishings values */
2158 {
2159 int i, /* Looping var */
2160 num_values = 0; /* Number of values */
2161 _pwg_finishings_t *f; /* Current finishings option */
2162 cups_option_t *option; /* Current option */
2163 const char *val; /* Value for option */
2164
2165
2166 /*
2167 * Range check input...
2168 */
2169
2170 DEBUG_printf(("_ppdCacheGetFinishingValues(pc=%p, num_options=%d, options=%p, max_values=%d, values=%p)", pc, num_options, options, max_values, values));
2171
2172 if (!pc || max_values < 1 || !values)
2173 {
2174 DEBUG_puts("_ppdCacheGetFinishingValues: Bad arguments, returning 0.");
2175 return (0);
2176 }
2177 else if (!pc->finishings)
2178 {
2179 DEBUG_puts("_ppdCacheGetFinishingValues: No finishings support, returning 0.");
2180 return (0);
2181 }
2182
2183 /*
2184 * Go through the finishings options and see what is set...
2185 */
2186
2187 for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
2188 f;
2189 f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
2190 {
2191 DEBUG_printf(("_ppdCacheGetFinishingValues: Checking %d (%s)", f->value, ippEnumString("finishings", f->value)));
2192
2193 for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2194 {
2195 DEBUG_printf(("_ppdCacheGetFinishingValues: %s=%s?", option->name, option->value));
2196
2197 if ((val = cupsGetOption(option->name, num_options, options)) == NULL ||
2198 _cups_strcasecmp(option->value, val))
2199 {
2200 DEBUG_puts("_ppdCacheGetFinishingValues: NO");
2201 break;
2202 }
2203 }
2204
2205 if (i == 0)
2206 {
2207 DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d (%s)", f->value, ippEnumString("finishings", f->value)));
2208
2209 values[num_values ++] = f->value;
2210
2211 if (num_values >= max_values)
2212 break;
2213 }
2214 }
2215
2216 if (num_values == 0)
2217 {
2218 /*
2219 * Always have at least "finishings" = 'none'...
2220 */
2221
2222 DEBUG_puts("_ppdCacheGetFinishingValues: Adding 3 (none).");
2223 values[0] = IPP_FINISHINGS_NONE;
2224 num_values ++;
2225 }
2226
2227 DEBUG_printf(("_ppdCacheGetFinishingValues: Returning %d.", num_values));
2228
2229 return (num_values);
2230 }
2231
2232
2233 /*
2234 * '_ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job
2235 * attributes or a keyword string.
2236 */
2237
2238 const char * /* O - PPD InputSlot or NULL */
2239 _ppdCacheGetInputSlot(
2240 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2241 ipp_t *job, /* I - Job attributes or NULL */
2242 const char *keyword) /* I - Keyword string or NULL */
2243 {
2244 /*
2245 * Range check input...
2246 */
2247
2248 if (!pc || pc->num_sources == 0 || (!job && !keyword))
2249 return (NULL);
2250
2251 if (job && !keyword)
2252 {
2253 /*
2254 * Lookup the media-col attribute and any media-source found there...
2255 */
2256
2257 ipp_attribute_t *media_col, /* media-col attribute */
2258 *media_source; /* media-source attribute */
2259 pwg_size_t size; /* Dimensional size */
2260 int margins_set; /* Were the margins set? */
2261
2262 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2263 if (media_col &&
2264 (media_source = ippFindAttribute(ippGetCollection(media_col, 0),
2265 "media-source",
2266 IPP_TAG_KEYWORD)) != NULL)
2267 {
2268 /*
2269 * Use the media-source value from media-col...
2270 */
2271
2272 keyword = ippGetString(media_source, 0, NULL);
2273 }
2274 else if (pwgInitSize(&size, job, &margins_set))
2275 {
2276 /*
2277 * For media <= 5x7, look for a photo tray...
2278 */
2279
2280 if (size.width <= (5 * 2540) && size.length <= (7 * 2540))
2281 keyword = "photo";
2282 }
2283 }
2284
2285 if (keyword)
2286 {
2287 int i; /* Looping var */
2288
2289 for (i = 0; i < pc->num_sources; i ++)
2290 if (!_cups_strcasecmp(keyword, pc->sources[i].pwg))
2291 return (pc->sources[i].ppd);
2292 }
2293
2294 return (NULL);
2295 }
2296
2297
2298 /*
2299 * '_ppdCacheGetMediaType()' - Get the PPD MediaType associated with the job
2300 * attributes or a keyword string.
2301 */
2302
2303 const char * /* O - PPD MediaType or NULL */
2304 _ppdCacheGetMediaType(
2305 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2306 ipp_t *job, /* I - Job attributes or NULL */
2307 const char *keyword) /* I - Keyword string or NULL */
2308 {
2309 /*
2310 * Range check input...
2311 */
2312
2313 if (!pc || pc->num_types == 0 || (!job && !keyword))
2314 return (NULL);
2315
2316 if (job && !keyword)
2317 {
2318 /*
2319 * Lookup the media-col attribute and any media-source found there...
2320 */
2321
2322 ipp_attribute_t *media_col, /* media-col attribute */
2323 *media_type; /* media-type attribute */
2324
2325 media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2326 if (media_col)
2327 {
2328 if ((media_type = ippFindAttribute(media_col->values[0].collection,
2329 "media-type",
2330 IPP_TAG_KEYWORD)) == NULL)
2331 media_type = ippFindAttribute(media_col->values[0].collection,
2332 "media-type", IPP_TAG_NAME);
2333
2334 if (media_type)
2335 keyword = media_type->values[0].string.text;
2336 }
2337 }
2338
2339 if (keyword)
2340 {
2341 int i; /* Looping var */
2342
2343 for (i = 0; i < pc->num_types; i ++)
2344 if (!_cups_strcasecmp(keyword, pc->types[i].pwg))
2345 return (pc->types[i].ppd);
2346 }
2347
2348 return (NULL);
2349 }
2350
2351
2352 /*
2353 * '_ppdCacheGetOutputBin()' - Get the PPD OutputBin associated with the keyword
2354 * string.
2355 */
2356
2357 const char * /* O - PPD OutputBin or NULL */
2358 _ppdCacheGetOutputBin(
2359 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2360 const char *output_bin) /* I - Keyword string */
2361 {
2362 int i; /* Looping var */
2363
2364
2365 /*
2366 * Range check input...
2367 */
2368
2369 if (!pc || !output_bin)
2370 return (NULL);
2371
2372 /*
2373 * Look up the OutputBin string...
2374 */
2375
2376
2377 for (i = 0; i < pc->num_bins; i ++)
2378 if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg))
2379 return (pc->bins[i].ppd);
2380
2381 return (NULL);
2382 }
2383
2384
2385 /*
2386 * '_ppdCacheGetPageSize()' - Get the PPD PageSize associated with the job
2387 * attributes or a keyword string.
2388 */
2389
2390 const char * /* O - PPD PageSize or NULL */
2391 _ppdCacheGetPageSize(
2392 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2393 ipp_t *job, /* I - Job attributes or NULL */
2394 const char *keyword, /* I - Keyword string or NULL */
2395 int *exact) /* O - 1 if exact match, 0 otherwise */
2396 {
2397 int i; /* Looping var */
2398 pwg_size_t *size, /* Current size */
2399 *closest, /* Closest size */
2400 jobsize; /* Size data from job */
2401 int margins_set, /* Were the margins set? */
2402 dwidth, /* Difference in width */
2403 dlength, /* Difference in length */
2404 dleft, /* Difference in left margins */
2405 dright, /* Difference in right margins */
2406 dbottom, /* Difference in bottom margins */
2407 dtop, /* Difference in top margins */
2408 dmin, /* Minimum difference */
2409 dclosest; /* Closest difference */
2410 const char *ppd_name; /* PPD media name */
2411
2412
2413 DEBUG_printf(("_ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)",
2414 pc, job, keyword, exact));
2415
2416 /*
2417 * Range check input...
2418 */
2419
2420 if (!pc || (!job && !keyword))
2421 return (NULL);
2422
2423 if (exact)
2424 *exact = 0;
2425
2426 ppd_name = keyword;
2427
2428 if (job)
2429 {
2430 /*
2431 * Try getting the PPD media name from the job attributes...
2432 */
2433
2434 ipp_attribute_t *attr; /* Job attribute */
2435
2436 if ((attr = ippFindAttribute(job, "PageSize", IPP_TAG_ZERO)) == NULL)
2437 if ((attr = ippFindAttribute(job, "PageRegion", IPP_TAG_ZERO)) == NULL)
2438 attr = ippFindAttribute(job, "media", IPP_TAG_ZERO);
2439
2440 #ifdef DEBUG
2441 if (attr)
2442 DEBUG_printf(("1_ppdCacheGetPageSize: Found attribute %s (%s)",
2443 attr->name, ippTagString(attr->value_tag)));
2444 else
2445 DEBUG_puts("1_ppdCacheGetPageSize: Did not find media attribute.");
2446 #endif /* DEBUG */
2447
2448 if (attr && (attr->value_tag == IPP_TAG_NAME ||
2449 attr->value_tag == IPP_TAG_KEYWORD))
2450 ppd_name = attr->values[0].string.text;
2451 }
2452
2453 DEBUG_printf(("1_ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name));
2454
2455 if (ppd_name)
2456 {
2457 /*
2458 * Try looking up the named PPD size first...
2459 */
2460
2461 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2462 {
2463 DEBUG_printf(("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]",
2464 (int)(size - pc->sizes), size->map.pwg, size->map.ppd));
2465
2466 if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
2467 !_cups_strcasecmp(ppd_name, size->map.pwg))
2468 {
2469 if (exact)
2470 *exact = 1;
2471
2472 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", ppd_name));
2473
2474 return (size->map.ppd);
2475 }
2476 }
2477 }
2478
2479 if (job && !keyword)
2480 {
2481 /*
2482 * Get the size using media-col or media, with the preference being
2483 * media-col.
2484 */
2485
2486 if (!pwgInitSize(&jobsize, job, &margins_set))
2487 return (NULL);
2488 }
2489 else
2490 {
2491 /*
2492 * Get the size using a media keyword...
2493 */
2494
2495 pwg_media_t *media; /* Media definition */
2496
2497
2498 if ((media = pwgMediaForPWG(keyword)) == NULL)
2499 if ((media = pwgMediaForLegacy(keyword)) == NULL)
2500 if ((media = pwgMediaForPPD(keyword)) == NULL)
2501 return (NULL);
2502
2503 jobsize.width = media->width;
2504 jobsize.length = media->length;
2505 margins_set = 0;
2506 }
2507
2508 /*
2509 * Now that we have the dimensions and possibly the margins, look at the
2510 * available sizes and find the match...
2511 */
2512
2513 closest = NULL;
2514 dclosest = 999999999;
2515
2516 if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
2517 _cups_strncasecmp(ppd_name, "custom_", 7))
2518 {
2519 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2520 {
2521 /*
2522 * Adobe uses a size matching algorithm with an epsilon of 5 points, which
2523 * is just about 176/2540ths...
2524 */
2525
2526 dwidth = size->width - jobsize.width;
2527 dlength = size->length - jobsize.length;
2528
2529 if (dwidth <= -176 || dwidth >= 176 || dlength <= -176 || dlength >= 176)
2530 continue;
2531
2532 if (margins_set)
2533 {
2534 /*
2535 * Use a tighter epsilon of 1 point (35/2540ths) for margins...
2536 */
2537
2538 dleft = size->left - jobsize.left;
2539 dright = size->right - jobsize.right;
2540 dtop = size->top - jobsize.top;
2541 dbottom = size->bottom - jobsize.bottom;
2542
2543 if (dleft <= -35 || dleft >= 35 || dright <= -35 || dright >= 35 ||
2544 dtop <= -35 || dtop >= 35 || dbottom <= -35 || dbottom >= 35)
2545 {
2546 dleft = dleft < 0 ? -dleft : dleft;
2547 dright = dright < 0 ? -dright : dright;
2548 dbottom = dbottom < 0 ? -dbottom : dbottom;
2549 dtop = dtop < 0 ? -dtop : dtop;
2550 dmin = dleft + dright + dbottom + dtop;
2551
2552 if (dmin < dclosest)
2553 {
2554 dclosest = dmin;
2555 closest = size;
2556 }
2557
2558 continue;
2559 }
2560 }
2561
2562 if (exact)
2563 *exact = 1;
2564
2565 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd));
2566
2567 return (size->map.ppd);
2568 }
2569 }
2570
2571 if (closest)
2572 {
2573 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (closest)",
2574 closest->map.ppd));
2575
2576 return (closest->map.ppd);
2577 }
2578
2579 /*
2580 * If we get here we need to check for custom page size support...
2581 */
2582
2583 if (jobsize.width >= pc->custom_min_width &&
2584 jobsize.width <= pc->custom_max_width &&
2585 jobsize.length >= pc->custom_min_length &&
2586 jobsize.length <= pc->custom_max_length)
2587 {
2588 /*
2589 * In range, format as Custom.WWWWxLLLL (points).
2590 */
2591
2592 snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d",
2593 (int)PWG_TO_POINTS(jobsize.width), (int)PWG_TO_POINTS(jobsize.length));
2594
2595 if (margins_set && exact)
2596 {
2597 dleft = pc->custom_size.left - jobsize.left;
2598 dright = pc->custom_size.right - jobsize.right;
2599 dtop = pc->custom_size.top - jobsize.top;
2600 dbottom = pc->custom_size.bottom - jobsize.bottom;
2601
2602 if (dleft > -35 && dleft < 35 && dright > -35 && dright < 35 &&
2603 dtop > -35 && dtop < 35 && dbottom > -35 && dbottom < 35)
2604 *exact = 1;
2605 }
2606 else if (exact)
2607 *exact = 1;
2608
2609 DEBUG_printf(("1_ppdCacheGetPageSize: Returning \"%s\" (custom)",
2610 pc->custom_ppd_size));
2611
2612 return (pc->custom_ppd_size);
2613 }
2614
2615 /*
2616 * No custom page size support or the size is out of range - return NULL.
2617 */
2618
2619 DEBUG_puts("1_ppdCacheGetPageSize: Returning NULL");
2620
2621 return (NULL);
2622 }
2623
2624
2625 /*
2626 * '_ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize.
2627 */
2628
2629 pwg_size_t * /* O - PWG size or NULL */
2630 _ppdCacheGetSize(
2631 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2632 const char *page_size) /* I - PPD PageSize */
2633 {
2634 int i; /* Looping var */
2635 pwg_media_t *media; /* Media */
2636 pwg_size_t *size; /* Current size */
2637
2638
2639 /*
2640 * Range check input...
2641 */
2642
2643 if (!pc || !page_size)
2644 return (NULL);
2645
2646 if (!_cups_strncasecmp(page_size, "Custom.", 7))
2647 {
2648 /*
2649 * Custom size; size name can be one of the following:
2650 *
2651 * Custom.WIDTHxLENGTHin - Size in inches
2652 * Custom.WIDTHxLENGTHft - Size in feet
2653 * Custom.WIDTHxLENGTHcm - Size in centimeters
2654 * Custom.WIDTHxLENGTHmm - Size in millimeters
2655 * Custom.WIDTHxLENGTHm - Size in meters
2656 * Custom.WIDTHxLENGTH[pt] - Size in points
2657 */
2658
2659 double w, l; /* Width and length of page */
2660 char *ptr; /* Pointer into PageSize */
2661 struct lconv *loc; /* Locale data */
2662
2663 loc = localeconv();
2664 w = (float)_cupsStrScand(page_size + 7, &ptr, loc);
2665 if (!ptr || *ptr != 'x')
2666 return (NULL);
2667
2668 l = (float)_cupsStrScand(ptr + 1, &ptr, loc);
2669 if (!ptr)
2670 return (NULL);
2671
2672 if (!_cups_strcasecmp(ptr, "in"))
2673 {
2674 w *= 2540.0;
2675 l *= 2540.0;
2676 }
2677 else if (!_cups_strcasecmp(ptr, "ft"))
2678 {
2679 w *= 12.0 * 2540.0;
2680 l *= 12.0 * 2540.0;
2681 }
2682 else if (!_cups_strcasecmp(ptr, "mm"))
2683 {
2684 w *= 100.0;
2685 l *= 100.0;
2686 }
2687 else if (!_cups_strcasecmp(ptr, "cm"))
2688 {
2689 w *= 1000.0;
2690 l *= 1000.0;
2691 }
2692 else if (!_cups_strcasecmp(ptr, "m"))
2693 {
2694 w *= 100000.0;
2695 l *= 100000.0;
2696 }
2697 else
2698 {
2699 w *= 2540.0 / 72.0;
2700 l *= 2540.0 / 72.0;
2701 }
2702
2703 pc->custom_size.width = (int)w;
2704 pc->custom_size.length = (int)l;
2705
2706 return (&(pc->custom_size));
2707 }
2708
2709 /*
2710 * Not a custom size - look it up...
2711 */
2712
2713 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2714 if (!_cups_strcasecmp(page_size, size->map.ppd) ||
2715 !_cups_strcasecmp(page_size, size->map.pwg))
2716 return (size);
2717
2718 /*
2719 * Look up standard sizes...
2720 */
2721
2722 if ((media = pwgMediaForPPD(page_size)) == NULL)
2723 if ((media = pwgMediaForLegacy(page_size)) == NULL)
2724 media = pwgMediaForPWG(page_size);
2725
2726 if (media)
2727 {
2728 pc->custom_size.width = media->width;
2729 pc->custom_size.length = media->length;
2730
2731 return (&(pc->custom_size));
2732 }
2733
2734 return (NULL);
2735 }
2736
2737
2738 /*
2739 * '_ppdCacheGetSource()' - Get the PWG media-source associated with a PPD
2740 * InputSlot.
2741 */
2742
2743 const char * /* O - PWG media-source keyword */
2744 _ppdCacheGetSource(
2745 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2746 const char *input_slot) /* I - PPD InputSlot */
2747 {
2748 int i; /* Looping var */
2749 pwg_map_t *source; /* Current source */
2750
2751
2752 /*
2753 * Range check input...
2754 */
2755
2756 if (!pc || !input_slot)
2757 return (NULL);
2758
2759 for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++)
2760 if (!_cups_strcasecmp(input_slot, source->ppd))
2761 return (source->pwg);
2762
2763 return (NULL);
2764 }
2765
2766
2767 /*
2768 * '_ppdCacheGetType()' - Get the PWG media-type associated with a PPD
2769 * MediaType.
2770 */
2771
2772 const char * /* O - PWG media-type keyword */
2773 _ppdCacheGetType(
2774 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2775 const char *media_type) /* I - PPD MediaType */
2776 {
2777 int i; /* Looping var */
2778 pwg_map_t *type; /* Current type */
2779
2780
2781 /*
2782 * Range check input...
2783 */
2784
2785 if (!pc || !media_type)
2786 return (NULL);
2787
2788 for (i = pc->num_types, type = pc->types; i > 0; i --, type ++)
2789 if (!_cups_strcasecmp(media_type, type->ppd))
2790 return (type->pwg);
2791
2792 return (NULL);
2793 }
2794
2795
2796 /*
2797 * '_ppdCacheWriteFile()' - Write PWG mapping data to a file.
2798 */
2799
2800 int /* O - 1 on success, 0 on failure */
2801 _ppdCacheWriteFile(
2802 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2803 const char *filename, /* I - File to write */
2804 ipp_t *attrs) /* I - Attributes to write, if any */
2805 {
2806 int i, j, k; /* Looping vars */
2807 cups_file_t *fp; /* Output file */
2808 pwg_size_t *size; /* Current size */
2809 pwg_map_t *map; /* Current map */
2810 _pwg_finishings_t *f; /* Current finishing option */
2811 cups_option_t *option; /* Current option */
2812 const char *value; /* String value */
2813 char newfile[1024]; /* New filename */
2814
2815
2816 /*
2817 * Range check input...
2818 */
2819
2820 if (!pc || !filename)
2821 {
2822 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
2823 return (0);
2824 }
2825
2826 /*
2827 * Open the file and write with compression...
2828 */
2829
2830 snprintf(newfile, sizeof(newfile), "%s.N", filename);
2831 if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
2832 {
2833 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
2834 return (0);
2835 }
2836
2837 /*
2838 * Standard header...
2839 */
2840
2841 cupsFilePrintf(fp, "#CUPS-PPD-CACHE-%d\n", _PPD_CACHE_VERSION);
2842
2843 /*
2844 * Output bins...
2845 */
2846
2847 if (pc->num_bins > 0)
2848 {
2849 cupsFilePrintf(fp, "NumBins %d\n", pc->num_bins);
2850 for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
2851 cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd);
2852 }
2853
2854 /*
2855 * Media sizes...
2856 */
2857
2858 cupsFilePrintf(fp, "NumSizes %d\n", pc->num_sizes);
2859 for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2860 cupsFilePrintf(fp, "Size %s %s %d %d %d %d %d %d\n", size->map.pwg,
2861 size->map.ppd, size->width, size->length, size->left,
2862 size->bottom, size->right, size->top);
2863 if (pc->custom_max_width > 0)
2864 cupsFilePrintf(fp, "CustomSize %d %d %d %d %d %d %d %d\n",
2865 pc->custom_max_width, pc->custom_max_length,
2866 pc->custom_min_width, pc->custom_min_length,
2867 pc->custom_size.left, pc->custom_size.bottom,
2868 pc->custom_size.right, pc->custom_size.top);
2869
2870 /*
2871 * Media sources...
2872 */
2873
2874 if (pc->source_option)
2875 cupsFilePrintf(fp, "SourceOption %s\n", pc->source_option);
2876
2877 if (pc->num_sources > 0)
2878 {
2879 cupsFilePrintf(fp, "NumSources %d\n", pc->num_sources);
2880 for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
2881 cupsFilePrintf(fp, "Source %s %s\n", map->pwg, map->ppd);
2882 }
2883
2884 /*
2885 * Media types...
2886 */
2887
2888 if (pc->num_types > 0)
2889 {
2890 cupsFilePrintf(fp, "NumTypes %d\n", pc->num_types);
2891 for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
2892 cupsFilePrintf(fp, "Type %s %s\n", map->pwg, map->ppd);
2893 }
2894
2895 /*
2896 * Presets...
2897 */
2898
2899 for (i = _PWG_PRINT_COLOR_MODE_MONOCHROME; i < _PWG_PRINT_COLOR_MODE_MAX; i ++)
2900 for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
2901 if (pc->num_presets[i][j])
2902 {
2903 cupsFilePrintf(fp, "Preset %d %d", i, j);
2904 for (k = pc->num_presets[i][j], option = pc->presets[i][j];
2905 k > 0;
2906 k --, option ++)
2907 cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2908 cupsFilePutChar(fp, '\n');
2909 }
2910
2911 /*
2912 * Duplex/sides...
2913 */
2914
2915 if (pc->sides_option)
2916 cupsFilePrintf(fp, "SidesOption %s\n", pc->sides_option);
2917
2918 if (pc->sides_1sided)
2919 cupsFilePrintf(fp, "Sides1Sided %s\n", pc->sides_1sided);
2920
2921 if (pc->sides_2sided_long)
2922 cupsFilePrintf(fp, "Sides2SidedLong %s\n", pc->sides_2sided_long);
2923
2924 if (pc->sides_2sided_short)
2925 cupsFilePrintf(fp, "Sides2SidedShort %s\n", pc->sides_2sided_short);
2926
2927 /*
2928 * Product, cupsFilter, cupsFilter2, and cupsPreFilter...
2929 */
2930
2931 if (pc->product)
2932 cupsFilePutConf(fp, "Product", pc->product);
2933
2934 for (value = (const char *)cupsArrayFirst(pc->filters);
2935 value;
2936 value = (const char *)cupsArrayNext(pc->filters))
2937 cupsFilePutConf(fp, "Filter", value);
2938
2939 for (value = (const char *)cupsArrayFirst(pc->prefilters);
2940 value;
2941 value = (const char *)cupsArrayNext(pc->prefilters))
2942 cupsFilePutConf(fp, "PreFilter", value);
2943
2944 cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
2945
2946 /*
2947 * Finishing options...
2948 */
2949
2950 for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
2951 f;
2952 f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
2953 {
2954 cupsFilePrintf(fp, "Finishings %d", f->value);
2955 for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2956 cupsFilePrintf(fp, " %s=%s", option->name, option->value);
2957 cupsFilePutChar(fp, '\n');
2958 }
2959
2960 for (value = (const char *)cupsArrayFirst(pc->templates); value; value = (const char *)cupsArrayNext(pc->templates))
2961 cupsFilePutConf(fp, "FinishingTemplate", value);
2962
2963 /*
2964 * Max copies...
2965 */
2966
2967 cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies);
2968
2969 /*
2970 * Accounting/quota/PIN/managed printing values...
2971 */
2972
2973 if (pc->charge_info_uri)
2974 cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri);
2975
2976 cupsFilePrintf(fp, "AccountId %s\n", pc->account_id ? "true" : "false");
2977 cupsFilePrintf(fp, "AccountingUserId %s\n",
2978 pc->accounting_user_id ? "true" : "false");
2979
2980 if (pc->password)
2981 cupsFilePutConf(fp, "Password", pc->password);
2982
2983 for (value = (char *)cupsArrayFirst(pc->mandatory);
2984 value;
2985 value = (char *)cupsArrayNext(pc->mandatory))
2986 cupsFilePutConf(fp, "Mandatory", value);
2987
2988 /*
2989 * Support files...
2990 */
2991
2992 for (value = (char *)cupsArrayFirst(pc->support_files);
2993 value;
2994 value = (char *)cupsArrayNext(pc->support_files))
2995 cupsFilePutConf(fp, "SupportFile", value);
2996
2997 /*
2998 * IPP attributes, if any...
2999 */
3000
3001 if (attrs)
3002 {
3003 cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
3004
3005 attrs->state = IPP_STATE_IDLE;
3006 ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
3007 }
3008
3009 /*
3010 * Close and return...
3011 */
3012
3013 if (cupsFileClose(fp))
3014 {
3015 unlink(newfile);
3016 return (0);
3017 }
3018
3019 unlink(filename);
3020 return (!rename(newfile, filename));
3021 }
3022
3023
3024 /*
3025 * '_ppdCreateFromIPP()' - Create a PPD file describing the capabilities
3026 * of an IPP printer.
3027 */
3028
3029 char * /* O - PPD filename or @code NULL@ on error */
3030 _ppdCreateFromIPP(char *buffer, /* I - Filename buffer */
3031 size_t bufsize, /* I - Size of filename buffer */
3032 ipp_t *response) /* I - Get-Printer-Attributes response */
3033 {
3034 cups_file_t *fp; /* PPD file */
3035 cups_array_t *sizes; /* Media sizes supported by printer */
3036 cups_size_t *size; /* Current media size */
3037 ipp_attribute_t *attr, /* xxx-supported */
3038 *defattr, /* xxx-default */
3039 *quality, /* print-quality-supported */
3040 *x_dim, *y_dim; /* Media dimensions */
3041 ipp_t *media_col, /* Media collection */
3042 *media_size; /* Media size collection */
3043 char make[256], /* Make and model */
3044 *model, /* Model name */
3045 ppdname[PPD_MAX_NAME];
3046 /* PPD keyword */
3047 int i, j, /* Looping vars */
3048 count, /* Number of values */
3049 bottom, /* Largest bottom margin */
3050 left, /* Largest left margin */
3051 right, /* Largest right margin */
3052 top, /* Largest top margin */
3053 max_length = 0, /* Maximum custom size */
3054 max_width = 0,
3055 min_length = INT_MAX,
3056 /* Minimum custom size */
3057 min_width = INT_MAX,
3058 is_apple = 0, /* Does the printer support Apple raster? */
3059 is_pdf = 0, /* Does the printer support PDF? */
3060 is_pwg = 0; /* Does the printer support PWG Raster? */
3061 pwg_media_t *pwg; /* PWG media size */
3062 int xres, yres; /* Resolution values */
3063 int resolutions[1000];
3064 /* Array of resolution indices */
3065 char msgid[256]; /* Message identifier (attr.value) */
3066 const char *keyword, /* Keyword value */
3067 *msgstr; /* Localized string */
3068 cups_lang_t *lang = cupsLangDefault();
3069 /* Localization info */
3070 cups_array_t *strings = NULL;/* Printer strings file */
3071 struct lconv *loc = localeconv();
3072 /* Locale data */
3073 cups_array_t *fin_options = NULL;
3074 /* Finishing options */
3075
3076
3077 /*
3078 * Range check input...
3079 */
3080
3081 if (buffer)
3082 *buffer = '\0';
3083
3084 if (!buffer || bufsize < 1)
3085 {
3086 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
3087 return (NULL);
3088 }
3089
3090 if (!response)
3091 {
3092 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No IPP attributes."), 1);
3093 return (NULL);
3094 }
3095
3096 /*
3097 * Open a temporary file for the PPD...
3098 */
3099
3100 if ((fp = cupsTempFile2(buffer, (int)bufsize)) == NULL)
3101 {
3102 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
3103 return (NULL);
3104 }
3105
3106 /*
3107 * Standard stuff for PPD file...
3108 */
3109
3110 cupsFilePuts(fp, "*PPD-Adobe: \"4.3\"\n");
3111 cupsFilePuts(fp, "*FormatVersion: \"4.3\"\n");
3112 cupsFilePrintf(fp, "*FileVersion: \"%d.%d\"\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
3113 cupsFilePuts(fp, "*LanguageVersion: English\n");
3114 cupsFilePuts(fp, "*LanguageEncoding: ISOLatin1\n");
3115 cupsFilePuts(fp, "*PSVersion: \"(3010.000) 0\"\n");
3116 cupsFilePuts(fp, "*LanguageLevel: \"3\"\n");
3117 cupsFilePuts(fp, "*FileSystem: False\n");
3118 cupsFilePuts(fp, "*PCFileName: \"ippeve.ppd\"\n");
3119
3120 if ((attr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT)) != NULL)
3121 strlcpy(make, ippGetString(attr, 0, NULL), sizeof(make));
3122 else
3123 strlcpy(make, "Unknown Printer", sizeof(make));
3124
3125 if (!_cups_strncasecmp(make, "Hewlett Packard ", 16) ||
3126 !_cups_strncasecmp(make, "Hewlett-Packard ", 16))
3127 {
3128 model = make + 16;
3129 strlcpy(make, "HP", sizeof(make));
3130 }
3131 else if ((model = strchr(make, ' ')) != NULL)
3132 *model++ = '\0';
3133 else
3134 model = make;
3135
3136 cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make);
3137 cupsFilePrintf(fp, "*ModelName: \"%s\"\n", model);
3138 cupsFilePrintf(fp, "*Product: \"(%s)\"\n", model);
3139 cupsFilePrintf(fp, "*NickName: \"%s\"\n", model);
3140 cupsFilePrintf(fp, "*ShortNickName: \"%s\"\n", model);
3141
3142 if ((attr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN)) != NULL && ippGetBoolean(attr, 0))
3143 cupsFilePuts(fp, "*ColorDevice: True\n");
3144 else
3145 cupsFilePuts(fp, "*ColorDevice: False\n");
3146
3147 cupsFilePrintf(fp, "*cupsVersion: %d.%d\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
3148 cupsFilePuts(fp, "*cupsSNMPSupplies: False\n");
3149 cupsFilePrintf(fp, "*cupsLanguages: \"%s\"\n", lang->language);
3150
3151 if ((attr = ippFindAttribute(response, "printer-more-info", IPP_TAG_URI)) != NULL)
3152 cupsFilePrintf(fp, "*APSupplies: \"%s\"\n", ippGetString(attr, 0, NULL));
3153
3154 if ((attr = ippFindAttribute(response, "printer-charge-info-uri", IPP_TAG_URI)) != NULL)
3155 cupsFilePrintf(fp, "*cupsChargeInfoURI: \"%s\"\n", ippGetString(attr, 0, NULL));
3156
3157 if ((attr = ippFindAttribute(response, "printer-strings-uri", IPP_TAG_URI)) != NULL)
3158 {
3159 http_t *http = NULL; /* Connection to printer */
3160 char stringsfile[1024]; /* Temporary strings file */
3161
3162 if (cups_get_url(&http, ippGetString(attr, 0, NULL), stringsfile, sizeof(stringsfile)))
3163 {
3164 cupsFilePrintf(fp, "*cupsStringsURI: \"%s\"\n", ippGetString(attr, 0, NULL));
3165
3166 strings = _cupsMessageLoad(stringsfile, _CUPS_MESSAGE_STRINGS | _CUPS_MESSAGE_UNQUOTE);
3167
3168 unlink(stringsfile);
3169 }
3170
3171 if (http)
3172 httpClose(http);
3173 }
3174
3175 /*
3176 * Filters...
3177 */
3178
3179 if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL)
3180 {
3181 is_apple = ippContainsString(attr, "image/urf");
3182 is_pdf = ippContainsString(attr, "application/pdf");
3183 is_pwg = ippContainsString(attr, "image/pwg-raster");
3184
3185 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3186 {
3187 const char *format = ippGetString(attr, i, NULL);
3188 /* PDL */
3189
3190 /*
3191 * Write cupsFilter2 lines for supported formats...
3192 */
3193
3194 if (!_cups_strcasecmp(format, "application/pdf"))
3195 cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 10 -\"\n");
3196 else if (!_cups_strcasecmp(format, "image/jpeg") || !_cups_strcasecmp(format, "image/png"))
3197 cupsFilePrintf(fp, "*cupsFilter2: \"%s %s 0 -\"\n", format, format);
3198 else if (!_cups_strcasecmp(format, "image/pwg-raster") || !_cups_strcasecmp(format, "image/urf"))
3199 cupsFilePrintf(fp, "*cupsFilter2: \"%s %s 100 -\"\n", format, format);
3200 }
3201 }
3202
3203 if (!is_apple && !is_pdf && !is_pwg)
3204 goto bad_ppd;
3205
3206 /*
3207 * PageSize/PageRegion/ImageableArea/PaperDimension
3208 */
3209
3210 if ((attr = ippFindAttribute(response, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != NULL)
3211 {
3212 for (i = 1, bottom = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3213 if (ippGetInteger(attr, i) > bottom)
3214 bottom = ippGetInteger(attr, i);
3215 }
3216 else
3217 bottom = 1270;
3218
3219 if ((attr = ippFindAttribute(response, "media-left-margin-supported", IPP_TAG_INTEGER)) != NULL)
3220 {
3221 for (i = 1, left = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3222 if (ippGetInteger(attr, i) > left)
3223 left = ippGetInteger(attr, i);
3224 }
3225 else
3226 left = 635;
3227
3228 if ((attr = ippFindAttribute(response, "media-right-margin-supported", IPP_TAG_INTEGER)) != NULL)
3229 {
3230 for (i = 1, right = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3231 if (ippGetInteger(attr, i) > right)
3232 right = ippGetInteger(attr, i);
3233 }
3234 else
3235 right = 635;
3236
3237 if ((attr = ippFindAttribute(response, "media-top-margin-supported", IPP_TAG_INTEGER)) != NULL)
3238 {
3239 for (i = 1, top = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3240 if (ippGetInteger(attr, i) > top)
3241 top = ippGetInteger(attr, i);
3242 }
3243 else
3244 top = 1270;
3245
3246 if ((defattr = ippFindAttribute(response, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3247 {
3248 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3249 {
3250 media_size = ippGetCollection(attr, 0);
3251 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3252 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3253
3254 if (x_dim && y_dim && (pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL)
3255 strlcpy(ppdname, pwg->ppd, sizeof(ppdname));
3256 else
3257 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3258 }
3259 else
3260 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3261 }
3262 else if ((pwg = pwgMediaForPWG(ippGetString(ippFindAttribute(response, "media-default", IPP_TAG_ZERO), 0, NULL))) != NULL)
3263 strlcpy(ppdname, pwg->ppd, sizeof(ppdname));
3264 else
3265 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3266
3267 sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes, NULL, NULL, 0, (cups_acopy_func_t)pwg_copy_size, (cups_afree_func_t)free);
3268
3269 if ((attr = ippFindAttribute(response, "media-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3270 {
3271 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3272 {
3273 cups_size_t temp; /* Current size */
3274 ipp_attribute_t *margin; /* media-xxx-margin attribute */
3275
3276 media_col = ippGetCollection(attr, i);
3277 media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0);
3278 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3279 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3280 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3281
3282 if (pwg)
3283 {
3284 temp.width = pwg->width;
3285 temp.length = pwg->length;
3286
3287 if ((margin = ippFindAttribute(media_col, "media-bottom-margin", IPP_TAG_INTEGER)) != NULL)
3288 temp.bottom = ippGetInteger(margin, 0);
3289 else
3290 temp.bottom = bottom;
3291
3292 if ((margin = ippFindAttribute(media_col, "media-left-margin", IPP_TAG_INTEGER)) != NULL)
3293 temp.left = ippGetInteger(margin, 0);
3294 else
3295 temp.left = left;
3296
3297 if ((margin = ippFindAttribute(media_col, "media-right-margin", IPP_TAG_INTEGER)) != NULL)
3298 temp.right = ippGetInteger(margin, 0);
3299 else
3300 temp.right = right;
3301
3302 if ((margin = ippFindAttribute(media_col, "media-top-margin", IPP_TAG_INTEGER)) != NULL)
3303 temp.top = ippGetInteger(margin, 0);
3304 else
3305 temp.top = top;
3306
3307 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3308 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3309 else
3310 strlcpy(temp.media, pwg->ppd, sizeof(temp.media));
3311
3312 if (!cupsArrayFind(sizes, &temp))
3313 cupsArrayAdd(sizes, &temp);
3314 }
3315 else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3316 {
3317 /*
3318 * Custom size - record the min/max values...
3319 */
3320
3321 int lower, upper; /* Range values */
3322
3323 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3324 lower = ippGetRange(x_dim, 0, &upper);
3325 else
3326 lower = upper = ippGetInteger(x_dim, 0);
3327
3328 if (lower < min_width)
3329 min_width = lower;
3330 if (upper > max_width)
3331 max_width = upper;
3332
3333 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3334 lower = ippGetRange(y_dim, 0, &upper);
3335 else
3336 lower = upper = ippGetInteger(y_dim, 0);
3337
3338 if (lower < min_length)
3339 min_length = lower;
3340 if (upper > max_length)
3341 max_length = upper;
3342 }
3343 }
3344
3345 if ((max_width == 0 || max_length == 0) && (attr = ippFindAttribute(response, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3346 {
3347 /*
3348 * Some printers don't list custom size support in media-col-database...
3349 */
3350
3351 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3352 {
3353 media_size = ippGetCollection(attr, i);
3354 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3355 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3356
3357 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3358 {
3359 /*
3360 * Custom size - record the min/max values...
3361 */
3362
3363 int lower, upper; /* Range values */
3364
3365 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3366 lower = ippGetRange(x_dim, 0, &upper);
3367 else
3368 lower = upper = ippGetInteger(x_dim, 0);
3369
3370 if (lower < min_width)
3371 min_width = lower;
3372 if (upper > max_width)
3373 max_width = upper;
3374
3375 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3376 lower = ippGetRange(y_dim, 0, &upper);
3377 else
3378 lower = upper = ippGetInteger(y_dim, 0);
3379
3380 if (lower < min_length)
3381 min_length = lower;
3382 if (upper > max_length)
3383 max_length = upper;
3384 }
3385 }
3386 }
3387 }
3388 else if ((attr = ippFindAttribute(response, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3389 {
3390 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3391 {
3392 cups_size_t temp; /* Current size */
3393
3394 media_size = ippGetCollection(attr, i);
3395 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3396 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3397 pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3398
3399 if (pwg)
3400 {
3401 temp.width = pwg->width;
3402 temp.length = pwg->length;
3403 temp.bottom = bottom;
3404 temp.left = left;
3405 temp.right = right;
3406 temp.top = top;
3407
3408 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3409 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3410 else
3411 strlcpy(temp.media, pwg->ppd, sizeof(temp.media));
3412
3413 if (!cupsArrayFind(sizes, &temp))
3414 cupsArrayAdd(sizes, &temp);
3415 }
3416 else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3417 {
3418 /*
3419 * Custom size - record the min/max values...
3420 */
3421
3422 int lower, upper; /* Range values */
3423
3424 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3425 lower = ippGetRange(x_dim, 0, &upper);
3426 else
3427 lower = upper = ippGetInteger(x_dim, 0);
3428
3429 if (lower < min_width)
3430 min_width = lower;
3431 if (upper > max_width)
3432 max_width = upper;
3433
3434 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3435 lower = ippGetRange(y_dim, 0, &upper);
3436 else
3437 lower = upper = ippGetInteger(y_dim, 0);
3438
3439 if (lower < min_length)
3440 min_length = lower;
3441 if (upper > max_length)
3442 max_length = upper;
3443 }
3444 }
3445 }
3446 else if ((attr = ippFindAttribute(response, "media-supported", IPP_TAG_ZERO)) != NULL)
3447 {
3448 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3449 {
3450 const char *pwg_size = ippGetString(attr, i, NULL);
3451 /* PWG size name */
3452 cups_size_t temp; /* Current size */
3453
3454 if ((pwg = pwgMediaForPWG(pwg_size)) != NULL)
3455 {
3456 if (strstr(pwg_size, "_max_") || strstr(pwg_size, "_max."))
3457 {
3458 if (pwg->width > max_width)
3459 max_width = pwg->width;
3460 if (pwg->length > max_length)
3461 max_length = pwg->length;
3462 }
3463 else if (strstr(pwg_size, "_min_") || strstr(pwg_size, "_min."))
3464 {
3465 if (pwg->width < min_width)
3466 min_width = pwg->width;
3467 if (pwg->length < min_length)
3468 min_length = pwg->length;
3469 }
3470 else
3471 {
3472 temp.width = pwg->width;
3473 temp.length = pwg->length;
3474 temp.bottom = bottom;
3475 temp.left = left;
3476 temp.right = right;
3477 temp.top = top;
3478
3479 if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3480 snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3481 else
3482 strlcpy(temp.media, pwg->ppd, sizeof(temp.media));
3483
3484 if (!cupsArrayFind(sizes, &temp))
3485 cupsArrayAdd(sizes, &temp);
3486 }
3487 }
3488 }
3489 }
3490
3491 if (cupsArrayCount(sizes) > 0)
3492 {
3493 /*
3494 * List all of the standard sizes...
3495 */
3496
3497 char tleft[256], /* Left string */
3498 tbottom[256], /* Bottom string */
3499 tright[256], /* Right string */
3500 ttop[256], /* Top string */
3501 twidth[256], /* Width string */
3502 tlength[256]; /* Length string */
3503
3504 cupsFilePrintf(fp, "*OpenUI *PageSize: PickOne\n"
3505 "*OrderDependency: 10 AnySetup *PageSize\n"
3506 "*DefaultPageSize: %s\n", ppdname);
3507 for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3508 {
3509 _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3510 _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3511
3512 cupsFilePrintf(fp, "*PageSize %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", size->media, twidth, tlength);
3513 }
3514 cupsFilePuts(fp, "*CloseUI: *PageSize\n");
3515
3516 cupsFilePrintf(fp, "*OpenUI *PageRegion: PickOne\n"
3517 "*OrderDependency: 10 AnySetup *PageRegion\n"
3518 "*DefaultPageRegion: %s\n", ppdname);
3519 for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3520 {
3521 _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3522 _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3523
3524 cupsFilePrintf(fp, "*PageRegion %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", size->media, twidth, tlength);
3525 }
3526 cupsFilePuts(fp, "*CloseUI: *PageRegion\n");
3527
3528 cupsFilePrintf(fp, "*DefaultImageableArea: %s\n"
3529 "*DefaultPaperDimension: %s\n", ppdname, ppdname);
3530
3531 for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3532 {
3533 _cupsStrFormatd(tleft, tleft + sizeof(tleft), size->left * 72.0 / 2540.0, loc);
3534 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), size->bottom * 72.0 / 2540.0, loc);
3535 _cupsStrFormatd(tright, tright + sizeof(tright), (size->width - size->right) * 72.0 / 2540.0, loc);
3536 _cupsStrFormatd(ttop, ttop + sizeof(ttop), (size->length - size->top) * 72.0 / 2540.0, loc);
3537 _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3538 _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3539
3540 cupsFilePrintf(fp, "*ImageableArea %s: \"%s %s %s %s\"\n", size->media, tleft, tbottom, tright, ttop);
3541 cupsFilePrintf(fp, "*PaperDimension %s: \"%s %s\"\n", size->media, twidth, tlength);
3542 }
3543
3544 cupsArrayDelete(sizes);
3545
3546 /*
3547 * Custom size support...
3548 */
3549
3550 if (max_width > 0 && min_width < INT_MAX && max_length > 0 && min_length < INT_MAX)
3551 {
3552 char tmax[256], tmin[256]; /* Min/max values */
3553
3554 _cupsStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc);
3555 _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), bottom * 72.0 / 2540.0, loc);
3556 _cupsStrFormatd(tright, tright + sizeof(tright), right * 72.0 / 2540.0, loc);
3557 _cupsStrFormatd(ttop, ttop + sizeof(ttop), top * 72.0 / 2540.0, loc);
3558
3559 cupsFilePrintf(fp, "*HWMargins: \"%s %s %s %s\"\n", tleft, tbottom, tright, ttop);
3560
3561 _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_width * 72.0 / 2540.0, loc);
3562 _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_width * 72.0 / 2540.0, loc);
3563 cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s\n", tmin, tmax);
3564
3565 _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_length * 72.0 / 2540.0, loc);
3566 _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_length * 72.0 / 2540.0, loc);
3567 cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s\n", tmin, tmax);
3568
3569 cupsFilePuts(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0\n");
3570 cupsFilePuts(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0\n");
3571 cupsFilePuts(fp, "*ParamCustomPageSize Orientation: 5 int 0 3\n");
3572 cupsFilePuts(fp, "*CustomPageSize True: \"pop pop pop <</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\"\n");
3573 }
3574 }
3575 else
3576 {
3577 cupsArrayDelete(sizes);
3578 goto bad_ppd;
3579 }
3580
3581 /*
3582 * InputSlot...
3583 */
3584
3585 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source", IPP_TAG_ZERO)) != NULL)
3586 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3587 else
3588 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3589
3590 if ((attr = ippFindAttribute(response, "media-source-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3591 {
3592 static const char * const sources[] =
3593 { /* Standard "media-source" strings */
3594 "auto",
3595 "main",
3596 "alternate",
3597 "large-capacity",
3598 "manual",
3599 "envelope",
3600 "disc",
3601 "photo",
3602 "hagaki",
3603 "main-roll",
3604 "alternate-roll",
3605 "top",
3606 "middle",
3607 "bottom",
3608 "side",
3609 "left",
3610 "right",
3611 "center",
3612 "rear",
3613 "by-pass-tray",
3614 "tray-1",
3615 "tray-2",
3616 "tray-3",
3617 "tray-4",
3618 "tray-5",
3619 "tray-6",
3620 "tray-7",
3621 "tray-8",
3622 "tray-9",
3623 "tray-10",
3624 "tray-11",
3625 "tray-12",
3626 "tray-13",
3627 "tray-14",
3628 "tray-15",
3629 "tray-16",
3630 "tray-17",
3631 "tray-18",
3632 "tray-19",
3633 "tray-20",
3634 "roll-1",
3635 "roll-2",
3636 "roll-3",
3637 "roll-4",
3638 "roll-5",
3639 "roll-6",
3640 "roll-7",
3641 "roll-8",
3642 "roll-9",
3643 "roll-10"
3644 };
3645
3646 cupsFilePrintf(fp, "*OpenUI *InputSlot: PickOne\n"
3647 "*OrderDependency: 10 AnySetup *InputSlot\n"
3648 "*DefaultInputSlot: %s\n", ppdname);
3649 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3650 {
3651 keyword = ippGetString(attr, i, NULL);
3652
3653 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3654
3655 for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++)
3656 if (!strcmp(sources[j], keyword))
3657 {
3658 snprintf(msgid, sizeof(msgid), "media-source.%s", keyword);
3659 cupsFilePrintf(fp, "*InputSlot %s/%s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, _cupsLangString(lang, msgid), j);
3660 break;
3661 }
3662 }
3663 cupsFilePuts(fp, "*CloseUI: *InputSlot\n");
3664 }
3665
3666 /*
3667 * MediaType...
3668 */
3669
3670 if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type", IPP_TAG_ZERO)) != NULL)
3671 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3672 else
3673 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3674
3675 if ((attr = ippFindAttribute(response, "media-type-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3676 {
3677 cupsFilePrintf(fp, "*OpenUI *MediaType: PickOne\n"
3678 "*OrderDependency: 10 AnySetup *MediaType\n"
3679 "*DefaultMediaType: %s\n", ppdname);
3680 for (i = 0; i < count; i ++)
3681 {
3682 keyword = ippGetString(attr, i, NULL);
3683
3684 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3685
3686 snprintf(msgid, sizeof(msgid), "media-type.%s", keyword);
3687 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
3688 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
3689 msgstr = keyword;
3690
3691 cupsFilePrintf(fp, "*MediaType %s/%s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, msgstr, ppdname);
3692 }
3693 cupsFilePuts(fp, "*CloseUI: *MediaType\n");
3694 }
3695
3696 /*
3697 * ColorModel...
3698 */
3699
3700 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) == NULL)
3701 if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
3702 if ((attr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) == NULL)
3703 attr = ippFindAttribute(response, "output-mode-supported", IPP_TAG_KEYWORD);
3704
3705 if (attr)
3706 {
3707 const char *default_color = NULL; /* Default */
3708
3709 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3710 {
3711 keyword = ippGetString(attr, i, NULL);
3712
3713 if (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
3714 {
3715 if (!default_color)
3716 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3717 "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
3718
3719 cupsFilePrintf(fp, "*ColorModel FastGray/%s: \"<</cupsColorSpace 3/cupsBitsPerColor 1/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Fast Grayscale")));
3720
3721 if (!default_color)
3722 default_color = "FastGray";
3723 }
3724 else if (!strcasecmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
3725 {
3726 if (!default_color)
3727 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3728 "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
3729
3730 cupsFilePrintf(fp, "*ColorModel Gray/%s: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Grayscale")));
3731
3732 if (!default_color || !strcmp(default_color, "FastGray"))
3733 default_color = "Gray";
3734 }
3735 else if (!strcasecmp(keyword, "srgb_8") || !strcmp(keyword, "SRGB24") || !strcmp(keyword, "color"))
3736 {
3737 if (!default_color)
3738 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3739 "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
3740
3741 cupsFilePrintf(fp, "*ColorModel RGB/%s: \"<</cupsColorSpace 19/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Color")));
3742
3743 default_color = "RGB";
3744 }
3745 else if (!strcasecmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48"))
3746 {
3747 if (!default_color)
3748 cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
3749 "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
3750
3751 cupsFilePrintf(fp, "*ColorModel AdobeRGB/%s: \"<</cupsColorSpace 20/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Deep Color")));
3752
3753 if (!default_color)
3754 default_color = "AdobeRGB";
3755 }
3756 }
3757
3758 if (default_color)
3759 {
3760 cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color);
3761 cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
3762 }
3763 }
3764
3765 /*
3766 * Duplex...
3767 */
3768
3769 if ((attr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL && ippContainsString(attr, "two-sided-long-edge"))
3770 {
3771 cupsFilePrintf(fp, "*OpenUI *Duplex/%s: PickOne\n"
3772 "*OrderDependency: 10 AnySetup *Duplex\n"
3773 "*DefaultDuplex: None\n"
3774 "*Duplex None/%s: \"<</Duplex false>>setpagedevice\"\n"
3775 "*Duplex DuplexNoTumble/%s: \"<</Duplex true/Tumble false>>setpagedevice\"\n"
3776 "*Duplex DuplexTumble/%s: \"<</Duplex true/Tumble true>>setpagedevice\"\n"
3777 "*CloseUI: *Duplex\n", _cupsLangString(lang, _("2-Sided Printing")), _cupsLangString(lang, _("Off (1-Sided)")), _cupsLangString(lang, _("Long-Edge (Portrait)")), _cupsLangString(lang, _("Short-Edge (Landscape)")));
3778
3779 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
3780 {
3781 for (i = 0, count = ippGetCount(attr); i < count; i ++)
3782 {
3783 const char *dm = ippGetString(attr, i, NULL);
3784 /* DM value */
3785
3786 if (!_cups_strcasecmp(dm, "DM1"))
3787 {
3788 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3789 break;
3790 }
3791 else if (!_cups_strcasecmp(dm, "DM2"))
3792 {
3793 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3794 break;
3795 }
3796 else if (!_cups_strcasecmp(dm, "DM3"))
3797 {
3798 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3799 break;
3800 }
3801 else if (!_cups_strcasecmp(dm, "DM4"))
3802 {
3803 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3804 break;
3805 }
3806 }
3807 }
3808 else if ((attr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
3809 {
3810 keyword = ippGetString(attr, 0, NULL);
3811
3812 if (!strcmp(keyword, "flipped"))
3813 cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
3814 else if (!strcmp(keyword, "manual-tumble"))
3815 cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
3816 else if (!strcmp(keyword, "normal"))
3817 cupsFilePuts(fp, "*cupsBackSide: Normal\n");
3818 else
3819 cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
3820 }
3821 }
3822
3823 /*
3824 * Output bin...
3825 */
3826
3827 if ((attr = ippFindAttribute(response, "output-bin-default", IPP_TAG_ZERO)) != NULL)
3828 pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3829 else
3830 strlcpy(ppdname, "Unknown", sizeof(ppdname));
3831
3832 if ((attr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3833 {
3834 cupsFilePrintf(fp, "*OpenUI *OutputBin: PickOne\n"
3835 "*OrderDependency: 10 AnySetup *OutputBin\n"
3836 "*DefaultOutputBin: %s\n", ppdname);
3837 for (i = 0; i < count; i ++)
3838 {
3839 keyword = ippGetString(attr, i, NULL);
3840
3841 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
3842
3843 snprintf(msgid, sizeof(msgid), "output-bin.%s", keyword);
3844 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
3845 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
3846 msgstr = keyword;
3847
3848 cupsFilePrintf(fp, "*OutputBin %s/%s: \"\"\n", ppdname, msgstr);
3849 }
3850 cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
3851 }
3852
3853 /*
3854 * Finishing options...
3855 */
3856
3857 if ((attr = ippFindAttribute(response, "finishings-supported", IPP_TAG_ENUM)) != NULL)
3858 {
3859 int value; /* Enum value */
3860 cups_array_t *names; /* Names we've added */
3861
3862 count = ippGetCount(attr);
3863 names = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
3864 fin_options = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3865
3866 /*
3867 * Staple/Bind/Stitch
3868 */
3869
3870 for (i = 0; i < count; i ++)
3871 {
3872 value = ippGetInteger(attr, i);
3873 keyword = ippEnumString("finishings", value);
3874
3875 if (!strncmp(keyword, "staple-", 7) || !strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch"))
3876 break;
3877 }
3878
3879 if (i < count)
3880 {
3881 cupsArrayAdd(fin_options, "*StapleLocation");
3882
3883 cupsFilePrintf(fp, "*OpenUI *StapleLocation/%s: PickOne\n", _cupsLangString(lang, _("Staple")));
3884 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
3885 cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
3886 cupsFilePrintf(fp, "*StapleLocation None/%s: \"\"\n", _cupsLangString(lang, _("None")));
3887
3888 for (; i < count; i ++)
3889 {
3890 value = ippGetInteger(attr, i);
3891 keyword = ippEnumString("finishings", value);
3892
3893 if (strncmp(keyword, "staple-", 7) && strncmp(keyword, "bind-", 5) && strncmp(keyword, "edge-stitch-", 12) && strcmp(keyword, "saddle-stitch"))
3894 continue;
3895
3896 if (cupsArrayFind(names, (char *)keyword))
3897 continue; /* Already did this finishing template */
3898
3899 cupsArrayAdd(names, (char *)keyword);
3900
3901 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
3902 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
3903 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
3904 msgstr = keyword;
3905
3906 cupsFilePrintf(fp, "*StapleLocation %s/%s: \"\"\n", keyword, msgstr);
3907 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, keyword, keyword);
3908 }
3909
3910 cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
3911 }
3912
3913 /*
3914 * Fold
3915 */
3916
3917 for (i = 0; i < count; i ++)
3918 {
3919 value = ippGetInteger(attr, i);
3920 keyword = ippEnumString("finishings", value);
3921
3922 if (!strncmp(keyword, "fold-", 5))
3923 break;
3924 }
3925
3926 if (i < count)
3927 {
3928 cupsArrayAdd(fin_options, "*FoldType");
3929
3930 cupsFilePrintf(fp, "*OpenUI *FoldType/%s: PickOne\n", _cupsLangString(lang, _("Fold")));
3931 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
3932 cupsFilePuts(fp, "*DefaultFoldType: None\n");
3933 cupsFilePrintf(fp, "*FoldType None/%s: \"\"\n", _cupsLangString(lang, _("None")));
3934
3935 for (; i < count; i ++)
3936 {
3937 value = ippGetInteger(attr, i);
3938 keyword = ippEnumString("finishings", value);
3939
3940 if (strncmp(keyword, "fold-", 5))
3941 continue;
3942
3943 if (cupsArrayFind(names, (char *)keyword))
3944 continue; /* Already did this finishing template */
3945
3946 cupsArrayAdd(names, (char *)keyword);
3947
3948 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
3949 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
3950 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
3951 msgstr = keyword;
3952
3953 cupsFilePrintf(fp, "*FoldType %s/%s: \"\"\n", keyword, msgstr);
3954 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, keyword, keyword);
3955 }
3956
3957 cupsFilePuts(fp, "*CloseUI: *FoldType\n");
3958 }
3959
3960 /*
3961 * Punch
3962 */
3963
3964 for (i = 0; i < count; i ++)
3965 {
3966 value = ippGetInteger(attr, i);
3967 keyword = ippEnumString("finishings", value);
3968
3969 if (!strncmp(keyword, "punch-", 6))
3970 break;
3971 }
3972
3973 if (i < count)
3974 {
3975 cupsArrayAdd(fin_options, "*PunchMedia");
3976
3977 cupsFilePrintf(fp, "*OpenUI *PunchMedia/%s: PickOne\n", _cupsLangString(lang, _("Punch")));
3978 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
3979 cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
3980 cupsFilePrintf(fp, "*PunchMedia None/%s: \"\"\n", _cupsLangString(lang, _("None")));
3981
3982 for (i = 0; i < count; i ++)
3983 {
3984 value = ippGetInteger(attr, i);
3985 keyword = ippEnumString("finishings", value);
3986
3987 if (strncmp(keyword, "punch-", 6))
3988 continue;
3989
3990 if (cupsArrayFind(names, (char *)keyword))
3991 continue; /* Already did this finishing template */
3992
3993 cupsArrayAdd(names, (char *)keyword);
3994
3995 snprintf(msgid, sizeof(msgid), "finishings.%d", value);
3996 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
3997 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
3998 msgstr = keyword;
3999
4000 cupsFilePrintf(fp, "*PunchMedia %s/%s: \"\"\n", keyword, msgstr);
4001 cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, keyword, keyword);
4002 }
4003
4004 cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
4005 }
4006
4007 /*
4008 * Booklet
4009 */
4010
4011 if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER))
4012 {
4013 cupsArrayAdd(fin_options, "*Booklet");
4014
4015 cupsFilePrintf(fp, "*OpenUI *Booklet/%s: Boolean\n", _cupsLangString(lang, _("Booklet")));
4016 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
4017 cupsFilePuts(fp, "*DefaultBooklet: False\n");
4018 cupsFilePuts(fp, "*Booklet False: \"\"\n");
4019 cupsFilePuts(fp, "*Booklet True: \"\"\n");
4020 cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n", IPP_FINISHINGS_BOOKLET_MAKER);
4021 cupsFilePuts(fp, "*CloseUI: *Booklet\n");
4022 }
4023
4024 cupsArrayDelete(names);
4025 }
4026
4027 if ((attr = ippFindAttribute(response, "finishings-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4028 {
4029 ipp_t *finishing_col; /* Current finishing collection */
4030 ipp_attribute_t *finishing_attr; /* Current finishing member attribute */
4031 cups_array_t *templates; /* Finishing templates */
4032
4033 cupsFilePrintf(fp, "*OpenUI *cupsFinishingTemplate/%s: PickOne\n", _cupsLangString(lang, _("Finishing Preset")));
4034 cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n");
4035 cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n");
4036 cupsFilePrintf(fp, "*cupsFinishingTemplate none/%s: \"\"\n", _cupsLangString(lang, _("None")));
4037
4038 templates = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4039 count = ippGetCount(attr);
4040
4041 for (i = 0; i < count; i ++)
4042 {
4043 finishing_col = ippGetCollection(attr, i);
4044 keyword = ippGetString(ippFindAttribute(finishing_col, "finishing-template", IPP_TAG_ZERO), 0, NULL);
4045
4046 if (!keyword || cupsArrayFind(templates, (void *)keyword))
4047 continue;
4048
4049 if (strncmp(keyword, "fold-", 5) && (strstr(keyword, "-bottom") || strstr(keyword, "-left") || strstr(keyword, "-right") || strstr(keyword, "-top")))
4050 continue;
4051
4052 cupsArrayAdd(templates, (void *)keyword);
4053
4054 snprintf(msgid, sizeof(msgid), "finishing-template.%s", keyword);
4055 if ((msgstr = _cupsLangString(lang, msgid)) == msgid || !strcmp(msgid, msgstr))
4056 if ((msgstr = _cupsMessageLookup(strings, msgid)) == msgid)
4057 msgstr = keyword;
4058
4059 cupsFilePrintf(fp, "*cupsFinishingTemplate %s/%s: \"\n", keyword, msgstr);
4060 for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr; finishing_attr = ippNextAttribute(finishing_col))
4061 {
4062 if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION)
4063 {
4064 const char *name = ippGetName(finishing_attr);
4065 /* Member attribute name */
4066
4067 if (strcmp(name, "media-size"))
4068 cupsFilePrintf(fp, "%s\n", name);
4069 }
4070 }
4071 cupsFilePuts(fp, "\"\n");
4072 cupsFilePuts(fp, "*End\n");
4073 }
4074
4075 cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n");
4076
4077 if (cupsArrayCount(fin_options))
4078 {
4079 const char *fin_option; /* Current finishing option */
4080
4081 cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate");
4082 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4083 cupsFilePrintf(fp, " %s", fin_option);
4084 cupsFilePuts(fp, "\"\n");
4085
4086 cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate None");
4087 for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4088 cupsFilePrintf(fp, " %s None", fin_option);
4089 cupsFilePuts(fp, "\"\n");
4090 }
4091
4092 cupsArrayDelete(templates);
4093 }
4094
4095 cupsArrayDelete(fin_options);
4096
4097 /*
4098 * cupsPrintQuality and DefaultResolution...
4099 */
4100
4101 quality = ippFindAttribute(response, "print-quality-supported", IPP_TAG_ENUM);
4102
4103 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
4104 {
4105 int lowdpi = 0, hidpi = 0; /* Lower and higher resolution */
4106
4107 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4108 {
4109 const char *rs = ippGetString(attr, i, NULL);
4110 /* RS value */
4111
4112 if (_cups_strncasecmp(rs, "RS", 2))
4113 continue;
4114
4115 lowdpi = atoi(rs + 2);
4116 if ((rs = strrchr(rs, '-')) != NULL)
4117 hidpi = atoi(rs + 1);
4118 else
4119 hidpi = lowdpi;
4120 break;
4121 }
4122
4123 if (lowdpi == 0)
4124 {
4125 /*
4126 * Invalid "urf-supported" value...
4127 */
4128
4129 goto bad_ppd;
4130 }
4131 else
4132 {
4133 /*
4134 * Generate print qualities based on low and high DPIs...
4135 */
4136
4137 cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi);
4138
4139 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
4140 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4141 "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
4142 if ((lowdpi & 1) == 0)
4143 cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), lowdpi, lowdpi / 2);
4144 else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4145 cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), lowdpi, lowdpi);
4146 cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), lowdpi, lowdpi);
4147 if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4148 cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), hidpi, hidpi);
4149 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4150 }
4151 }
4152 else if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
4153 {
4154 /*
4155 * Make a sorted list of resolutions.
4156 */
4157
4158 count = ippGetCount(attr);
4159 if (count > (int)(sizeof(resolutions) / sizeof(resolutions[0])))
4160 count = (int)(sizeof(resolutions) / sizeof(resolutions[0]));
4161
4162 resolutions[0] = 0; /* Not in loop to silence Clang static analyzer... */
4163 for (i = 1; i < count; i ++)
4164 resolutions[i] = i;
4165
4166 for (i = 0; i < (count - 1); i ++)
4167 {
4168 for (j = i + 1; j < count; j ++)
4169 {
4170 int ix, iy, /* First X and Y resolution */
4171 jx, jy, /* Second X and Y resolution */
4172 temp; /* Swap variable */
4173 ipp_res_t units; /* Resolution units */
4174
4175 ix = ippGetResolution(attr, resolutions[i], &iy, &units);
4176 jx = ippGetResolution(attr, resolutions[j], &jy, &units);
4177
4178 if (ix > jx || (ix == jx && iy > jy))
4179 {
4180 /*
4181 * Swap these two resolutions...
4182 */
4183
4184 temp = resolutions[i];
4185 resolutions[i] = resolutions[j];
4186 resolutions[j] = temp;
4187 }
4188 }
4189 }
4190
4191 /*
4192 * Generate print quality options...
4193 */
4194
4195 pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, ppdname, sizeof(ppdname));
4196 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4197
4198 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
4199 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4200 "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
4201 if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4202 {
4203 pwg_ppdize_resolution(attr, resolutions[0], &xres, &yres, NULL, 0);
4204 cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), xres, yres);
4205 }
4206 pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, NULL, 0);
4207 cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), xres, yres);
4208 if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4209 {
4210 pwg_ppdize_resolution(attr, resolutions[count - 1], &xres, &yres, NULL, 0);
4211 cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), xres, yres);
4212 }
4213
4214 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4215 }
4216 else if (is_apple || is_pwg)
4217 goto bad_ppd;
4218 else
4219 {
4220 if ((attr = ippFindAttribute(response, "printer-resolution-default", IPP_TAG_RESOLUTION)) != NULL)
4221 {
4222 pwg_ppdize_resolution(attr, 0, &xres, &yres, ppdname, sizeof(ppdname));
4223 }
4224 else
4225 {
4226 xres = yres = 300;
4227 strlcpy(ppdname, "300dpi", sizeof(ppdname));
4228 }
4229
4230 cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4231
4232 cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
4233 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4234 "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
4235 if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4236 cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), xres, yres);
4237 cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), xres, yres);
4238 if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
4239 cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), xres, yres);
4240 cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4241 }
4242
4243 /*
4244 * Presets...
4245 */
4246
4247 if ((attr = ippFindAttribute(response, "job-presets-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4248 {
4249 for (i = 0, count = ippGetCount(attr); i < count; i ++)
4250 {
4251 ipp_t *preset = ippGetCollection(attr, i);
4252 /* Preset collection */
4253 const char *preset_name = ippGetString(ippFindAttribute(preset, "preset-name", IPP_TAG_ZERO), 0, NULL),
4254 /* Preset name */
4255 *localized_name; /* Localized preset name */
4256 ipp_attribute_t *member; /* Member attribute in preset */
4257 const char *member_name; /* Member attribute name */
4258 char member_value[256]; /* Member attribute value */
4259
4260 if (!preset || !preset_name)
4261 continue;
4262
4263 if ((localized_name = _cupsMessageLookup(strings, preset_name)) == preset_name)
4264 cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", preset_name);
4265 else
4266 cupsFilePrintf(fp, "*APPrinterPreset %s/%s: \"\n", preset_name, localized_name);
4267
4268 for (member = ippFirstAttribute(preset); member; member = ippNextAttribute(preset))
4269 {
4270 member_name = ippGetName(member);
4271
4272 if (!member_name || !strcmp(member_name, "preset-name"))
4273 continue;
4274
4275 if (!strcmp(member_name, "finishings"))
4276 {
4277 for (i = 0, count = ippGetCount(member); i < count; i ++)
4278 {
4279 const char *option = NULL; /* PPD option name */
4280
4281 keyword = ippEnumString("finishings", ippGetInteger(member, i));
4282
4283 if (!strcmp(keyword, "booklet-maker"))
4284 {
4285 option = "Booklet";
4286 keyword = "True";
4287 }
4288 else if (!strncmp(keyword, "fold-", 5))
4289 option = "FoldType";
4290 else if (!strncmp(keyword, "punch-", 6))
4291 option = "PunchMedia";
4292 else if (!strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch") || !strncmp(keyword, "staple-", 7))
4293 option = "StapleLocation";
4294
4295 if (option && keyword)
4296 cupsFilePrintf(fp, "*%s %s\n", option, keyword);
4297 }
4298 }
4299 else if (!strcmp(member_name, "finishings-col"))
4300 {
4301 ipp_t *fin_col; /* finishings-col value */
4302
4303 for (i = 0, count = ippGetCount(member); i < count; i ++)
4304 {
4305 fin_col = ippGetCollection(member, i);
4306
4307 if ((keyword = ippGetString(ippFindAttribute(fin_col, "finishing-template", IPP_TAG_ZERO), 0, NULL)) != NULL)
4308 cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", keyword);
4309 }
4310 }
4311 else if (!strcmp(member_name, "media"))
4312 {
4313 /*
4314 * Map media to PageSize...
4315 */
4316
4317 if ((pwg = pwgMediaForPWG(ippGetString(member, 0, NULL))) != NULL && pwg->ppd)
4318 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4319 }
4320 else if (!strcmp(member_name, "media-col"))
4321 {
4322 media_col = ippGetCollection(member, 0);
4323
4324 if ((media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0)) != NULL)
4325 {
4326 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
4327 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
4328 if ((pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL && pwg->ppd)
4329 cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
4330 }
4331
4332 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL)) != NULL)
4333 {
4334 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4335 cupsFilePrintf(fp, "*InputSlot %s\n", keyword);
4336 }
4337
4338 if ((keyword = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL)) != NULL)
4339 {
4340 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4341 cupsFilePrintf(fp, "*MediaType %s\n", keyword);
4342 }
4343 }
4344 else if (!strcmp(member_name, "print-quality"))
4345 {
4346 /*
4347 * Map print-quality to cupsPrintQuality...
4348 */
4349
4350 int qval = ippGetInteger(member, 0);
4351 /* print-quality value */
4352 static const char * const qualities[] = { "Draft", "Normal", "High" };
4353 /* cupsPrintQuality values */
4354
4355 if (qval >= IPP_QUALITY_DRAFT && qval <= IPP_QUALITY_HIGH)
4356 cupsFilePrintf(fp, "*cupsPrintQuality %s\n", qualities[qval - IPP_QUALITY_DRAFT]);
4357 }
4358 else if (!strcmp(member_name, "output-bin"))
4359 {
4360 pwg_ppdize_name(ippGetString(member, 0, NULL), ppdname, sizeof(ppdname));
4361 cupsFilePrintf(fp, "*OutputBin %s\n", ppdname);
4362 }
4363 else if (!strcmp(member_name, "sides"))
4364 {
4365 keyword = ippGetString(member, 0, NULL);
4366 if (keyword && !strcmp(keyword, "one-sided"))
4367 cupsFilePuts(fp, "*Duplex None\n");
4368 else if (keyword && !strcmp(keyword, "two-sided-long-edge"))
4369 cupsFilePuts(fp, "*Duplex DuplexNoTumble\n");
4370 else if (keyword && !strcmp(keyword, "two-sided-short-edge"))
4371 cupsFilePuts(fp, "*Duplex DuplexTumble\n");
4372 }
4373 else
4374 {
4375 /*
4376 * Add attribute name and value as-is...
4377 */
4378
4379 ippAttributeString(member, member_value, sizeof(member_value));
4380 cupsFilePrintf(fp, "*%s %s\n", member_name, member_value);
4381 }
4382 }
4383
4384 cupsFilePuts(fp, "\"\n*End\n");
4385 }
4386 }
4387
4388 /*
4389 * Close up and return...
4390 */
4391
4392 cupsFileClose(fp);
4393
4394 return (buffer);
4395
4396 /*
4397 * If we get here then there was a problem creating the PPD...
4398 */
4399
4400 bad_ppd:
4401
4402 cupsFileClose(fp);
4403 unlink(buffer);
4404 *buffer = '\0';
4405
4406 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Printer does not support required IPP attributes or document formats."), 1);
4407
4408 return (NULL);
4409 }
4410
4411
4412 /*
4413 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
4414 * media-source.
4415 */
4416
4417 const char * /* O - InputSlot name */
4418 _pwgInputSlotForSource(
4419 const char *media_source, /* I - PWG media-source */
4420 char *name, /* I - Name buffer */
4421 size_t namesize) /* I - Size of name buffer */
4422 {
4423 /*
4424 * Range check input...
4425 */
4426
4427 if (!media_source || !name || namesize < PPD_MAX_NAME)
4428 return (NULL);
4429
4430 if (_cups_strcasecmp(media_source, "main"))
4431 strlcpy(name, "Cassette", namesize);
4432 else if (_cups_strcasecmp(media_source, "alternate"))
4433 strlcpy(name, "Multipurpose", namesize);
4434 else if (_cups_strcasecmp(media_source, "large-capacity"))
4435 strlcpy(name, "LargeCapacity", namesize);
4436 else if (_cups_strcasecmp(media_source, "bottom"))
4437 strlcpy(name, "Lower", namesize);
4438 else if (_cups_strcasecmp(media_source, "middle"))
4439 strlcpy(name, "Middle", namesize);
4440 else if (_cups_strcasecmp(media_source, "top"))
4441 strlcpy(name, "Upper", namesize);
4442 else if (_cups_strcasecmp(media_source, "rear"))
4443 strlcpy(name, "Rear", namesize);
4444 else if (_cups_strcasecmp(media_source, "side"))
4445 strlcpy(name, "Side", namesize);
4446 else if (_cups_strcasecmp(media_source, "envelope"))
4447 strlcpy(name, "Envelope", namesize);
4448 else if (_cups_strcasecmp(media_source, "main-roll"))
4449 strlcpy(name, "Roll", namesize);
4450 else if (_cups_strcasecmp(media_source, "alternate-roll"))
4451 strlcpy(name, "Roll2", namesize);
4452 else
4453 pwg_ppdize_name(media_source, name, namesize);
4454
4455 return (name);
4456 }
4457
4458
4459 /*
4460 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
4461 * media-type.
4462 */
4463
4464 const char * /* O - MediaType name */
4465 _pwgMediaTypeForType(
4466 const char *media_type, /* I - PWG media-type */
4467 char *name, /* I - Name buffer */
4468 size_t namesize) /* I - Size of name buffer */
4469 {
4470 /*
4471 * Range check input...
4472 */
4473
4474 if (!media_type || !name || namesize < PPD_MAX_NAME)
4475 return (NULL);
4476
4477 if (_cups_strcasecmp(media_type, "auto"))
4478 strlcpy(name, "Auto", namesize);
4479 else if (_cups_strcasecmp(media_type, "cardstock"))
4480 strlcpy(name, "Cardstock", namesize);
4481 else if (_cups_strcasecmp(media_type, "envelope"))
4482 strlcpy(name, "Envelope", namesize);
4483 else if (_cups_strcasecmp(media_type, "photographic-glossy"))
4484 strlcpy(name, "Glossy", namesize);
4485 else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
4486 strlcpy(name, "HighGloss", namesize);
4487 else if (_cups_strcasecmp(media_type, "photographic-matte"))
4488 strlcpy(name, "Matte", namesize);
4489 else if (_cups_strcasecmp(media_type, "stationery"))
4490 strlcpy(name, "Plain", namesize);
4491 else if (_cups_strcasecmp(media_type, "stationery-coated"))
4492 strlcpy(name, "Coated", namesize);
4493 else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
4494 strlcpy(name, "Inkjet", namesize);
4495 else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
4496 strlcpy(name, "Letterhead", namesize);
4497 else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
4498 strlcpy(name, "Preprinted", namesize);
4499 else if (_cups_strcasecmp(media_type, "transparency"))
4500 strlcpy(name, "Transparency", namesize);
4501 else
4502 pwg_ppdize_name(media_type, name, namesize);
4503
4504 return (name);
4505 }
4506
4507
4508 /*
4509 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
4510 */
4511
4512 const char * /* O - PageSize name */
4513 _pwgPageSizeForMedia(
4514 pwg_media_t *media, /* I - Media */
4515 char *name, /* I - PageSize name buffer */
4516 size_t namesize) /* I - Size of name buffer */
4517 {
4518 const char *sizeptr, /* Pointer to size in PWG name */
4519 *dimptr; /* Pointer to dimensions in PWG name */
4520
4521
4522 /*
4523 * Range check input...
4524 */
4525
4526 if (!media || !name || namesize < PPD_MAX_NAME)
4527 return (NULL);
4528
4529 /*
4530 * Copy or generate a PageSize name...
4531 */
4532
4533 if (media->ppd)
4534 {
4535 /*
4536 * Use a standard Adobe name...
4537 */
4538
4539 strlcpy(name, media->ppd, namesize);
4540 }
4541 else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
4542 (sizeptr = strchr(media->pwg, '_')) == NULL ||
4543 (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
4544 (size_t)(dimptr - sizeptr) > namesize)
4545 {
4546 /*
4547 * Use a name of the form "wNNNhNNN"...
4548 */
4549
4550 snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
4551 (int)PWG_TO_POINTS(media->length));
4552 }
4553 else
4554 {
4555 /*
4556 * Copy the size name from class_sizename_dimensions...
4557 */
4558
4559 memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
4560 name[dimptr - sizeptr - 1] = '\0';
4561 }
4562
4563 return (name);
4564 }
4565
4566
4567 /*
4568 * 'cups_get_url()' - Get a copy of the file at the given URL.
4569 */
4570
4571 static int /* O - 1 on success, 0 on failure */
4572 cups_get_url(http_t **http, /* IO - Current HTTP connection */
4573 const char *url, /* I - URL to get */
4574 char *name, /* I - Temporary filename */
4575 size_t namesize) /* I - Size of temporary filename buffer */
4576 {
4577 char scheme[32], /* URL scheme */
4578 userpass[256], /* URL username:password */
4579 host[256], /* URL host */
4580 curhost[256], /* Current host */
4581 resource[256]; /* URL resource */
4582 int port; /* URL port */
4583 http_encryption_t encryption; /* Type of encryption to use */
4584 http_status_t status; /* Status of GET request */
4585 int fd; /* Temporary file */
4586
4587
4588 if (httpSeparateURI(HTTP_URI_CODING_ALL, url, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
4589 return (0);
4590
4591 if (port == 443 || !strcmp(scheme, "https"))
4592 encryption = HTTP_ENCRYPTION_ALWAYS;
4593 else
4594 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
4595
4596 if (!*http || strcasecmp(host, httpGetHostname(*http, curhost, sizeof(curhost))) || httpAddrPort(httpGetAddress(*http)) != port)
4597 {
4598 httpClose(*http);
4599 *http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 5000, NULL);
4600 }
4601
4602 if (!*http)
4603 return (0);
4604
4605 if ((fd = cupsTempFd(name, (int)namesize)) < 0)
4606 return (0);
4607
4608 status = cupsGetFd(*http, resource, fd);
4609
4610 close(fd);
4611
4612 if (status != HTTP_STATUS_OK)
4613 {
4614 unlink(name);
4615 *name = '\0';
4616 return (0);
4617 }
4618
4619 return (1);
4620 }
4621
4622
4623 /*
4624 * 'pwg_add_finishing()' - Add a finishings value.
4625 */
4626
4627 static void
4628 pwg_add_finishing(
4629 cups_array_t *finishings, /* I - Finishings array */
4630 ipp_finishings_t template, /* I - Finishing template */
4631 const char *name, /* I - PPD option */
4632 const char *value) /* I - PPD choice */
4633 {
4634 _pwg_finishings_t *f; /* New finishings value */
4635
4636
4637 if ((f = (_pwg_finishings_t *)calloc(1, sizeof(_pwg_finishings_t))) != NULL)
4638 {
4639 f->value = template;
4640 f->num_options = cupsAddOption(name, value, 0, &f->options);
4641
4642 cupsArrayAdd(finishings, f);
4643 }
4644 }
4645
4646
4647 /*
4648 * 'pwg_add_message()' - Add a message to the PPD cached strings.
4649 */
4650
4651 static void
4652 pwg_add_message(cups_array_t *a, /* I - Message catalog */
4653 const char *msg, /* I - Message identifier */
4654 const char *str) /* I - Localized string */
4655 {
4656 _cups_message_t *m; /* New message */
4657
4658
4659 if ((m = calloc(1, sizeof(_cups_message_t))) != NULL)
4660 {
4661 m->msg = strdup(msg);
4662 m->str = strdup(str);
4663 cupsArrayAdd(a, m);
4664 }
4665 }
4666
4667
4668 /*
4669 * 'pwg_compare_finishings()' - Compare two finishings values.
4670 */
4671
4672 static int /* O - Result of comparison */
4673 pwg_compare_finishings(
4674 _pwg_finishings_t *a, /* I - First finishings value */
4675 _pwg_finishings_t *b) /* I - Second finishings value */
4676 {
4677 return ((int)b->value - (int)a->value);
4678 }
4679
4680
4681 /*
4682 * 'pwg_compare_sizes()' - Compare two media sizes...
4683 */
4684
4685 static int /* O - Result of comparison */
4686 pwg_compare_sizes(cups_size_t *a, /* I - First media size */
4687 cups_size_t *b) /* I - Second media size */
4688 {
4689 return (strcmp(a->media, b->media));
4690 }
4691
4692
4693 /*
4694 * 'pwg_copy_size()' - Copy a media size.
4695 */
4696
4697 static cups_size_t * /* O - New media size */
4698 pwg_copy_size(cups_size_t *size) /* I - Media size to copy */
4699 {
4700 cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t));
4701 /* New media size */
4702
4703 if (newsize)
4704 memcpy(newsize, size, sizeof(cups_size_t));
4705
4706 return (newsize);
4707 }
4708
4709
4710 /*
4711 * 'pwg_free_finishings()' - Free a finishings value.
4712 */
4713
4714 static void
4715 pwg_free_finishings(
4716 _pwg_finishings_t *f) /* I - Finishings value */
4717 {
4718 cupsFreeOptions(f->num_options, f->options);
4719 free(f);
4720 }
4721
4722
4723 /*
4724 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
4725 */
4726
4727 static void
4728 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
4729 char *name, /* I - Name buffer */
4730 size_t namesize) /* I - Size of name buffer */
4731 {
4732 char *ptr, /* Pointer into name buffer */
4733 *end; /* End of name buffer */
4734
4735
4736 if (!ipp)
4737 {
4738 *name = '\0';
4739 return;
4740 }
4741
4742 *name = (char)toupper(*ipp++);
4743
4744 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
4745 {
4746 if (*ipp == '-' && _cups_isalnum(ipp[1]))
4747 {
4748 ipp ++;
4749 *ptr++ = (char)toupper(*ipp++ & 255);
4750 }
4751 else
4752 *ptr++ = *ipp++;
4753 }
4754
4755 *ptr = '\0';
4756 }
4757
4758
4759 /*
4760 * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values.
4761 */
4762
4763 static void
4764 pwg_ppdize_resolution(
4765 ipp_attribute_t *attr, /* I - Attribute to convert */
4766 int element, /* I - Element to convert */
4767 int *xres, /* O - X resolution in DPI */
4768 int *yres, /* O - Y resolution in DPI */
4769 char *name, /* I - Name buffer */
4770 size_t namesize) /* I - Size of name buffer */
4771 {
4772 ipp_res_t units; /* Units for resolution */
4773
4774
4775 *xres = ippGetResolution(attr, element, yres, &units);
4776
4777 if (units == IPP_RES_PER_CM)
4778 {
4779 *xres = (int)(*xres * 2.54);
4780 *yres = (int)(*yres * 2.54);
4781 }
4782
4783 if (name && namesize > 4)
4784 {
4785 if (*xres == *yres)
4786 snprintf(name, namesize, "%ddpi", *xres);
4787 else
4788 snprintf(name, namesize, "%dx%ddpi", *xres, *yres);
4789 }
4790 }
4791
4792
4793 /*
4794 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
4795 */
4796
4797 static void
4798 pwg_unppdize_name(const char *ppd, /* I - PPD keyword */
4799 char *name, /* I - Name buffer */
4800 size_t namesize, /* I - Size of name buffer */
4801 const char *dashchars)/* I - Characters to be replaced by dashes */
4802 {
4803 char *ptr, /* Pointer into name buffer */
4804 *end; /* End of name buffer */
4805
4806
4807 if (_cups_islower(*ppd))
4808 {
4809 /*
4810 * Already lowercase name, use as-is?
4811 */
4812
4813 const char *ppdptr; /* Pointer into PPD keyword */
4814
4815 for (ppdptr = ppd + 1; *ppdptr; ppdptr ++)
4816 if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr))
4817 break;
4818
4819 if (!*ppdptr)
4820 {
4821 strlcpy(name, ppd, namesize);
4822 return;
4823 }
4824 }
4825
4826 for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
4827 {
4828 if (_cups_isalnum(*ppd) || *ppd == '-')
4829 *ptr++ = (char)tolower(*ppd & 255);
4830 else if (strchr(dashchars, *ppd))
4831 *ptr++ = '-';
4832 else
4833 *ptr++ = *ppd;
4834
4835 if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
4836 _cups_isupper(ppd[1]) && ptr < end)
4837 *ptr++ = '-';
4838 else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
4839 *ptr++ = '-';
4840 }
4841
4842 *ptr = '\0';
4843 }