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