]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/emit.c
Several programs did not support the cupsFilter2 keyword in PPD files.
[thirdparty/cups.git] / cups / emit.c
CommitLineData
ea930040 1/*
b2e10895 2 * "$Id$"
ea930040 3 *
3d94661a 4 * PPD code emission routines for CUPS.
ea930040 5 *
c6fab96f 6 * Copyright 2007-2011 by Apple Inc.
35e7de17 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ea930040 8 *
3a193f5e 9 * These coded instructions, statements, and computer programs are the
4e8d321f 10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
ea930040 14 *
2b85e375 15 * PostScript is a trademark of Adobe Systems, Inc.
16 *
dab1a4d8 17 * This file is subject to the Apple OS-Developed Software exception.
18 *
ea930040 19 * Contents:
20 *
98d1b1a1 21 * ppdCollect() - Collect all marked options that reside in the
22 * specified section.
23 * ppdCollect2() - Collect all marked options that reside in the
24 * specified section and minimum order.
25 * ppdEmit() - Emit code for marked options to a file.
26 * ppdEmitAfterOrder() - Emit a subset of the code for marked options to a
27 * file.
28 * ppdEmitFd() - Emit code for marked options to a file.
29 * ppdEmitJCL() - Emit code for JCL options to a file.
30 * ppdEmitJCLEnd() - Emit JCLEnd code to a file.
31 * ppdEmitString() - Get a string containing the code for marked
32 * options.
33 * ppd_compare_cparams() - Compare the order of two custom parameters.
34 * ppd_handle_media() - Handle media selection...
ea930040 35 */
36
37/*
2b85e375 38 * Include necessary headers...
39 */
40
3d94661a 41#include "cups-private.h"
3b960317 42#if defined(WIN32) || defined(__EMX__)
2456b740 43# include <io.h>
44#else
45# include <unistd.h>
3b960317 46#endif /* WIN32 || __EMX__ */
2b85e375 47
48
8c1333e2 49/*
50 * Local functions...
51 */
52
98d1b1a1 53static int ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b);
ed6078a4 54static void ppd_handle_media(ppd_file_t *ppd);
cbcf8057 55
56
ed6078a4 57/*
58 * Local globals...
59 */
60
03f61bf3 61static const char ppd_custom_code[] =
ed6078a4 62 "pop pop pop\n"
63 "<</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\n";
64
65
cbcf8057 66/*
67 * 'ppdCollect()' - Collect all marked options that reside in the specified
68 * section.
8f6753d3 69 *
70 * The choices array should be freed using @code free@ when you are
71 * finished with it.
cbcf8057 72 */
73
74int /* O - Number of options marked */
75ppdCollect(ppd_file_t *ppd, /* I - PPD file data */
76 ppd_section_t section, /* I - Section to collect */
77 ppd_choice_t ***choices) /* O - Pointers to choices */
c50b38cf 78{
79 return (ppdCollect2(ppd, section, 0.0, choices));
80}
81
82
83/*
84 * 'ppdCollect2()' - Collect all marked options that reside in the
85 * specified section and minimum order.
86 *
8f6753d3 87 * The choices array should be freed using @code free@ when you are
88 * finished with it.
89 *
373b3e5f 90 * @since CUPS 1.2/Mac OS X 10.5@
c50b38cf 91 */
92
93int /* O - Number of options marked */
94ppdCollect2(ppd_file_t *ppd, /* I - PPD file data */
95 ppd_section_t section, /* I - Section to collect */
96 float min_order, /* I - Minimum OrderDependency value */
97 ppd_choice_t ***choices) /* O - Pointers to choices */
cbcf8057 98{
cbcf8057 99 ppd_choice_t *c; /* Current choice */
be140c52 100 ppd_section_t csection; /* Current section */
101 float corder; /* Current OrderDependency value */
cbcf8057 102 int count; /* Number of choices collected */
103 ppd_choice_t **collect; /* Collected choices */
be140c52 104 float *orders; /* Collected order values */
cbcf8057 105
106
571af0ea 107 DEBUG_printf(("ppdCollect2(ppd=%p, section=%d, min_order=%f, choices=%p)",
d6b8e0f9 108 ppd, section, min_order, choices));
109
09751e0f 110 if (!ppd || !choices)
111 {
112 if (choices)
113 *choices = NULL;
114
cbcf8057 115 return (0);
09751e0f 116 }
cbcf8057 117
118 /*
be140c52 119 * Allocate memory for up to N selected choices...
cbcf8057 120 */
121
09751e0f 122 count = 0;
123 if ((collect = calloc(sizeof(ppd_choice_t *),
124 cupsArrayCount(ppd->marked))) == NULL)
125 {
126 *choices = NULL;
127 return (0);
128 }
129
130 if ((orders = calloc(sizeof(float), cupsArrayCount(ppd->marked))) == NULL)
131 {
132 *choices = NULL;
133 free(collect);
134 return (0);
135 }
cbcf8057 136
137 /*
138 * Loop through all options and add choices as needed...
139 */
140
be140c52 141 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
142 c;
143 c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
cbcf8057 144 {
be140c52 145 csection = c->option->section;
146 corder = c->option->order;
147
148 if (!strcmp(c->choice, "Custom"))
149 {
150 ppd_attr_t *attr; /* NonUIOrderDependency value */
151 float aorder; /* Order value */
152 char asection[17], /* Section name */
153 amain[PPD_MAX_NAME + 1],
154 aoption[PPD_MAX_NAME];
155 /* *CustomFoo and True */
156
157
158 for (attr = ppdFindAttr(ppd, "NonUIOrderDependency", NULL);
159 attr;
160 attr = ppdFindNextAttr(ppd, "NonUIOrderDependency", NULL))
161 if (attr->value &&
162 sscanf(attr->value, "%f%16s%41s%40s", &aorder, asection, amain,
163 aoption) == 4 &&
164 !strncmp(amain, "*Custom", 7) &&
165 !strcmp(amain + 7, c->option->keyword) && !strcmp(aoption, "True"))
166 {
167 /*
168 * Use this NonUIOrderDependency...
169 */
cbcf8057 170
be140c52 171 corder = aorder;
172
173 if (!strcmp(asection, "DocumentSetup"))
174 csection = PPD_ORDER_DOCUMENT;
175 else if (!strcmp(asection, "ExitServer"))
176 csection = PPD_ORDER_EXIT;
177 else if (!strcmp(asection, "JCLSetup"))
178 csection = PPD_ORDER_JCL;
179 else if (!strcmp(asection, "PageSetup"))
180 csection = PPD_ORDER_PAGE;
181 else if (!strcmp(asection, "Prolog"))
182 csection = PPD_ORDER_PROLOG;
183 else
184 csection = PPD_ORDER_ANY;
185
186 break;
187 }
188 }
189
190 if (csection == section && corder >= min_order)
191 {
192 collect[count] = c;
193 orders[count] = corder;
194 count ++;
195 }
cbcf8057 196 }
197
198 /*
199 * If we have more than 1 marked choice, sort them...
200 */
201
202 if (count > 1)
be140c52 203 {
204 int i, j; /* Looping vars */
205
206 for (i = 0; i < (count - 1); i ++)
207 for (j = i + 1; j < count; j ++)
208 if (orders[i] > orders[j])
209 {
210 c = collect[i];
211 corder = orders[i];
212 collect[i] = collect[j];
213 orders[i] = orders[j];
214 collect[j] = c;
215 orders[j] = corder;
216 }
217 }
218
219 free(orders);
cbcf8057 220
571af0ea 221 DEBUG_printf(("2ppdCollect2: %d marked choices...", count));
d6b8e0f9 222
cbcf8057 223 /*
224 * Return the array and number of choices; if 0, free the array since
225 * it isn't needed.
226 */
227
228 if (count > 0)
229 {
230 *choices = collect;
231 return (count);
232 }
233 else
234 {
235 *choices = NULL;
236 free(collect);
237 return (0);
238 }
239}
8c1333e2 240
241
2b85e375 242/*
243 * 'ppdEmit()' - Emit code for marked options to a file.
244 */
245
246int /* O - 0 on success, -1 on failure */
247ppdEmit(ppd_file_t *ppd, /* I - PPD file record */
248 FILE *fp, /* I - File to write to */
249 ppd_section_t section) /* I - Section to write */
c50b38cf 250{
251 return (ppdEmitAfterOrder(ppd, fp, section, 0, 0.0));
252}
253
254
255/*
256 * 'ppdEmitAfterOrder()' - Emit a subset of the code for marked options to a file.
257 *
258 * When "limit" is non-zero, this function only emits options whose
259 * OrderDependency value is greater than or equal to "min_order".
260 *
261 * When "limit" is zero, this function is identical to ppdEmit().
262 *
373b3e5f 263 * @since CUPS 1.2/Mac OS X 10.5@
c50b38cf 264 */
265
266int /* O - 0 on success, -1 on failure */
267ppdEmitAfterOrder(
268 ppd_file_t *ppd, /* I - PPD file record */
269 FILE *fp, /* I - File to write to */
270 ppd_section_t section, /* I - Section to write */
94ffdf17 271 int limit, /* I - Non-zero to use min_order */
272 float min_order) /* I - Lowest OrderDependency */
2b85e375 273{
94ffdf17 274 char *buffer; /* Option code */
275 int status; /* Return status */
8c1333e2 276
277
ed6078a4 278 /*
94ffdf17 279 * Range check input...
ed6078a4 280 */
281
94ffdf17 282 if (!ppd || !fp)
283 return (-1);
ed6078a4 284
285 /*
94ffdf17 286 * Get the string...
ed6078a4 287 */
288
4a241564 289 buffer = ppdEmitString(ppd, section, limit ? min_order : 0.0);
6fdf969a 290
94ffdf17 291 /*
292 * Write it as needed and return...
293 */
8c1333e2 294
94ffdf17 295 if (buffer)
296 {
297 status = fputs(buffer, fp) < 0 ? -1 : 0;
96831d7f 298
94ffdf17 299 free(buffer);
300 }
301 else
302 status = 0;
8c1333e2 303
94ffdf17 304 return (status);
2b85e375 305}
306
307
308/*
309 * 'ppdEmitFd()' - Emit code for marked options to a file.
ea930040 310 */
311
2b85e375 312int /* O - 0 on success, -1 on failure */
313ppdEmitFd(ppd_file_t *ppd, /* I - PPD file record */
314 int fd, /* I - File to write to */
315 ppd_section_t section) /* I - Section to write */
316{
94ffdf17 317 char *buffer, /* Option code */
318 *bufptr; /* Pointer into code */
319 size_t buflength; /* Length of option code */
320 ssize_t bytes; /* Bytes written */
321 int status; /* Return status */
8c1333e2 322
323
ed6078a4 324 /*
94ffdf17 325 * Range check input...
ed6078a4 326 */
327
94ffdf17 328 if (!ppd || fd < 0)
329 return (-1);
ed6078a4 330
331 /*
94ffdf17 332 * Get the string...
ed6078a4 333 */
334
94ffdf17 335 buffer = ppdEmitString(ppd, section, 0.0);
96831d7f 336
94ffdf17 337 /*
338 * Write it as needed and return...
339 */
ed6078a4 340
94ffdf17 341 if (buffer)
342 {
343 buflength = strlen(buffer);
344 bufptr = buffer;
345 bytes = 0;
8c1333e2 346
94ffdf17 347 while (buflength > 0)
348 {
35e7de17 349#ifdef WIN32
350 if ((bytes = (ssize_t)write(fd, bufptr, (unsigned)buflength)) < 0)
351#else
94ffdf17 352 if ((bytes = write(fd, bufptr, buflength)) < 0)
35e7de17 353#endif /* WIN32 */
8c1333e2 354 {
94ffdf17 355 if (errno == EAGAIN || errno == EINTR)
356 continue;
96831d7f 357
94ffdf17 358 break;
96831d7f 359 }
94ffdf17 360
361 buflength -= bytes;
362 bufptr += bytes;
8c1333e2 363 }
364
94ffdf17 365 status = bytes < 0 ? -1 : 0;
366
367 free(buffer);
368 }
369 else
370 status = 0;
371
372 return (status);
2b85e375 373}
ea930040 374
375
ee6a18b5 376/*
377 * 'ppdEmitJCL()' - Emit code for JCL options to a file.
378 */
379
380int /* O - 0 on success, -1 on failure */
381ppdEmitJCL(ppd_file_t *ppd, /* I - PPD file record */
382 FILE *fp, /* I - File to write to */
383 int job_id, /* I - Job ID */
384 const char *user, /* I - Username */
385 const char *title) /* I - Title */
386{
33b8a82d 387 char *ptr; /* Pointer into JCL string */
4e8f2046 388 char temp[65], /* Local title string */
389 displaymsg[33]; /* Local display string */
ee6a18b5 390
391
b2e10895 392 /*
393 * Range check the input...
394 */
395
94ffdf17 396 if (!ppd || !ppd->jcl_begin || !ppd->jcl_ps)
ee6a18b5 397 return (0);
398
b2e10895 399 /*
400 * See if the printer supports HP PJL...
401 */
402
625ced05 403 if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10))
ee6a18b5 404 {
405 /*
406 * This printer uses HP PJL commands for output; filter the output
407 * so that we only have a single "@PJL JOB" command in the header...
ae90e335 408 *
409 * To avoid bugs in the PJL implementation of certain vendors' products
410 * (Xerox in particular), we add a dummy "@PJL" command at the beginning
411 * of the PJL commands to initialize PJL processing.
ee6a18b5 412 */
413
482ab49b 414 ppd_attr_t *charset; /* PJL charset */
c86a789f 415 ppd_attr_t *display; /* PJL display command */
482ab49b 416
417
418 if ((charset = ppdFindAttr(ppd, "cupsPJLCharset", NULL)) != NULL)
419 {
c6fab96f 420 if (!charset->value || _cups_strcasecmp(charset->value, "UTF-8"))
482ab49b 421 charset = NULL;
422 }
423
c86a789f 424 if ((display = ppdFindAttr(ppd, "cupsPJLDisplay", NULL)) != NULL)
425 {
426 if (!display->value)
427 display = NULL;
428 }
429
ae90e335 430 fputs("\033%-12345X@PJL\n", fp);
ee6a18b5 431 for (ptr = ppd->jcl_begin + 9; *ptr;)
625ced05 432 if (!strncmp(ptr, "@PJL JOB", 8))
ee6a18b5 433 {
434 /*
435 * Skip job command...
436 */
437
438 for (;*ptr; ptr ++)
439 if (*ptr == '\n')
440 break;
441
442 if (*ptr)
443 ptr ++;
444 }
445 else
446 {
447 /*
448 * Copy line...
449 */
450
451 for (;*ptr; ptr ++)
452 {
453 putc(*ptr, fp);
454 if (*ptr == '\n')
455 break;
456 }
457
458 if (*ptr)
459 ptr ++;
460 }
461
b2e10895 462 /*
4e8f2046 463 * Clean up the job title...
b2e10895 464 */
465
466 if ((ptr = strrchr(title, '/')) != NULL)
4e8f2046 467 {
468 /*
469 * Only show basename of file path...
470 */
471
b2e10895 472 title = ptr + 1;
4e8f2046 473 }
474
475 if (!strncmp(title, "smbprn.", 7))
476 {
477 /*
478 * Skip leading smbprn.######## from Samba jobs...
479 */
480
481 for (title += 7; *title && isdigit(*title & 255); title ++);
e00a8345 482 while (_cups_isspace(*title))
483 title ++;
4e8f2046 484
485 if ((ptr = strstr(title, " - ")) != NULL)
486 {
487 /*
488 * Skip application name in "Some Application - Title of job"...
489 */
490
491 title = ptr + 3;
492 }
493 }
b2e10895 494
ae90e335 495 /*
4e8f2046 496 * Replace double quotes with single quotes and UTF-8 characters with
482ab49b 497 * question marks so that the title does not cause a PJL syntax error.
ae90e335 498 */
499
500 strlcpy(temp, title, sizeof(temp));
501
502 for (ptr = temp; *ptr; ptr ++)
503 if (*ptr == '\"')
504 *ptr = '\'';
4e8f2046 505 else if (!charset && (*ptr & 128))
482ab49b 506 *ptr = '?';
ae90e335 507
4e8f2046 508 /*
509 * CUPS STR #3125: Long PJL JOB NAME causes problems with some printers
510 *
511 * Generate the display message, truncating at 32 characters + nul to avoid
512 * issues with some printer's PJL implementations...
513 */
514
515 snprintf(displaymsg, sizeof(displaymsg), "%d %s %s", job_id, user, temp);
516
ee6a18b5 517 /*
625ced05 518 * Send PJL JOB and PJL RDYMSG commands before we enter PostScript mode...
ee6a18b5 519 */
520
c86a789f 521 if (display && strcmp(display->value, "job"))
522 {
523 fprintf(fp, "@PJL JOB NAME = \"%s\"\n", temp);
524
525 if (display && !strcmp(display->value, "rdymsg"))
4e8f2046 526 fprintf(fp, "@PJL RDYMSG DISPLAY = \"%s\"\n", displaymsg);
c86a789f 527 }
528 else
4e8f2046 529 fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%s\"\n", temp,
530 displaymsg);
ee6a18b5 531 }
532 else
af50faa7 533 fputs(ppd->jcl_begin, fp);
ee6a18b5 534
af50faa7 535 ppdEmit(ppd, fp, PPD_ORDER_JCL);
536 fputs(ppd->jcl_ps, fp);
37ac5c5e 537
538 return (0);
ee6a18b5 539}
540
541
625ced05 542/*
543 * 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file.
0341722a 544 *
373b3e5f 545 * @since CUPS 1.2/Mac OS X 10.5@
625ced05 546 */
547
548int /* O - 0 on success, -1 on failure */
549ppdEmitJCLEnd(ppd_file_t *ppd, /* I - PPD file record */
550 FILE *fp) /* I - File to write to */
551{
625ced05 552 /*
553 * Range check the input...
554 */
555
94ffdf17 556 if (!ppd)
625ced05 557 return (0);
558
94ffdf17 559 if (!ppd->jcl_end)
625ced05 560 {
f7aaa873 561 if ((ppd->num_filters == 0 && !ppdFindAttr(ppd, "cupsFilter2", NULL)))
94ffdf17 562 putc(0x04, fp);
625ced05 563
564 return (0);
565 }
566
567 /*
568 * See if the printer supports HP PJL...
569 */
570
571 if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10))
572 {
573 /*
574 * This printer uses HP PJL commands for output; filter the output
575 * so that we only have a single "@PJL JOB" command in the header...
576 *
577 * To avoid bugs in the PJL implementation of certain vendors' products
578 * (Xerox in particular), we add a dummy "@PJL" command at the beginning
579 * of the PJL commands to initialize PJL processing.
580 */
581
582 fputs("\033%-12345X@PJL\n", fp);
c687ccbc 583 fputs("@PJL RDYMSG DISPLAY = \"\"\n", fp);
625ced05 584 fputs(ppd->jcl_end + 9, fp);
585 }
586 else
587 fputs(ppd->jcl_end, fp);
588
589 return (0);
590}
591
592
94ffdf17 593/*
594 * 'ppdEmitString()' - Get a string containing the code for marked options.
595 *
596 * When "min_order" is greater than zero, this function only includes options
597 * whose OrderDependency value is greater than or equal to "min_order".
598 * Otherwise, all options in the specified section are included in the
599 * returned string.
600 *
601 * The return string is allocated on the heap and should be freed using
8f6753d3 602 * @code free@ when you are done with it.
94ffdf17 603 *
373b3e5f 604 * @since CUPS 1.2/Mac OS X 10.5@
94ffdf17 605 */
606
8f6753d3 607char * /* O - String containing option code or @code NULL@ if there is no option code */
94ffdf17 608ppdEmitString(ppd_file_t *ppd, /* I - PPD file record */
609 ppd_section_t section, /* I - Section to write */
610 float min_order) /* I - Lowest OrderDependency */
611{
612 int i, j, /* Looping vars */
613 count; /* Number of choices */
614 ppd_choice_t **choices; /* Choices */
615 ppd_size_t *size; /* Custom page size */
616 ppd_coption_t *coption; /* Custom option */
617 ppd_cparam_t *cparam; /* Custom parameter */
618 size_t bufsize; /* Size of string buffer needed */
619 char *buffer, /* String buffer */
620 *bufptr, /* Pointer into buffer */
621 *bufend; /* End of buffer */
622 struct lconv *loc; /* Locale data */
623
624
571af0ea 625 DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)",
d6b8e0f9 626 ppd, section, min_order));
627
94ffdf17 628 /*
629 * Range check input...
630 */
631
632 if (!ppd)
633 return (NULL);
634
635 /*
636 * Use PageSize or PageRegion as required...
637 */
638
639 ppd_handle_media(ppd);
640
641 /*
642 * Collect the options we need to emit...
643 */
644
645 if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0)
646 return (NULL);
647
648 /*
649 * Count the number of bytes that are required to hold all of the
650 * option code...
651 */
652
653 for (i = 0, bufsize = 1; i < count; i ++)
654 {
c53265fe 655 if (section == PPD_ORDER_JCL)
656 {
c6fab96f 657 if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
c53265fe 658 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
659 != NULL)
660 {
661 /*
662 * Add space to account for custom parameter substitution...
663 */
664
665 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
666 cparam;
667 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
668 {
669 switch (cparam->type)
670 {
671 case PPD_CUSTOM_CURVE :
672 case PPD_CUSTOM_INVCURVE :
673 case PPD_CUSTOM_POINTS :
674 case PPD_CUSTOM_REAL :
675 case PPD_CUSTOM_INT :
676 bufsize += 10;
677 break;
678
679 case PPD_CUSTOM_PASSCODE :
680 case PPD_CUSTOM_PASSWORD :
681 case PPD_CUSTOM_STRING :
c9b4942e 682 if (cparam->current.custom_string)
683 bufsize += strlen(cparam->current.custom_string);
c53265fe 684 break;
685 }
686 }
687 }
688 }
689 else if (section != PPD_ORDER_EXIT)
94ffdf17 690 {
691 bufsize += 3; /* [{\n */
692
c6fab96f 693 if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
694 !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
695 !_cups_strcasecmp(choices[i]->choice, "Custom"))
94ffdf17 696 {
571af0ea 697 DEBUG_puts("2ppdEmitString: Custom size set!");
d6b8e0f9 698
dd5c0aa5 699 bufsize += 37; /* %%BeginFeature: *CustomPageSize True\n */
94ffdf17 700 bufsize += 50; /* Five 9-digit numbers + newline */
701 }
c6fab96f 702 else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
94ffdf17 703 (coption = ppdFindCustomOption(ppd,
704 choices[i]->option->keyword))
705 != NULL)
706 {
c7de194a 707 bufsize += 23 + strlen(choices[i]->option->keyword) + 6;
708 /* %%BeginFeature: *Customkeyword True\n */
94ffdf17 709
c6fab96f 710
94ffdf17 711 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
712 cparam;
713 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
714 {
715 switch (cparam->type)
716 {
717 case PPD_CUSTOM_CURVE :
718 case PPD_CUSTOM_INVCURVE :
719 case PPD_CUSTOM_POINTS :
720 case PPD_CUSTOM_REAL :
721 case PPD_CUSTOM_INT :
722 bufsize += 10;
723 break;
724
725 case PPD_CUSTOM_PASSCODE :
726 case PPD_CUSTOM_PASSWORD :
727 case PPD_CUSTOM_STRING :
c9b4942e 728 bufsize += 3;
729 if (cparam->current.custom_string)
730 bufsize += 4 * strlen(cparam->current.custom_string);
94ffdf17 731 break;
732 }
733 }
734 }
735 else
dd5c0aa5 736 bufsize += 17 + strlen(choices[i]->option->keyword) + 1 +
737 strlen(choices[i]->choice) + 1;
738 /* %%BeginFeature: *keyword choice\n */
94ffdf17 739
740 bufsize += 13; /* %%EndFeature\n */
741 bufsize += 22; /* } stopped cleartomark\n */
742 }
743
744 if (choices[i]->code)
dd5c0aa5 745 bufsize += strlen(choices[i]->code) + 1;
94ffdf17 746 else
747 bufsize += strlen(ppd_custom_code);
748 }
749
750 /*
751 * Allocate memory...
752 */
753
571af0ea 754 DEBUG_printf(("2ppdEmitString: Allocating %d bytes for string...",
7e8f437e 755 (int)bufsize));
d6b8e0f9 756
94ffdf17 757 if ((buffer = calloc(1, bufsize)) == NULL)
758 {
759 free(choices);
760 return (NULL);
761 }
762
763 bufend = buffer + bufsize - 1;
764 loc = localeconv();
765
766 /*
767 * Copy the option code to the buffer...
768 */
769
770 for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr))
c53265fe 771 if (section == PPD_ORDER_JCL)
772 {
c6fab96f 773 if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
0d34d098 774 choices[i]->code &&
c53265fe 775 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
776 != NULL)
777 {
778 /*
779 * Handle substitutions in custom JCL options...
780 */
781
782 char *cptr; /* Pointer into code */
783 int pnum; /* Parameter number */
784
785
786 for (cptr = choices[i]->code; *cptr && bufptr < bufend;)
787 {
788 if (*cptr == '\\')
789 {
790 cptr ++;
791
792 if (isdigit(*cptr & 255))
793 {
794 /*
795 * Substitute parameter...
796 */
797
798 pnum = *cptr++ - '0';
81b2d5a3 799 while (isdigit(*cptr & 255))
800 pnum = pnum * 10 + *cptr++ - '0';
c53265fe 801
802 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
803 cparam;
804 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
805 if (cparam->order == pnum)
806 break;
807
808 if (cparam)
809 {
810 switch (cparam->type)
811 {
812 case PPD_CUSTOM_CURVE :
813 case PPD_CUSTOM_INVCURVE :
814 case PPD_CUSTOM_POINTS :
815 case PPD_CUSTOM_REAL :
816 bufptr = _cupsStrFormatd(bufptr, bufend,
817 cparam->current.custom_real,
818 loc);
819 break;
820
821 case PPD_CUSTOM_INT :
822 snprintf(bufptr, bufend - bufptr, "%d",
823 cparam->current.custom_int);
824 bufptr += strlen(bufptr);
825 break;
826
827 case PPD_CUSTOM_PASSCODE :
828 case PPD_CUSTOM_PASSWORD :
829 case PPD_CUSTOM_STRING :
c9b4942e 830 if (cparam->current.custom_string)
831 {
832 strlcpy(bufptr, cparam->current.custom_string,
833 bufend - bufptr);
834 bufptr += strlen(bufptr);
835 }
c53265fe 836 break;
837 }
838 }
839 }
840 else if (*cptr)
841 *bufptr++ = *cptr++;
842 }
843 else
844 *bufptr++ = *cptr++;
845 }
846 }
847 else
848 {
849 /*
850 * Otherwise just copy the option code directly...
851 */
852
853 strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
854 bufptr += strlen(bufptr);
855 }
856 }
857 else if (section != PPD_ORDER_EXIT)
94ffdf17 858 {
859 /*
860 * Add wrapper commands to prevent printer errors for unsupported
861 * options...
862 */
863
864 strlcpy(bufptr, "[{\n", bufend - bufptr + 1);
865 bufptr += 3;
866
867 /*
868 * Send DSC comments with option...
869 */
870
571af0ea 871 DEBUG_printf(("2ppdEmitString: Adding code for %s=%s...",
872 choices[i]->option->keyword, choices[i]->choice));
d6b8e0f9 873
c6fab96f 874 if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
875 !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
876 !_cups_strcasecmp(choices[i]->choice, "Custom"))
94ffdf17 877 {
878 /*
879 * Variable size; write out standard size options, using the
880 * parameter positions defined in the PPD file...
881 */
882
883 ppd_attr_t *attr; /* PPD attribute */
884 int pos, /* Position of custom value */
885 orientation; /* Orientation to use */
886 float values[5]; /* Values for custom command */
887
888
889 strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n",
890 bufend - bufptr + 1);
891 bufptr += 37;
892
893 size = ppdPageSize(ppd, "Custom");
894
895 memset(values, 0, sizeof(values));
896
897 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
898 {
899 pos = atoi(attr->value) - 1;
900
901 if (pos < 0 || pos > 4)
902 pos = 0;
903 }
904 else
905 pos = 0;
906
907 values[pos] = size->width;
908
909 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
910 {
911 pos = atoi(attr->value) - 1;
912
913 if (pos < 0 || pos > 4)
914 pos = 1;
915 }
916 else
917 pos = 1;
918
919 values[pos] = size->length;
920
921 /*
922 * According to the Adobe PPD specification, an orientation of 1
923 * will produce a print that comes out upside-down with the X
924 * axis perpendicular to the direction of feed, which is exactly
925 * what we want to be consistent with non-PS printers.
926 *
927 * We could also use an orientation of 3 to produce output that
928 * comes out rightside-up (this is the default for many large format
929 * printer PPDs), however for consistency we will stick with the
930 * value 1.
931 *
932 * If we wanted to get fancy, we could use orientations of 0 or
933 * 2 and swap the width and length, however we don't want to get
934 * fancy, we just want it to work consistently.
935 *
936 * The orientation value is range limited by the Orientation
937 * parameter definition, so certain non-PS printer drivers that
938 * only support an Orientation of 0 will get the value 0 as
939 * expected.
940 */
941
942 orientation = 1;
943
944 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
945 "Orientation")) != NULL)
946 {
947 int min_orient, max_orient; /* Minimum and maximum orientations */
948
949
950 if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
951 &max_orient) != 3)
952 pos = 4;
953 else
954 {
955 pos --;
956
957 if (pos < 0 || pos > 4)
958 pos = 4;
959
960 if (orientation > max_orient)
961 orientation = max_orient;
962 else if (orientation < min_orient)
963 orientation = min_orient;
964 }
965 }
966 else
967 pos = 4;
968
35e7de17 969 values[pos] = (float)orientation;
94ffdf17 970
971 for (pos = 0; pos < 5; pos ++)
972 {
973 bufptr = _cupsStrFormatd(bufptr, bufend, values[pos], loc);
974 *bufptr++ = '\n';
975 }
976
977 if (!choices[i]->code)
978 {
979 /*
980 * This can happen with certain buggy PPD files that don't include
981 * a CustomPageSize command sequence... We just use a generic
982 * Level 2 command sequence...
983 */
984
985 strlcpy(bufptr, ppd_custom_code, bufend - bufptr + 1);
986 bufptr += strlen(bufptr);
987 }
988 }
c6fab96f 989 else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
c53265fe 990 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
94ffdf17 991 != NULL)
992 {
993 /*
994 * Custom option...
995 */
996
997 const char *s; /* Pointer into string value */
98d1b1a1 998 cups_array_t *params; /* Parameters in the correct output order */
94ffdf17 999
1000
98d1b1a1 1001 params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
1002
1003 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
1004 cparam;
1005 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
1006 cupsArrayAdd(params, cparam);
1007
94ffdf17 1008 snprintf(bufptr, bufend - bufptr + 1,
1009 "%%%%BeginFeature: *Custom%s True\n", coption->keyword);
1010 bufptr += strlen(bufptr);
1011
98d1b1a1 1012 for (cparam = (ppd_cparam_t *)cupsArrayFirst(params);
94ffdf17 1013 cparam;
98d1b1a1 1014 cparam = (ppd_cparam_t *)cupsArrayNext(params))
94ffdf17 1015 {
1016 switch (cparam->type)
1017 {
1018 case PPD_CUSTOM_CURVE :
1019 case PPD_CUSTOM_INVCURVE :
1020 case PPD_CUSTOM_POINTS :
1021 case PPD_CUSTOM_REAL :
1022 bufptr = _cupsStrFormatd(bufptr, bufend,
1023 cparam->current.custom_real, loc);
1024 *bufptr++ = '\n';
1025 break;
1026
1027 case PPD_CUSTOM_INT :
1028 snprintf(bufptr, bufend - bufptr + 1, "%d\n",
1029 cparam->current.custom_int);
1030 bufptr += strlen(bufptr);
1031 break;
1032
1033 case PPD_CUSTOM_PASSCODE :
1034 case PPD_CUSTOM_PASSWORD :
1035 case PPD_CUSTOM_STRING :
1036 *bufptr++ = '(';
1037
c9b4942e 1038 if (cparam->current.custom_string)
1039 {
1040 for (s = cparam->current.custom_string; *s; s ++)
94ffdf17 1041 {
c9b4942e 1042 if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
1043 {
1044 snprintf(bufptr, bufend - bufptr + 1, "\\%03o", *s & 255);
1045 bufptr += strlen(bufptr);
1046 }
1047 else
1048 *bufptr++ = *s;
94ffdf17 1049 }
c9b4942e 1050 }
94ffdf17 1051
1052 *bufptr++ = ')';
1053 *bufptr++ = '\n';
1054 break;
1055 }
1056 }
98d1b1a1 1057
1058 cupsArrayDelete(params);
94ffdf17 1059 }
1060 else
1061 {
1062 snprintf(bufptr, bufend - bufptr + 1, "%%%%BeginFeature: *%s %s\n",
1063 choices[i]->option->keyword, choices[i]->choice);
1064 bufptr += strlen(bufptr);
1065 }
1066
1067 if (choices[i]->code && choices[i]->code[0])
1068 {
35e7de17 1069 j = (int)strlen(choices[i]->code);
94ffdf17 1070 memcpy(bufptr, choices[i]->code, j);
1071 bufptr += j;
1072
1073 if (choices[i]->code[j - 1] != '\n')
1074 *bufptr++ = '\n';
1075 }
1076
1077 strlcpy(bufptr, "%%EndFeature\n"
1078 "} stopped cleartomark\n", bufend - bufptr + 1);
1079 bufptr += strlen(bufptr);
d6b8e0f9 1080
571af0ea 1081 DEBUG_printf(("2ppdEmitString: Offset in string is %d...",
7e8f437e 1082 (int)(bufptr - buffer)));
94ffdf17 1083 }
1084 else
1085 {
1086 strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
1087 bufptr += strlen(bufptr);
1088 }
1089
1090 /*
1091 * Nul-terminate, free, and return...
1092 */
1093
1094 *bufptr = '\0';
1095
1096 free(choices);
1097
1098 return (buffer);
1099}
1100
1101
98d1b1a1 1102/*
1103 * 'ppd_compare_cparams()' - Compare the order of two custom parameters.
1104 */
1105
1106static int /* O - Result of comparison */
1107ppd_compare_cparams(ppd_cparam_t *a, /* I - First parameter */
1108 ppd_cparam_t *b) /* I - Second parameter */
1109{
1110 return (a->order - b->order);
1111}
1112
1113
ed6078a4 1114/*
1115 * 'ppd_handle_media()' - Handle media selection...
1116 */
1117
1118static void
98d1b1a1 1119ppd_handle_media(ppd_file_t *ppd) /* I - PPD file */
ed6078a4 1120{
1121 ppd_choice_t *manual_feed, /* ManualFeed choice, if any */
6853f3f0 1122 *input_slot; /* InputSlot choice, if any */
ed6078a4 1123 ppd_size_t *size; /* Current media size */
1b98ab76 1124 ppd_attr_t *rpr; /* RequiresPageRegion value */
ed6078a4 1125
1126
1127 /*
e536ea8b 1128 * This function determines what page size code to use, if any, for the
1129 * current media size, InputSlot, and ManualFeed selections.
1130 *
2a09632b 1131 * We use the PageSize code if:
e536ea8b 1132 *
1133 * 1. A custom media size is selected.
1134 * 2. ManualFeed and InputSlot are not selected (or do not exist).
1135 * 3. ManualFeed is selected but is False and InputSlot is not selected or
1136 * the selection has no code - the latter check done to support "auto" or
1137 * "printer default" InputSlot options.
e536ea8b 1138 *
2a09632b 1139 * We use the PageRegion code if:
e536ea8b 1140 *
2a09632b 1141 * 4. RequiresPageRegion does not exist and the PPD contains cupsFilter
1142 * keywords, indicating this is a CUPS-based driver.
e536ea8b 1143 * 5. RequiresPageRegion exists for the selected InputSlot (or "All" for any
1144 * InputSlot or ManualFeed selection) and is True.
1145 *
1146 * If none of the 5 conditions are true, no page size code is used and we
1147 * unmark any existing PageSize or PageRegion choices.
ed6078a4 1148 */
1149
1150 if ((size = ppdPageSize(ppd, NULL)) == NULL)
1151 return;
1152
1153 manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed");
1154 input_slot = ppdFindMarkedChoice(ppd, "InputSlot");
1155
b3291c5a 1156 if (input_slot != NULL)
1157 rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice);
1158 else
1159 rpr = NULL;
1160
1161 if (!rpr)
1162 rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All");
1163
c6fab96f 1164 if (!_cups_strcasecmp(size->name, "Custom") ||
6853f3f0 1165 (!manual_feed && !input_slot) ||
c6fab96f 1166 (manual_feed && !_cups_strcasecmp(manual_feed->choice, "False") &&
c2c85a91 1167 (!input_slot || (input_slot->code && !input_slot->code[0]))) ||
f7aaa873 1168 (!rpr && (ppd->num_filters > 0 || ppdFindAttr(ppd, "cupsFilter2", NULL))))
ed6078a4 1169 {
1170 /*
e536ea8b 1171 * Use PageSize code...
ed6078a4 1172 */
1173
1174 ppdMarkOption(ppd, "PageSize", size->name);
1175 }
c6fab96f 1176 else if (rpr && rpr->value && !_cups_strcasecmp(rpr->value, "True"))
ed6078a4 1177 {
1178 /*
e536ea8b 1179 * Use PageRegion code...
ed6078a4 1180 */
1181
1182 ppdMarkOption(ppd, "PageRegion", size->name);
1183 }
e536ea8b 1184 else
1185 {
1186 /*
1187 * Do not use PageSize or PageRegion code...
1188 */
1189
1190 ppd_choice_t *page; /* PageSize/Region choice, if any */
1191
1192 if ((page = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
1193 {
1194 /*
1195 * Unmark PageSize...
1196 */
1197
1198 page->marked = 0;
1199 cupsArrayRemove(ppd->marked, page);
1200 }
1201
1202 if ((page = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
1203 {
1204 /*
1205 * Unmark PageRegion...
1206 */
1207
1208 page->marked = 0;
1209 cupsArrayRemove(ppd->marked, page);
1210 }
1211 }
ed6078a4 1212}
1213
1214
8c1333e2 1215/*
b2e10895 1216 * End of "$Id$".
ea930040 1217 */