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