]> git.ipfire.org Git - thirdparty/cups.git/blob - filter/pstops.c
Fixed the number-up option stuff.
[thirdparty/cups.git] / filter / pstops.c
1 /*
2 * "$Id: pstops.c,v 1.29 1999/11/17 18:27:13 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 {
396 if (NumPages & (NUp - 1))
397 end_nup(NUp - 1);
398 else
399 end_nup(0);
400 }
401
402 if (slowcollate || sloworder)
403 {
404 Pages[NumPages] = ftell(temp);
405 page = 1;
406
407 if (!sloworder)
408 {
409 while (Copies > 0)
410 {
411 rewind(temp);
412
413 for (number = 0; number < NumPages; number ++)
414 {
415 if ((number % NUp) == 0)
416 {
417 if (ppd == NULL || ppd->num_filters == 0)
418 fprintf(stderr, "PAGE: %d 1\n", page);
419
420 printf("%%%%Page: %d %d\n", page, page);
421 page ++;
422 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
423 }
424
425 start_nup(number);
426 copy_bytes(temp, Pages[number + 1] - Pages[number]);
427 end_nup(number);
428 }
429
430 if (NumPages & (NUp - 1))
431 end_nup(NUp - 1);
432 else
433 end_nup(0);
434
435 Copies --;
436 }
437 }
438 else
439 {
440 do
441 {
442 for (number = NumPages - 1; number >= 0; number --)
443 {
444 if ((number % NUp) == 0)
445 {
446 if (ppd == NULL || ppd->num_filters == 0)
447 fprintf(stderr, "PAGE: %d %d\n", page,
448 slowcollate ? 1 : Copies);
449
450 printf("%%%%Page: %d %d\n", page, page);
451 page ++;
452 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
453 }
454
455 start_nup(NumPages - 1 - number);
456 fseek(temp, Pages[number], SEEK_SET);
457 copy_bytes(temp, Pages[number + 1] - Pages[number]);
458 end_nup(NumPages - 1 - number);
459 }
460
461 if (NumPages & (NUp - 1))
462 end_nup(NUp - 1);
463 else
464 end_nup(0);
465
466 Copies --;
467 }
468 while (Copies > 0 || !slowcollate);
469 }
470 }
471 }
472 else
473 {
474 /*
475 * No DSC comments - write any page commands and then the rest of the file...
476 */
477
478 if (ppd == NULL || ppd->num_filters == 0)
479 fprintf(stderr, "PAGE: 1 %d\n", slowcollate ? 1 : Copies);
480
481 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
482
483 while (psgets(line, sizeof(line), fp) != NULL)
484 {
485 puts(line);
486
487 if (slowcollate)
488 {
489 fputs(line, temp);
490 putc('\n', temp);
491 }
492 }
493
494 if (slowcollate)
495 {
496 while (Copies > 1)
497 {
498 if (ppd == NULL || ppd->num_filters == 0)
499 fputs("PAGE: 1 1\n", stderr);
500
501 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
502 rewind(temp);
503 copy_bytes(temp, 0);
504 }
505 }
506 }
507
508 /*
509 * End the job with the appropriate JCL command or CTRL-D otherwise.
510 */
511
512 if (ppd != NULL && ppd->jcl_end)
513 fputs(ppd->jcl_end, stdout);
514 else
515 putchar(0x04);
516
517 /*
518 * Close files and remove the temporary file if needed...
519 */
520
521 if (slowcollate || sloworder)
522 {
523 fclose(temp);
524 unlink(tempfile);
525 }
526
527 ppdClose(ppd);
528
529 if (fp != stdin)
530 fclose(fp);
531
532 return (0);
533 }
534
535
536 /*
537 * 'check_range()' - Check to see if the current page is selected for
538 * printing.
539 */
540
541 static int /* O - 1 if selected, 0 otherwise */
542 check_range(int page) /* I - Page number */
543 {
544 const char *range; /* Pointer into range string */
545 int lower, upper; /* Lower and upper page numbers */
546
547
548 if (PageSet != NULL)
549 {
550 /*
551 * See if we only print even or odd pages...
552 */
553
554 if (strcasecmp(PageSet, "even") == 0 && (page & 1))
555 return (0);
556 if (strcasecmp(PageSet, "odd") == 0 && !(page & 1))
557 return (0);
558 }
559
560 if (PageRanges == NULL)
561 return (1); /* No range, print all pages... */
562
563 for (range = PageRanges; *range != '\0';)
564 {
565 if (*range == '-')
566 {
567 lower = 1;
568 range ++;
569 upper = strtol(range, (char **)&range, 10);
570 }
571 else
572 {
573 lower = strtol(range, (char **)&range, 10);
574
575 if (*range == '-')
576 {
577 range ++;
578 if (!isdigit(*range))
579 upper = 65535;
580 else
581 upper = strtol(range, (char **)&range, 10);
582 }
583 else
584 upper = lower;
585 }
586
587 if (page >= lower && page <= upper)
588 return (1);
589
590 if (*range == ',')
591 range ++;
592 else
593 break;
594 }
595
596 return (0);
597 }
598
599
600 /*
601 * 'copy_bytes()' - Copy bytes from the input file to stdout...
602 */
603
604 static void
605 copy_bytes(FILE *fp, /* I - File to read from */
606 size_t length) /* I - Length of page data */
607 {
608 char buffer[8192]; /* Data buffer */
609 size_t nbytes, /* Number of bytes read */
610 nleft; /* Number of bytes left/remaining */
611
612
613 nleft = length;
614
615 while (nleft > 0 || length == 0)
616 {
617 if ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) < 1)
618 return;
619
620 nleft -= nbytes;
621
622 fwrite(buffer, 1, nbytes, stdout);
623 }
624 }
625
626
627 /*
628 * 'end_nup()' - End processing for N-up printing...
629 */
630
631 static void
632 end_nup(int number) /* I - Page number */
633 {
634 if (Flip || Orientation || NUp > 1)
635 puts("ESPsave restore");
636
637 switch (NUp)
638 {
639 case 2 :
640 if ((number & 1) == 1)
641 puts("ESPshowpage");
642 break;
643
644 case 4 :
645 if ((number & 3) == 3)
646 puts("ESPshowpage");
647 break;
648 }
649 }
650
651
652 /*
653 * 'psgets()' - Get a line from a file.
654 *
655 * Note:
656 *
657 * This function differs from the gets() function in that it
658 * handles any combination of CR, LF, or CR LF to end input
659 * lines.
660 */
661
662 static char * /* O - String or NULL if EOF */
663 psgets(char *buf, /* I - Buffer to read into */
664 size_t len, /* I - Length of buffer */
665 FILE *fp) /* I - File to read from */
666 {
667 char *bufptr; /* Pointer into buffer */
668 int ch; /* Character from file */
669
670
671 len --;
672 bufptr = buf;
673
674 while ((bufptr - buf) < len)
675 {
676 if ((ch = getc(fp)) == EOF)
677 break;
678
679 if (ch == 0x0d)
680 {
681 /*
682 * Got a CR; see if there is a LF as well...
683 */
684
685 ch = getc(fp);
686 if (ch != EOF && ch != 0x0a)
687 ungetc(ch, fp); /* Nope, save it for later... */
688
689 break;
690 }
691 else if (ch == 0x0a)
692 break;
693 else
694 *bufptr++ = ch;
695 }
696
697 /*
698 * Nul-terminate the string and return it (or NULL for EOF).
699 */
700
701 *bufptr = '\0';
702
703 if (ch == EOF && bufptr == buf)
704 return (NULL);
705 else
706 return (buf);
707 }
708
709
710 /*
711 * 'start_nup()' - Start processing for N-up printing...
712 */
713
714 static void
715 start_nup(int number) /* I - Page number */
716 {
717 int x, y; /* Relative position of subpage */
718 float w, l, /* Width and length of subpage */
719 tx, ty; /* Translation values for subpage */
720 float pw, pl; /* Printable width and length of full page */
721
722
723 if (Flip || Orientation || NUp > 1)
724 puts("/ESPsave save def");
725
726 if (Flip)
727 printf("%.0f 0 translate -1 1 scale\n", PageWidth);
728
729 pw = PageRight - PageLeft;
730 pl = PageTop - PageBottom;
731
732 switch (Orientation)
733 {
734 case 1 : /* Landscape */
735 printf("%.0f 0 translate 90 rotate\n", PageLength);
736 break;
737 case 2 : /* Reverse Portrait */
738 printf("%.0f %.0f translate 180 rotate\n", PageWidth, PageLength);
739 break;
740 case 3 : /* Reverse Landscape */
741 printf("0 %.0f translate -90 rotate\n", PageWidth);
742 break;
743 }
744
745 switch (NUp)
746 {
747 case 2 :
748 x = number & 1;
749
750 if (Orientation & 1)
751 {
752 x = 1 - x;
753 w = pl;
754 l = w * PageLength / PageWidth;
755
756 if (l > (pw * 0.5))
757 {
758 l = pw * 0.5;
759 w = l * PageWidth / PageLength;
760 }
761
762 tx = pw * 0.5 - l;
763 ty = (pl - w) * 0.5;
764 }
765 else
766 {
767 l = pw;
768 w = l * PageWidth / PageLength;
769
770 if (w > (pl * 0.5))
771 {
772 w = pl * 0.5;
773 l = w * PageLength / PageWidth;
774 }
775
776 tx = pl * 0.5 - w;
777 ty = (pw - l) * 0.5;
778 }
779
780 if (Duplex && (number & 2))
781 printf("%.0f %.0f translate\n", PageWidth - PageRight, PageBottom);
782 else
783 printf("%.0f %.0f translate\n", PageLeft, PageBottom);
784
785 if (Orientation & 1)
786 {
787 printf("0 %.0f translate -90 rotate\n", pl);
788 printf("%.0f %.0f translate %.3f %.3f scale\n",
789 ty, tx + l * x, w / pw, l / pl);
790 }
791 else
792 {
793 printf("%.0f 0 translate 90 rotate\n", pw);
794 printf("%.0f %.0f translate %.3f %.3f scale\n",
795 tx + w * x, ty, w / pw, l / pl);
796 }
797
798 printf("newpath\n"
799 "0 0 moveto\n"
800 "%.0f 0 lineto\n"
801 "%.0f %.0f lineto\n"
802 "0 %.0f lineto\n"
803 "closepath clip newpath\n",
804 PageWidth, PageWidth, PageLength, PageLength);
805 break;
806
807 case 4 :
808 x = number & 1;
809 y = 1 - ((number & 2) != 0);
810
811 w = pw * 0.5;
812 l = w * PageLength / PageWidth;
813
814 if (l > (pl * 0.5))
815 {
816 l = pl * 0.5;
817 w = l * PageWidth / PageLength;
818 }
819
820 if (Duplex && (number & 4))
821 printf("%.0f %.0f translate\n", PageWidth - PageRight, PageBottom);
822 else
823 printf("%.0f %.0f translate\n", PageLeft, PageBottom);
824
825 printf("%.0f %.0f translate %.3f %.3f scale\n", x * w, y * l,
826 w / PageWidth, l / PageLength);
827 printf("newpath\n"
828 "0 0 moveto\n"
829 "%.0f 0 lineto\n"
830 "%.0f %.0f lineto\n"
831 "0 %.0f lineto\n"
832 "closepath clip newpath\n",
833 PageWidth, PageWidth, PageLength, PageLength);
834 break;
835 }
836 }
837
838
839 /*
840 * End of "$Id: pstops.c,v 1.29 1999/11/17 18:27:13 mike Exp $".
841 */