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