1 /*
2  * PostScript filter for CUPS.
3  *
4  * Copyright 2007-2015 by Apple Inc.
5  * Copyright 1993-2007 by Easy Software Products.
6  *
7  * These coded instructions, statements, and computer programs are the
8  * property of Apple Inc. and are protected by Federal copyright
9  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
10  * which should have been included with this file.  If this file is
11  * missing or damaged, see the license at "http://www.cups.org/".
12  *
13  * This file is subject to the Apple OS-Developed Software exception.
14  */
15 
16 /*
17  * Include necessary headers...
18  */
19 
20 #include "common.h"
21 #include <limits.h>
22 #include <math.h>
23 #include <cups/file.h>
24 #include <cups/array.h>
25 #include <cups/language-private.h>
26 #include <signal.h>
27 
28 
29 /*
30  * Constants...
31  */
32 
33 #define PSTOPS_BORDERNONE	0	/* No border or hairline border */
34 #define PSTOPS_BORDERTHICK	1	/* Think border */
35 #define PSTOPS_BORDERSINGLE	2	/* Single-line hairline border */
36 #define PSTOPS_BORDERSINGLE2	3	/* Single-line thick border */
37 #define PSTOPS_BORDERDOUBLE	4	/* Double-line hairline border */
38 #define PSTOPS_BORDERDOUBLE2	5	/* Double-line thick border */
39 
40 #define PSTOPS_LAYOUT_LRBT	0	/* Left to right, bottom to top */
41 #define PSTOPS_LAYOUT_LRTB	1	/* Left to right, top to bottom */
42 #define PSTOPS_LAYOUT_RLBT	2	/* Right to left, bottom to top */
43 #define PSTOPS_LAYOUT_RLTB	3	/* Right to left, top to bottom */
44 #define PSTOPS_LAYOUT_BTLR	4	/* Bottom to top, left to right */
45 #define PSTOPS_LAYOUT_TBLR	5	/* Top to bottom, left to right */
46 #define PSTOPS_LAYOUT_BTRL	6	/* Bottom to top, right to left */
47 #define PSTOPS_LAYOUT_TBRL	7	/* Top to bottom, right to left */
48 
49 #define PSTOPS_LAYOUT_NEGATEY	1	/* The bits for the layout */
50 #define PSTOPS_LAYOUT_NEGATEX	2	/* definitions above... */
51 #define PSTOPS_LAYOUT_VERTICAL	4
52 
53 
54 /*
55  * Types...
56  */
57 
58 typedef struct				/**** Page information ****/
59 {
60   char		*label;			/* Page label */
61   int		bounding_box[4];	/* PageBoundingBox */
62   off_t		offset;			/* Offset to start of page */
63   ssize_t	length;			/* Number of bytes for page */
64   int		num_options;		/* Number of options for this page */
65   cups_option_t	*options;		/* Options for this page */
66 } pstops_page_t;
67 
68 typedef struct				/**** Document information ****/
69 {
70   int		page;			/* Current page */
71   int		bounding_box[4];	/* BoundingBox from header */
72   int		new_bounding_box[4];	/* New composite bounding box */
73   int		num_options;		/* Number of document-wide options */
74   cups_option_t	*options;		/* Document-wide options */
75   int		normal_landscape,	/* Normal rotation for landscape? */
76 		saw_eof,		/* Saw the %%EOF comment? */
77 		slow_collate,		/* Collate copies by hand? */
78 		slow_duplex,		/* Duplex pages slowly? */
79 		slow_order,		/* Reverse pages slowly? */
80 		use_ESPshowpage;	/* Use ESPshowpage? */
81   cups_array_t	*pages;			/* Pages in document */
82   cups_file_t	*temp;			/* Temporary file, if any */
83   char		tempfile[1024];		/* Temporary filename */
84   int		job_id;			/* Job ID */
85   const char	*user,			/* User name */
86 		*title;			/* Job name */
87   int		copies;			/* Number of copies */
88   const char	*ap_input_slot,		/* AP_FIRSTPAGE_InputSlot value */
89 		*ap_manual_feed,	/* AP_FIRSTPAGE_ManualFeed value */
90 		*ap_media_color,	/* AP_FIRSTPAGE_MediaColor value */
91 		*ap_media_type,		/* AP_FIRSTPAGE_MediaType value */
92 		*ap_page_region,	/* AP_FIRSTPAGE_PageRegion value */
93 		*ap_page_size;		/* AP_FIRSTPAGE_PageSize value */
94   int		collate,		/* Collate copies? */
95 		emit_jcl,		/* Emit JCL commands? */
96 		fit_to_page;		/* Fit pages to media */
97   const char	*input_slot,		/* InputSlot value */
98 		*manual_feed,		/* ManualFeed value */
99 		*media_color,		/* MediaColor value */
100 		*media_type,		/* MediaType value */
101 		*page_region,		/* PageRegion value */
102 		*page_size;		/* PageSize value */
103   int		mirror,			/* doc->mirror/mirror pages */
104 		number_up,		/* Number of pages on each sheet */
105 		number_up_layout,	/* doc->number_up_layout of N-up pages */
106 		output_order,		/* Requested reverse output order? */
107 		page_border;		/* doc->page_border around pages */
108   const char	*page_label,		/* page-label option, if any */
109 		*page_ranges,		/* page-ranges option, if any */
110 		*page_set;		/* page-set option, if any */
111 } pstops_doc_t;
112 
113 
114 /*
115  * Convenience macros...
116  */
117 
118 #define	is_first_page(p)	(doc->number_up == 1 || \
119 				 ((p) % doc->number_up) == 1)
120 #define	is_last_page(p)		(doc->number_up == 1 || \
121 				 ((p) % doc->number_up) == 0)
122 #define is_not_last_page(p)	(doc->number_up > 1 && \
123 				 ((p) % doc->number_up) != 0)
124 
125 
126 /*
127  * Local globals...
128  */
129 
130 static int		JobCanceled = 0;/* Set to 1 on SIGTERM */
131 
132 
133 /*
134  * Local functions...
135  */
136 
137 static pstops_page_t	*add_page(pstops_doc_t *doc, const char *label);
138 static void		cancel_job(int sig);
139 static int		check_range(pstops_doc_t *doc, int page);
140 static void		copy_bytes(cups_file_t *fp, off_t offset,
141 			           size_t length);
142 static ssize_t		copy_comments(cups_file_t *fp, pstops_doc_t *doc,
143 			              ppd_file_t *ppd, char *line,
144 				      ssize_t linelen, size_t linesize);
145 static void		copy_dsc(cups_file_t *fp, pstops_doc_t *doc,
146 			         ppd_file_t *ppd, char *line, ssize_t linelen,
147 				 size_t linesize);
148 static void		copy_non_dsc(cups_file_t *fp, pstops_doc_t *doc,
149 			             ppd_file_t *ppd, char *line,
150 				     ssize_t linelen, size_t linesize);
151 static ssize_t		copy_page(cups_file_t *fp, pstops_doc_t *doc,
152 			          ppd_file_t *ppd, int number, char *line,
153 				  ssize_t linelen, size_t linesize);
154 static ssize_t		copy_prolog(cups_file_t *fp, pstops_doc_t *doc,
155 			            ppd_file_t *ppd, char *line,
156 				    ssize_t linelen, size_t linesize);
157 static ssize_t		copy_setup(cups_file_t *fp, pstops_doc_t *doc,
158 			           ppd_file_t *ppd, char *line,
159 				   ssize_t linelen, size_t linesize);
160 static ssize_t		copy_trailer(cups_file_t *fp, pstops_doc_t *doc,
161 			             ppd_file_t *ppd, int number, char *line,
162 				     ssize_t linelen, size_t linesize);
163 static void		do_prolog(pstops_doc_t *doc, ppd_file_t *ppd);
164 static void 		do_setup(pstops_doc_t *doc, ppd_file_t *ppd);
165 static void		doc_printf(pstops_doc_t *doc, const char *format, ...)
166 			__attribute__ ((__format__ (__printf__, 2, 3)));
167 static void		doc_puts(pstops_doc_t *doc, const char *s);
168 static void		doc_write(pstops_doc_t *doc, const char *s, size_t len);
169 static void		end_nup(pstops_doc_t *doc, int number);
170 static int		include_feature(ppd_file_t *ppd, const char *line,
171 			                int num_options,
172 					cups_option_t **options);
173 static char		*parse_text(const char *start, char **end, char *buffer,
174 			            size_t bufsize);
175 static void		set_pstops_options(pstops_doc_t *doc, ppd_file_t *ppd,
176 			                   char *argv[], int num_options,
177 			                   cups_option_t *options);
178 static ssize_t		skip_page(cups_file_t *fp, char *line, ssize_t linelen,
179 				  size_t linesize);
180 static void		start_nup(pstops_doc_t *doc, int number,
181 				  int show_border, const int *bounding_box);
182 static void		write_label_prolog(pstops_doc_t *doc, const char *label,
183 			                   float bottom, float top,
184 					   float width);
185 static void		write_labels(pstops_doc_t *doc, int orient);
186 static void		write_options(pstops_doc_t  *doc, ppd_file_t *ppd,
187 			              int num_options, cups_option_t *options);
188 
189 
190 /*
191  * 'main()' - Main entry.
192  */
193 
194 int					/* O - Exit status */
main(int argc,char * argv[])195 main(int  argc,				/* I - Number of command-line args */
196      char *argv[])			/* I - Command-line arguments */
197 {
198   pstops_doc_t	doc;			/* Document information */
199   cups_file_t	*fp;			/* Print file */
200   ppd_file_t	*ppd;			/* PPD file */
201   int		num_options;		/* Number of print options */
202   cups_option_t	*options;		/* Print options */
203   char		line[8192];		/* Line buffer */
204   ssize_t	len;			/* Length of line buffer */
205 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
206   struct sigaction action;		/* Actions for POSIX signals */
207 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
208 
209 
210  /*
211   * Make sure status messages are not buffered...
212   */
213 
214   setbuf(stderr, NULL);
215 
216  /*
217   * Ignore broken pipe signals...
218   */
219 
220   signal(SIGPIPE, SIG_IGN);
221 
222  /*
223   * Check command-line...
224   */
225 
226   if (argc < 6 || argc > 7)
227   {
228     _cupsLangPrintf(stderr,
229                     _("Usage: %s job-id user title copies options [file]"),
230                     argv[0]);
231     return (1);
232   }
233 
234  /*
235   * Register a signal handler to cleanly cancel a job.
236   */
237 
238 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
239   sigset(SIGTERM, cancel_job);
240 #elif defined(HAVE_SIGACTION)
241   memset(&action, 0, sizeof(action));
242 
243   sigemptyset(&action.sa_mask);
244   action.sa_handler = cancel_job;
245   sigaction(SIGTERM, &action, NULL);
246 #else
247   signal(SIGTERM, cancel_job);
248 #endif /* HAVE_SIGSET */
249 
250  /*
251   * If we have 7 arguments, print the file named on the command-line.
252   * Otherwise, send stdin instead...
253   */
254 
255   if (argc == 6)
256     fp = cupsFileStdin();
257   else
258   {
259    /*
260     * Try to open the print file...
261     */
262 
263     if ((fp = cupsFileOpen(argv[6], "r")) == NULL)
264     {
265       if (!JobCanceled)
266       {
267         fprintf(stderr, "DEBUG: Unable to open \"%s\".\n", argv[6]);
268         _cupsLangPrintError("ERROR", _("Unable to open print file"));
269       }
270 
271       return (1);
272     }
273   }
274 
275  /*
276   * Read the first line to see if we have DSC comments...
277   */
278 
279   if ((len = (ssize_t)cupsFileGetLine(fp, line, sizeof(line))) == 0)
280   {
281     fputs("DEBUG: The print file is empty.\n", stderr);
282     return (1);
283   }
284 
285  /*
286   * Process command-line options...
287   */
288 
289   options     = NULL;
290   num_options = cupsParseOptions(argv[5], 0, &options);
291   ppd         = SetCommonOptions(num_options, options, 1);
292 
293   set_pstops_options(&doc, ppd, argv, num_options, options);
294 
295  /*
296   * Write any "exit server" options that have been selected...
297   */
298 
299   ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
300 
301  /*
302   * Write any JCL commands that are needed to print PostScript code...
303   */
304 
305   if (doc.emit_jcl)
306     ppdEmitJCL(ppd, stdout, doc.job_id, doc.user, doc.title);
307 
308  /*
309   * Start with a DSC header...
310   */
311 
312   puts("%!PS-Adobe-3.0");
313 
314  /*
315   * Skip leading PJL in the document...
316   */
317 
318   while (!strncmp(line, "\033%-12345X", 9) || !strncmp(line, "@PJL ", 5))
319   {
320    /*
321     * Yup, we have leading PJL fun, so skip it until we hit the line
322     * with "ENTER LANGUAGE"...
323     */
324 
325     fputs("DEBUG: Skipping PJL header...\n", stderr);
326 
327     while (strstr(line, "ENTER LANGUAGE") == NULL && strncmp(line, "%!", 2))
328       if ((len = (ssize_t)cupsFileGetLine(fp, line, sizeof(line))) == 0)
329         break;
330 
331     if (!strncmp(line, "%!", 2))
332       break;
333 
334     if ((len = (ssize_t)cupsFileGetLine(fp, line, sizeof(line))) == 0)
335       break;
336   }
337 
338  /*
339   * Now see if the document conforms to the Adobe Document Structuring
340   * Conventions...
341   */
342 
343   if (!strncmp(line, "%!PS-Adobe-", 11))
344   {
345    /*
346     * Yes, filter the document...
347     */
348 
349     copy_dsc(fp, &doc, ppd, line, len, sizeof(line));
350   }
351   else
352   {
353    /*
354     * No, display an error message and treat the file as if it contains
355     * a single page...
356     */
357 
358     copy_non_dsc(fp, &doc, ppd, line, len, sizeof(line));
359   }
360 
361  /*
362   * Send %%EOF as needed...
363   */
364 
365   if (!doc.saw_eof)
366     puts("%%EOF");
367 
368  /*
369   * End the job with the appropriate JCL command or CTRL-D...
370   */
371 
372   if (doc.emit_jcl)
373   {
374     if (ppd && ppd->jcl_end)
375       ppdEmitJCLEnd(ppd, stdout);
376     else
377       putchar(0x04);
378   }
379 
380  /*
381   * Close files and remove the temporary file if needed...
382   */
383 
384   if (doc.temp)
385   {
386     cupsFileClose(doc.temp);
387     unlink(doc.tempfile);
388   }
389 
390   ppdClose(ppd);
391   cupsFreeOptions(num_options, options);
392 
393   cupsFileClose(fp);
394 
395   return (0);
396 }
397 
398 
399 /*
400  * 'add_page()' - Add a page to the pages array.
401  */
402 
403 static pstops_page_t *			/* O - New page info object */
add_page(pstops_doc_t * doc,const char * label)404 add_page(pstops_doc_t *doc,		/* I - Document information */
405          const char   *label)		/* I - Page label */
406 {
407   pstops_page_t	*pageinfo;		/* New page info object */
408 
409 
410   if (!doc->pages)
411     doc->pages = cupsArrayNew(NULL, NULL);
412 
413   if (!doc->pages)
414   {
415     _cupsLangPrintError("EMERG", _("Unable to allocate memory for pages array"));
416     exit(1);
417   }
418 
419   if ((pageinfo = calloc(1, sizeof(pstops_page_t))) == NULL)
420   {
421     _cupsLangPrintError("EMERG", _("Unable to allocate memory for page info"));
422     exit(1);
423   }
424 
425   pageinfo->label  = strdup(label);
426   pageinfo->offset = cupsFileTell(doc->temp);
427 
428   cupsArrayAdd(doc->pages, pageinfo);
429 
430   doc->page ++;
431 
432   return (pageinfo);
433 }
434 
435 
436 /*
437  * 'cancel_job()' - Flag the job as canceled.
438  */
439 
440 static void
cancel_job(int sig)441 cancel_job(int sig)			/* I - Signal number (unused) */
442 {
443   (void)sig;
444 
445   JobCanceled = 1;
446 }
447 
448 
449 /*
450  * 'check_range()' - Check to see if the current page is selected for
451  *                   printing.
452  */
453 
454 static int				/* O - 1 if selected, 0 otherwise */
check_range(pstops_doc_t * doc,int page)455 check_range(pstops_doc_t *doc,		/* I - Document information */
456             int          page)		/* I - Page number */
457 {
458   const char	*range;			/* Pointer into range string */
459   int		lower, upper;		/* Lower and upper page numbers */
460 
461 
462   if (doc->page_set)
463   {
464    /*
465     * See if we only print even or odd pages...
466     */
467 
468     if (!_cups_strcasecmp(doc->page_set, "even") && (page & 1))
469       return (0);
470 
471     if (!_cups_strcasecmp(doc->page_set, "odd") && !(page & 1))
472       return (0);
473   }
474 
475   if (!doc->page_ranges)
476     return (1);				/* No range, print all pages... */
477 
478   for (range = doc->page_ranges; *range != '\0';)
479   {
480     if (*range == '-')
481     {
482       lower = 1;
483       range ++;
484       upper = (int)strtol(range, (char **)&range, 10);
485     }
486     else
487     {
488       lower = (int)strtol(range, (char **)&range, 10);
489 
490       if (*range == '-')
491       {
492         range ++;
493 	if (!isdigit(*range & 255))
494 	  upper = 65535;
495 	else
496 	  upper = (int)strtol(range, (char **)&range, 10);
497       }
498       else
499         upper = lower;
500     }
501 
502     if (page >= lower && page <= upper)
503       return (1);
504 
505     if (*range == ',')
506       range ++;
507     else
508       break;
509   }
510 
511   return (0);
512 }
513 
514 
515 /*
516  * 'copy_bytes()' - Copy bytes from the input file to stdout.
517  */
518 
519 static void
copy_bytes(cups_file_t * fp,off_t offset,size_t length)520 copy_bytes(cups_file_t *fp,		/* I - File to read from */
521            off_t       offset,		/* I - Offset to page data */
522            size_t      length)		/* I - Length of page data */
523 {
524   char		buffer[8192];		/* Data buffer */
525   ssize_t	nbytes;			/* Number of bytes read */
526   size_t	nleft;			/* Number of bytes left/remaining */
527 
528 
529   nleft = length;
530 
531   if (cupsFileSeek(fp, offset) < 0)
532   {
533     _cupsLangPrintError("ERROR", _("Unable to see in file"));
534     return;
535   }
536 
537   while (nleft > 0 || length == 0)
538   {
539     if (nleft > sizeof(buffer) || length == 0)
540       nbytes = sizeof(buffer);
541     else
542       nbytes = (ssize_t)nleft;
543 
544     if ((nbytes = cupsFileRead(fp, buffer, (size_t)nbytes)) < 1)
545       return;
546 
547     nleft -= (size_t)nbytes;
548 
549     fwrite(buffer, 1, (size_t)nbytes, stdout);
550   }
551 }
552 
553 
554 /*
555  * 'copy_comments()' - Copy all of the comments section.
556  *
557  * This function expects "line" to be filled with a comment line.
558  * On return, "line" will contain the next line in the file, if any.
559  */
560 
561 static ssize_t				/* O - Length of next line */
copy_comments(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,char * line,ssize_t linelen,size_t linesize)562 copy_comments(cups_file_t  *fp,		/* I - File to read from */
563               pstops_doc_t *doc,	/* I - Document info */
564 	      ppd_file_t   *ppd,	/* I - PPD file */
565               char         *line,	/* I - Line buffer */
566 	      ssize_t      linelen,	/* I - Length of initial line */
567 	      size_t       linesize)	/* I - Size of line buffer */
568 {
569   int	saw_bounding_box,		/* Saw %%BoundingBox: comment? */
570 	saw_for,			/* Saw %%For: comment? */
571 	saw_pages,			/* Saw %%Pages: comment? */
572 	saw_title;			/* Saw %%Title: comment? */
573 
574 
575  /*
576   * Loop until we see %%EndComments or a non-comment line...
577   */
578 
579   saw_bounding_box = 0;
580   saw_for          = 0;
581   saw_pages        = 0;
582   saw_title        = 0;
583 
584   while (line[0] == '%')
585   {
586    /*
587     * Strip trailing whitespace...
588     */
589 
590     while (linelen > 0)
591     {
592       linelen --;
593 
594       if (!isspace(line[linelen] & 255))
595         break;
596       else
597         line[linelen] = '\0';
598     }
599 
600    /*
601     * Log the header...
602     */
603 
604     fprintf(stderr, "DEBUG: %s\n", line);
605 
606    /*
607     * Pull the headers out...
608     */
609 
610     if (!strncmp(line, "%%Pages:", 8))
611     {
612       int	pages;			/* Number of pages */
613 
614       if (saw_pages)
615 	fputs("DEBUG: A duplicate %%Pages: comment was seen.\n", stderr);
616 
617       saw_pages = 1;
618 
619       if (Duplex && (pages = atoi(line + 8)) > 0 && pages <= doc->number_up)
620       {
621        /*
622         * Since we will only be printing on a single page, disable duplexing.
623 	*/
624 
625 	Duplex           = 0;
626 	doc->slow_duplex = 0;
627 
628 	if (cupsGetOption("sides", doc->num_options, doc->options))
629 	  doc->num_options = cupsAddOption("sides", "one-sided",
630 	                                   doc->num_options, &(doc->options));
631 
632 	if (cupsGetOption("Duplex", doc->num_options, doc->options))
633 	  doc->num_options = cupsAddOption("Duplex", "None",
634 	                                   doc->num_options, &(doc->options));
635 
636 	if (cupsGetOption("EFDuplex", doc->num_options, doc->options))
637 	  doc->num_options = cupsAddOption("EFDuplex", "None",
638 	                                   doc->num_options, &(doc->options));
639 
640 	if (cupsGetOption("EFDuplexing", doc->num_options, doc->options))
641 	  doc->num_options = cupsAddOption("EFDuplexing", "False",
642 	                                   doc->num_options, &(doc->options));
643 
644 	if (cupsGetOption("KD03Duplex", doc->num_options, doc->options))
645 	  doc->num_options = cupsAddOption("KD03Duplex", "None",
646 	                                   doc->num_options, &(doc->options));
647 
648 	if (cupsGetOption("JCLDuplex", doc->num_options, doc->options))
649 	  doc->num_options = cupsAddOption("JCLDuplex", "None",
650 	                                   doc->num_options, &(doc->options));
651 
652 	ppdMarkOption(ppd, "Duplex", "None");
653 	ppdMarkOption(ppd, "EFDuplex", "None");
654 	ppdMarkOption(ppd, "EFDuplexing", "False");
655 	ppdMarkOption(ppd, "KD03Duplex", "None");
656 	ppdMarkOption(ppd, "JCLDuplex", "None");
657       }
658     }
659     else if (!strncmp(line, "%%BoundingBox:", 14))
660     {
661       if (saw_bounding_box)
662 	fputs("DEBUG: A duplicate %%BoundingBox: comment was seen.\n", stderr);
663       else if (strstr(line + 14, "(atend)"))
664       {
665        /*
666         * Do nothing for now but use the default imageable area...
667 	*/
668       }
669       else if (sscanf(line + 14, "%d%d%d%d", doc->bounding_box + 0,
670 	              doc->bounding_box + 1, doc->bounding_box + 2,
671 		      doc->bounding_box + 3) != 4)
672       {
673 	fputs("DEBUG: A bad %%BoundingBox: comment was seen.\n", stderr);
674 
675 	doc->bounding_box[0] = (int)PageLeft;
676 	doc->bounding_box[1] = (int)PageBottom;
677 	doc->bounding_box[2] = (int)PageRight;
678 	doc->bounding_box[3] = (int)PageTop;
679       }
680 
681       saw_bounding_box = 1;
682     }
683     else if (!strncmp(line, "%%For:", 6))
684     {
685       saw_for = 1;
686       doc_printf(doc, "%s\n", line);
687     }
688     else if (!strncmp(line, "%%Title:", 8))
689     {
690       saw_title = 1;
691       doc_printf(doc, "%s\n", line);
692     }
693     else if (!strncmp(line, "%cupsRotation:", 14))
694     {
695      /*
696       * Reset orientation of document?
697       */
698 
699       int orient = (atoi(line + 14) / 90) & 3;
700 
701       if (orient != Orientation)
702       {
703        /*
704         * Yes, update things so that the pages come out right...
705 	*/
706 
707 	Orientation = (4 - Orientation + orient) & 3;
708 	UpdatePageVars();
709 	Orientation = orient;
710       }
711     }
712     else if (!strcmp(line, "%%EndComments"))
713     {
714       linelen = (ssize_t)cupsFileGetLine(fp, line, linesize);
715       break;
716     }
717     else if (strncmp(line, "%!", 2) && strncmp(line, "%cups", 5))
718       doc_printf(doc, "%s\n", line);
719 
720     if ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) == 0)
721       break;
722   }
723 
724   if (!saw_bounding_box)
725     fputs("DEBUG: There wasn't a %%BoundingBox: comment in the header.\n",
726           stderr);
727 
728   if (!saw_pages)
729     fputs("DEBUG: There wasn't a %%Pages: comment in the header.\n", stderr);
730 
731   if (!saw_for)
732     WriteTextComment("For", doc->user);
733 
734   if (!saw_title)
735     WriteTextComment("Title", doc->title);
736 
737   if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
738   {
739    /*
740     * Tell the document processor the copy and duplex options
741     * that are required...
742     */
743 
744     doc_printf(doc, "%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
745                doc->collate ? " collate" : "",
746 	       Duplex ? " duplex" : "");
747 
748    /*
749     * Apple uses RBI comments for various non-PPD options...
750     */
751 
752     doc_printf(doc, "%%RBINumCopies: %d\n", doc->copies);
753   }
754   else
755   {
756    /*
757     * Tell the document processor the duplex option that is required...
758     */
759 
760     if (Duplex)
761       doc_puts(doc, "%%Requirements: duplex\n");
762 
763    /*
764     * Apple uses RBI comments for various non-PPD options...
765     */
766 
767     doc_puts(doc, "%RBINumCopies: 1\n");
768   }
769 
770   doc_puts(doc, "%%Pages: (atend)\n");
771   doc_puts(doc, "%%BoundingBox: (atend)\n");
772   doc_puts(doc, "%%EndComments\n");
773 
774   return (linelen);
775 }
776 
777 
778 /*
779  * 'copy_dsc()' - Copy a DSC-conforming document.
780  *
781  * This function expects "line" to be filled with the %!PS-Adobe comment line.
782  */
783 
784 static void
copy_dsc(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,char * line,ssize_t linelen,size_t linesize)785 copy_dsc(cups_file_t  *fp,		/* I - File to read from */
786          pstops_doc_t *doc,		/* I - Document info */
787          ppd_file_t   *ppd,		/* I - PPD file */
788 	 char         *line,		/* I - Line buffer */
789 	 ssize_t      linelen,		/* I - Length of initial line */
790 	 size_t       linesize)		/* I - Size of line buffer */
791 {
792   int		number;			/* Page number */
793   pstops_page_t	*pageinfo;		/* Page information */
794 
795 
796  /*
797   * Make sure we use ESPshowpage for EPS files...
798   */
799 
800   if (strstr(line, "EPSF"))
801   {
802     doc->use_ESPshowpage = 1;
803     doc->number_up       = 1;
804   }
805 
806  /*
807   * Start sending the document with any commands needed...
808   */
809 
810   fprintf(stderr, "DEBUG: Before copy_comments - %s", line);
811   linelen = copy_comments(fp, doc, ppd, line, linelen, linesize);
812 
813  /*
814   * Now find the prolog section, if any...
815   */
816 
817   fprintf(stderr, "DEBUG: Before copy_prolog - %s", line);
818   linelen = copy_prolog(fp, doc, ppd, line, linelen, linesize);
819 
820  /*
821   * Then the document setup section...
822   */
823 
824   fprintf(stderr, "DEBUG: Before copy_setup - %s", line);
825   linelen = copy_setup(fp, doc, ppd, line, linelen, linesize);
826 
827  /*
828   * Copy until we see %%Page:...
829   */
830 
831   while (strncmp(line, "%%Page:", 7) && strncmp(line, "%%Trailer", 9))
832   {
833     doc_write(doc, line, (size_t)linelen);
834 
835     if ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) == 0)
836       break;
837   }
838 
839  /*
840   * Then process pages until we have no more...
841   */
842 
843   number = 0;
844 
845   fprintf(stderr, "DEBUG: Before page loop - %s", line);
846   while (!strncmp(line, "%%Page:", 7))
847   {
848     if (JobCanceled)
849       break;
850 
851     number ++;
852 
853     if (check_range(doc, (number - 1) / doc->number_up + 1))
854     {
855       fprintf(stderr, "DEBUG: Copying page %d...\n", number);
856       linelen = copy_page(fp, doc, ppd, number, line, linelen, linesize);
857     }
858     else
859     {
860       fprintf(stderr, "DEBUG: Skipping page %d...\n", number);
861       linelen = skip_page(fp, line, linelen, linesize);
862     }
863   }
864 
865  /*
866   * Finish up the last page(s)...
867   */
868 
869   if (number && is_not_last_page(number) && cupsArrayLast(doc->pages) &&
870       check_range(doc, (number - 1) / doc->number_up + 1))
871   {
872     pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
873 
874     start_nup(doc, doc->number_up, 0, doc->bounding_box);
875     doc_puts(doc, "showpage\n");
876     end_nup(doc, doc->number_up);
877 
878     pageinfo->length = (ssize_t)(cupsFileTell(doc->temp) - pageinfo->offset);
879   }
880 
881   if (doc->slow_duplex && (doc->page & 1))
882   {
883    /*
884     * Make sure we have an even number of pages...
885     */
886 
887     pageinfo = add_page(doc, "(filler)");
888 
889     if (!doc->slow_order)
890     {
891       if (!ppd || !ppd->num_filters)
892 	fprintf(stderr, "PAGE: %d %d\n", doc->page,
893         	doc->slow_collate ? 1 : doc->copies);
894 
895       printf("%%%%Page: (filler) %d\n", doc->page);
896     }
897 
898     start_nup(doc, doc->number_up, 0, doc->bounding_box);
899     doc_puts(doc, "showpage\n");
900     end_nup(doc, doc->number_up);
901 
902     pageinfo->length = (ssize_t)(cupsFileTell(doc->temp) - pageinfo->offset);
903   }
904 
905  /*
906   * Make additional copies as necessary...
907   */
908 
909   number = doc->slow_order ? 0 : doc->page;
910 
911   if (doc->temp && !JobCanceled && cupsArrayCount(doc->pages) > 0)
912   {
913     int	copy;				/* Current copy */
914 
915 
916    /*
917     * Reopen the temporary file for reading...
918     */
919 
920     cupsFileClose(doc->temp);
921 
922     doc->temp = cupsFileOpen(doc->tempfile, "r");
923 
924    /*
925     * Make the copies...
926     */
927 
928     if (doc->slow_collate)
929       copy = !doc->slow_order;
930     else
931       copy = doc->copies - 1;
932 
933     for (; copy < doc->copies; copy ++)
934     {
935       if (JobCanceled)
936 	break;
937 
938      /*
939       * Send end-of-job stuff followed by any start-of-job stuff required
940       * for the JCL options...
941       */
942 
943       if (number && doc->emit_jcl && ppd && ppd->jcl_end)
944       {
945        /*
946         * Send the trailer...
947 	*/
948 
949         puts("%%Trailer");
950 	printf("%%%%Pages: %d\n", cupsArrayCount(doc->pages));
951 	if (doc->number_up > 1 || doc->fit_to_page)
952 	  printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
953 		 PageLeft, PageBottom, PageRight, PageTop);
954 	else
955 	  printf("%%%%BoundingBox: %d %d %d %d\n",
956 		 doc->new_bounding_box[0], doc->new_bounding_box[1],
957 		 doc->new_bounding_box[2], doc->new_bounding_box[3]);
958         puts("%%EOF");
959 
960        /*
961         * Start a new document...
962 	*/
963 
964         ppdEmitJCLEnd(ppd, stdout);
965         ppdEmitJCL(ppd, stdout, doc->job_id, doc->user, doc->title);
966 
967 	puts("%!PS-Adobe-3.0");
968 
969 	number = 0;
970       }
971 
972      /*
973       * Copy the prolog as needed...
974       */
975 
976       if (!number)
977       {
978         pageinfo = (pstops_page_t *)cupsArrayFirst(doc->pages);
979 	copy_bytes(doc->temp, 0, (size_t)pageinfo->offset);
980       }
981 
982      /*
983       * Then copy all of the pages...
984       */
985 
986       pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayLast(doc->pages) :
987                                    (pstops_page_t *)cupsArrayFirst(doc->pages);
988 
989       while (pageinfo)
990       {
991         if (JobCanceled)
992 	  break;
993 
994         number ++;
995 
996 	if (!ppd || !ppd->num_filters)
997 	  fprintf(stderr, "PAGE: %d %d\n", number,
998 	          doc->slow_collate ? 1 : doc->copies);
999 
1000 	if (doc->number_up > 1)
1001 	{
1002 	  printf("%%%%Page: (%d) %d\n", number, number);
1003 	  printf("%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
1004 		 PageLeft, PageBottom, PageRight, PageTop);
1005 	}
1006 	else
1007 	{
1008           printf("%%%%Page: %s %d\n", pageinfo->label, number);
1009 	  printf("%%%%PageBoundingBox: %d %d %d %d\n",
1010 		 pageinfo->bounding_box[0], pageinfo->bounding_box[1],
1011 		 pageinfo->bounding_box[2], pageinfo->bounding_box[3]);
1012 	}
1013 
1014 	copy_bytes(doc->temp, pageinfo->offset, (size_t)pageinfo->length);
1015 
1016 	pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayPrev(doc->pages) :
1017                                      (pstops_page_t *)cupsArrayNext(doc->pages);
1018       }
1019     }
1020   }
1021 
1022  /*
1023   * Restore the old showpage operator as needed...
1024   */
1025 
1026   if (doc->use_ESPshowpage)
1027     puts("userdict/showpage/ESPshowpage load put\n");
1028 
1029  /*
1030   * Write/copy the trailer...
1031   */
1032 
1033   if (!JobCanceled)
1034     copy_trailer(fp, doc, ppd, number, line, linelen, linesize);
1035 }
1036 
1037 
1038 /*
1039  * 'copy_non_dsc()' - Copy a document that does not conform to the DSC.
1040  *
1041  * This function expects "line" to be filled with the %! comment line.
1042  */
1043 
1044 static void
copy_non_dsc(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,char * line,ssize_t linelen,size_t linesize)1045 copy_non_dsc(cups_file_t  *fp,		/* I - File to read from */
1046              pstops_doc_t *doc,		/* I - Document info */
1047              ppd_file_t   *ppd,		/* I - PPD file */
1048 	     char         *line,	/* I - Line buffer */
1049 	     ssize_t      linelen,	/* I - Length of initial line */
1050 	     size_t       linesize)	/* I - Size of line buffer */
1051 {
1052   int		copy;			/* Current copy */
1053   char		buffer[8192];		/* Copy buffer */
1054   ssize_t	bytes;			/* Number of bytes copied */
1055 
1056 
1057   (void)linesize;
1058 
1059  /*
1060   * First let the user know that they are attempting to print a file
1061   * that may not print correctly...
1062   */
1063 
1064   fputs("DEBUG: This document does not conform to the Adobe Document "
1065         "Structuring Conventions and may not print correctly.\n", stderr);
1066 
1067  /*
1068   * Then write a standard DSC comment section...
1069   */
1070 
1071   printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", PageLeft, PageBottom,
1072          PageRight, PageTop);
1073 
1074   if (doc->slow_collate && doc->copies > 1)
1075     printf("%%%%Pages: %d\n", doc->copies);
1076   else
1077     puts("%%Pages: 1");
1078 
1079   WriteTextComment("For", doc->user);
1080   WriteTextComment("Title", doc->title);
1081 
1082   if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
1083   {
1084    /*
1085     * Tell the document processor the copy and duplex options
1086     * that are required...
1087     */
1088 
1089     printf("%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
1090            doc->collate ? " collate" : "",
1091 	   Duplex ? " duplex" : "");
1092 
1093    /*
1094     * Apple uses RBI comments for various non-PPD options...
1095     */
1096 
1097     printf("%%RBINumCopies: %d\n", doc->copies);
1098   }
1099   else
1100   {
1101    /*
1102     * Tell the document processor the duplex option that is required...
1103     */
1104 
1105     if (Duplex)
1106       puts("%%Requirements: duplex");
1107 
1108    /*
1109     * Apple uses RBI comments for various non-PPD options...
1110     */
1111 
1112     puts("%RBINumCopies: 1");
1113   }
1114 
1115   puts("%%EndComments");
1116 
1117  /*
1118   * Then the prolog...
1119   */
1120 
1121   puts("%%BeginProlog");
1122 
1123   do_prolog(doc, ppd);
1124 
1125   puts("%%EndProlog");
1126 
1127  /*
1128   * Then the setup section...
1129   */
1130 
1131   puts("%%BeginSetup");
1132 
1133   do_setup(doc, ppd);
1134 
1135   puts("%%EndSetup");
1136 
1137  /*
1138   * Finally, embed a copy of the file inside a %%Page...
1139   */
1140 
1141   if (!ppd || !ppd->num_filters)
1142     fprintf(stderr, "PAGE: 1 %d\n", doc->temp ? 1 : doc->copies);
1143 
1144   puts("%%Page: 1 1");
1145   puts("%%BeginPageSetup");
1146   ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
1147   puts("%%EndPageSetup");
1148   puts("%%BeginDocument: nondsc");
1149 
1150   fwrite(line, (size_t)linelen, 1, stdout);
1151 
1152   if (doc->temp)
1153     cupsFileWrite(doc->temp, line, (size_t)linelen);
1154 
1155   while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
1156   {
1157     fwrite(buffer, 1, (size_t)bytes, stdout);
1158 
1159     if (doc->temp)
1160       cupsFileWrite(doc->temp, buffer, (size_t)bytes);
1161   }
1162 
1163   puts("%%EndDocument");
1164 
1165   if (doc->use_ESPshowpage)
1166   {
1167     WriteLabels(Orientation);
1168     puts("ESPshowpage");
1169   }
1170 
1171   if (doc->temp && !JobCanceled)
1172   {
1173    /*
1174     * Reopen the temporary file for reading...
1175     */
1176 
1177     cupsFileClose(doc->temp);
1178 
1179     doc->temp = cupsFileOpen(doc->tempfile, "r");
1180 
1181    /*
1182     * Make the additional copies as needed...
1183     */
1184 
1185     for (copy = 1; copy < doc->copies; copy ++)
1186     {
1187       if (JobCanceled)
1188 	break;
1189 
1190       if (!ppd || !ppd->num_filters)
1191 	fputs("PAGE: 1 1\n", stderr);
1192 
1193       printf("%%%%Page: %d %d\n", copy + 1, copy + 1);
1194       puts("%%BeginPageSetup");
1195       ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
1196       puts("%%EndPageSetup");
1197       puts("%%BeginDocument: nondsc");
1198 
1199       copy_bytes(doc->temp, 0, 0);
1200 
1201       puts("%%EndDocument");
1202 
1203       if (doc->use_ESPshowpage)
1204       {
1205 	WriteLabels(Orientation);
1206         puts("ESPshowpage");
1207       }
1208     }
1209   }
1210 
1211  /*
1212   * Restore the old showpage operator as needed...
1213   */
1214 
1215   if (doc->use_ESPshowpage)
1216     puts("userdict/showpage/ESPshowpage load put\n");
1217 }
1218 
1219 
1220 /*
1221  * 'copy_page()' - Copy a page description.
1222  *
1223  * This function expects "line" to be filled with a %%Page comment line.
1224  * On return, "line" will contain the next line in the file, if any.
1225  */
1226 
1227 static ssize_t				/* O - Length of next line */
copy_page(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,int number,char * line,ssize_t linelen,size_t linesize)1228 copy_page(cups_file_t  *fp,		/* I - File to read from */
1229           pstops_doc_t *doc,		/* I - Document info */
1230           ppd_file_t   *ppd,		/* I - PPD file */
1231 	  int          number,		/* I - Current page number */
1232 	  char         *line,		/* I - Line buffer */
1233 	  ssize_t      linelen,		/* I - Length of initial line */
1234 	  size_t       linesize)	/* I - Size of line buffer */
1235 {
1236   char		label[256],		/* Page label string */
1237 		*ptr;			/* Pointer into line */
1238   int		level;			/* Embedded document level */
1239   pstops_page_t	*pageinfo;		/* Page information */
1240   int		first_page;		/* First page on N-up output? */
1241   int		has_page_setup = 0;	/* Does the page have %%Begin/EndPageSetup? */
1242   int		bounding_box[4];	/* PageBoundingBox */
1243 
1244 
1245  /*
1246   * Get the page label for this page...
1247   */
1248 
1249   first_page = is_first_page(number);
1250 
1251   if (!parse_text(line + 7, &ptr, label, sizeof(label)))
1252   {
1253     fputs("DEBUG: There was a bad %%Page: comment in the file.\n", stderr);
1254     label[0] = '\0';
1255     number   = doc->page;
1256   }
1257   else if (strtol(ptr, &ptr, 10) == LONG_MAX || !isspace(*ptr & 255))
1258   {
1259     fputs("DEBUG: There was a bad %%Page: comment in the file.\n", stderr);
1260     number = doc->page;
1261   }
1262 
1263  /*
1264   * Create or update the current output page...
1265   */
1266 
1267   if (first_page)
1268     pageinfo = add_page(doc, label);
1269   else
1270     pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
1271 
1272  /*
1273   * Handle first page override...
1274   */
1275 
1276   if (doc->ap_input_slot || doc->ap_manual_feed)
1277   {
1278     if ((doc->page == 1 && (!doc->slow_order || !Duplex)) ||
1279         (doc->page == 2 && doc->slow_order && Duplex))
1280     {
1281      /*
1282       * First page/sheet gets AP_FIRSTPAGE_* options...
1283       */
1284 
1285       pageinfo->num_options = cupsAddOption("InputSlot", doc->ap_input_slot,
1286                                             pageinfo->num_options,
1287 					    &(pageinfo->options));
1288       pageinfo->num_options = cupsAddOption("ManualFeed",
1289                                             doc->ap_input_slot ? "False" :
1290 					        doc->ap_manual_feed,
1291                                             pageinfo->num_options,
1292 					    &(pageinfo->options));
1293       pageinfo->num_options = cupsAddOption("MediaColor", doc->ap_media_color,
1294                                             pageinfo->num_options,
1295 					    &(pageinfo->options));
1296       pageinfo->num_options = cupsAddOption("MediaType", doc->ap_media_type,
1297                                             pageinfo->num_options,
1298 					    &(pageinfo->options));
1299       pageinfo->num_options = cupsAddOption("PageRegion", doc->ap_page_region,
1300                                             pageinfo->num_options,
1301 					    &(pageinfo->options));
1302       pageinfo->num_options = cupsAddOption("PageSize", doc->ap_page_size,
1303                                             pageinfo->num_options,
1304 					    &(pageinfo->options));
1305     }
1306     else if (doc->page == (Duplex + 2))
1307     {
1308      /*
1309       * Second page/sheet gets default options...
1310       */
1311 
1312       pageinfo->num_options = cupsAddOption("InputSlot", doc->input_slot,
1313                                             pageinfo->num_options,
1314 					    &(pageinfo->options));
1315       pageinfo->num_options = cupsAddOption("ManualFeed",
1316                                             doc->input_slot ? "False" :
1317 					        doc->manual_feed,
1318                                             pageinfo->num_options,
1319 					    &(pageinfo->options));
1320       pageinfo->num_options = cupsAddOption("MediaColor", doc->media_color,
1321                                             pageinfo->num_options,
1322 					    &(pageinfo->options));
1323       pageinfo->num_options = cupsAddOption("MediaType", doc->media_type,
1324                                             pageinfo->num_options,
1325 					    &(pageinfo->options));
1326       pageinfo->num_options = cupsAddOption("PageRegion", doc->page_region,
1327                                             pageinfo->num_options,
1328 					    &(pageinfo->options));
1329       pageinfo->num_options = cupsAddOption("PageSize", doc->page_size,
1330                                             pageinfo->num_options,
1331 					    &(pageinfo->options));
1332     }
1333   }
1334 
1335  /*
1336   * Scan comments until we see something other than %%Page*: or
1337   * %%Include*...
1338   */
1339 
1340   memcpy(bounding_box, doc->bounding_box, sizeof(bounding_box));
1341 
1342   while ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) > 0)
1343   {
1344     if (!strncmp(line, "%%PageBoundingBox:", 18))
1345     {
1346      /*
1347       * %%PageBoundingBox: llx lly urx ury
1348       */
1349 
1350       if (sscanf(line + 18, "%d%d%d%d", bounding_box + 0,
1351                  bounding_box + 1, bounding_box + 2,
1352 		 bounding_box + 3) != 4)
1353       {
1354 	fputs("DEBUG: There was a bad %%PageBoundingBox: comment in the file.\n", stderr);
1355         memcpy(bounding_box, doc->bounding_box,
1356 	       sizeof(bounding_box));
1357       }
1358       else if (doc->number_up == 1 && !doc->fit_to_page  && Orientation)
1359       {
1360         int	temp_bbox[4];		/* Temporary bounding box */
1361 
1362 
1363         memcpy(temp_bbox, bounding_box, sizeof(temp_bbox));
1364 
1365         fprintf(stderr, "DEBUG: Orientation = %d\n", Orientation);
1366         fprintf(stderr, "DEBUG: original bounding_box = [ %d %d %d %d ]\n",
1367 		bounding_box[0], bounding_box[1],
1368 		bounding_box[2], bounding_box[3]);
1369         fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
1370 	        PageWidth, PageLength);
1371 
1372         switch (Orientation)
1373 	{
1374 	  case 1 : /* Landscape */
1375 	      bounding_box[0] = (int)(PageLength - temp_bbox[3]);
1376 	      bounding_box[1] = temp_bbox[0];
1377 	      bounding_box[2] = (int)(PageLength - temp_bbox[1]);
1378 	      bounding_box[3] = temp_bbox[2];
1379               break;
1380 
1381 	  case 2 : /* Reverse Portrait */
1382 	      bounding_box[0] = (int)(PageWidth - temp_bbox[2]);
1383 	      bounding_box[1] = (int)(PageLength - temp_bbox[3]);
1384 	      bounding_box[2] = (int)(PageWidth - temp_bbox[0]);
1385 	      bounding_box[3] = (int)(PageLength - temp_bbox[1]);
1386               break;
1387 
1388 	  case 3 : /* Reverse Landscape */
1389 	      bounding_box[0] = temp_bbox[1];
1390 	      bounding_box[1] = (int)(PageWidth - temp_bbox[2]);
1391 	      bounding_box[2] = temp_bbox[3];
1392 	      bounding_box[3] = (int)(PageWidth - temp_bbox[0]);
1393               break;
1394 	}
1395 
1396         fprintf(stderr, "DEBUG: updated bounding_box = [ %d %d %d %d ]\n",
1397 		bounding_box[0], bounding_box[1],
1398 		bounding_box[2], bounding_box[3]);
1399       }
1400     }
1401 #if 0
1402     else if (!strncmp(line, "%%PageCustomColors:", 19) ||
1403              !strncmp(line, "%%PageMedia:", 12) ||
1404 	     !strncmp(line, "%%PageOrientation:", 18) ||
1405 	     !strncmp(line, "%%PageProcessColors:", 20) ||
1406 	     !strncmp(line, "%%PageRequirements:", 18) ||
1407 	     !strncmp(line, "%%PageResources:", 16))
1408     {
1409      /*
1410       * Copy literal...
1411       */
1412     }
1413 #endif /* 0 */
1414     else if (!strncmp(line, "%%PageCustomColors:", 19))
1415     {
1416      /*
1417       * %%PageCustomColors: ...
1418       */
1419     }
1420     else if (!strncmp(line, "%%PageMedia:", 12))
1421     {
1422      /*
1423       * %%PageMedia: ...
1424       */
1425     }
1426     else if (!strncmp(line, "%%PageOrientation:", 18))
1427     {
1428      /*
1429       * %%PageOrientation: ...
1430       */
1431     }
1432     else if (!strncmp(line, "%%PageProcessColors:", 20))
1433     {
1434      /*
1435       * %%PageProcessColors: ...
1436       */
1437     }
1438     else if (!strncmp(line, "%%PageRequirements:", 18))
1439     {
1440      /*
1441       * %%PageRequirements: ...
1442       */
1443     }
1444     else if (!strncmp(line, "%%PageResources:", 16))
1445     {
1446      /*
1447       * %%PageResources: ...
1448       */
1449     }
1450     else if (!strncmp(line, "%%IncludeFeature:", 17))
1451     {
1452      /*
1453       * %%IncludeFeature: *MainKeyword OptionKeyword
1454       */
1455 
1456       if (doc->number_up == 1 &&!doc->fit_to_page)
1457 	pageinfo->num_options = include_feature(ppd, line,
1458 	                                        pageinfo->num_options,
1459                                         	&(pageinfo->options));
1460     }
1461     else if (!strncmp(line, "%%BeginPageSetup", 16))
1462     {
1463       has_page_setup = 1;
1464       break;
1465     }
1466     else
1467       break;
1468   }
1469 
1470   if (doc->number_up == 1)
1471   {
1472    /*
1473     * Update the document's composite and page bounding box...
1474     */
1475 
1476     memcpy(pageinfo->bounding_box, bounding_box,
1477            sizeof(pageinfo->bounding_box));
1478 
1479     if (bounding_box[0] < doc->new_bounding_box[0])
1480       doc->new_bounding_box[0] = bounding_box[0];
1481     if (bounding_box[1] < doc->new_bounding_box[1])
1482       doc->new_bounding_box[1] = bounding_box[1];
1483     if (bounding_box[2] > doc->new_bounding_box[2])
1484       doc->new_bounding_box[2] = bounding_box[2];
1485     if (bounding_box[3] > doc->new_bounding_box[3])
1486       doc->new_bounding_box[3] = bounding_box[3];
1487   }
1488 
1489  /*
1490   * Output the page header as needed...
1491   */
1492 
1493   if (!doc->slow_order && first_page)
1494   {
1495     if (!ppd || !ppd->num_filters)
1496       fprintf(stderr, "PAGE: %d %d\n", doc->page,
1497 	      doc->slow_collate ? 1 : doc->copies);
1498 
1499     if (doc->number_up > 1)
1500     {
1501       printf("%%%%Page: (%d) %d\n", doc->page, doc->page);
1502       printf("%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
1503 	     PageLeft, PageBottom, PageRight, PageTop);
1504     }
1505     else
1506     {
1507       printf("%%%%Page: %s %d\n", pageinfo->label, doc->page);
1508       printf("%%%%PageBoundingBox: %d %d %d %d\n",
1509 	     pageinfo->bounding_box[0], pageinfo->bounding_box[1],
1510 	     pageinfo->bounding_box[2], pageinfo->bounding_box[3]);
1511     }
1512   }
1513 
1514  /*
1515   * Copy any page setup commands...
1516   */
1517 
1518   if (first_page)
1519     doc_puts(doc, "%%BeginPageSetup\n");
1520 
1521   if (has_page_setup)
1522   {
1523     int	feature = 0;			/* In a Begin/EndFeature block? */
1524 
1525     while ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) > 0)
1526     {
1527       if (!strncmp(line, "%%EndPageSetup", 14))
1528 	break;
1529       else if (!strncmp(line, "%%BeginFeature:", 15))
1530       {
1531 	feature = 1;
1532 
1533 	if (doc->number_up > 1 || doc->fit_to_page)
1534 	  continue;
1535       }
1536       else if (!strncmp(line, "%%EndFeature", 12))
1537       {
1538 	feature = 0;
1539 
1540 	if (doc->number_up > 1 || doc->fit_to_page)
1541 	  continue;
1542       }
1543       else if (!strncmp(line, "%%IncludeFeature:", 17))
1544       {
1545 	pageinfo->num_options = include_feature(ppd, line,
1546 						pageinfo->num_options,
1547 						&(pageinfo->options));
1548 	continue;
1549       }
1550       else if (!strncmp(line, "%%Include", 9))
1551 	continue;
1552 
1553       if (line[0] != '%' && !feature)
1554         break;
1555 
1556       if (!feature || (doc->number_up == 1 && !doc->fit_to_page))
1557 	doc_write(doc, line, (size_t)linelen);
1558     }
1559 
1560    /*
1561     * Skip %%EndPageSetup...
1562     */
1563 
1564     if (linelen > 0 && !strncmp(line, "%%EndPageSetup", 14))
1565       linelen = (ssize_t)cupsFileGetLine(fp, line, linesize);
1566   }
1567 
1568   if (first_page)
1569   {
1570     char	*page_setup;		/* PageSetup commands to send */
1571 
1572 
1573     if (pageinfo->num_options > 0)
1574       write_options(doc, ppd, pageinfo->num_options, pageinfo->options);
1575 
1576    /*
1577     * Output commands for the current page...
1578     */
1579 
1580     page_setup = ppdEmitString(ppd, PPD_ORDER_PAGE, 0);
1581 
1582     if (page_setup)
1583     {
1584       doc_puts(doc, page_setup);
1585       free(page_setup);
1586     }
1587   }
1588 
1589  /*
1590   * Prep for the start of the page description...
1591   */
1592 
1593   start_nup(doc, number, 1, bounding_box);
1594 
1595   if (first_page)
1596     doc_puts(doc, "%%EndPageSetup\n");
1597 
1598  /*
1599   * Read the rest of the page description...
1600   */
1601 
1602   level = 0;
1603 
1604   do
1605   {
1606     if (level == 0 &&
1607         (!strncmp(line, "%%Page:", 7) ||
1608 	 !strncmp(line, "%%Trailer", 9) ||
1609 	 !strncmp(line, "%%EOF", 5)))
1610       break;
1611     else if (!strncmp(line, "%%BeginDocument", 15) ||
1612 	     !strncmp(line, "%ADO_BeginApplication", 21))
1613     {
1614       doc_write(doc, line, (size_t)linelen);
1615 
1616       level ++;
1617     }
1618     else if ((!strncmp(line, "%%EndDocument", 13) ||
1619 	      !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
1620     {
1621       doc_write(doc, line, (size_t)linelen);
1622 
1623       level --;
1624     }
1625     else if (!strncmp(line, "%%BeginBinary:", 14) ||
1626              (!strncmp(line, "%%BeginData:", 12) &&
1627 	      !strstr(line, "ASCII") && !strstr(line, "Hex")))
1628     {
1629      /*
1630       * Copy binary data...
1631       */
1632 
1633       int	bytes;			/* Bytes of data */
1634 
1635 
1636       doc_write(doc, line, (size_t)linelen);
1637 
1638       bytes = atoi(strchr(line, ':') + 1);
1639 
1640       while (bytes > 0)
1641       {
1642 	if ((size_t)bytes > linesize)
1643 	  linelen = cupsFileRead(fp, line, linesize);
1644 	else
1645 	  linelen = cupsFileRead(fp, line, (size_t)bytes);
1646 
1647 	if (linelen < 1)
1648 	{
1649 	  line[0] = '\0';
1650 	  perror("ERROR: Early end-of-file while reading binary data");
1651 	  return (0);
1652 	}
1653 
1654         doc_write(doc, line, (size_t)linelen);
1655 
1656 	bytes -= linelen;
1657       }
1658     }
1659     else
1660       doc_write(doc, line, (size_t)linelen);
1661   }
1662   while ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) > 0);
1663 
1664  /*
1665   * Finish up this page and return...
1666   */
1667 
1668   end_nup(doc, number);
1669 
1670   pageinfo->length = (ssize_t)(cupsFileTell(doc->temp) - pageinfo->offset);
1671 
1672   return (linelen);
1673 }
1674 
1675 
1676 /*
1677  * 'copy_prolog()' - Copy the document prolog section.
1678  *
1679  * This function expects "line" to be filled with a %%BeginProlog comment line.
1680  * On return, "line" will contain the next line in the file, if any.
1681  */
1682 
1683 static ssize_t				/* O - Length of next line */
copy_prolog(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,char * line,ssize_t linelen,size_t linesize)1684 copy_prolog(cups_file_t  *fp,		/* I - File to read from */
1685             pstops_doc_t *doc,		/* I - Document info */
1686             ppd_file_t   *ppd,		/* I - PPD file */
1687 	    char         *line,		/* I - Line buffer */
1688 	    ssize_t      linelen,	/* I - Length of initial line */
1689 	    size_t       linesize)	/* I - Size of line buffer */
1690 {
1691   while (strncmp(line, "%%BeginProlog", 13))
1692   {
1693     if (!strncmp(line, "%%BeginSetup", 12) || !strncmp(line, "%%Page:", 7))
1694       break;
1695 
1696     doc_write(doc, line, (size_t)linelen);
1697 
1698     if ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) == 0)
1699       break;
1700   }
1701 
1702   doc_puts(doc, "%%BeginProlog\n");
1703 
1704   do_prolog(doc, ppd);
1705 
1706   if (!strncmp(line, "%%BeginProlog", 13))
1707   {
1708     while ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) > 0)
1709     {
1710       if (!strncmp(line, "%%EndProlog", 11) ||
1711           !strncmp(line, "%%BeginSetup", 12) ||
1712           !strncmp(line, "%%Page:", 7))
1713         break;
1714 
1715       doc_write(doc, line, (size_t)linelen);
1716     }
1717 
1718     if (!strncmp(line, "%%EndProlog", 11))
1719       linelen = (ssize_t)cupsFileGetLine(fp, line, linesize);
1720     else
1721       fputs("DEBUG: The %%EndProlog comment is missing.\n", stderr);
1722   }
1723 
1724   doc_puts(doc, "%%EndProlog\n");
1725 
1726   return (linelen);
1727 }
1728 
1729 
1730 /*
1731  * 'copy_setup()' - Copy the document setup section.
1732  *
1733  * This function expects "line" to be filled with a %%BeginSetup comment line.
1734  * On return, "line" will contain the next line in the file, if any.
1735  */
1736 
1737 static ssize_t				/* O - Length of next line */
copy_setup(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,char * line,ssize_t linelen,size_t linesize)1738 copy_setup(cups_file_t  *fp,		/* I - File to read from */
1739            pstops_doc_t *doc,		/* I - Document info */
1740            ppd_file_t   *ppd,		/* I - PPD file */
1741 	   char         *line,		/* I - Line buffer */
1742 	   ssize_t      linelen,	/* I - Length of initial line */
1743 	   size_t       linesize)	/* I - Size of line buffer */
1744 {
1745   int		num_options;		/* Number of options */
1746   cups_option_t	*options;		/* Options */
1747 
1748 
1749   while (strncmp(line, "%%BeginSetup", 12))
1750   {
1751     if (!strncmp(line, "%%Page:", 7))
1752       break;
1753 
1754     doc_write(doc, line, (size_t)linelen);
1755 
1756     if ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) == 0)
1757       break;
1758   }
1759 
1760   doc_puts(doc, "%%BeginSetup\n");
1761 
1762   do_setup(doc, ppd);
1763 
1764   num_options = 0;
1765   options     = NULL;
1766 
1767   if (!strncmp(line, "%%BeginSetup", 12))
1768   {
1769     while (strncmp(line, "%%EndSetup", 10))
1770     {
1771       if (!strncmp(line, "%%Page:", 7))
1772         break;
1773       else if (!strncmp(line, "%%IncludeFeature:", 17))
1774       {
1775        /*
1776 	* %%IncludeFeature: *MainKeyword OptionKeyword
1777 	*/
1778 
1779         if (doc->number_up == 1 && !doc->fit_to_page)
1780 	  num_options = include_feature(ppd, line, num_options, &options);
1781       }
1782       else if (strncmp(line, "%%BeginSetup", 12))
1783         doc_write(doc, line, (size_t)linelen);
1784 
1785       if ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) == 0)
1786 	break;
1787     }
1788 
1789     if (!strncmp(line, "%%EndSetup", 10))
1790       linelen = (ssize_t)cupsFileGetLine(fp, line, linesize);
1791     else
1792       fputs("DEBUG: The %%EndSetup comment is missing.\n", stderr);
1793   }
1794 
1795   if (num_options > 0)
1796   {
1797     write_options(doc, ppd, num_options, options);
1798     cupsFreeOptions(num_options, options);
1799   }
1800 
1801   doc_puts(doc, "%%EndSetup\n");
1802 
1803   return (linelen);
1804 }
1805 
1806 
1807 /*
1808  * 'copy_trailer()' - Copy the document trailer.
1809  *
1810  * This function expects "line" to be filled with a %%Trailer comment line.
1811  * On return, "line" will contain the next line in the file, if any.
1812  */
1813 
1814 static ssize_t				/* O - Length of next line */
copy_trailer(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,int number,char * line,ssize_t linelen,size_t linesize)1815 copy_trailer(cups_file_t  *fp,		/* I - File to read from */
1816              pstops_doc_t *doc,		/* I - Document info */
1817              ppd_file_t   *ppd,		/* I - PPD file */
1818 	     int          number,	/* I - Number of pages */
1819 	     char         *line,	/* I - Line buffer */
1820 	     ssize_t      linelen,	/* I - Length of initial line */
1821 	     size_t       linesize)	/* I - Size of line buffer */
1822 {
1823  /*
1824   * Write the trailer comments...
1825   */
1826 
1827   (void)ppd;
1828 
1829   puts("%%Trailer");
1830 
1831   while (linelen > 0)
1832   {
1833     if (!strncmp(line, "%%EOF", 5))
1834       break;
1835     else if (strncmp(line, "%%Trailer", 9) &&
1836              strncmp(line, "%%Pages:", 8) &&
1837              strncmp(line, "%%BoundingBox:", 14))
1838       fwrite(line, 1, (size_t)linelen, stdout);
1839 
1840     linelen = (ssize_t)cupsFileGetLine(fp, line, linesize);
1841   }
1842 
1843   fprintf(stderr, "DEBUG: Wrote %d pages...\n", number);
1844 
1845   printf("%%%%Pages: %d\n", number);
1846   if (doc->number_up > 1 || doc->fit_to_page)
1847     printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
1848 	   PageLeft, PageBottom, PageRight, PageTop);
1849   else
1850     printf("%%%%BoundingBox: %d %d %d %d\n",
1851 	   doc->new_bounding_box[0], doc->new_bounding_box[1],
1852 	   doc->new_bounding_box[2], doc->new_bounding_box[3]);
1853 
1854   return (linelen);
1855 }
1856 
1857 
1858 /*
1859  * 'do_prolog()' - Send the necessary document prolog commands.
1860  */
1861 
1862 static void
do_prolog(pstops_doc_t * doc,ppd_file_t * ppd)1863 do_prolog(pstops_doc_t *doc,		/* I - Document information */
1864           ppd_file_t   *ppd)		/* I - PPD file */
1865 {
1866   char	*ps;				/* PS commands */
1867 
1868 
1869  /*
1870   * Send the document prolog commands...
1871   */
1872 
1873   if (ppd && ppd->patches)
1874   {
1875     doc_puts(doc, "%%BeginFeature: *JobPatchFile 1\n");
1876     doc_puts(doc, ppd->patches);
1877     doc_puts(doc, "\n%%EndFeature\n");
1878   }
1879 
1880   if ((ps = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL)
1881   {
1882     doc_puts(doc, ps);
1883     free(ps);
1884   }
1885 
1886  /*
1887   * Define ESPshowpage here so that applications that define their
1888   * own procedure to do a showpage pick it up...
1889   */
1890 
1891   if (doc->use_ESPshowpage)
1892     doc_puts(doc, "userdict/ESPshowpage/showpage load put\n"
1893 	          "userdict/showpage{}put\n");
1894 }
1895 
1896 
1897 /*
1898  * 'do_setup()' - Send the necessary document setup commands.
1899  */
1900 
1901 static void
do_setup(pstops_doc_t * doc,ppd_file_t * ppd)1902 do_setup(pstops_doc_t *doc,		/* I - Document information */
1903          ppd_file_t   *ppd)		/* I - PPD file */
1904 {
1905   char	*ps;				/* PS commands */
1906 
1907 
1908  /*
1909   * Disable CTRL-D so that embedded files don't cause printing
1910   * errors...
1911   */
1912 
1913   doc_puts(doc, "% Disable CTRL-D as an end-of-file marker...\n");
1914   doc_puts(doc, "userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n");
1915 
1916  /*
1917   * Mark job options...
1918   */
1919 
1920   cupsMarkOptions(ppd, doc->num_options, doc->options);
1921 
1922  /*
1923   * Send all the printer-specific setup commands...
1924   */
1925 
1926   if ((ps = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL)
1927   {
1928     doc_puts(doc, ps);
1929     free(ps);
1930   }
1931 
1932   if ((ps = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL)
1933   {
1934     doc_puts(doc, ps);
1935     free(ps);
1936   }
1937 
1938  /*
1939   * Set the number of copies for the job...
1940   */
1941 
1942   if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
1943   {
1944     doc_printf(doc, "%%RBIBeginNonPPDFeature: *NumCopies %d\n", doc->copies);
1945     doc_printf(doc,
1946                "%d/languagelevel where{pop languagelevel 2 ge}{false}ifelse\n"
1947                "{1 dict begin/NumCopies exch def currentdict end "
1948 	       "setpagedevice}\n"
1949 	       "{userdict/#copies 3 -1 roll put}ifelse\n", doc->copies);
1950     doc_puts(doc, "%RBIEndNonPPDFeature\n");
1951   }
1952 
1953  /*
1954   * If we are doing N-up printing, disable setpagedevice...
1955   */
1956 
1957   if (doc->number_up > 1)
1958   {
1959     doc_puts(doc, "userdict/CUPSsetpagedevice/setpagedevice load put\n");
1960     doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
1961   }
1962 
1963  /*
1964   * Make sure we have rectclip and rectstroke procedures of some sort...
1965   */
1966 
1967   doc_puts(doc,
1968            "% x y w h ESPrc - Clip to a rectangle.\n"
1969 	   "userdict/ESPrc/rectclip where{pop/rectclip load}\n"
1970 	   "{{newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
1971 	   "neg 0 rlineto closepath clip newpath}bind}ifelse put\n");
1972 
1973   doc_puts(doc,
1974            "% x y w h ESPrf - Fill a rectangle.\n"
1975 	   "userdict/ESPrf/rectfill where{pop/rectfill load}\n"
1976 	   "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
1977 	   "neg 0 rlineto closepath fill grestore}bind}ifelse put\n");
1978 
1979   doc_puts(doc,
1980            "% x y w h ESPrs - Stroke a rectangle.\n"
1981 	   "userdict/ESPrs/rectstroke where{pop/rectstroke load}\n"
1982 	   "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
1983 	   "neg 0 rlineto closepath stroke grestore}bind}ifelse put\n");
1984 
1985  /*
1986   * Write the page and label prologs...
1987   */
1988 
1989   if (doc->number_up == 2 || doc->number_up == 6)
1990   {
1991    /*
1992     * For 2- and 6-up output, rotate the labels to match the orientation
1993     * of the pages...
1994     */
1995 
1996     if (Orientation & 1)
1997       write_label_prolog(doc, doc->page_label, PageBottom,
1998                          PageWidth - PageLength + PageTop, PageLength);
1999     else
2000       write_label_prolog(doc, doc->page_label, PageLeft, PageRight,
2001                          PageLength);
2002   }
2003   else
2004     write_label_prolog(doc, doc->page_label, PageBottom, PageTop, PageWidth);
2005 }
2006 
2007 
2008 /*
2009  * 'doc_printf()' - Send a formatted string to stdout and/or the temp file.
2010  *
2011  * This function should be used for all page-level output that is affected
2012  * by ordering, collation, etc.
2013  */
2014 
2015 static void
doc_printf(pstops_doc_t * doc,const char * format,...)2016 doc_printf(pstops_doc_t *doc,		/* I - Document information */
2017            const char   *format,	/* I - Printf-style format string */
2018 	   ...)				/* I - Additional arguments as needed */
2019 {
2020   va_list	ap;			/* Pointer to arguments */
2021   char		buffer[1024];		/* Output buffer */
2022   ssize_t	bytes;			/* Number of bytes to write */
2023 
2024 
2025   va_start(ap, format);
2026   bytes = vsnprintf(buffer, sizeof(buffer), format, ap);
2027   va_end(ap);
2028 
2029   if ((size_t)bytes > sizeof(buffer))
2030   {
2031     _cupsLangPrintFilter(stderr, "ERROR",
2032                          _("Buffer overflow detected, aborting."));
2033     exit(1);
2034   }
2035 
2036   doc_write(doc, buffer, (size_t)bytes);
2037 }
2038 
2039 
2040 /*
2041  * 'doc_puts()' - Send a nul-terminated string to stdout and/or the temp file.
2042  *
2043  * This function should be used for all page-level output that is affected
2044  * by ordering, collation, etc.
2045  */
2046 
2047 static void
doc_puts(pstops_doc_t * doc,const char * s)2048 doc_puts(pstops_doc_t *doc,		/* I - Document information */
2049          const char   *s)		/* I - String to send */
2050 {
2051   doc_write(doc, s, strlen(s));
2052 }
2053 
2054 
2055 /*
2056  * 'doc_write()' - Send data to stdout and/or the temp file.
2057  */
2058 
2059 static void
doc_write(pstops_doc_t * doc,const char * s,size_t len)2060 doc_write(pstops_doc_t *doc,		/* I - Document information */
2061           const char   *s,		/* I - Data to send */
2062 	  size_t       len)		/* I - Number of bytes to send */
2063 {
2064   if (!doc->slow_order)
2065     fwrite(s, 1, len, stdout);
2066 
2067   if (doc->temp)
2068     cupsFileWrite(doc->temp, s, len);
2069 }
2070 
2071 
2072 /*
2073  * 'end_nup()' - End processing for N-up printing.
2074  */
2075 
2076 static void
end_nup(pstops_doc_t * doc,int number)2077 end_nup(pstops_doc_t *doc,		/* I - Document information */
2078         int          number)		/* I - Page number */
2079 {
2080   if (doc->number_up > 1)
2081     doc_puts(doc, "userdict/ESPsave get restore\n");
2082 
2083   switch (doc->number_up)
2084   {
2085     case 1 :
2086 	if (doc->use_ESPshowpage)
2087 	{
2088 	  write_labels(doc, Orientation);
2089           doc_puts(doc, "ESPshowpage\n");
2090 	}
2091 	break;
2092 
2093     case 2 :
2094     case 6 :
2095 	if (is_last_page(number) && doc->use_ESPshowpage)
2096 	{
2097 	  if (Orientation & 1)
2098 	  {
2099 	   /*
2100 	    * Rotate the labels back to portrait...
2101 	    */
2102 
2103 	    write_labels(doc, Orientation - 1);
2104 	  }
2105 	  else if (Orientation == 0)
2106 	  {
2107 	   /*
2108 	    * Rotate the labels to landscape...
2109 	    */
2110 
2111 	    write_labels(doc, doc->normal_landscape ? 1 : 3);
2112 	  }
2113 	  else
2114 	  {
2115 	   /*
2116 	    * Rotate the labels to landscape...
2117 	    */
2118 
2119 	    write_labels(doc, doc->normal_landscape ? 3 : 1);
2120 	  }
2121 
2122           doc_puts(doc, "ESPshowpage\n");
2123 	}
2124         break;
2125 
2126     default :
2127 	if (is_last_page(number) && doc->use_ESPshowpage)
2128 	{
2129 	  write_labels(doc, Orientation);
2130           doc_puts(doc, "ESPshowpage\n");
2131 	}
2132         break;
2133   }
2134 
2135   fflush(stdout);
2136 }
2137 
2138 
2139 /*
2140  * 'include_feature()' - Include a printer option/feature command.
2141  */
2142 
2143 static int				/* O  - New number of options */
include_feature(ppd_file_t * ppd,const char * line,int num_options,cups_option_t ** options)2144 include_feature(
2145     ppd_file_t    *ppd,			/* I  - PPD file */
2146     const char    *line,		/* I  - DSC line */
2147     int           num_options,		/* I  - Number of options */
2148     cups_option_t **options)		/* IO - Options */
2149 {
2150   char		name[255],		/* Option name */
2151 		value[255];		/* Option value */
2152   ppd_option_t	*option;		/* Option in file */
2153 
2154 
2155  /*
2156   * Get the "%%IncludeFeature: *Keyword OptionKeyword" values...
2157   */
2158 
2159   if (sscanf(line + 17, "%254s%254s", name, value) != 2)
2160   {
2161     fputs("DEBUG: The %%IncludeFeature: comment is not valid.\n", stderr);
2162     return (num_options);
2163   }
2164 
2165  /*
2166   * Find the option and choice...
2167   */
2168 
2169   if ((option = ppdFindOption(ppd, name + 1)) == NULL)
2170   {
2171     _cupsLangPrintFilter(stderr, "WARNING", _("Unknown option \"%s\"."),
2172                          name + 1);
2173     return (num_options);
2174   }
2175 
2176   if (option->section == PPD_ORDER_EXIT ||
2177       option->section == PPD_ORDER_JCL)
2178   {
2179     _cupsLangPrintFilter(stderr, "WARNING",
2180                          _("Option \"%s\" cannot be included via "
2181 			   "%%%%IncludeFeature."), name + 1);
2182     return (num_options);
2183   }
2184 
2185   if (!ppdFindChoice(option, value))
2186   {
2187     _cupsLangPrintFilter(stderr, "WARNING",
2188 			 _("Unknown choice \"%s\" for option \"%s\"."),
2189 			 value, name + 1);
2190     return (num_options);
2191   }
2192 
2193  /*
2194   * Add the option to the option array and return...
2195   */
2196 
2197   return (cupsAddOption(name + 1, value, num_options, options));
2198 }
2199 
2200 
2201 /*
2202  * 'parse_text()' - Parse a text value in a comment.
2203  *
2204  * This function parses a DSC text value as defined on page 36 of the
2205  * DSC specification.  Text values are either surrounded by parenthesis
2206  * or whitespace-delimited.
2207  *
2208  * The value returned is the literal characters for the entire text
2209  * string, including any parenthesis and escape characters.
2210  */
2211 
2212 static char *				/* O - Value or NULL on error */
parse_text(const char * start,char ** end,char * buffer,size_t bufsize)2213 parse_text(const char *start,		/* I - Start of text value */
2214            char       **end,		/* O - End of text value */
2215 	   char       *buffer,		/* I - Buffer */
2216            size_t     bufsize)		/* I - Size of buffer */
2217 {
2218   char	*bufptr,			/* Pointer in buffer */
2219 	*bufend;			/* End of buffer */
2220   int	level;				/* Parenthesis level */
2221 
2222 
2223  /*
2224   * Skip leading whitespace...
2225   */
2226 
2227   while (isspace(*start & 255))
2228     start ++;
2229 
2230  /*
2231   * Then copy the value...
2232   */
2233 
2234   level  = 0;
2235   bufptr = buffer;
2236   bufend = buffer + bufsize - 1;
2237 
2238   while (bufptr < bufend)
2239   {
2240     if (isspace(*start & 255) && !level)
2241       break;
2242 
2243     *bufptr++ = *start;
2244 
2245     if (*start == '(')
2246       level ++;
2247     else if (*start == ')')
2248     {
2249       if (!level)
2250       {
2251         start ++;
2252         break;
2253       }
2254       else
2255         level --;
2256     }
2257     else if (*start == '\\')
2258     {
2259      /*
2260       * Copy escaped character...
2261       */
2262 
2263       int	i;			/* Looping var */
2264 
2265 
2266       for (i = 1;
2267            i <= 3 && isdigit(start[i] & 255) && bufptr < bufend;
2268 	   *bufptr++ = start[i], i ++);
2269     }
2270 
2271     start ++;
2272   }
2273 
2274   *bufptr = '\0';
2275 
2276  /*
2277   * Return the value and new pointer into the line...
2278   */
2279 
2280   if (end)
2281     *end = (char *)start;
2282 
2283   if (bufptr == bufend)
2284     return (NULL);
2285   else
2286     return (buffer);
2287 }
2288 
2289 
2290 /*
2291  * 'set_pstops_options()' - Set pstops options.
2292  */
2293 
2294 static void
set_pstops_options(pstops_doc_t * doc,ppd_file_t * ppd,char * argv[],int num_options,cups_option_t * options)2295 set_pstops_options(
2296     pstops_doc_t  *doc,			/* I - Document information */
2297     ppd_file_t    *ppd,			/* I - PPD file */
2298     char          *argv[],		/* I - Command-line arguments */
2299     int           num_options,		/* I - Number of options */
2300     cups_option_t *options)		/* I - Options */
2301 {
2302   const char	*val;			/* Option value */
2303   int		intval;			/* Integer option value */
2304   ppd_attr_t	*attr;			/* PPD attribute */
2305   ppd_option_t	*option;		/* PPD option */
2306   ppd_choice_t	*choice;		/* PPD choice */
2307   const char	*content_type;		/* Original content type */
2308   int		max_copies;		/* Maximum number of copies supported */
2309 
2310 
2311  /*
2312   * Initialize document information structure...
2313   */
2314 
2315   memset(doc, 0, sizeof(pstops_doc_t));
2316 
2317   doc->job_id = atoi(argv[1]);
2318   doc->user   = argv[2];
2319   doc->title  = argv[3];
2320   doc->copies = atoi(argv[4]);
2321 
2322   if (ppd && ppd->landscape > 0)
2323     doc->normal_landscape = 1;
2324 
2325   doc->bounding_box[0] = (int)PageLeft;
2326   doc->bounding_box[1] = (int)PageBottom;
2327   doc->bounding_box[2] = (int)PageRight;
2328   doc->bounding_box[3] = (int)PageTop;
2329 
2330   doc->new_bounding_box[0] = INT_MAX;
2331   doc->new_bounding_box[1] = INT_MAX;
2332   doc->new_bounding_box[2] = INT_MIN;
2333   doc->new_bounding_box[3] = INT_MIN;
2334 
2335  /*
2336   * AP_FIRSTPAGE_* and the corresponding non-first-page options.
2337   */
2338 
2339   doc->ap_input_slot  = cupsGetOption("AP_FIRSTPAGE_InputSlot", num_options,
2340                                       options);
2341   doc->ap_manual_feed = cupsGetOption("AP_FIRSTPAGE_ManualFeed", num_options,
2342                                       options);
2343   doc->ap_media_color = cupsGetOption("AP_FIRSTPAGE_MediaColor", num_options,
2344                                       options);
2345   doc->ap_media_type  = cupsGetOption("AP_FIRSTPAGE_MediaType", num_options,
2346                                       options);
2347   doc->ap_page_region = cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options,
2348                                       options);
2349   doc->ap_page_size   = cupsGetOption("AP_FIRSTPAGE_PageSize", num_options,
2350                                       options);
2351 
2352   if ((choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
2353     doc->input_slot = choice->choice;
2354   if ((choice = ppdFindMarkedChoice(ppd, "ManualFeed")) != NULL)
2355     doc->manual_feed = choice->choice;
2356   if ((choice = ppdFindMarkedChoice(ppd, "MediaColor")) != NULL)
2357     doc->media_color = choice->choice;
2358   if ((choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL)
2359     doc->media_type = choice->choice;
2360   if ((choice = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
2361     doc->page_region = choice->choice;
2362   if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
2363     doc->page_size = choice->choice;
2364 
2365  /*
2366   * collate, multiple-document-handling
2367   */
2368 
2369   if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
2370   {
2371    /*
2372     * This IPP attribute is unnecessarily complicated...
2373     *
2374     *   single-document, separate-documents-collated-copies, and
2375     *   single-document-new-sheet all require collated copies.
2376     *
2377     *   separate-documents-uncollated-copies allows for uncollated copies.
2378     */
2379 
2380     doc->collate = _cups_strcasecmp(val, "separate-documents-uncollated-copies") != 0;
2381   }
2382 
2383   if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
2384       (!_cups_strcasecmp(val, "true") ||!_cups_strcasecmp(val, "on") ||
2385        !_cups_strcasecmp(val, "yes")))
2386     doc->collate = 1;
2387 
2388  /*
2389   * emit-jcl
2390   */
2391 
2392   if ((val = cupsGetOption("emit-jcl", num_options, options)) != NULL &&
2393       (!_cups_strcasecmp(val, "false") || !_cups_strcasecmp(val, "off") ||
2394        !_cups_strcasecmp(val, "no") || !strcmp(val, "0")))
2395     doc->emit_jcl = 0;
2396   else
2397     doc->emit_jcl = 1;
2398 
2399  /*
2400   * fit-to-page/ipp-attribute-fidelity
2401   *
2402   * (Only for original PostScript content)
2403   */
2404 
2405   if ((content_type = getenv("CONTENT_TYPE")) == NULL)
2406     content_type = "application/postscript";
2407 
2408   if (!_cups_strcasecmp(content_type, "application/postscript"))
2409   {
2410     if ((val = cupsGetOption("fit-to-page", num_options, options)) != NULL &&
2411 	!_cups_strcasecmp(val, "true"))
2412       doc->fit_to_page = 1;
2413     else if ((val = cupsGetOption("ipp-attribute-fidelity", num_options,
2414                                   options)) != NULL &&
2415 	     !_cups_strcasecmp(val, "true"))
2416       doc->fit_to_page = 1;
2417   }
2418 
2419  /*
2420   * mirror/MirrorPrint
2421   */
2422 
2423   if ((choice = ppdFindMarkedChoice(ppd, "MirrorPrint")) != NULL)
2424   {
2425     val = choice->choice;
2426     choice->marked = 0;
2427   }
2428   else
2429     val = cupsGetOption("mirror", num_options, options);
2430 
2431   if (val && (!_cups_strcasecmp(val, "true") || !_cups_strcasecmp(val, "on") ||
2432               !_cups_strcasecmp(val, "yes")))
2433     doc->mirror = 1;
2434 
2435  /*
2436   * number-up
2437   */
2438 
2439   if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
2440   {
2441     switch (intval = atoi(val))
2442     {
2443       case 1 :
2444       case 2 :
2445       case 4 :
2446       case 6 :
2447       case 9 :
2448       case 16 :
2449           doc->number_up = intval;
2450 	  break;
2451       default :
2452           _cupsLangPrintFilter(stderr, "ERROR",
2453 	                       _("Unsupported number-up value %d, using "
2454 				 "number-up=1."), intval);
2455           doc->number_up = 1;
2456 	  break;
2457     }
2458   }
2459   else
2460     doc->number_up = 1;
2461 
2462  /*
2463   * number-up-layout
2464   */
2465 
2466   if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL)
2467   {
2468     if (!_cups_strcasecmp(val, "lrtb"))
2469       doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2470     else if (!_cups_strcasecmp(val, "lrbt"))
2471       doc->number_up_layout = PSTOPS_LAYOUT_LRBT;
2472     else if (!_cups_strcasecmp(val, "rltb"))
2473       doc->number_up_layout = PSTOPS_LAYOUT_RLTB;
2474     else if (!_cups_strcasecmp(val, "rlbt"))
2475       doc->number_up_layout = PSTOPS_LAYOUT_RLBT;
2476     else if (!_cups_strcasecmp(val, "tblr"))
2477       doc->number_up_layout = PSTOPS_LAYOUT_TBLR;
2478     else if (!_cups_strcasecmp(val, "tbrl"))
2479       doc->number_up_layout = PSTOPS_LAYOUT_TBRL;
2480     else if (!_cups_strcasecmp(val, "btlr"))
2481       doc->number_up_layout = PSTOPS_LAYOUT_BTLR;
2482     else if (!_cups_strcasecmp(val, "btrl"))
2483       doc->number_up_layout = PSTOPS_LAYOUT_BTRL;
2484     else
2485     {
2486       _cupsLangPrintFilter(stderr, "ERROR",
2487                            _("Unsupported number-up-layout value %s, using "
2488 			     "number-up-layout=lrtb."), val);
2489       doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2490     }
2491   }
2492   else
2493     doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2494 
2495  /*
2496   * OutputOrder
2497   */
2498 
2499   if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL)
2500   {
2501     if (!_cups_strcasecmp(val, "Reverse"))
2502       doc->output_order = 1;
2503   }
2504   else if (ppd)
2505   {
2506    /*
2507     * Figure out the right default output order from the PPD file...
2508     */
2509 
2510     if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL &&
2511         (attr = ppdFindAttr(ppd, "PageStackOrder", choice->choice)) != NULL &&
2512 	attr->value)
2513       doc->output_order = !_cups_strcasecmp(attr->value, "Reverse");
2514     else if ((attr = ppdFindAttr(ppd, "DefaultOutputOrder", NULL)) != NULL &&
2515              attr->value)
2516       doc->output_order = !_cups_strcasecmp(attr->value, "Reverse");
2517   }
2518 
2519  /*
2520   * page-border
2521   */
2522 
2523   if ((val = cupsGetOption("page-border", num_options, options)) != NULL)
2524   {
2525     if (!_cups_strcasecmp(val, "none"))
2526       doc->page_border = PSTOPS_BORDERNONE;
2527     else if (!_cups_strcasecmp(val, "single"))
2528       doc->page_border = PSTOPS_BORDERSINGLE;
2529     else if (!_cups_strcasecmp(val, "single-thick"))
2530       doc->page_border = PSTOPS_BORDERSINGLE2;
2531     else if (!_cups_strcasecmp(val, "double"))
2532       doc->page_border = PSTOPS_BORDERDOUBLE;
2533     else if (!_cups_strcasecmp(val, "double-thick"))
2534       doc->page_border = PSTOPS_BORDERDOUBLE2;
2535     else
2536     {
2537       _cupsLangPrintFilter(stderr, "ERROR",
2538                            _("Unsupported page-border value %s, using "
2539 			     "page-border=none."), val);
2540       doc->page_border = PSTOPS_BORDERNONE;
2541     }
2542   }
2543   else
2544     doc->page_border = PSTOPS_BORDERNONE;
2545 
2546  /*
2547   * page-label
2548   */
2549 
2550   doc->page_label = cupsGetOption("page-label", num_options, options);
2551 
2552  /*
2553   * page-ranges
2554   */
2555 
2556   doc->page_ranges = cupsGetOption("page-ranges", num_options, options);
2557 
2558  /*
2559   * page-set
2560   */
2561 
2562   doc->page_set = cupsGetOption("page-set", num_options, options);
2563 
2564  /*
2565   * Now figure out if we have to force collated copies, etc.
2566   */
2567 
2568   if ((attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
2569     max_copies = atoi(attr->value);
2570   else if (ppd && ppd->manual_copies)
2571     max_copies = 1;
2572   else
2573     max_copies = 9999;
2574 
2575   if (doc->copies > max_copies)
2576     doc->collate = 1;
2577   else if (ppd && ppd->manual_copies && Duplex && doc->copies > 1)
2578   {
2579    /*
2580     * Force collated copies when printing a duplexed document to
2581     * a non-PS printer that doesn't do hardware copy generation.
2582     * Otherwise the copies will end up on the front/back side of
2583     * each page.
2584     */
2585 
2586     doc->collate = 1;
2587   }
2588 
2589  /*
2590   * See if we have to filter the fast or slow way...
2591   */
2592 
2593   if (doc->collate && doc->copies > 1)
2594   {
2595    /*
2596     * See if we need to manually collate the pages...
2597     */
2598 
2599     doc->slow_collate = 1;
2600 
2601     if (doc->copies <= max_copies &&
2602         (choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL &&
2603         !_cups_strcasecmp(choice->choice, "True"))
2604     {
2605      /*
2606       * Hardware collate option is selected, see if the option is
2607       * conflicting - if not, collate in hardware.  Otherwise,
2608       * turn the hardware collate option off...
2609       */
2610 
2611       if ((option = ppdFindOption(ppd, "Collate")) != NULL &&
2612           !option->conflicted)
2613 	doc->slow_collate = 0;
2614       else
2615         ppdMarkOption(ppd, "Collate", "False");
2616     }
2617   }
2618   else
2619     doc->slow_collate = 0;
2620 
2621   if (!ppdFindOption(ppd, "OutputOrder") && doc->output_order)
2622     doc->slow_order = 1;
2623   else
2624     doc->slow_order = 0;
2625 
2626   if (Duplex &&
2627        (doc->slow_collate || doc->slow_order ||
2628         ((attr = ppdFindAttr(ppd, "cupsEvenDuplex", NULL)) != NULL &&
2629 	 attr->value && !_cups_strcasecmp(attr->value, "true"))))
2630     doc->slow_duplex = 1;
2631   else
2632     doc->slow_duplex = 0;
2633 
2634  /*
2635   * Create a temporary file for page data if we need to filter slowly...
2636   */
2637 
2638   if (doc->slow_order || doc->slow_collate)
2639   {
2640     if ((doc->temp = cupsTempFile2(doc->tempfile,
2641                                    sizeof(doc->tempfile))) == NULL)
2642     {
2643       perror("DEBUG: Unable to create temporary file");
2644       exit(1);
2645     }
2646   }
2647 
2648  /*
2649   * Figure out if we should use ESPshowpage or not...
2650   */
2651 
2652   if (doc->page_label || getenv("CLASSIFICATION") || doc->number_up > 1 ||
2653       doc->page_border)
2654   {
2655    /*
2656     * Yes, use ESPshowpage...
2657     */
2658 
2659     doc->use_ESPshowpage = 1;
2660   }
2661 
2662   fprintf(stderr, "DEBUG: slow_collate=%d, slow_duplex=%d, slow_order=%d\n",
2663           doc->slow_collate, doc->slow_duplex, doc->slow_order);
2664 }
2665 
2666 
2667 /*
2668  * 'skip_page()' - Skip past a page that won't be printed.
2669  */
2670 
2671 static ssize_t				/* O - Length of next line */
skip_page(cups_file_t * fp,char * line,ssize_t linelen,size_t linesize)2672 skip_page(cups_file_t *fp,		/* I - File to read from */
2673           char        *line,		/* I - Line buffer */
2674 	  ssize_t     linelen,		/* I - Length of initial line */
2675           size_t      linesize)		/* I - Size of line buffer */
2676 {
2677   int	level;				/* Embedded document level */
2678 
2679 
2680   level = 0;
2681 
2682   while ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) > 0)
2683   {
2684     if (level == 0 &&
2685         (!strncmp(line, "%%Page:", 7) || !strncmp(line, "%%Trailer", 9)))
2686       break;
2687     else if (!strncmp(line, "%%BeginDocument", 15) ||
2688 	     !strncmp(line, "%ADO_BeginApplication", 21))
2689       level ++;
2690     else if ((!strncmp(line, "%%EndDocument", 13) ||
2691 	      !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
2692       level --;
2693     else if (!strncmp(line, "%%BeginBinary:", 14) ||
2694              (!strncmp(line, "%%BeginData:", 12) &&
2695 	      !strstr(line, "ASCII") && !strstr(line, "Hex")))
2696     {
2697      /*
2698       * Skip binary data...
2699       */
2700 
2701       ssize_t	bytes;			/* Bytes of data */
2702 
2703       bytes = atoi(strchr(line, ':') + 1);
2704 
2705       while (bytes > 0)
2706       {
2707 	if ((size_t)bytes > linesize)
2708 	  linelen = (ssize_t)cupsFileRead(fp, line, linesize);
2709 	else
2710 	  linelen = (ssize_t)cupsFileRead(fp, line, (size_t)bytes);
2711 
2712 	if (linelen < 1)
2713 	{
2714 	  line[0] = '\0';
2715 	  perror("ERROR: Early end-of-file while reading binary data");
2716 	  return (0);
2717 	}
2718 
2719 	bytes -= linelen;
2720       }
2721     }
2722   }
2723 
2724   return (linelen);
2725 }
2726 
2727 
2728 /*
2729  * 'start_nup()' - Start processing for N-up printing.
2730  */
2731 
2732 static void
start_nup(pstops_doc_t * doc,int number,int show_border,const int * bounding_box)2733 start_nup(pstops_doc_t *doc,		/* I - Document information */
2734           int          number,		/* I - Page number */
2735 	  int          show_border,	/* I - Show the border? */
2736 	  const int    *bounding_box)	/* I - BoundingBox value */
2737 {
2738   int		pos;			/* Position on page */
2739   int		x, y;			/* Relative position of subpage */
2740   double	w, l,			/* Width and length of subpage */
2741 		tx, ty;			/* Translation values for subpage */
2742   double	pagew,			/* Printable width of page */
2743 		pagel;			/* Printable height of page */
2744   int		bboxx,			/* BoundingBox X origin */
2745 		bboxy,			/* BoundingBox Y origin */
2746 		bboxw,			/* BoundingBox width */
2747 		bboxl;			/* BoundingBox height */
2748   double	margin = 0;		/* Current margin for border */
2749 
2750 
2751   if (doc->number_up > 1)
2752     doc_puts(doc, "userdict/ESPsave save put\n");
2753 
2754   pos   = (number - 1) % doc->number_up;
2755   pagew = PageRight - PageLeft;
2756   pagel = PageTop - PageBottom;
2757 
2758   if (doc->fit_to_page)
2759   {
2760     bboxx = bounding_box[0];
2761     bboxy = bounding_box[1];
2762     bboxw = bounding_box[2] - bounding_box[0];
2763     bboxl = bounding_box[3] - bounding_box[1];
2764   }
2765   else
2766   {
2767     bboxx = 0;
2768     bboxy = 0;
2769     bboxw = (int)PageWidth;
2770     bboxl = (int)PageLength;
2771   }
2772 
2773   fprintf(stderr, "DEBUG: pagew = %.1f, pagel = %.1f\n", pagew, pagel);
2774   fprintf(stderr, "DEBUG: bboxx = %d, bboxy = %d, bboxw = %d, bboxl = %d\n",
2775           bboxx, bboxy, bboxw, bboxl);
2776   fprintf(stderr, "DEBUG: PageLeft = %.1f, PageRight = %.1f\n",
2777           PageLeft, PageRight);
2778   fprintf(stderr, "DEBUG: PageTop = %.1f, PageBottom = %.1f\n",
2779           PageTop, PageBottom);
2780   fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
2781           PageWidth, PageLength);
2782 
2783   switch (Orientation)
2784   {
2785     case 1 : /* Landscape */
2786         doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", PageLength);
2787         break;
2788     case 2 : /* Reverse Portrait */
2789         doc_printf(doc, "%.1f %.1f translate 180 rotate\n", PageWidth,
2790 	           PageLength);
2791         break;
2792     case 3 : /* Reverse Landscape */
2793         doc_printf(doc, "0.0 %.1f translate -90 rotate\n", PageWidth);
2794         break;
2795   }
2796 
2797  /*
2798   * Mirror the page as needed...
2799   */
2800 
2801   if (doc->mirror)
2802     doc_printf(doc, "%.1f 0.0 translate -1 1 scale\n", PageWidth);
2803 
2804  /*
2805   * Offset and scale as necessary for fit_to_page/fit-to-page/number-up...
2806   */
2807 
2808   if (Duplex && doc->number_up > 1 && ((number / doc->number_up) & 1))
2809     doc_printf(doc, "%.1f %.1f translate\n", PageWidth - PageRight, PageBottom);
2810   else if (doc->number_up > 1 || doc->fit_to_page)
2811     doc_printf(doc, "%.1f %.1f translate\n", PageLeft, PageBottom);
2812 
2813   switch (doc->number_up)
2814   {
2815     default :
2816         if (doc->fit_to_page)
2817 	{
2818           w = pagew;
2819           l = w * bboxl / bboxw;
2820 
2821           if (l > pagel)
2822           {
2823             l = pagel;
2824             w = l * bboxw / bboxl;
2825           }
2826 
2827           tx = 0.5 * (pagew - w);
2828           ty = 0.5 * (pagel - l);
2829 
2830 	  doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", tx, ty,
2831 	             w / bboxw, l / bboxl);
2832 	}
2833 	else
2834           w = PageWidth;
2835 	break;
2836 
2837     case 2 :
2838         if (Orientation & 1)
2839 	{
2840           x = pos & 1;
2841 
2842           if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2843 	    x = 1 - x;
2844 
2845           w = pagel;
2846           l = w * bboxl / bboxw;
2847 
2848           if (l > (pagew * 0.5))
2849           {
2850             l = pagew * 0.5;
2851             w = l * bboxw / bboxl;
2852           }
2853 
2854           tx = 0.5 * (pagew * 0.5 - l);
2855           ty = 0.5 * (pagel - w);
2856 
2857           if (doc->normal_landscape)
2858             doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
2859 	  else
2860 	    doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
2861 
2862           doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2863                      ty, tx + pagew * 0.5 * x, w / bboxw, l / bboxl);
2864         }
2865 	else
2866 	{
2867           x = pos & 1;
2868 
2869           if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2870 	    x = 1 - x;
2871 
2872           l = pagew;
2873           w = l * bboxw / bboxl;
2874 
2875           if (w > (pagel * 0.5))
2876           {
2877             w = pagel * 0.5;
2878             l = w * bboxl / bboxw;
2879           }
2880 
2881           tx = 0.5 * (pagel * 0.5 - w);
2882           ty = 0.5 * (pagew - l);
2883 
2884           if (doc->normal_landscape)
2885 	    doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
2886 	  else
2887             doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
2888 
2889           doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2890                      tx + pagel * 0.5 * x, ty, w / bboxw, l / bboxl);
2891         }
2892         break;
2893 
2894     case 4 :
2895         if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
2896 	{
2897 	  x = (pos / 2) & 1;
2898           y = pos & 1;
2899         }
2900 	else
2901 	{
2902           x = pos & 1;
2903 	  y = (pos / 2) & 1;
2904         }
2905 
2906         if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2907 	  x = 1 - x;
2908 
2909 	if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2910 	  y = 1 - y;
2911 
2912         w = pagew * 0.5;
2913 	l = w * bboxl / bboxw;
2914 
2915 	if (l > (pagel * 0.5))
2916 	{
2917 	  l = pagel * 0.5;
2918 	  w = l * bboxw / bboxl;
2919 	}
2920 
2921         tx = 0.5 * (pagew * 0.5 - w);
2922         ty = 0.5 * (pagel * 0.5 - l);
2923 
2924 	doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2925 	           tx + x * pagew * 0.5, ty + y * pagel * 0.5,
2926 	           w / bboxw, l / bboxl);
2927         break;
2928 
2929     case 6 :
2930         if (Orientation & 1)
2931 	{
2932 	  if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
2933 	  {
2934 	    x = pos / 3;
2935 	    y = pos % 3;
2936 
2937             if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2938 	      x = 1 - x;
2939 
2940             if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2941 	      y = 2 - y;
2942 	  }
2943 	  else
2944 	  {
2945 	    x = pos & 1;
2946 	    y = pos / 2;
2947 
2948             if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2949 	      x = 1 - x;
2950 
2951             if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2952 	      y = 2 - y;
2953 	  }
2954 
2955           w = pagel * 0.5;
2956           l = w * bboxl / bboxw;
2957 
2958           if (l > (pagew * 0.333))
2959           {
2960             l = pagew * 0.333;
2961             w = l * bboxw / bboxl;
2962           }
2963 
2964           tx = 0.5 * (pagel - 2 * w);
2965           ty = 0.5 * (pagew - 3 * l);
2966 
2967           if (doc->normal_landscape)
2968             doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
2969 	  else
2970 	    doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
2971 
2972           doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2973                      tx + x * w, ty + y * l, l / bboxl, w / bboxw);
2974         }
2975 	else
2976 	{
2977 	  if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
2978 	  {
2979 	    x = pos / 2;
2980 	    y = pos & 1;
2981 
2982             if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2983 	      x = 2 - x;
2984 
2985             if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2986 	      y = 1 - y;
2987 	  }
2988 	  else
2989 	  {
2990 	    x = pos % 3;
2991 	    y = pos / 3;
2992 
2993             if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2994 	      x = 2 - x;
2995 
2996             if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2997 	      y = 1 - y;
2998 	  }
2999 
3000           l = pagew * 0.5;
3001           w = l * bboxw / bboxl;
3002 
3003           if (w > (pagel * 0.333))
3004           {
3005             w = pagel * 0.333;
3006             l = w * bboxl / bboxw;
3007           }
3008 
3009 	  tx = 0.5 * (pagel - 3 * w);
3010 	  ty = 0.5 * (pagew - 2 * l);
3011 
3012           if (doc->normal_landscape)
3013 	    doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
3014 	  else
3015             doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
3016 
3017           doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3018                      tx + w * x, ty + l * y, w / bboxw, l / bboxl);
3019 
3020         }
3021         break;
3022 
3023     case 9 :
3024         if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
3025 	{
3026 	  x = (pos / 3) % 3;
3027           y = pos % 3;
3028         }
3029 	else
3030 	{
3031           x = pos % 3;
3032 	  y = (pos / 3) % 3;
3033         }
3034 
3035         if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
3036 	  x = 2 - x;
3037 
3038 	if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
3039 	  y = 2 - y;
3040 
3041         w = pagew * 0.333;
3042 	l = w * bboxl / bboxw;
3043 
3044 	if (l > (pagel * 0.333))
3045 	{
3046 	  l = pagel * 0.333;
3047 	  w = l * bboxw / bboxl;
3048 	}
3049 
3050         tx = 0.5 * (pagew * 0.333 - w);
3051         ty = 0.5 * (pagel * 0.333 - l);
3052 
3053 	doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3054 	           tx + x * pagew * 0.333, ty + y * pagel * 0.333,
3055 	           w / bboxw, l / bboxl);
3056         break;
3057 
3058     case 16 :
3059         if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
3060 	{
3061 	  x = (pos / 4) & 3;
3062           y = pos & 3;
3063         }
3064 	else
3065 	{
3066           x = pos & 3;
3067 	  y = (pos / 4) & 3;
3068         }
3069 
3070         if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
3071 	  x = 3 - x;
3072 
3073 	if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
3074 	  y = 3 - y;
3075 
3076         w = pagew * 0.25;
3077 	l = w * bboxl / bboxw;
3078 
3079 	if (l > (pagel * 0.25))
3080 	{
3081 	  l = pagel * 0.25;
3082 	  w = l * bboxw / bboxl;
3083 	}
3084 
3085         tx = 0.5 * (pagew * 0.25 - w);
3086         ty = 0.5 * (pagel * 0.25 - l);
3087 
3088 	doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3089 	           tx + x * pagew * 0.25, ty + y * pagel * 0.25,
3090 	           w / bboxw, l / bboxl);
3091         break;
3092   }
3093 
3094  /*
3095   * Draw borders as necessary...
3096   */
3097 
3098   if (doc->page_border && show_border)
3099   {
3100     int		rects;			/* Number of border rectangles */
3101     double	fscale;			/* Scaling value for points */
3102 
3103 
3104     rects  = (doc->page_border & PSTOPS_BORDERDOUBLE) ? 2 : 1;
3105     fscale = PageWidth / w;
3106     margin = 2.25 * fscale;
3107 
3108    /*
3109     * Set the line width and color...
3110     */
3111 
3112     doc_puts(doc, "gsave\n");
3113     doc_printf(doc, "%.3f setlinewidth 0 setgray newpath\n",
3114                (doc->page_border & PSTOPS_BORDERTHICK) ? 0.5 * fscale :
3115 	                                                 0.24 * fscale);
3116 
3117    /*
3118     * Draw border boxes...
3119     */
3120 
3121     for (; rects > 0; rects --, margin += 2 * fscale)
3122       if (doc->number_up > 1)
3123 	doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
3124 		   margin,
3125 		   margin,
3126 		   bboxw - 2 * margin,
3127 		   bboxl - 2 * margin);
3128       else
3129 	doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
3130         	   PageLeft + margin,
3131 		   PageBottom + margin,
3132 		   PageRight - PageLeft - 2 * margin,
3133 		   PageTop - PageBottom - 2 * margin);
3134 
3135    /*
3136     * Restore pen settings...
3137     */
3138 
3139     doc_puts(doc, "grestore\n");
3140   }
3141 
3142   if (doc->fit_to_page)
3143   {
3144    /*
3145     * Offset the page by its bounding box...
3146     */
3147 
3148     doc_printf(doc, "%d %d translate\n", -bounding_box[0],
3149                -bounding_box[1]);
3150   }
3151 
3152   if (doc->fit_to_page || doc->number_up > 1)
3153   {
3154    /*
3155     * Clip the page to the page's bounding box...
3156     */
3157 
3158     doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrc\n",
3159                bboxx + margin, bboxy + margin,
3160                bboxw - 2 * margin, bboxl - 2 * margin);
3161   }
3162 }
3163 
3164 
3165 /*
3166  * 'write_label_prolog()' - Write the prolog with the classification
3167  *                          and page label.
3168  */
3169 
3170 static void
write_label_prolog(pstops_doc_t * doc,const char * label,float bottom,float top,float width)3171 write_label_prolog(pstops_doc_t *doc,	/* I - Document info */
3172                    const char   *label,	/* I - Page label */
3173 		   float        bottom,	/* I - Bottom position in points */
3174 		   float        top,	/* I - Top position in points */
3175 		   float        width)	/* I - Width in points */
3176 {
3177   const char	*classification;	/* CLASSIFICATION environment variable */
3178   const char	*ptr;			/* Temporary string pointer */
3179 
3180 
3181  /*
3182   * First get the current classification...
3183   */
3184 
3185   if ((classification = getenv("CLASSIFICATION")) == NULL)
3186     classification = "";
3187   if (strcmp(classification, "none") == 0)
3188     classification = "";
3189 
3190  /*
3191   * If there is nothing to show, bind an empty 'write labels' procedure
3192   * and return...
3193   */
3194 
3195   if (!classification[0] && (label == NULL || !label[0]))
3196   {
3197     doc_puts(doc, "userdict/ESPwl{}bind put\n");
3198     return;
3199   }
3200 
3201  /*
3202   * Set the classification + page label string...
3203   */
3204 
3205   doc_puts(doc, "userdict");
3206   if (!strcmp(classification, "confidential"))
3207     doc_puts(doc, "/ESPpl(CONFIDENTIAL");
3208   else if (!strcmp(classification, "classified"))
3209     doc_puts(doc, "/ESPpl(CLASSIFIED");
3210   else if (!strcmp(classification, "secret"))
3211     doc_puts(doc, "/ESPpl(SECRET");
3212   else if (!strcmp(classification, "topsecret"))
3213     doc_puts(doc, "/ESPpl(TOP SECRET");
3214   else if (!strcmp(classification, "unclassified"))
3215     doc_puts(doc, "/ESPpl(UNCLASSIFIED");
3216   else
3217   {
3218     doc_puts(doc, "/ESPpl(");
3219 
3220     for (ptr = classification; *ptr; ptr ++)
3221     {
3222       if (*ptr < 32 || *ptr > 126)
3223         doc_printf(doc, "\\%03o", *ptr);
3224       else if (*ptr == '_')
3225         doc_puts(doc, " ");
3226       else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
3227 	doc_printf(doc, "\\%c", *ptr);
3228       else
3229         doc_printf(doc, "%c", *ptr);
3230     }
3231   }
3232 
3233   if (label)
3234   {
3235     if (classification[0])
3236       doc_puts(doc, " - ");
3237 
3238    /*
3239     * Quote the label string as needed...
3240     */
3241 
3242     for (ptr = label; *ptr; ptr ++)
3243     {
3244       if (*ptr < 32 || *ptr > 126)
3245         doc_printf(doc, "\\%03o", *ptr);
3246       else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
3247 	doc_printf(doc, "\\%c", *ptr);
3248       else
3249         doc_printf(doc, "%c", *ptr);
3250     }
3251   }
3252 
3253   doc_puts(doc, ")put\n");
3254 
3255  /*
3256   * Then get a 14 point Helvetica-Bold font...
3257   */
3258 
3259   doc_puts(doc, "userdict/ESPpf /Helvetica-Bold findfont 14 scalefont put\n");
3260 
3261  /*
3262   * Finally, the procedure to write the labels on the page...
3263   */
3264 
3265   doc_puts(doc, "userdict/ESPwl{\n");
3266   doc_puts(doc, "  ESPpf setfont\n");
3267   doc_printf(doc, "  ESPpl stringwidth pop dup 12 add exch -0.5 mul %.0f add\n",
3268              width * 0.5f);
3269   doc_puts(doc, "  1 setgray\n");
3270   doc_printf(doc, "  dup 6 sub %.0f 3 index 20 ESPrf\n", bottom - 2.0);
3271   doc_printf(doc, "  dup 6 sub %.0f 3 index 20 ESPrf\n", top - 18.0);
3272   doc_puts(doc, "  0 setgray\n");
3273   doc_printf(doc, "  dup 6 sub %.0f 3 index 20 ESPrs\n", bottom - 2.0);
3274   doc_printf(doc, "  dup 6 sub %.0f 3 index 20 ESPrs\n", top - 18.0);
3275   doc_printf(doc, "  dup %.0f moveto ESPpl show\n", bottom + 2.0);
3276   doc_printf(doc, "  %.0f moveto ESPpl show\n", top - 14.0);
3277   doc_puts(doc, "pop\n");
3278   doc_puts(doc, "}bind put\n");
3279 }
3280 
3281 
3282 /*
3283  * 'write_labels()' - Write the actual page labels.
3284  *
3285  * This function is a copy of the one in common.c since we need to
3286  * use doc_puts/doc_printf instead of puts/printf...
3287  */
3288 
3289 static void
write_labels(pstops_doc_t * doc,int orient)3290 write_labels(pstops_doc_t *doc,		/* I - Document information */
3291              int          orient)	/* I - Orientation of the page */
3292 {
3293   float	width,				/* Width of page */
3294 	length;				/* Length of page */
3295 
3296 
3297   doc_puts(doc, "gsave\n");
3298 
3299   if ((orient ^ Orientation) & 1)
3300   {
3301     width  = PageLength;
3302     length = PageWidth;
3303   }
3304   else
3305   {
3306     width  = PageWidth;
3307     length = PageLength;
3308   }
3309 
3310   switch (orient & 3)
3311   {
3312     case 1 : /* Landscape */
3313         doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", length);
3314         break;
3315     case 2 : /* Reverse Portrait */
3316         doc_printf(doc, "%.1f %.1f translate 180 rotate\n", width, length);
3317         break;
3318     case 3 : /* Reverse Landscape */
3319         doc_printf(doc, "0.0 %.1f translate -90 rotate\n", width);
3320         break;
3321   }
3322 
3323   doc_puts(doc, "ESPwl\n");
3324   doc_puts(doc, "grestore\n");
3325 }
3326 
3327 
3328 /*
3329  * 'write_options()' - Write options provided via %%IncludeFeature.
3330  */
3331 
3332 static void
write_options(pstops_doc_t * doc,ppd_file_t * ppd,int num_options,cups_option_t * options)3333 write_options(
3334     pstops_doc_t  *doc,		/* I - Document */
3335     ppd_file_t    *ppd,		/* I - PPD file */
3336     int           num_options,	/* I - Number of options */
3337     cups_option_t *options)	/* I - Options */
3338 {
3339   int		i;		/* Looping var */
3340   ppd_option_t	*option;	/* PPD option */
3341   float		min_order;	/* Minimum OrderDependency value */
3342   char		*doc_setup,	/* DocumentSetup commands to send */
3343 		*any_setup;	/* AnySetup commands to send */
3344 
3345 
3346  /*
3347   * Figure out the minimum OrderDependency value...
3348   */
3349 
3350   if ((option = ppdFindOption(ppd, "PageRegion")) != NULL)
3351     min_order = option->order;
3352   else
3353     min_order = 999.0f;
3354 
3355   for (i = 0; i < num_options; i ++)
3356     if ((option = ppdFindOption(ppd, options[i].name)) != NULL &&
3357 	option->order < min_order)
3358       min_order = option->order;
3359 
3360  /*
3361   * Mark and extract them...
3362   */
3363 
3364   cupsMarkOptions(ppd, num_options, options);
3365 
3366   doc_setup = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, min_order);
3367   any_setup = ppdEmitString(ppd, PPD_ORDER_ANY, min_order);
3368 
3369  /*
3370   * Then send them out...
3371   */
3372 
3373   if (doc->number_up > 1)
3374   {
3375    /*
3376     * Temporarily restore setpagedevice so we can set the options...
3377     */
3378 
3379     doc_puts(doc, "userdict/setpagedevice/CUPSsetpagedevice load put\n");
3380   }
3381 
3382   if (doc_setup)
3383   {
3384     doc_puts(doc, doc_setup);
3385     free(doc_setup);
3386   }
3387 
3388   if (any_setup)
3389   {
3390     doc_puts(doc, any_setup);
3391     free(any_setup);
3392   }
3393 
3394   if (doc->number_up > 1)
3395   {
3396    /*
3397     * Disable setpagedevice again...
3398     */
3399 
3400     doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
3401   }
3402 }
3403