]> git.ipfire.org Git - thirdparty/cups.git/blob - filter/pstops.c
Fixed bug in N-up printing - grestoreall dumps duplex/size state on some
[thirdparty/cups.git] / filter / pstops.c
1 /*
2 * "$Id: pstops.c,v 1.27 1999/11/03 19:05:31 mike Exp $"
3 *
4 * PostScript filter for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1993-1999 by Easy Software Products.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * main() - Main entry...
27 * check_range() - Check to see if the current page is selected for
28 * copy_bytes() - Copy bytes from the input file to stdout...
29 * end_nup() - End processing for N-up printing...
30 * psgets() - Get a line from a file.
31 * start_nup() - Start processing for N-up printing...
32 */
33
34 /*
35 * Include necessary headers...
36 */
37
38 #include "common.h"
39
40
41 /*
42 * Constants...
43 */
44
45 #define MAX_PAGES 10000
46
47
48 /*
49 * Globals...
50 */
51
52 int NumPages = 0; /* Number of pages in file */
53 long Pages[MAX_PAGES]; /* Offsets to each page */
54 char PageLabels[MAX_PAGES][64];
55 /* Page labels */
56 const char *PageRanges = NULL; /* Range of pages selected */
57 const char *PageSet = NULL; /* All, Even, Odd pages */
58 int Order = 0, /* 0 = normal, 1 = reverse pages */
59 Flip = 0, /* Flip/mirror pages */
60 NUp = 1, /* Number of pages on each sheet (1, 2, 4) */
61 Collate = 0, /* Collate copies? */
62 Copies = 1; /* Number of copies */
63
64
65 /*
66 * Local functions...
67 */
68
69 static int check_range(int page);
70 static void copy_bytes(FILE *fp, size_t length);
71 static void end_nup(int number);
72 static char *psgets(char *buf, size_t len, FILE *fp);
73 static void start_nup(int number);
74
75
76 /*
77 * 'main()' - Main entry...
78 */
79
80 int /* O - Exit status */
81 main(int argc, /* I - Number of command-line arguments */
82 char *argv[]) /* I - Command-line arguments */
83 {
84 FILE *fp; /* Print file */
85 ppd_file_t *ppd; /* PPD file */
86 int num_options; /* Number of print options */
87 cups_option_t *options; /* Print options */
88 const char *val; /* Option value */
89 char tempfile[255]; /* Temporary file name */
90 FILE *temp; /* Temporary file */
91 int number; /* Page number */
92 int slowcollate; /* 1 if we need to collate manually */
93 int sloworder; /* 1 if we need to order manually */
94 char line[8192]; /* Line buffer */
95 float g; /* Gamma correction value */
96 float b; /* Brightness factor */
97 int level; /* Nesting level for embedded files */
98 int nbytes, /* Number of bytes read */
99 tbytes; /* Total bytes to read for binary data */
100 int page; /* Current page sequence number */
101
102
103 if (argc < 6 || argc > 7)
104 {
105 fputs("ERROR: pstops job-id user title copies options [file]\n", stderr);
106 return (1);
107 }
108
109 /*
110 * If we have 7 arguments, print the file named on the command-line.
111 * Otherwise, send stdin instead...
112 */
113
114 if (argc == 6)
115 fp = stdin;
116 else
117 {
118 /*
119 * Try to open the print file...
120 */
121
122 if ((fp = fopen(argv[6], "rb")) == NULL)
123 {
124 perror("ERROR: unable to open print file - ");
125 return (1);
126 }
127 }
128
129 /*
130 * Process command-line options and write the prolog...
131 */
132
133 g = 1.0;
134 b = 1.0;
135
136 Copies = atoi(argv[4]);
137
138 ppd = ppdOpenFile(getenv("PPD"));
139
140 options = NULL;
141 num_options = cupsParseOptions(argv[5], 0, &options);
142
143 ppd = SetCommonOptions(num_options, options, 1);
144
145 ppdMarkDefaults(ppd);
146 cupsMarkOptions(ppd, num_options, options);
147
148 if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL)
149 PageRanges = val;
150
151 if ((val = cupsGetOption("page-set", num_options, options)) != NULL)
152 PageSet = val;
153
154 if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
155 {
156 /*
157 * This IPP attribute is unnecessarily complicated...
158 *
159 * single-document, separate-documents-collated-copies, and
160 * single-document-new-sheet all require collated copies.
161 *
162 * separate-documents-collated-copies allows for uncollated copies.
163 */
164
165 Collate = strcasecmp(val, "separate-documents-collated-copies") != 0;
166 }
167
168 if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
169 strcasecmp(val, "True") == 0)
170 Collate = 1;
171
172 if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL &&
173 strcasecmp(val, "Reverse") == 0)
174 Order = 1;
175
176 if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
177 NUp = atoi(val);
178
179 if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
180 g = atoi(val) * 0.001f;
181
182 if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
183 b = atoi(val) * 0.01f;
184
185 /*
186 * See if we have to filter the fast or slow way...
187 */
188
189 if (ppdFindOption(ppd, "Collate") == NULL && Collate && Copies > 1)
190 slowcollate = 1;
191 else
192 slowcollate = 0;
193
194 if (ppdFindOption(ppd, "OutputOrder") == NULL && Order)
195 sloworder = 1;
196 else
197 sloworder = 0;
198
199 /*
200 * If we need to filter slowly, then create a temporary file for page data...
201 *
202 * If the temp file can't be created, then we'll ignore the collating/output
203 * order options...
204 */
205
206 if (sloworder || slowcollate)
207 {
208 temp = fopen(cupsTempFile(tempfile, sizeof(tempfile)), "wb+");
209
210 if (temp == NULL)
211 slowcollate = sloworder = 0;
212 }
213
214 /*
215 * Write any "exit server" options that have been selected...
216 */
217
218 ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
219
220 /*
221 * Write any JCL commands that are needed to print PostScript code...
222 */
223
224 if (ppd != NULL && ppd->jcl_begin && ppd->jcl_ps)
225 {
226 fputs(ppd->jcl_begin, stdout);
227 ppdEmit(ppd, stdout, PPD_ORDER_JCL);
228 fputs(ppd->jcl_ps, stdout);
229 }
230
231 /*
232 * Read the first line to see if we have DSC comments...
233 */
234
235 if (psgets(line, sizeof(line), fp) == NULL)
236 {
237 fputs("ERROR: Empty print file!\n", stderr);
238 ppdClose(ppd);
239 return (1);
240 }
241
242 /*
243 * Start sending the document with any commands needed...
244 */
245
246 puts(line);
247
248 if (ppd != NULL && ppd->patches != NULL)
249 puts(ppd->patches);
250
251 ppdEmit(ppd, stdout, PPD_ORDER_DOCUMENT);
252 ppdEmit(ppd, stdout, PPD_ORDER_ANY);
253 ppdEmit(ppd, stdout, PPD_ORDER_PROLOG);
254
255 if (NUp > 1)
256 puts("userdict begin\n"
257 "/ESPshowpage /showpage load def\n"
258 "/showpage { } def\n"
259 "end");
260
261 if (g != 1.0 || b != 1.0)
262 printf("{ neg 1 add dup 0 lt { pop 1 } { %.3f exp neg 1 add } "
263 "ifelse %.3f mul } bind settransfer\n", g, b);
264
265 if (Copies > 1 && (!Collate || !slowcollate))
266 printf("/#copies %d def\n", Copies);
267
268 if (strncmp(line, "%!PS-Adobe-", 11) == 0)
269 {
270 /*
271 * OK, we have DSC comments; read until we find a %%Page comment...
272 */
273
274 level = 0;
275
276 while (psgets(line, sizeof(line), fp) != NULL)
277 if (strncmp(line, "%%BeginDocument:", 16) == 0 ||
278 strncmp(line, "%%BeginDocument ", 16) == 0) /* Adobe Acrobat BUG */
279 level ++;
280 else if (strcmp(line, "%%EndDocument") == 0 && level > 0)
281 level --;
282 else if (strncmp(line, "%%Page:", 7) == 0 && level == 0)
283 break;
284 else if (strncmp(line, "%%BeginBinary:", 14) == 0 ||
285 (strncmp(line, "%%BeginData:", 12) == 0 &&
286 strstr(line, "Binary") != NULL))
287 {
288 /*
289 * Copy binary data...
290 */
291
292 tbytes = atoi(strchr(line, ':') + 1);
293 while (tbytes > 0)
294 {
295 nbytes = fread(line, 1, sizeof(line), fp);
296 fwrite(line, 1, nbytes, stdout);
297 tbytes -= nbytes;
298 }
299 }
300 else
301 puts(line);
302
303 /*
304 * Then read all of the pages, filtering as needed...
305 */
306
307 for (page = 1;;)
308 {
309 if (strncmp(line, "%%BeginDocument:", 16) == 0 ||
310 strncmp(line, "%%BeginDocument ", 16) == 0) /* Adobe Acrobat BUG */
311 level ++;
312 else if (strcmp(line, "%%EndDocument") == 0 && level > 0)
313 level --;
314 else if (strncmp(line, "%%Page:", 7) == 0 && level == 0)
315 {
316 if (sscanf(line, "%*s%*s%d", &number) == 1)
317 {
318 if (!check_range(number))
319 {
320 while (psgets(line, sizeof(line), fp) != NULL)
321 if (strncmp(line, "%%BeginDocument:", 16) == 0 ||
322 strncmp(line, "%%BeginDocument ", 16) == 0) /* Adobe Acrobat BUG */
323 level ++;
324 else if (strcmp(line, "%%EndDocument") == 0 && level > 0)
325 level --;
326 else if (strncmp(line, "%%Page:", 7) == 0 && level == 0)
327 break;
328
329 continue;
330 }
331
332 if (!sloworder && NumPages > 0)
333 end_nup(NumPages - 1);
334
335 if (slowcollate || sloworder)
336 Pages[NumPages] = ftell(temp);
337
338 if (!sloworder)
339 {
340 if ((NumPages % NUp) == 0)
341 {
342 if (ppd == NULL || ppd->num_filters == 0)
343 fprintf(stderr, "PAGE: %d %d\n", page, Copies);
344
345 printf("%%%%Page: %d %d\n", page, page);
346 page ++;
347 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
348 }
349
350 start_nup(NumPages);
351 }
352
353 NumPages ++;
354 }
355 }
356 else if (strncmp(line, "%%BeginBinary:", 14) == 0 ||
357 (strncmp(line, "%%BeginData:", 12) == 0 &&
358 strstr(line, "Binary") != NULL))
359 {
360 /*
361 * Copy binary data...
362 */
363
364 tbytes = atoi(strchr(line, ':') + 1);
365 while (tbytes > 0)
366 {
367 nbytes = fread(line, 1, sizeof(line), fp);
368
369 if (!sloworder)
370 fwrite(line, 1, nbytes, stdout);
371
372 if (slowcollate || sloworder)
373 fwrite(line, 1, nbytes, stdout);
374
375 tbytes -= nbytes;
376 }
377 }
378 else
379 {
380 if (!sloworder)
381 puts(line);
382
383 if (slowcollate || sloworder)
384 {
385 fputs(line, temp);
386 putc('\n', temp);
387 }
388 }
389
390 if (psgets(line, sizeof(line), fp) == NULL)
391 break;
392 }
393
394 if (!sloworder)
395 end_nup((NumPages + NUp - 1) & (NUp - 1));
396
397 if (slowcollate || sloworder)
398 {
399 Pages[NumPages] = ftell(temp);
400 page = 1;
401
402 if (!sloworder)
403 {
404 while (Copies > 0)
405 {
406 rewind(temp);
407
408 for (number = 0; number < NumPages; number ++)
409 {
410 if ((number % NUp) == 0)
411 {
412 if (ppd == NULL || ppd->num_filters == 0)
413 fprintf(stderr, "PAGE: %d 1\n", page);
414
415 printf("%%%%Page: %d %d\n", page, page);
416 page ++;
417 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
418 }
419
420 start_nup(number);
421 copy_bytes(temp, Pages[number + 1] - Pages[number]);
422 end_nup(number);
423 }
424
425 Copies --;
426 }
427 }
428 else
429 {
430 do
431 {
432 for (number = NumPages - 1; number >= 0; number --)
433 {
434 if ((number % NUp) == 0)
435 {
436 if (ppd == NULL || ppd->num_filters == 0)
437 fprintf(stderr, "PAGE: %d %d\n", page,
438 slowcollate ? 1 : Copies);
439
440 printf("%%%%Page: %d %d\n", page, page);
441 page ++;
442 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
443 }
444
445 start_nup(NumPages - 1 - number);
446 fseek(temp, Pages[number], SEEK_SET);
447 copy_bytes(temp, Pages[number + 1] - Pages[number]);
448 end_nup(NumPages - 1 - number);
449 }
450
451 Copies --;
452 }
453 while (Copies > 0 || !slowcollate);
454 }
455 }
456 }
457 else
458 {
459 /*
460 * No DSC comments - write any page commands and then the rest of the file...
461 */
462
463 if (ppd == NULL || ppd->num_filters == 0)
464 fprintf(stderr, "PAGE: 1 %d\n", slowcollate ? 1 : Copies);
465
466 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
467
468 while (psgets(line, sizeof(line), fp) != NULL)
469 {
470 puts(line);
471
472 if (slowcollate)
473 {
474 fputs(line, temp);
475 putc('\n', temp);
476 }
477 }
478
479 if (slowcollate)
480 {
481 while (Copies > 1)
482 {
483 if (ppd == NULL || ppd->num_filters == 0)
484 fputs("PAGE: 1 1\n", stderr);
485
486 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
487 rewind(temp);
488 copy_bytes(temp, 0);
489 }
490 }
491 }
492
493 /*
494 * End the job with the appropriate JCL command or CTRL-D otherwise.
495 */
496
497 if (ppd != NULL && ppd->jcl_end)
498 fputs(ppd->jcl_end, stdout);
499 else
500 putchar(0x04);
501
502 /*
503 * Close files and remove the temporary file if needed...
504 */
505
506 if (slowcollate || sloworder)
507 {
508 fclose(temp);
509 unlink(tempfile);
510 }
511
512 ppdClose(ppd);
513
514 if (fp != stdin)
515 fclose(fp);
516
517 return (0);
518 }
519
520
521 /*
522 * 'check_range()' - Check to see if the current page is selected for
523 * printing.
524 */
525
526 static int /* O - 1 if selected, 0 otherwise */
527 check_range(int page) /* I - Page number */
528 {
529 const char *range; /* Pointer into range string */
530 int lower, upper; /* Lower and upper page numbers */
531
532
533 if (PageSet != NULL)
534 {
535 /*
536 * See if we only print even or odd pages...
537 */
538
539 if (strcasecmp(PageSet, "even") == 0 && (page & 1))
540 return (0);
541 if (strcasecmp(PageSet, "odd") == 0 && !(page & 1))
542 return (0);
543 }
544
545 if (PageRanges == NULL)
546 return (1); /* No range, print all pages... */
547
548 for (range = PageRanges; *range != '\0';)
549 {
550 if (*range == '-')
551 {
552 lower = 1;
553 range ++;
554 upper = strtol(range, (char **)&range, 10);
555 }
556 else
557 {
558 lower = strtol(range, (char **)&range, 10);
559
560 if (*range == '-')
561 {
562 range ++;
563 if (!isdigit(*range))
564 upper = 65535;
565 else
566 upper = strtol(range, (char **)&range, 10);
567 }
568 else
569 upper = lower;
570 }
571
572 if (page >= lower && page <= upper)
573 return (1);
574
575 if (*range == ',')
576 range ++;
577 else
578 break;
579 }
580
581 return (0);
582 }
583
584
585 /*
586 * 'copy_bytes()' - Copy bytes from the input file to stdout...
587 */
588
589 static void
590 copy_bytes(FILE *fp, /* I - File to read from */
591 size_t length) /* I - Length of page data */
592 {
593 char buffer[8192]; /* Data buffer */
594 size_t nbytes, /* Number of bytes read */
595 nleft; /* Number of bytes left/remaining */
596
597
598 nleft = length;
599
600 while (nleft > 0 || length == 0)
601 {
602 if ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) < 1)
603 return;
604
605 nleft -= nbytes;
606
607 fwrite(buffer, 1, nbytes, stdout);
608 }
609 }
610
611
612 /*
613 * 'end_nup()' - End processing for N-up printing...
614 */
615
616 static void
617 end_nup(int number) /* I - Page number */
618 {
619 if (Flip || Orientation || NUp > 1)
620 puts("ESPsave restore");
621
622 switch (NUp)
623 {
624 case 2 :
625 if ((number & 1) == 1)
626 puts("ESPshowpage");
627 break;
628
629 case 4 :
630 if ((number & 3) == 3)
631 puts("ESPshowpage");
632 break;
633 }
634 }
635
636
637 /*
638 * 'psgets()' - Get a line from a file.
639 *
640 * Note:
641 *
642 * This function differs from the gets() function in that it
643 * handles any combination of CR, LF, or CR LF to end input
644 * lines.
645 */
646
647 static char * /* O - String or NULL if EOF */
648 psgets(char *buf, /* I - Buffer to read into */
649 size_t len, /* I - Length of buffer */
650 FILE *fp) /* I - File to read from */
651 {
652 char *bufptr; /* Pointer into buffer */
653 int ch; /* Character from file */
654
655
656 len --;
657 bufptr = buf;
658
659 while ((bufptr - buf) < len)
660 {
661 if ((ch = getc(fp)) == EOF)
662 break;
663
664 if (ch == 0x0d)
665 {
666 /*
667 * Got a CR; see if there is a LF as well...
668 */
669
670 ch = getc(fp);
671 if (ch != EOF && ch != 0x0a)
672 ungetc(ch, fp); /* Nope, save it for later... */
673
674 break;
675 }
676 else if (ch == 0x0a)
677 break;
678 else
679 *bufptr++ = ch;
680 }
681
682 /*
683 * Nul-terminate the string and return it (or NULL for EOF).
684 */
685
686 *bufptr = '\0';
687
688 if (ch == EOF && bufptr == buf)
689 return (NULL);
690 else
691 return (buf);
692 }
693
694
695 /*
696 * 'start_nup()' - Start processing for N-up printing...
697 */
698
699 static void
700 start_nup(int number) /* I - Page number */
701 {
702 int x, y; /* Relative position of subpage */
703 float w, l, /* Width and length of subpage */
704 tx, ty; /* Translation values for subpage */
705 float pw, pl; /* Printable width and length of full page */
706
707
708 if (Flip || Orientation || NUp > 1)
709 puts("/ESPsave save def");
710
711 if (Flip)
712 printf("%.0f 0 translate -1 1 scale\n", PageWidth);
713
714 pw = PageRight - PageLeft;
715 pl = PageTop - PageBottom;
716
717 switch (Orientation)
718 {
719 case 1 : /* Landscape */
720 printf("%.0f 0 translate 90 rotate\n", PageLength);
721 break;
722 case 2 : /* Reverse Portrait */
723 printf("%.0f %.0f translate 180 rotate\n", PageWidth, PageLength);
724 break;
725 case 3 : /* Reverse Landscape */
726 printf("0 %.0f translate -90 rotate\n", PageWidth);
727 break;
728 }
729
730 switch (NUp)
731 {
732 case 2 :
733 x = number & 1;
734
735 if (Orientation & 1)
736 {
737 x = 1 - x;
738 w = pl;
739 l = w * pl / pw;
740
741 if (l > (pw * 0.5))
742 {
743 l = pw * 0.5;
744 w = l * pw / pl;
745 }
746
747 tx = pw * 0.5 - l;
748 ty = (pl - w) * 0.5;
749 }
750 else
751 {
752 l = pw;
753 w = l * pw / pl;
754
755 if (w > (pl * 0.5))
756 {
757 w = pl * 0.5;
758 l = w * pl / pw;
759 }
760
761 tx = pl * 0.5 - w;
762 ty = (pw - l) * 0.5;
763 }
764
765 if (Duplex && (number & 2))
766 printf("%.0f %.0f translate\n", PageWidth - PageRight, PageBottom);
767 else
768 printf("%.0f %.0f translate\n", PageLeft, PageBottom);
769
770 if (Orientation & 1)
771 {
772 printf("0 %.0f translate -90 rotate\n", pl);
773 printf("%.0f %.0f translate %.3f %.3f scale\n",
774 ty, tx + l * x, w / pw, l / pl);
775 }
776 else
777 {
778 printf("%.0f 0 translate 90 rotate\n", pw);
779 printf("%.0f %.0f translate %.3f %.3f scale\n",
780 tx + w * x, ty, w / pw, l / pl);
781 }
782
783 printf("newpath\n"
784 "0 0 moveto\n"
785 "%.0f 0 lineto\n"
786 "%.0f %.0f lineto\n"
787 "0 %.0f lineto\n"
788 "closepath clip newpath\n",
789 pw, pw, pl, pl);
790 break;
791
792 case 4 :
793 x = number & 1;
794 y = 1 - ((number & 2) != 0);
795 w = pw * 0.5;
796 l = pl * 0.5;
797
798 if (Duplex && (number & 4))
799 printf("%.0f %.0f translate\n", PageWidth - PageRight, PageBottom);
800 else
801 printf("%.0f %.0f translate\n", PageLeft, PageBottom);
802
803 printf("%.0f %.0f translate 0.5 0.5 scale\n", x * w, y * l);
804 printf("newpath\n"
805 "0 0 moveto\n"
806 "%.0f 0 lineto\n"
807 "%.0f %.0f lineto\n"
808 "0 %.0f lineto\n"
809 "closepath clip newpath\n",
810 pw, pw, pl, pl);
811 break;
812 }
813 }
814
815
816 /*
817 * End of "$Id: pstops.c,v 1.27 1999/11/03 19:05:31 mike Exp $".
818 */