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