]> git.ipfire.org Git - thirdparty/cups.git/blob - filter/pstops.c
Use sigaction instead of sigset under Linux.
[thirdparty/cups.git] / filter / pstops.c
1 /*
2 * "$Id: pstops.c,v 1.36 2000/03/21 04:03:28 mike Exp $"
3 *
4 * PostScript filter for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1993-2000 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 int page_count; /* Page count for NUp */
102 int subpage; /* Sub-page number */
103 int copy; /* Current copy */
104
105
106 if (argc < 6 || argc > 7)
107 {
108 fputs("ERROR: pstops job-id user title copies options [file]\n", stderr);
109 return (1);
110 }
111
112 /*
113 * If we have 7 arguments, print the file named on the command-line.
114 * Otherwise, send stdin instead...
115 */
116
117 if (argc == 6)
118 fp = stdin;
119 else
120 {
121 /*
122 * Try to open the print file...
123 */
124
125 if ((fp = fopen(argv[6], "rb")) == NULL)
126 {
127 perror("ERROR: unable to open print file - ");
128 return (1);
129 }
130 }
131
132 /*
133 * Process command-line options and write the prolog...
134 */
135
136 g = 1.0;
137 b = 1.0;
138
139 Copies = atoi(argv[4]);
140
141 options = NULL;
142 num_options = cupsParseOptions(argv[5], 0, &options);
143
144 ppd = SetCommonOptions(num_options, options, 1);
145
146 if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL)
147 PageRanges = val;
148
149 if ((val = cupsGetOption("page-set", num_options, options)) != NULL)
150 PageSet = val;
151
152 if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
153 {
154 /*
155 * This IPP attribute is unnecessarily complicated...
156 *
157 * single-document, separate-documents-collated-copies, and
158 * single-document-new-sheet all require collated copies.
159 *
160 * separate-documents-collated-copies allows for uncollated copies.
161 */
162
163 Collate = strcasecmp(val, "separate-documents-collated-copies") != 0;
164 }
165
166 if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
167 strcasecmp(val, "True") == 0)
168 Collate = 1;
169
170 if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL &&
171 strcasecmp(val, "Reverse") == 0)
172 Order = 1;
173
174 if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
175 NUp = atoi(val);
176
177 if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
178 g = atoi(val) * 0.001f;
179
180 if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
181 b = atoi(val) * 0.01f;
182
183 /*
184 * See if we have to filter the fast or slow way...
185 */
186
187 if (ppdFindOption(ppd, "Collate") == NULL && Collate && Copies > 1)
188 slowcollate = 1;
189 else
190 slowcollate = 0;
191
192 if (ppdFindOption(ppd, "OutputOrder") == NULL && Order)
193 sloworder = 1;
194 else
195 sloworder = 0;
196
197 /*
198 * If we need to filter slowly, then create a temporary file for page data...
199 *
200 * If the temp file can't be created, then we'll ignore the collating/output
201 * order options...
202 */
203
204 if (sloworder || slowcollate)
205 {
206 temp = fopen(cupsTempFile(tempfile, sizeof(tempfile)), "wb+");
207
208 if (temp == NULL)
209 slowcollate = sloworder = 0;
210 }
211 else
212 temp = NULL;
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 - 1)) == 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 if (strcmp(line, "%%Trailer") == 0 && level == 0)
379 break;
380 else
381 {
382 if (!sloworder)
383 puts(line);
384
385 if (slowcollate || sloworder)
386 {
387 fputs(line, temp);
388 putc('\n', temp);
389 }
390 }
391
392 if (psgets(line, sizeof(line), fp) == NULL)
393 break;
394 }
395
396 if (!sloworder)
397 {
398 end_nup(NumPages - 1);
399
400 if (NumPages & (NUp - 1))
401 {
402 start_nup(NUp - 1);
403 end_nup(NUp - 1);
404 }
405 }
406
407 if (slowcollate || sloworder)
408 {
409 Pages[NumPages] = ftell(temp);
410 page = 1;
411
412 if (!sloworder)
413 {
414 while (Copies > 0)
415 {
416 rewind(temp);
417
418 for (number = 0; number < NumPages; number ++)
419 {
420 if ((number & (NUp - 1)) == 0)
421 {
422 if (ppd == NULL || ppd->num_filters == 0)
423 fprintf(stderr, "PAGE: %d 1\n", page);
424
425 printf("%%%%Page: %d %d\n", page, page);
426 page ++;
427 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
428 }
429
430 start_nup(number);
431 copy_bytes(temp, Pages[number + 1] - Pages[number]);
432 end_nup(number);
433 }
434
435 if (NumPages & (NUp - 1))
436 {
437 start_nup(NUp - 1);
438 end_nup(NUp - 1);
439 }
440
441 Copies --;
442 }
443 }
444 else
445 {
446 page_count = (NumPages + NUp - 1) / NUp;
447 copy = 0;
448
449 do
450 {
451 for (page = page_count - 1; page >= 0; page --)
452 {
453 if (ppd == NULL || ppd->num_filters == 0)
454 fprintf(stderr, "PAGE: %d %d\n", page + 1,
455 slowcollate ? 1 : Copies);
456
457 if (slowcollate)
458 printf("%%%%Page: %d %d\n", page + 1,
459 page_count - page + copy * page_count);
460 else
461 printf("%%%%Page: %d %d\n", page + 1, page_count - page);
462
463 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
464
465 for (subpage = 0, number = page * NUp;
466 subpage < NUp && number < NumPages;
467 subpage ++, number ++)
468 {
469 start_nup(number);
470 fseek(temp, Pages[number], SEEK_SET);
471 copy_bytes(temp, Pages[number + 1] - Pages[number]);
472 end_nup(number);
473 }
474
475 if (number & (NUp - 1))
476 {
477 start_nup(NUp - 1);
478 end_nup(NUp - 1);
479 }
480 }
481
482 copy ++;
483 }
484 while (copy < Copies && slowcollate);
485 }
486 }
487
488 /*
489 * Copy the trailer, if any...
490 */
491
492 while ((nbytes = fread(line, 1, sizeof(line), fp)) > 0)
493 fwrite(line, 1, nbytes, stdout);
494 }
495 else
496 {
497 /*
498 * No DSC comments - write any page commands and then the rest of the file...
499 */
500
501 if (ppd == NULL || ppd->num_filters == 0)
502 fprintf(stderr, "PAGE: 1 %d\n", slowcollate ? 1 : Copies);
503
504 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
505
506 while (psgets(line, sizeof(line), fp) != NULL)
507 {
508 puts(line);
509
510 if (slowcollate)
511 {
512 fputs(line, temp);
513 putc('\n', temp);
514 }
515 }
516
517 if (slowcollate)
518 {
519 while (Copies > 1)
520 {
521 if (ppd == NULL || ppd->num_filters == 0)
522 fputs("PAGE: 1 1\n", stderr);
523
524 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
525 rewind(temp);
526 copy_bytes(temp, 0);
527 }
528 }
529 }
530
531 /*
532 * End the job with the appropriate JCL command or CTRL-D otherwise.
533 */
534
535 if (ppd != NULL && ppd->jcl_end)
536 fputs(ppd->jcl_end, stdout);
537 else
538 putchar(0x04);
539
540 /*
541 * Close files and remove the temporary file if needed...
542 */
543
544 if (slowcollate || sloworder)
545 {
546 fclose(temp);
547 unlink(tempfile);
548 }
549
550 ppdClose(ppd);
551
552 if (fp != stdin)
553 fclose(fp);
554
555 return (0);
556 }
557
558
559 /*
560 * 'check_range()' - Check to see if the current page is selected for
561 * printing.
562 */
563
564 static int /* O - 1 if selected, 0 otherwise */
565 check_range(int page) /* I - Page number */
566 {
567 const char *range; /* Pointer into range string */
568 int lower, upper; /* Lower and upper page numbers */
569
570
571 if (PageSet != NULL)
572 {
573 /*
574 * See if we only print even or odd pages...
575 */
576
577 if (strcasecmp(PageSet, "even") == 0 && (page & 1))
578 return (0);
579 if (strcasecmp(PageSet, "odd") == 0 && !(page & 1))
580 return (0);
581 }
582
583 if (PageRanges == NULL)
584 return (1); /* No range, print all pages... */
585
586 for (range = PageRanges; *range != '\0';)
587 {
588 if (*range == '-')
589 {
590 lower = 1;
591 range ++;
592 upper = strtol(range, (char **)&range, 10);
593 }
594 else
595 {
596 lower = strtol(range, (char **)&range, 10);
597
598 if (*range == '-')
599 {
600 range ++;
601 if (!isdigit(*range))
602 upper = 65535;
603 else
604 upper = strtol(range, (char **)&range, 10);
605 }
606 else
607 upper = lower;
608 }
609
610 if (page >= lower && page <= upper)
611 return (1);
612
613 if (*range == ',')
614 range ++;
615 else
616 break;
617 }
618
619 return (0);
620 }
621
622
623 /*
624 * 'copy_bytes()' - Copy bytes from the input file to stdout...
625 */
626
627 static void
628 copy_bytes(FILE *fp, /* I - File to read from */
629 size_t length) /* I - Length of page data */
630 {
631 char buffer[8192]; /* Data buffer */
632 size_t nbytes, /* Number of bytes read */
633 nleft; /* Number of bytes left/remaining */
634
635
636 nleft = length;
637
638 while (nleft > 0 || length == 0)
639 {
640 if (nleft > sizeof(buffer))
641 nbytes = sizeof(buffer);
642 else
643 nbytes = nleft;
644
645 if ((nbytes = fread(buffer, 1, nbytes, fp)) < 1)
646 return;
647
648 nleft -= nbytes;
649
650 fwrite(buffer, 1, nbytes, stdout);
651 }
652 }
653
654
655 /*
656 * 'end_nup()' - End processing for N-up printing...
657 */
658
659 static void
660 end_nup(int number) /* I - Page number */
661 {
662 if (Flip || Orientation || NUp > 1)
663 puts("ESPsave restore");
664
665 switch (NUp)
666 {
667 case 2 :
668 if ((number & 1) == 1)
669 puts("ESPshowpage");
670 break;
671
672 case 4 :
673 if ((number & 3) == 3)
674 puts("ESPshowpage");
675 break;
676 }
677 }
678
679
680 /*
681 * 'psgets()' - Get a line from a file.
682 *
683 * Note:
684 *
685 * This function differs from the gets() function in that it
686 * handles any combination of CR, LF, or CR LF to end input
687 * lines.
688 */
689
690 static char * /* O - String or NULL if EOF */
691 psgets(char *buf, /* I - Buffer to read into */
692 size_t len, /* I - Length of buffer */
693 FILE *fp) /* I - File to read from */
694 {
695 char *bufptr; /* Pointer into buffer */
696 int ch; /* Character from file */
697
698
699 len --;
700 bufptr = buf;
701 ch = EOF;
702
703 while ((bufptr - buf) < len)
704 {
705 if ((ch = getc(fp)) == EOF)
706 break;
707
708 if (ch == 0x0d)
709 {
710 /*
711 * Got a CR; see if there is a LF as well...
712 */
713
714 ch = getc(fp);
715 if (ch != EOF && ch != 0x0a)
716 ungetc(ch, fp); /* Nope, save it for later... */
717
718 break;
719 }
720 else if (ch == 0x0a)
721 break;
722 else
723 *bufptr++ = ch;
724 }
725
726 /*
727 * Nul-terminate the string and return it (or NULL for EOF).
728 */
729
730 *bufptr = '\0';
731
732 if (ch == EOF && bufptr == buf)
733 return (NULL);
734 else
735 return (buf);
736 }
737
738
739 /*
740 * 'start_nup()' - Start processing for N-up printing...
741 */
742
743 static void
744 start_nup(int number) /* I - Page number */
745 {
746 int x, y; /* Relative position of subpage */
747 float w, l, /* Width and length of subpage */
748 tx, ty; /* Translation values for subpage */
749 float pw, pl; /* Printable width and length of full page */
750
751
752 if (Flip || Orientation || NUp > 1)
753 puts("/ESPsave save def");
754
755 if (Flip)
756 printf("%.0f 0 translate -1 1 scale\n", PageWidth);
757
758 pw = PageRight - PageLeft;
759 pl = PageTop - PageBottom;
760
761 switch (Orientation)
762 {
763 case 1 : /* Landscape */
764 printf("%.0f 0 translate 90 rotate\n", PageLength);
765 break;
766 case 2 : /* Reverse Portrait */
767 printf("%.0f %.0f translate 180 rotate\n", PageWidth, PageLength);
768 break;
769 case 3 : /* Reverse Landscape */
770 printf("0 %.0f translate -90 rotate\n", PageWidth);
771 break;
772 }
773
774 switch (NUp)
775 {
776 case 2 :
777 x = number & 1;
778
779 if (Orientation & 1)
780 {
781 x = 1 - x;
782 w = pl;
783 l = w * PageLength / PageWidth;
784
785 if (l > (pw * 0.5))
786 {
787 l = pw * 0.5;
788 w = l * PageWidth / PageLength;
789 }
790
791 tx = pw * 0.5 - l;
792 ty = (pl - w) * 0.5;
793 }
794 else
795 {
796 l = pw;
797 w = l * PageWidth / PageLength;
798
799 if (w > (pl * 0.5))
800 {
801 w = pl * 0.5;
802 l = w * PageLength / PageWidth;
803 }
804
805 tx = pl * 0.5 - w;
806 ty = (pw - l) * 0.5;
807 }
808
809 if (Duplex && (number & 2))
810 printf("%.0f %.0f translate\n", PageWidth - PageRight, PageBottom);
811 else
812 printf("%.0f %.0f translate\n", PageLeft, PageBottom);
813
814 if (Orientation & 1)
815 {
816 printf("0 %.0f translate -90 rotate\n", pl);
817 printf("%.0f %.0f translate %.3f %.3f scale\n",
818 ty, tx + l * x, w / pw, l / pl);
819 }
820 else
821 {
822 printf("%.0f 0 translate 90 rotate\n", pw);
823 printf("%.0f %.0f translate %.3f %.3f scale\n",
824 tx + w * x, ty, w / pw, l / pl);
825 }
826
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 case 4 :
837 x = number & 1;
838 y = 1 - ((number & 2) != 0);
839
840 w = pw * 0.5;
841 l = w * PageLength / PageWidth;
842
843 if (l > (pl * 0.5))
844 {
845 l = pl * 0.5;
846 w = l * PageWidth / PageLength;
847 }
848
849 if (Duplex && (number & 4))
850 printf("%.0f %.0f translate\n", PageWidth - PageRight, PageBottom);
851 else
852 printf("%.0f %.0f translate\n", PageLeft, PageBottom);
853
854 printf("%.0f %.0f translate %.3f %.3f scale\n", x * w, y * l,
855 w / PageWidth, l / PageLength);
856 printf("newpath\n"
857 "0 0 moveto\n"
858 "%.0f 0 lineto\n"
859 "%.0f %.0f lineto\n"
860 "0 %.0f lineto\n"
861 "closepath clip newpath\n",
862 PageWidth, PageWidth, PageLength, PageLength);
863 break;
864 }
865 }
866
867
868 /*
869 * End of "$Id: pstops.c,v 1.36 2000/03/21 04:03:28 mike Exp $".
870 */