]> git.ipfire.org Git - thirdparty/cups.git/blob - filter/pstops.c
LOTS of changes:
[thirdparty/cups.git] / filter / pstops.c
1 /*
2 * "$Id: pstops.c,v 1.17 1999/05/18 21:21:46 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 char *PageRanges = NULL; /* Range of pages selected */
57 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 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
98
99 if (argc < 6 || argc > 7)
100 {
101 fputs("ERROR: pstops job-id user title copies options [file]\n", stderr);
102 return (1);
103 }
104
105 /*
106 * If we have 7 arguments, print the file named on the command-line.
107 * Otherwise, send stdin instead...
108 */
109
110 if (argc == 6)
111 fp = stdin;
112 else
113 {
114 /*
115 * Try to open the print file...
116 */
117
118 if ((fp = fopen(argv[6], "rb")) == NULL)
119 {
120 perror("ERROR: unable to open print file - ");
121 return (1);
122 }
123 }
124
125 /*
126 * Process command-line options and write the prolog...
127 */
128
129 g = 1.0;
130 b = 1.0;
131
132 ppd = ppdOpenFile(getenv("PPD"));
133
134 options = NULL;
135 num_options = cupsParseOptions(argv[5], 0, &options);
136
137 ppd = SetCommonOptions(num_options, options, 1);
138
139 ppdMarkDefaults(ppd);
140 cupsMarkOptions(ppd, num_options, options);
141
142 if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL)
143 PageRanges = val;
144
145 if ((val = cupsGetOption("page-set", num_options, options)) != NULL)
146 PageSet = val;
147
148 if ((val = cupsGetOption("copies", num_options, options)) != NULL)
149 Copies = atoi(val);
150
151 if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
152 {
153 /*
154 * This IPP attribute is unnecessarily complicated...
155 *
156 * single-document, separate-documents-collated-copies, and
157 * single-document-new-sheet all require collated copies.
158 *
159 * separate-documents-collated-copies allows for uncollated copies.
160 */
161
162 Collate = strcmp(val, "separate-documents-collated-copies") != 0;
163 }
164
165 if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
166 strcmp(val, "True") == 0)
167 Collate = 1;
168
169 if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL &&
170 strcmp(val, "Reverse") == 0)
171 Order = 1;
172
173 if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
174 NUp = atoi(val);
175
176 if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
177 g = atoi(val) * 0.001f;
178
179 if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
180 b = atoi(val) * 0.01f;
181
182 /*
183 * See if we have to filter the fast or slow way...
184 */
185
186 if (ppdFindOption(ppd, "Collate") == NULL && Collate && Copies > 1)
187 slowcollate = 1;
188 else
189 slowcollate = 0;
190
191 if (ppdFindOption(ppd, "OutputOrder") == NULL && Order)
192 sloworder = 1;
193 else
194 sloworder = 0;
195
196 /*
197 * If we need to filter slowly, then create a temporary file for page data...
198 *
199 * If the temp file can't be created, then we'll ignore the collating/output
200 * order options...
201 */
202
203 if (sloworder || slowcollate)
204 {
205 temp = fopen(tmpnam(tempfile), "wb+");
206
207 if (temp == NULL)
208 slowcollate = sloworder = 0;
209 }
210
211 /*
212 * Write any "exit server" options that have been selected...
213 */
214
215 ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
216
217 /*
218 * Write any JCL commands that are needed to print PostScript code...
219 */
220
221 if (ppd != NULL && ppd->jcl_begin && ppd->jcl_ps)
222 {
223 fputs(ppd->jcl_begin, stdout);
224 ppdEmit(ppd, stdout, PPD_ORDER_JCL);
225 fputs(ppd->jcl_ps, stdout);
226 }
227
228 /*
229 * Read the first line to see if we have DSC comments...
230 */
231
232 if (psgets(line, sizeof(line), fp) == NULL)
233 {
234 fputs("ERROR: Empty print file!\n", stderr);
235 ppdClose(ppd);
236 return (1);
237 }
238
239 /*
240 * Start sending the document with any commands needed...
241 */
242
243 puts(line);
244
245 ppdEmit(ppd, stdout, PPD_ORDER_DOCUMENT);
246 ppdEmit(ppd, stdout, PPD_ORDER_ANY);
247 ppdEmit(ppd, stdout, PPD_ORDER_PROLOG);
248
249 if (NUp > 1)
250 puts("userdict begin\n"
251 "/ESPshowpage /showpage load def\n"
252 "/showpage { } def\n"
253 "end");
254
255 if (g != 1.0 || b != 1.0)
256 printf("{ neg 1 add %.3f exp neg 1 add %.3f mul } bind settransfer\n", g, b);
257
258 if (Copies > 1 && (!Collate || !slowcollate))
259 printf("/#copies %d def\n", Copies);
260
261 if (strncmp(line, "%!PS-Adobe-", 11) == 0)
262 {
263 /*
264 * OK, we have DSC comments; read until we find a %%Page comment...
265 */
266
267 while (psgets(line, sizeof(line), fp) != NULL)
268 if (strncmp(line, "%%Page:", 7) == 0)
269 break;
270 else
271 puts(line);
272
273 /*
274 * Then read all of the pages, filtering as needed...
275 */
276
277 for (;;)
278 {
279 if (strncmp(line, "%%Page:", 7) == 0)
280 {
281 if (sscanf(line, "%*s%*s%d", &number) == 1)
282 {
283 if (!check_range(number))
284 {
285 while (psgets(line, sizeof(line), fp) != NULL)
286 if (strncmp(line, "%%Page:", 7) == 0)
287 break;
288 continue;
289 }
290
291 if (!sloworder && NumPages > 0)
292 end_nup(NumPages - 1);
293
294 if (slowcollate || sloworder)
295 Pages[NumPages] = ftell(temp);
296
297 NumPages ++;
298
299 if (!sloworder)
300 {
301 if (ppd == NULL || ppd->num_filters == 0)
302 fprintf(stderr, "PAGE: %d %d\n", NumPages, Copies);
303
304 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
305 start_nup(NumPages - 1);
306 }
307 }
308 }
309 else
310 {
311 if (!sloworder)
312 puts(line);
313
314 if (slowcollate || sloworder)
315 {
316 fputs(line, temp);
317 putc('\n', temp);
318 }
319 }
320
321 if (psgets(line, sizeof(line), fp) == NULL)
322 break;
323 }
324
325 if (!sloworder)
326 end_nup((NumPages + NUp - 1) & (NUp - 1));
327
328 if (slowcollate || sloworder)
329 {
330 Pages[NumPages] = ftell(temp);
331
332 if (!sloworder)
333 {
334 while (Copies > 1)
335 {
336 rewind(temp);
337
338 for (number = 0; number < NumPages; number ++)
339 {
340 if (ppd == NULL || ppd->num_filters == 0)
341 fprintf(stderr, "PAGE: %d 1\n", number + 1);
342
343 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
344 start_nup(number);
345 copy_bytes(temp, Pages[number + 1] - Pages[number]);
346 end_nup(number);
347 }
348
349 Copies --;
350 }
351 }
352 else
353 {
354 do
355 {
356 for (number = NumPages - 1; number >= 0; number --)
357 {
358 if (ppd == NULL || ppd->num_filters == 0)
359 fprintf(stderr, "PAGE: %d %d\n", NumPages - number,
360 slowcollate ? 1 : Copies);
361
362 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
363 start_nup(NumPages - 1 - number);
364 fseek(temp, Pages[number], SEEK_SET);
365 copy_bytes(temp, Pages[number + 1] - Pages[number]);
366 end_nup(NumPages - 1 - number);
367 }
368
369 Copies --;
370 }
371 while (Copies > 0 || !slowcollate);
372 }
373 }
374 }
375 else
376 {
377 /*
378 * No DSC comments - write any page commands and then the rest of the file...
379 */
380
381 if (ppd == NULL || ppd->num_filters == 0)
382 fprintf(stderr, "PAGE: 1 %d\n", slowcollate ? 1 : Copies);
383
384 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
385
386 while (psgets(line, sizeof(line), fp) != NULL)
387 {
388 puts(line);
389
390 if (slowcollate)
391 {
392 fputs(line, temp);
393 putc('\n', temp);
394 }
395 }
396
397 if (slowcollate)
398 {
399 while (Copies > 1)
400 {
401 if (ppd == NULL || ppd->num_filters == 0)
402 fputs("PAGE: 1 1\n", stderr);
403
404 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
405 rewind(temp);
406 copy_bytes(temp, 0);
407 }
408 }
409 }
410
411 /*
412 * End the job with the appropriate JCL command or CTRL-D otherwise.
413 */
414
415 if (ppd != NULL && ppd->jcl_end)
416 fputs(ppd->jcl_end, stdout);
417 else
418 putchar(0x04);
419
420 /*
421 * Close files and remove the temporary file if needed...
422 */
423
424 if (slowcollate || sloworder)
425 {
426 fclose(temp);
427 unlink(tempfile);
428 }
429
430 ppdClose(ppd);
431
432 if (fp != stdin)
433 fclose(fp);
434
435 return (0);
436 }
437
438
439 /*
440 * 'check_range()' - Check to see if the current page is selected for
441 * printing.
442 */
443
444 static int /* O - 1 if selected, 0 otherwise */
445 check_range(int page) /* I - Page number */
446 {
447 char *range; /* Pointer into range string */
448 int lower, upper; /* Lower and upper page numbers */
449
450
451 if (PageSet != NULL)
452 {
453 /*
454 * See if we only print even or odd pages...
455 */
456
457 if (strcmp(PageSet, "even") == 0 && (page & 1))
458 return (0);
459 if (strcmp(PageSet, "odd") == 0 && !(page & 1))
460 return (0);
461 }
462
463 if (PageRanges == NULL)
464 return (1); /* No range, print all pages... */
465
466 for (range = PageRanges; *range != '\0';)
467 {
468 if (*range == '-')
469 {
470 lower = 1;
471 range ++;
472 upper = strtol(range, &range, 10);
473 }
474 else
475 {
476 lower = strtol(range, &range, 10);
477
478 if (*range == '-')
479 {
480 range ++;
481 if (!isdigit(*range))
482 upper = 65535;
483 else
484 upper = strtol(range, &range, 10);
485 }
486 else
487 upper = lower;
488 }
489
490 if (page >= lower && page <= upper)
491 return (1);
492
493 if (*range == ',')
494 range ++;
495 else
496 break;
497 }
498
499 return (0);
500 }
501
502
503 /*
504 * 'copy_bytes()' - Copy bytes from the input file to stdout...
505 */
506
507 static void
508 copy_bytes(FILE *fp, /* I - File to read from */
509 size_t length) /* I - Length of page data */
510 {
511 char buffer[8192]; /* Data buffer */
512 size_t nbytes, /* Number of bytes read */
513 nleft; /* Number of bytes left/remaining */
514
515
516 nleft = length;
517
518 while (nleft > 0 || length == 0)
519 {
520 if ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) < 1)
521 return;
522
523 nleft -= nbytes;
524
525 fwrite(buffer, 1, nbytes, stdout);
526 }
527 }
528
529
530 /*
531 * 'end_nup()' - End processing for N-up printing...
532 */
533
534 static void
535 end_nup(int number) /* I - Page number */
536 {
537 puts("grestore");
538
539 switch (NUp)
540 {
541 case 2 :
542 if ((number & 1) == 1)
543 puts("ESPshowpage");
544 break;
545
546 case 4 :
547 if ((number & 3) == 3)
548 puts("ESPshowpage");
549 break;
550 }
551 }
552
553
554 /*
555 * 'psgets()' - Get a line from a file.
556 *
557 * Note:
558 *
559 * This function differs from the gets() function in that it
560 * handles any combination of CR, LF, or CR LF to end input
561 * lines.
562 */
563
564 static char * /* O - String or NULL if EOF */
565 psgets(char *buf, /* I - Buffer to read into */
566 size_t len, /* I - Length of buffer */
567 FILE *fp) /* I - File to read from */
568 {
569 char *bufptr; /* Pointer into buffer */
570 int ch; /* Character from file */
571
572
573 len --;
574 bufptr = buf;
575
576 while ((bufptr - buf) < len)
577 {
578 if ((ch = getc(fp)) == EOF)
579 break;
580
581 if (ch == 0x0d)
582 {
583 /*
584 * Got a CR; see if there is a LF as well...
585 */
586
587 ch = getc(fp);
588 if (ch != EOF && ch != 0x0a)
589 ungetc(ch, fp); /* Nope, save it for later... */
590
591 break;
592 }
593 else if (ch == 0x0a)
594 break;
595 else
596 *bufptr++ = ch;
597 }
598
599 /*
600 * Nul-terminate the string and return it (or NULL for EOF).
601 */
602
603 *bufptr = '\0';
604
605 if (ch == EOF && bufptr == buf)
606 return (NULL);
607 else
608 return (buf);
609 }
610
611
612 /*
613 * 'start_nup()' - Start processing for N-up printing...
614 */
615
616 static void
617 start_nup(int number) /* I - Page number */
618 {
619 int x, y; /* Relative position of subpage */
620 float w, l, /* Width and length of subpage */
621 tx, ty; /* Translation values for subpage */
622
623
624 puts("gsave");
625
626 if (Flip)
627 printf("%.0f 0 translate -1 1 scale\n", PageWidth);
628
629 switch (Orientation)
630 {
631 case 1 : /* Landscape */
632 printf("%.0f 0 translate 90 rotate\n", PageLength);
633 break;
634 case 2 : /* Reverse Portrait */
635 printf("%.0f %.0f translate 180 rotate\n", PageWidth, PageLength);
636 break;
637 case 3 : /* Reverse Landscape */
638 printf("0 %.0f translate -90 rotate\n", PageWidth);
639 break;
640 }
641
642 switch (NUp)
643 {
644 case 2 :
645 x = number & 1;
646
647 if (Orientation & 1)
648 {
649 x = 1 - x;
650 w = PageLength;
651 l = w * PageLength / PageWidth;
652
653 if (l > (PageWidth * 0.5))
654 {
655 l = PageWidth * 0.5;
656 w = l * PageWidth / PageLength;
657 }
658
659 tx = PageWidth * 0.5 - l;
660 ty = (PageLength - w) * 0.5;
661 }
662 else
663 {
664 l = PageWidth;
665 w = l * PageWidth / PageLength;
666
667 if (w > (PageLength * 0.5))
668 {
669 w = PageLength * 0.5;
670 l = w * PageLength / PageWidth;
671 }
672
673 tx = PageLength * 0.5 - w;
674 ty = (PageWidth - l) * 0.5;
675 }
676
677 if (Orientation & 1)
678 {
679 printf("0 %.0f translate -90 rotate\n", PageLength);
680 printf("%.0f %.0f translate %.3f %.3f scale\n",
681 ty, tx + l * x, w / PageWidth, l / PageLength);
682 }
683 else
684 {
685 printf("%.0f 0 translate 90 rotate\n", PageWidth);
686 printf("%.0f %.0f translate %.3f %.3f scale\n",
687 tx + w * x, ty, w / PageWidth, l / PageLength);
688 }
689
690 printf("newpath\n"
691 "0 0 moveto\n"
692 "%.0f 0 lineto\n"
693 "%.0f %.0f lineto\n"
694 "0 %.0f lineto\n"
695 "closepath clip newpath\n",
696 PageWidth, PageWidth, PageLength, PageLength);
697 break;
698
699 case 4 :
700 x = number & 1;
701 y = 1 - ((number & 2) != 0);
702 w = PageWidth * 0.5;
703 l = PageLength * 0.5;
704
705 printf("%.0f %.0f translate 0.5 0.5 scale\n", x * w, y * l);
706 printf("newpath\n"
707 "0 0 moveto\n"
708 "%.0f 0 lineto\n"
709 "%.0f %.0f lineto\n"
710 "0 %.0f lineto\n"
711 "closepath clip newpath\n",
712 PageWidth, PageWidth, PageLength, PageLength);
713 break;
714 }
715 }
716
717
718 /*
719 * End of "$Id: pstops.c,v 1.17 1999/05/18 21:21:46 mike Exp $".
720 */