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