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