4 * PWG PPD mapping API implementation for CUPS.
6 * Copyright 2010 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * _pwgCreateWithPPD() - Create PWG mapping data from a PPD file.
19 * _pwgGetBin() - Get the PWG output-bin keyword associated with a
21 * _pwgGetInputSlot() - Get the PPD InputSlot associated with the job
22 * attributes or a keyword string.
23 * _pwgGetMediaType() - Get the PPD MediaType associated with the job
24 * attributes or a keyword string.
25 * _pwgGetOutputBin() - Get the PPD OutputBin associated with the
27 * _pwgGetPageSize() - Get the PPD PageSize associated with the job
28 * attributes or a keyword string.
29 * _pwgGetSize() - Get the PWG size associated with a PPD PageSize.
30 * _pwgGetSource() - Get the PWG media-source associated with a PPD
32 * _pwgGetType() - Get the PWG media-type associated with a PPD
34 * _pwgInputSlotForSource() - Get the InputSlot name for the given PWG source.
35 * _pwgMediaTypeForType() - Get the MediaType name for the given PWG type.
36 * _pwgPageSizeForMedia() - Get the PageSize name for the given media.
37 * pwg_ppdize_name() - Convert an IPP keyword to a PPD keyword.
38 * pwg_unppdize_name() - Convert a PPD keyword to a lowercase IPP
43 * Include necessary headers...
46 #include "cups-private.h"
51 * Macro to test for two almost-equal PWG measurements.
54 #define _PWG_EQUIVALENT(x, y) (abs((x)-(y)) < 2)
61 static void pwg_ppdize_name(const char *ipp
, char *name
, size_t namesize
);
62 static void pwg_unppdize_name(const char *ppd
, char *name
, size_t namesize
);
66 * '_pwgCreateWithPPD()' - Create PWG mapping data from a PPD file.
69 _pwg_t
* /* O - PWG mapping data */
70 _pwgCreateWithPPD(ppd_file_t
*ppd
) /* I - PPD file */
72 int i
, j
, k
; /* Looping vars */
73 _pwg_t
*pwg
; /* PWG mapping data */
74 ppd_option_t
*input_slot
, /* InputSlot option */
75 *media_type
, /* MediaType option */
76 *output_bin
, /* OutputBin option */
77 *color_model
, /* ColorModel option */
78 *duplex
; /* Duplex option */
79 ppd_choice_t
*choice
; /* Current InputSlot/MediaType */
80 _pwg_map_t
*map
; /* Current source/type map */
81 ppd_attr_t
*ppd_attr
; /* Current PPD preset attribute */
82 int num_options
; /* Number of preset options and props */
83 cups_option_t
*options
; /* Preset options and properties */
84 ppd_size_t
*ppd_size
; /* Current PPD size */
85 _pwg_size_t
*pwg_size
; /* Current PWG size */
86 char pwg_keyword
[3 + PPD_MAX_NAME
+ 1 + 12 + 1 + 12 + 3],
87 /* PWG keyword string */
88 ppd_name
[PPD_MAX_NAME
];
89 /* Normalized PPD name */
90 const char *pwg_name
; /* Standard PWG media name */
91 _pwg_media_t
*pwg_media
; /* PWG media data */
92 _pwg_output_mode_t pwg_output_mode
;/* output-mode index */
93 _pwg_print_quality_t pwg_print_quality
;
94 /* print-quality index */
95 int similar
; /* Are the old and new size similar? */
96 _pwg_size_t
*old_size
; /* Current old size */
97 int old_imageable
, /* Old imageable length in 2540ths */
98 old_borderless
, /* Old borderless state */
99 old_known_pwg
; /* Old PWG name is well-known */
100 int new_width
, /* New width in 2540ths */
101 new_length
, /* New length in 2540ths */
102 new_left
, /* New left margin in 2540ths */
103 new_bottom
, /* New bottom margin in 2540ths */
104 new_right
, /* New right margin in 2540ths */
105 new_top
, /* New top margin in 2540ths */
106 new_imageable
, /* New imageable length in 2540ths */
107 new_borderless
, /* New borderless state */
108 new_known_pwg
; /* New PWG name is well-known */
109 _pwg_size_t
*new_size
; /* New size to add, if any */
112 DEBUG_printf(("_pwgCreateWithPPD(ppd=%p)", ppd
));
115 * Range check input...
125 if ((pwg
= calloc(1, sizeof(_pwg_t
))) == NULL
)
127 DEBUG_puts("_pwgCreateWithPPD: Unable to allocate _pwg_t.");
132 * Copy and convert size data...
135 if (ppd
->num_sizes
== 0)
137 DEBUG_puts("_pwgCreateWithPPD: No page sizes in PPD.");
141 if ((pwg
->sizes
= calloc(ppd
->num_sizes
, sizeof(_pwg_size_t
))) == NULL
)
143 DEBUG_printf(("_pwgCreateWithPPD: Unable to allocate %d _pwg_size_t's.",
148 for (i
= ppd
->num_sizes
, pwg_size
= pwg
->sizes
, ppd_size
= ppd
->sizes
;
153 * Don't copy over custom size...
156 if (!strcasecmp(ppd_size
->name
, "Custom"))
160 * Convert the PPD size name to the corresponding PWG keyword name.
163 if ((pwg_media
= _pwgMediaForPPD(ppd_size
->name
)) != NULL
)
166 * Standard name, do we have conflicts?
169 for (j
= 0; j
< pwg
->num_sizes
; j
++)
170 if (!strcmp(pwg
->sizes
[j
].map
.pwg
, pwg_media
->pwg
))
180 * Standard name and no conflicts, use it!
183 pwg_name
= pwg_media
->pwg
;
189 * Not a standard name; convert it to a PWG vendor name of the form:
191 * pp_lowerppd_WIDTHxHEIGHTuu
194 pwg_name
= pwg_keyword
;
197 pwg_unppdize_name(ppd_size
->name
, ppd_name
, sizeof(ppd_name
));
198 _pwgGenerateSize(pwg_keyword
, sizeof(pwg_keyword
), NULL
, ppd_name
,
199 _PWG_FROMPTS(ppd_size
->width
),
200 _PWG_FROMPTS(ppd_size
->length
));
204 * If we have a similar paper with non-zero margins then we only
205 * want to keep it if it has a larger imageable area length.
208 new_width
= _PWG_FROMPTS(ppd_size
->width
);
209 new_length
= _PWG_FROMPTS(ppd_size
->length
);
210 new_left
= _PWG_FROMPTS(ppd_size
->left
);
211 new_bottom
= _PWG_FROMPTS(ppd_size
->bottom
);
212 new_right
= _PWG_FROMPTS(ppd_size
->width
- ppd_size
->right
);
213 new_top
= _PWG_FROMPTS(ppd_size
->length
- ppd_size
->top
);
214 new_imageable
= new_length
- new_top
- new_bottom
;
215 new_borderless
= new_bottom
== 0 && new_top
== 0 &&
216 new_left
== 0 && new_right
== 0;
218 for (k
= pwg
->num_sizes
, similar
= 0, old_size
= pwg
->sizes
, new_size
= NULL
;
222 old_imageable
= old_size
->length
- old_size
->top
- old_size
->bottom
;
223 old_borderless
= old_size
->left
== 0 && old_size
->bottom
== 0 &&
224 old_size
->right
== 0 && old_size
->top
== 0;
225 old_known_pwg
= strncmp(old_size
->map
.pwg
, "oe_", 3) &&
226 strncmp(old_size
->map
.pwg
, "om_", 3);
228 similar
= old_borderless
== new_borderless
&&
229 _PWG_EQUIVALENT(old_size
->width
, new_width
) &&
230 _PWG_EQUIVALENT(old_size
->length
, new_length
);
233 (new_known_pwg
|| (!old_known_pwg
&& new_imageable
> old_imageable
)))
236 * The new paper has a larger imageable area so it could replace
237 * the older paper. Regardless of the imageable area, we always
238 * prefer the size with a well-known PWG name.
242 _cupsStrFree(old_size
->map
.ppd
);
243 _cupsStrFree(old_size
->map
.pwg
);
250 * The paper was unique enough to deserve its own entry so add it to the
254 new_size
= pwg_size
++;
264 new_size
->map
.ppd
= _cupsStrAlloc(ppd_size
->name
);
265 new_size
->map
.pwg
= _cupsStrAlloc(pwg_name
);
266 new_size
->width
= new_width
;
267 new_size
->length
= new_length
;
268 new_size
->left
= new_left
;
269 new_size
->bottom
= new_bottom
;
270 new_size
->right
= new_right
;
271 new_size
->top
= new_top
;
275 if (ppd
->variable_sizes
)
278 * Generate custom size data...
281 _pwgGenerateSize(pwg_keyword
, sizeof(pwg_keyword
), "custom", "max",
282 _PWG_FROMPTS(ppd
->custom_max
[0]),
283 _PWG_FROMPTS(ppd
->custom_max
[1]));
284 pwg
->custom_max_keyword
= _cupsStrAlloc(pwg_keyword
);
285 pwg
->custom_max_width
= _PWG_FROMPTS(ppd
->custom_max
[0]);
286 pwg
->custom_max_length
= _PWG_FROMPTS(ppd
->custom_max
[1]);
288 _pwgGenerateSize(pwg_keyword
, sizeof(pwg_keyword
), "custom", "min",
289 _PWG_FROMPTS(ppd
->custom_min
[0]),
290 _PWG_FROMPTS(ppd
->custom_min
[1]));
291 pwg
->custom_min_keyword
= _cupsStrAlloc(pwg_keyword
);
292 pwg
->custom_min_width
= _PWG_FROMPTS(ppd
->custom_min
[0]);
293 pwg
->custom_min_length
= _PWG_FROMPTS(ppd
->custom_min
[1]);
295 pwg
->custom_size
.left
= _PWG_FROMPTS(ppd
->custom_margins
[0]);
296 pwg
->custom_size
.bottom
= _PWG_FROMPTS(ppd
->custom_margins
[1]);
297 pwg
->custom_size
.right
= _PWG_FROMPTS(ppd
->custom_margins
[2]);
298 pwg
->custom_size
.top
= _PWG_FROMPTS(ppd
->custom_margins
[3]);
302 * Copy and convert InputSlot data...
305 if ((input_slot
= ppdFindOption(ppd
, "InputSlot")) == NULL
)
306 input_slot
= ppdFindOption(ppd
, "HPPaperSource");
310 pwg
->source_option
= _cupsStrAlloc(input_slot
->keyword
);
312 if ((pwg
->sources
= calloc(input_slot
->num_choices
,
313 sizeof(_pwg_map_t
))) == NULL
)
315 DEBUG_printf(("_pwgCreateWithPPD: Unable to allocate %d _pwg_map_t's "
316 "for InputSlot.", input_slot
->num_choices
));
320 pwg
->num_sources
= input_slot
->num_choices
;
322 for (i
= input_slot
->num_choices
, choice
= input_slot
->choices
,
325 i
--, choice
++, map
++)
327 if (!strncasecmp(choice
->choice
, "Auto", 4) ||
328 !strcasecmp(choice
->choice
, "Default"))
330 else if (!strcasecmp(choice
->choice
, "Cassette"))
332 else if (!strcasecmp(choice
->choice
, "PhotoTray"))
334 else if (!strcasecmp(choice
->choice
, "CDTray"))
336 else if (!strncasecmp(choice
->choice
, "Multipurpose", 12) ||
337 !strcasecmp(choice
->choice
, "MP") ||
338 !strcasecmp(choice
->choice
, "MPTray"))
339 pwg_name
= "alternate";
340 else if (!strcasecmp(choice
->choice
, "LargeCapacity"))
341 pwg_name
= "large-capacity";
342 else if (!strncasecmp(choice
->choice
, "Lower", 5))
344 else if (!strncasecmp(choice
->choice
, "Middle", 6))
346 else if (!strncasecmp(choice
->choice
, "Upper", 5))
348 else if (!strncasecmp(choice
->choice
, "Side", 4))
350 else if (!strcasecmp(choice
->choice
, "Roll") ||
351 !strcasecmp(choice
->choice
, "Roll1"))
352 pwg_name
= "main-roll";
353 else if (!strcasecmp(choice
->choice
, "Roll2"))
354 pwg_name
= "alternate-roll";
358 * Convert PPD name to lowercase...
361 pwg_name
= pwg_keyword
;
362 pwg_unppdize_name(choice
->choice
, pwg_keyword
, sizeof(pwg_keyword
));
365 map
->pwg
= _cupsStrAlloc(pwg_name
);
366 map
->ppd
= _cupsStrAlloc(choice
->choice
);
371 * Copy and convert MediaType data...
374 if ((media_type
= ppdFindOption(ppd
, "MediaType")) != NULL
)
376 if ((pwg
->types
= calloc(media_type
->num_choices
,
377 sizeof(_pwg_map_t
))) == NULL
)
379 DEBUG_printf(("_pwgCreateWithPPD: Unable to allocate %d _pwg_map_t's "
380 "for MediaType.", media_type
->num_choices
));
384 pwg
->num_types
= media_type
->num_choices
;
386 for (i
= media_type
->num_choices
, choice
= media_type
->choices
,
389 i
--, choice
++, map
++)
391 if (!strncasecmp(choice
->choice
, "Auto", 4) ||
392 !strcasecmp(choice
->choice
, "Any") ||
393 !strcasecmp(choice
->choice
, "Default"))
395 else if (!strncasecmp(choice
->choice
, "Card", 4))
396 pwg_name
= "cardstock";
397 else if (!strncasecmp(choice
->choice
, "Env", 3))
398 pwg_name
= "envelope";
399 else if (!strncasecmp(choice
->choice
, "Gloss", 5))
400 pwg_name
= "photographic-glossy";
401 else if (!strcasecmp(choice
->choice
, "HighGloss"))
402 pwg_name
= "photographic-high-gloss";
403 else if (!strcasecmp(choice
->choice
, "Matte"))
404 pwg_name
= "photographic-matte";
405 else if (!strncasecmp(choice
->choice
, "Plain", 5))
406 pwg_name
= "stationery";
407 else if (!strncasecmp(choice
->choice
, "Coated", 6))
408 pwg_name
= "stationery-coated";
409 else if (!strcasecmp(choice
->choice
, "Inkjet"))
410 pwg_name
= "stationery-inkjet";
411 else if (!strcasecmp(choice
->choice
, "Letterhead"))
412 pwg_name
= "stationery-letterhead";
413 else if (!strncasecmp(choice
->choice
, "Preprint", 8))
414 pwg_name
= "stationery-preprinted";
415 else if (!strncasecmp(choice
->choice
, "Transparen", 10))
416 pwg_name
= "transparency";
420 * Convert PPD name to lowercase...
423 pwg_name
= pwg_keyword
;
424 pwg_unppdize_name(choice
->choice
, pwg_keyword
, sizeof(pwg_keyword
));
427 map
->pwg
= _cupsStrAlloc(pwg_name
);
428 map
->ppd
= _cupsStrAlloc(choice
->choice
);
434 * Copy and convert OutputBin data...
437 if ((output_bin
= ppdFindOption(ppd
, "OutputBin")) != NULL
)
439 if ((pwg
->bins
= calloc(output_bin
->num_choices
,
440 sizeof(_pwg_map_t
))) == NULL
)
442 DEBUG_printf(("_pwgCreateWithPPD: Unable to allocate %d _pwg_map_t's "
443 "for OutputBin.", output_bin
->num_choices
));
447 pwg
->num_bins
= output_bin
->num_choices
;
449 for (i
= output_bin
->num_choices
, choice
= output_bin
->choices
,
452 i
--, choice
++, map
++)
454 pwg_unppdize_name(choice
->choice
, pwg_keyword
, sizeof(pwg_keyword
));
456 map
->pwg
= _cupsStrAlloc(pwg_keyword
);
457 map
->ppd
= _cupsStrAlloc(choice
->choice
);
461 if ((ppd_attr
= ppdFindAttr(ppd
, "APPrinterPreset", NULL
)) != NULL
)
464 * Copy and convert APPrinterPreset (output-mode + print-quality) data...
467 const char *quality
, /* com.apple.print.preset.quality value */
468 *output_mode
, /* com.apple.print.preset.output-mode value */
469 *color_model_val
, /* ColorModel choice */
470 *graphics_type
, /* com.apple.print.preset.graphicsType value */
471 *paper_coating
; /* com.apple.print.preset.media-front-coating value */
476 num_options
= _ppdParseOptions(ppd_attr
->value
, 0, &options
,
479 if ((quality
= cupsGetOption("com.apple.print.preset.quality",
480 num_options
, options
)) != NULL
)
483 * Get the print-quality for this preset...
486 if (!strcmp(quality
, "low"))
487 pwg_print_quality
= _PWG_PRINT_QUALITY_DRAFT
;
488 else if (!strcmp(quality
, "high"))
489 pwg_print_quality
= _PWG_PRINT_QUALITY_HIGH
;
491 pwg_print_quality
= _PWG_PRINT_QUALITY_NORMAL
;
494 * Ignore graphicsType "Photo" presets that are not high quality.
497 graphics_type
= cupsGetOption("com.apple.print.preset.graphicsType",
498 num_options
, options
);
500 if (pwg_print_quality
!= _PWG_PRINT_QUALITY_HIGH
&& graphics_type
&&
501 !strcmp(graphics_type
, "Photo"))
505 * Ignore presets for normal and draft quality where the coating
506 * isn't "none" or "autodetect".
509 paper_coating
= cupsGetOption(
510 "com.apple.print.preset.media-front-coating",
511 num_options
, options
);
513 if (pwg_print_quality
!= _PWG_PRINT_QUALITY_HIGH
&& paper_coating
&&
514 strcmp(paper_coating
, "none") &&
515 strcmp(paper_coating
, "autodetect"))
519 * Get the output mode for this preset...
522 output_mode
= cupsGetOption("com.apple.print.preset.output-mode",
523 num_options
, options
);
524 color_model_val
= cupsGetOption("ColorModel", num_options
, options
);
528 if (!strcmp(output_mode
, "monochrome"))
529 pwg_output_mode
= _PWG_OUTPUT_MODE_MONOCHROME
;
531 pwg_output_mode
= _PWG_OUTPUT_MODE_COLOR
;
533 else if (color_model_val
)
535 if (!strcasecmp(color_model_val
, "Gray"))
536 pwg_output_mode
= _PWG_OUTPUT_MODE_MONOCHROME
;
538 pwg_output_mode
= _PWG_OUTPUT_MODE_COLOR
;
541 pwg_output_mode
= _PWG_OUTPUT_MODE_COLOR
;
544 * Save the options for this combination as needed...
547 if (!pwg
->num_presets
[pwg_output_mode
][pwg_print_quality
])
548 pwg
->num_presets
[pwg_output_mode
][pwg_print_quality
] =
549 _ppdParseOptions(ppd_attr
->value
, 0,
550 pwg
->presets
[pwg_output_mode
] +
551 pwg_print_quality
, _PPD_PARSE_OPTIONS
);
554 cupsFreeOptions(num_options
, options
);
556 while ((ppd_attr
= ppdFindNextAttr(ppd
, "APPrinterPreset", NULL
)) != NULL
);
559 if (!pwg
->num_presets
[_PWG_OUTPUT_MODE_MONOCHROME
][_PWG_PRINT_QUALITY_DRAFT
] &&
560 !pwg
->num_presets
[_PWG_OUTPUT_MODE_MONOCHROME
][_PWG_PRINT_QUALITY_NORMAL
] &&
561 !pwg
->num_presets
[_PWG_OUTPUT_MODE_MONOCHROME
][_PWG_PRINT_QUALITY_HIGH
])
564 * Try adding some common color options to create grayscale presets. These
565 * are listed in order of popularity...
568 const char *color_option
= NULL
, /* Color control option */
569 *gray_choice
= NULL
; /* Choice to select grayscale */
571 if ((color_model
= ppdFindOption(ppd
, "ColorModel")) != NULL
&&
572 ppdFindChoice(color_model
, "Gray"))
574 color_option
= "ColorModel";
575 gray_choice
= "Gray";
577 else if ((color_model
= ppdFindOption(ppd
, "HPColorMode")) != NULL
&&
578 ppdFindChoice(color_model
, "grayscale"))
580 color_option
= "HPColorMode";
581 gray_choice
= "grayscale";
583 else if ((color_model
= ppdFindOption(ppd
, "BRMonoColor")) != NULL
&&
584 ppdFindChoice(color_model
, "Mono"))
586 color_option
= "BRMonoColor";
587 gray_choice
= "Mono";
589 else if ((color_model
= ppdFindOption(ppd
, "CNIJSGrayScale")) != NULL
&&
590 ppdFindChoice(color_model
, "1"))
592 color_option
= "CNIJSGrayScale";
595 else if ((color_model
= ppdFindOption(ppd
, "HPColorAsGray")) != NULL
&&
596 ppdFindChoice(color_model
, "True"))
598 color_option
= "HPColorAsGray";
599 gray_choice
= "True";
602 if (color_option
&& gray_choice
)
605 * Copy and convert ColorModel (output-mode) data...
608 cups_option_t
*coption
, /* Color option */
609 *moption
; /* Monochrome option */
611 for (pwg_print_quality
= _PWG_PRINT_QUALITY_DRAFT
;
612 pwg_print_quality
< _PWG_PRINT_QUALITY_MAX
;
613 pwg_print_quality
++)
615 if (pwg
->num_presets
[_PWG_OUTPUT_MODE_COLOR
][pwg_print_quality
])
618 * Copy the color options...
621 num_options
= pwg
->num_presets
[_PWG_OUTPUT_MODE_COLOR
]
623 options
= calloc(sizeof(cups_option_t
), num_options
);
627 for (i
= num_options
, moption
= options
,
628 coption
= pwg
->presets
[_PWG_OUTPUT_MODE_COLOR
]
631 i
--, moption
++, coption
++)
633 moption
->name
= _cupsStrRetain(coption
->name
);
634 moption
->value
= _cupsStrRetain(coption
->value
);
637 pwg
->num_presets
[_PWG_OUTPUT_MODE_MONOCHROME
][pwg_print_quality
] =
639 pwg
->presets
[_PWG_OUTPUT_MODE_MONOCHROME
][pwg_print_quality
] =
643 else if (pwg_print_quality
!= _PWG_PRINT_QUALITY_NORMAL
)
647 * Add the grayscale option to the preset...
650 pwg
->num_presets
[_PWG_OUTPUT_MODE_MONOCHROME
][pwg_print_quality
] =
651 cupsAddOption(color_option
, gray_choice
,
652 pwg
->num_presets
[_PWG_OUTPUT_MODE_MONOCHROME
]
654 pwg
->presets
[_PWG_OUTPUT_MODE_MONOCHROME
] +
661 * Copy and convert Duplex (sides) data...
664 if ((duplex
= ppdFindOption(ppd
, "Duplex")) == NULL
)
665 if ((duplex
= ppdFindOption(ppd
, "JCLDuplex")) == NULL
)
666 if ((duplex
= ppdFindOption(ppd
, "EFDuplex")) == NULL
)
667 if ((duplex
= ppdFindOption(ppd
, "EFDuplexing")) == NULL
)
668 duplex
= ppdFindOption(ppd
, "KD03Duplex");
672 pwg
->sides_option
= _cupsStrAlloc(duplex
->keyword
);
674 for (i
= duplex
->num_choices
, choice
= duplex
->choices
;
678 if ((!strcasecmp(choice
->choice
, "None") ||
679 !strcasecmp(choice
->choice
, "False")) && !pwg
->sides_1sided
)
680 pwg
->sides_1sided
= _cupsStrAlloc(choice
->choice
);
681 else if ((!strcasecmp(choice
->choice
, "DuplexNoTumble") ||
682 !strcasecmp(choice
->choice
, "LongEdge") ||
683 !strcasecmp(choice
->choice
, "Top")) && !pwg
->sides_2sided_long
)
684 pwg
->sides_2sided_long
= _cupsStrAlloc(choice
->choice
);
685 else if ((!strcasecmp(choice
->choice
, "DuplexTumble") ||
686 !strcasecmp(choice
->choice
, "ShortEdge") ||
687 !strcasecmp(choice
->choice
, "Bottom")) &&
688 !pwg
->sides_2sided_short
)
689 pwg
->sides_2sided_short
= _cupsStrAlloc(choice
->choice
);
696 * If we get here we need to destroy the PWG mapping data and return NULL...
701 _cupsSetError(IPP_INTERNAL_ERROR
, _("Out of memory."), 1);
709 * '_pwgGetBin()' - Get the PWG output-bin keyword associated with a PPD
713 const char * /* O - output-bin or NULL */
714 _pwgGetBin(_pwg_t
*pwg
, /* I - PWG mapping data */
715 const char *output_bin
) /* I - PPD OutputBin string */
717 int i
; /* Looping var */
721 * Range check input...
724 if (!pwg
|| !output_bin
)
728 * Look up the OutputBin string...
732 for (i
= 0; i
< pwg
->num_bins
; i
++)
733 if (!strcasecmp(output_bin
, pwg
->bins
[i
].ppd
))
734 return (pwg
->bins
[i
].pwg
);
741 * '_pwgGetInputSlot()' - Get the PPD InputSlot associated with the job
742 * attributes or a keyword string.
745 const char * /* O - PPD InputSlot or NULL */
746 _pwgGetInputSlot(_pwg_t
*pwg
, /* I - PWG mapping data */
747 ipp_t
*job
, /* I - Job attributes or NULL */
748 const char *keyword
) /* I - Keyword string or NULL */
751 * Range check input...
754 if (!pwg
|| pwg
->num_sources
== 0 || (!job
&& !keyword
))
760 * Lookup the media-col attribute and any media-source found there...
763 ipp_attribute_t
*media_col
, /* media-col attribute */
764 *media_source
; /* media-source attribute */
765 _pwg_size_t size
; /* Dimensional size */
766 int margins_set
; /* Were the margins set? */
768 media_col
= ippFindAttribute(job
, "media-col", IPP_TAG_BEGIN_COLLECTION
);
770 (media_source
= ippFindAttribute(media_col
->values
[0].collection
,
772 IPP_TAG_KEYWORD
)) != NULL
)
775 * Use the media-source value from media-col...
778 keyword
= media_source
->values
[0].string
.text
;
780 else if (_pwgInitSize(&size
, job
, &margins_set
))
783 * For media <= 5x7, look for a photo tray...
786 if (size
.width
<= (5 * 2540) && size
.length
<= (7 * 2540))
793 int i
; /* Looping var */
795 for (i
= 0; i
< pwg
->num_sources
; i
++)
796 if (!strcasecmp(keyword
, pwg
->sources
[i
].pwg
))
797 return (pwg
->sources
[i
].ppd
);
805 * '_pwgGetMediaType()' - Get the PPD MediaType associated with the job
806 * attributes or a keyword string.
809 const char * /* O - PPD MediaType or NULL */
810 _pwgGetMediaType(_pwg_t
*pwg
, /* I - PWG mapping data */
811 ipp_t
*job
, /* I - Job attributes or NULL */
812 const char *keyword
) /* I - Keyword string or NULL */
815 * Range check input...
818 if (!pwg
|| pwg
->num_types
== 0 || (!job
&& !keyword
))
824 * Lookup the media-col attribute and any media-source found there...
827 ipp_attribute_t
*media_col
, /* media-col attribute */
828 *media_type
; /* media-type attribute */
830 media_col
= ippFindAttribute(job
, "media-col", IPP_TAG_BEGIN_COLLECTION
);
833 if ((media_type
= ippFindAttribute(media_col
->values
[0].collection
,
835 IPP_TAG_KEYWORD
)) == NULL
)
836 media_type
= ippFindAttribute(media_col
->values
[0].collection
,
837 "media-type", IPP_TAG_NAME
);
840 keyword
= media_type
->values
[0].string
.text
;
846 int i
; /* Looping var */
848 for (i
= 0; i
< pwg
->num_types
; i
++)
849 if (!strcasecmp(keyword
, pwg
->types
[i
].pwg
))
850 return (pwg
->types
[i
].ppd
);
858 * '_pwgGetOutputBin()' - Get the PPD OutputBin associated with the keyword
862 const char * /* O - PPD OutputBin or NULL */
863 _pwgGetOutputBin(_pwg_t
*pwg
, /* I - PWG mapping data */
864 const char *output_bin
)/* I - Keyword string */
866 int i
; /* Looping var */
870 * Range check input...
873 if (!pwg
|| !output_bin
)
877 * Look up the OutputBin string...
881 for (i
= 0; i
< pwg
->num_bins
; i
++)
882 if (!strcasecmp(output_bin
, pwg
->bins
[i
].pwg
))
883 return (pwg
->bins
[i
].ppd
);
890 * '_pwgGetPageSize()' - Get the PPD PageSize associated with the job
891 * attributes or a keyword string.
894 const char * /* O - PPD PageSize or NULL */
895 _pwgGetPageSize(_pwg_t
*pwg
, /* I - PWG mapping data */
896 ipp_t
*job
, /* I - Job attributes or NULL */
897 const char *keyword
, /* I - Keyword string or NULL */
898 int *exact
) /* I - 1 if exact match, 0 otherwise */
900 int i
; /* Looping var */
901 _pwg_size_t
*size
, /* Current size */
902 *closest
, /* Closest size */
903 jobsize
; /* Size data from job */
904 int margins_set
, /* Were the margins set? */
905 dwidth
, /* Difference in width */
906 dlength
, /* Difference in length */
907 dleft
, /* Difference in left margins */
908 dright
, /* Difference in right margins */
909 dbottom
, /* Difference in bottom margins */
910 dtop
, /* Difference in top margins */
911 dmin
, /* Minimum difference */
912 dclosest
; /* Closest difference */
913 const char *ppd_name
; /* PPD media name */
916 DEBUG_printf(("_pwgGetPageSize(pwg=%p, job=%p, keyword=\"%s\", exact=%p)",
917 pwg
, job
, keyword
, exact
));
920 * Range check input...
923 if (!pwg
|| (!job
&& !keyword
))
934 * Try getting the PPD media name from the job attributes...
937 ipp_attribute_t
*attr
; /* Job attribute */
939 if ((attr
= ippFindAttribute(job
, "PageSize", IPP_TAG_ZERO
)) == NULL
)
940 if ((attr
= ippFindAttribute(job
, "PageRegion", IPP_TAG_ZERO
)) == NULL
)
941 attr
= ippFindAttribute(job
, "media", IPP_TAG_ZERO
);
945 DEBUG_printf(("1_pwgGetPageSize: Found attribute %s (%s)", attr
->name
,
946 ippTagString(attr
->value_tag
)));
948 DEBUG_puts("1_pwgGetPageSize: Did not find media attribute.");
951 if (attr
&& (attr
->value_tag
== IPP_TAG_NAME
||
952 attr
->value_tag
== IPP_TAG_KEYWORD
))
953 ppd_name
= attr
->values
[0].string
.text
;
956 DEBUG_printf(("1_pwgGetPageSize: ppd_name=\"%s\"", ppd_name
));
961 * Try looking up the named PPD size first...
964 for (i
= pwg
->num_sizes
, size
= pwg
->sizes
; i
> 0; i
--, size
++)
966 DEBUG_printf(("2_pwgGetPageSize: size[%d]=[\"%s\" \"%s\"]",
967 (int)(size
- pwg
->sizes
), size
->map
.pwg
, size
->map
.ppd
));
969 if (!strcasecmp(ppd_name
, size
->map
.ppd
))
974 DEBUG_printf(("1_pwgGetPageSize: Returning \"%s\"", ppd_name
));
984 * Get the size using media-col or media, with the preference being
988 if (!_pwgInitSize(&jobsize
, job
, &margins_set
))
994 * Get the size using a media keyword...
997 _pwg_media_t
*media
; /* Media definition */
1000 if ((media
= _pwgMediaForPWG(keyword
)) == NULL
)
1001 if ((media
= _pwgMediaForLegacy(keyword
)) == NULL
)
1002 if ((media
= _pwgMediaForPPD(keyword
)) == NULL
)
1005 jobsize
.width
= media
->width
;
1006 jobsize
.length
= media
->length
;
1011 * Now that we have the dimensions and possibly the margins, look at the
1012 * available sizes and find the match...
1016 dclosest
= 999999999;
1018 if (!ppd_name
|| strncasecmp(ppd_name
, "Custom.", 7) ||
1019 strncasecmp(ppd_name
, "custom_", 7))
1021 for (i
= pwg
->num_sizes
, size
= pwg
->sizes
; i
> 0; i
--, size
++)
1024 * Adobe uses a size matching algorithm with an epsilon of 5 points, which
1025 * is just about 176/2540ths...
1028 dwidth
= size
->width
- jobsize
.width
;
1029 dlength
= size
->length
- jobsize
.length
;
1031 if (dwidth
<= -176 || dwidth
>= 176 || dlength
<= -176 || dlength
>= 176)
1037 * Use a tighter epsilon of 1 point (35/2540ths) for margins...
1040 dleft
= size
->left
- jobsize
.left
;
1041 dright
= size
->right
- jobsize
.right
;
1042 dtop
= size
->top
- jobsize
.top
;
1043 dbottom
= size
->bottom
- jobsize
.bottom
;
1045 if (dleft
<= -35 || dleft
>= 35 || dright
<= -35 || dright
>= 35 ||
1046 dtop
<= -35 || dtop
>= 35 || dbottom
<= -35 || dbottom
>= 35)
1048 dleft
= dleft
< 0 ? -dleft
: dleft
;
1049 dright
= dright
< 0 ? -dright
: dright
;
1050 dbottom
= dbottom
< 0 ? -dbottom
: dbottom
;
1051 dtop
= dtop
< 0 ? -dtop
: dtop
;
1052 dmin
= dleft
+ dright
+ dbottom
+ dtop
;
1054 if (dmin
< dclosest
)
1067 DEBUG_printf(("1_pwgGetPageSize: Returning \"%s\"", size
->map
.ppd
));
1069 return (size
->map
.ppd
);
1075 DEBUG_printf(("1_pwgGetPageSize: Returning \"%s\" (closest)",
1078 return (closest
->map
.ppd
);
1082 * If we get here we need to check for custom page size support...
1085 if (jobsize
.width
>= pwg
->custom_min_width
&&
1086 jobsize
.width
<= pwg
->custom_max_width
&&
1087 jobsize
.length
>= pwg
->custom_min_length
&&
1088 jobsize
.length
<= pwg
->custom_max_length
)
1091 * In range, format as Custom.WWWWxLLLL (points).
1094 snprintf(pwg
->custom_ppd_size
, sizeof(pwg
->custom_ppd_size
), "Custom.%dx%d",
1095 (int)_PWG_TOPTS(jobsize
.width
), (int)_PWG_TOPTS(jobsize
.length
));
1097 if (margins_set
&& exact
)
1099 dleft
= pwg
->custom_size
.left
- jobsize
.left
;
1100 dright
= pwg
->custom_size
.right
- jobsize
.right
;
1101 dtop
= pwg
->custom_size
.top
- jobsize
.top
;
1102 dbottom
= pwg
->custom_size
.bottom
- jobsize
.bottom
;
1104 if (dleft
> -35 && dleft
< 35 && dright
> -35 && dright
< 35 &&
1105 dtop
> -35 && dtop
< 35 && dbottom
> -35 && dbottom
< 35)
1111 DEBUG_printf(("1_pwgGetPageSize: Returning \"%s\" (custom)",
1112 pwg
->custom_ppd_size
));
1114 return (pwg
->custom_ppd_size
);
1118 * No custom page size support or the size is out of range - return NULL.
1121 DEBUG_puts("1_pwgGetPageSize: Returning NULL");
1128 * '_pwgGetSize()' - Get the PWG size associated with a PPD PageSize.
1131 _pwg_size_t
* /* O - PWG size or NULL */
1132 _pwgGetSize(_pwg_t
*pwg
, /* I - PWG mapping data */
1133 const char *page_size
) /* I - PPD PageSize */
1136 _pwg_size_t
*size
; /* Current size */
1140 * Range check input...
1143 if (!pwg
|| !page_size
)
1146 if (!strncasecmp(page_size
, "Custom.", 7))
1149 * Custom size; size name can be one of the following:
1151 * Custom.WIDTHxLENGTHin - Size in inches
1152 * Custom.WIDTHxLENGTHft - Size in feet
1153 * Custom.WIDTHxLENGTHcm - Size in centimeters
1154 * Custom.WIDTHxLENGTHmm - Size in millimeters
1155 * Custom.WIDTHxLENGTHm - Size in meters
1156 * Custom.WIDTHxLENGTH[pt] - Size in points
1159 double w
, l
; /* Width and length of page */
1160 char *ptr
; /* Pointer into PageSize */
1161 struct lconv
*loc
; /* Locale data */
1164 w
= (float)_cupsStrScand(page_size
+ 7, &ptr
, loc
);
1165 if (!ptr
|| *ptr
!= 'x')
1168 l
= (float)_cupsStrScand(ptr
+ 1, &ptr
, loc
);
1172 if (!strcasecmp(ptr
, "in"))
1177 else if (!strcasecmp(ptr
, "ft"))
1182 else if (!strcasecmp(ptr
, "mm"))
1187 else if (!strcasecmp(ptr
, "cm"))
1192 else if (!strcasecmp(ptr
, "m"))
1203 pwg
->custom_size
.width
= (int)w
;
1204 pwg
->custom_size
.length
= (int)l
;
1206 return (&(pwg
->custom_size
));
1210 * Not a custom size - look it up...
1213 for (i
= pwg
->num_sizes
, size
= pwg
->sizes
; i
> 0; i
--, size
++)
1214 if (!strcasecmp(page_size
, size
->map
.ppd
))
1222 * '_pwgGetSource()' - Get the PWG media-source associated with a PPD InputSlot.
1225 const char * /* O - PWG media-source keyword */
1226 _pwgGetSource(_pwg_t
*pwg
, /* I - PWG mapping data */
1227 const char *input_slot
) /* I - PPD InputSlot */
1229 int i
; /* Looping var */
1230 _pwg_map_t
*source
; /* Current source */
1234 * Range check input...
1237 if (!pwg
|| !input_slot
)
1240 for (i
= pwg
->num_sources
, source
= pwg
->sources
; i
> 0; i
--, source
++)
1241 if (!strcasecmp(input_slot
, source
->ppd
))
1242 return (source
->pwg
);
1249 * '_pwgGetType()' - Get the PWG media-type associated with a PPD MediaType.
1252 const char * /* O - PWG media-type keyword */
1253 _pwgGetType(_pwg_t
*pwg
, /* I - PWG mapping data */
1254 const char *media_type
) /* I - PPD MediaType */
1256 int i
; /* Looping var */
1257 _pwg_map_t
*type
; /* Current type */
1261 * Range check input...
1264 if (!pwg
|| !media_type
)
1267 for (i
= pwg
->num_types
, type
= pwg
->types
; i
> 0; i
--, type
++)
1268 if (!strcasecmp(media_type
, type
->ppd
))
1276 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG source.
1279 const char * /* O - InputSlot name */
1280 _pwgInputSlotForSource(
1281 const char *media_source
, /* I - PWG media-source */
1282 char *name
, /* I - Name buffer */
1283 size_t namesize
) /* I - Size of name buffer */
1286 * Range check input...
1289 if (!media_source
|| !name
|| namesize
< PPD_MAX_NAME
)
1292 if (strcasecmp(media_source
, "main"))
1293 strlcpy(name
, "Cassette", namesize
);
1294 else if (strcasecmp(media_source
, "alternate"))
1295 strlcpy(name
, "Multipurpose", namesize
);
1296 else if (strcasecmp(media_source
, "large-capacity"))
1297 strlcpy(name
, "LargeCapacity", namesize
);
1298 else if (strcasecmp(media_source
, "bottom"))
1299 strlcpy(name
, "Lower", namesize
);
1300 else if (strcasecmp(media_source
, "middle"))
1301 strlcpy(name
, "Middle", namesize
);
1302 else if (strcasecmp(media_source
, "top"))
1303 strlcpy(name
, "Upper", namesize
);
1304 else if (strcasecmp(media_source
, "rear"))
1305 strlcpy(name
, "Rear", namesize
);
1306 else if (strcasecmp(media_source
, "side"))
1307 strlcpy(name
, "Side", namesize
);
1308 else if (strcasecmp(media_source
, "envelope"))
1309 strlcpy(name
, "Envelope", namesize
);
1310 else if (strcasecmp(media_source
, "main-roll"))
1311 strlcpy(name
, "Roll", namesize
);
1312 else if (strcasecmp(media_source
, "alternate-roll"))
1313 strlcpy(name
, "Roll2", namesize
);
1315 pwg_ppdize_name(media_source
, name
, namesize
);
1322 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG type.
1325 const char * /* O - MediaType name */
1326 _pwgMediaTypeForType(
1327 const char *media_type
, /* I - PWG media-source */
1328 char *name
, /* I - Name buffer */
1329 size_t namesize
) /* I - Size of name buffer */
1332 * Range check input...
1335 if (!media_type
|| !name
|| namesize
< PPD_MAX_NAME
)
1338 if (strcasecmp(media_type
, "auto"))
1339 strlcpy(name
, "Auto", namesize
);
1340 else if (strcasecmp(media_type
, "cardstock"))
1341 strlcpy(name
, "Cardstock", namesize
);
1342 else if (strcasecmp(media_type
, "envelope"))
1343 strlcpy(name
, "Envelope", namesize
);
1344 else if (strcasecmp(media_type
, "photographic-glossy"))
1345 strlcpy(name
, "Glossy", namesize
);
1346 else if (strcasecmp(media_type
, "photographic-high-gloss"))
1347 strlcpy(name
, "HighGloss", namesize
);
1348 else if (strcasecmp(media_type
, "photographic-matte"))
1349 strlcpy(name
, "Matte", namesize
);
1350 else if (strcasecmp(media_type
, "stationery"))
1351 strlcpy(name
, "Plain", namesize
);
1352 else if (strcasecmp(media_type
, "stationery-coated"))
1353 strlcpy(name
, "Coated", namesize
);
1354 else if (strcasecmp(media_type
, "stationery-inkjet"))
1355 strlcpy(name
, "Inkjet", namesize
);
1356 else if (strcasecmp(media_type
, "stationery-letterhead"))
1357 strlcpy(name
, "Letterhead", namesize
);
1358 else if (strcasecmp(media_type
, "stationery-preprinted"))
1359 strlcpy(name
, "Preprinted", namesize
);
1360 else if (strcasecmp(media_type
, "transparency"))
1361 strlcpy(name
, "Transparency", namesize
);
1363 pwg_ppdize_name(media_type
, name
, namesize
);
1370 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
1373 const char * /* O - PageSize name */
1374 _pwgPageSizeForMedia(
1375 _pwg_media_t
*media
, /* I - Media */
1376 char *name
, /* I - PageSize name buffer */
1377 size_t namesize
) /* I - Size of name buffer */
1379 const char *sizeptr
, /* Pointer to size in PWG name */
1380 *dimptr
; /* Pointer to dimensions in PWG name */
1384 * Range check input...
1387 if (!media
|| !name
|| namesize
< PPD_MAX_NAME
)
1391 * Copy or generate a PageSize name...
1397 * Use a standard Adobe name...
1400 strlcpy(name
, media
->ppd
, namesize
);
1402 else if (!media
->pwg
|| !strncmp(media
->pwg
, "custom_", 7) ||
1403 (sizeptr
= strchr(media
->pwg
, '_')) == NULL
||
1404 (dimptr
= strchr(sizeptr
+ 1, '_')) == NULL
||
1405 (size_t)(dimptr
- sizeptr
) > namesize
)
1408 * Use a name of the form "wNNNhNNN"...
1411 snprintf(name
, namesize
, "w%dh%d", (int)_PWG_TOPTS(media
->width
),
1412 (int)_PWG_TOPTS(media
->length
));
1417 * Copy the size name from class_sizename_dimensions...
1420 memcpy(name
, sizeptr
+ 1, dimptr
- sizeptr
- 1);
1421 name
[dimptr
- sizeptr
- 1] = '\0';
1429 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
1433 pwg_ppdize_name(const char *ipp
, /* I - IPP keyword */
1434 char *name
, /* I - Name buffer */
1435 size_t namesize
) /* I - Size of name buffer */
1437 char *ptr
, /* Pointer into name buffer */
1438 *end
; /* End of name buffer */
1441 *name
= toupper(*ipp
++);
1443 for (ptr
= name
+ 1, end
= name
+ namesize
- 1; *ipp
&& ptr
< end
;)
1445 if (*ipp
== '-' && _cups_isalpha(ipp
[1]))
1448 *ptr
++ = toupper(*ipp
++ & 255);
1459 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
1463 pwg_unppdize_name(const char *ppd
, /* I - PPD keyword */
1464 char *name
, /* I - Name buffer */
1465 size_t namesize
) /* I - Size of name buffer */
1467 char *ptr
, /* Pointer into name buffer */
1468 *end
; /* End of name buffer */
1471 for (ptr
= name
, end
= name
+ namesize
- 1; *ppd
&& ptr
< end
; ppd
++)
1473 if (_cups_isalnum(*ppd
) || *ppd
== '-')
1474 *ptr
++ = tolower(*ppd
& 255);
1475 else if (*ppd
== '_' || *ppd
== '.')
1478 if (!_cups_isupper(*ppd
) && _cups_isalnum(*ppd
) &&
1479 _cups_isupper(ppd
[1]) && ptr
< end
)