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