Coverage Report

Created: 2026-07-24 07:44

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