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