Coverage Report

Created: 2025-08-03 06:42

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