1 /*
2 * Hewlett-Packard Page Control Language filter for CUPS.
3 *
4 * Copyright 2007-2015 by Apple Inc.
5 * Copyright 1993-2007 by Easy Software Products.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * missing or damaged, see the license at "http://www.cups.org/".
12 *
13 * This file is subject to the Apple OS-Developed Software exception.
14 */
15
16 /*
17 * Include necessary headers...
18 */
19
20 #include <cups/cups.h>
21 #include <cups/ppd.h>
22 #include <cups/string-private.h>
23 #include <cups/language-private.h>
24 #include <cups/raster.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <signal.h>
28
29
30 /*
31 * Globals...
32 */
33
34 unsigned char *Planes[4], /* Output buffers */
35 *CompBuffer, /* Compression buffer */
36 *BitBuffer; /* Buffer for output bits */
37 unsigned NumPlanes, /* Number of color planes */
38 ColorBits, /* Number of bits per color */
39 Feed; /* Number of lines to skip */
40 int Duplex, /* Current duplex mode */
41 Page, /* Current page number */
42 Canceled; /* Has the current job been canceled? */
43
44
45 /*
46 * Prototypes...
47 */
48
49 void Setup(void);
50 void StartPage(ppd_file_t *ppd, cups_page_header2_t *header);
51 void EndPage(void);
52 void Shutdown(void);
53
54 void CancelJob(int sig);
55 void CompressData(unsigned char *line, unsigned length, unsigned plane, unsigned type);
56 void OutputLine(cups_page_header2_t *header);
57
58
59 /*
60 * 'Setup()' - Prepare the printer for printing.
61 */
62
63 void
Setup(void)64 Setup(void)
65 {
66 /*
67 * Send a PCL reset sequence.
68 */
69
70 putchar(0x1b);
71 putchar('E');
72 }
73
74
75 /*
76 * 'StartPage()' - Start a page of graphics.
77 */
78
79 void
StartPage(ppd_file_t * ppd,cups_page_header2_t * header)80 StartPage(ppd_file_t *ppd, /* I - PPD file */
81 cups_page_header2_t *header) /* I - Page header */
82 {
83 unsigned plane; /* Looping var */
84
85
86 /*
87 * Show page device dictionary...
88 */
89
90 fprintf(stderr, "DEBUG: StartPage...\n");
91 fprintf(stderr, "DEBUG: Duplex = %d\n", header->Duplex);
92 fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", header->HWResolution[0], header->HWResolution[1]);
93 fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n", header->ImagingBoundingBox[0], header->ImagingBoundingBox[1], header->ImagingBoundingBox[2], header->ImagingBoundingBox[3]);
94 fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", header->Margins[0], header->Margins[1]);
95 fprintf(stderr, "DEBUG: ManualFeed = %d\n", header->ManualFeed);
96 fprintf(stderr, "DEBUG: MediaPosition = %d\n", header->MediaPosition);
97 fprintf(stderr, "DEBUG: NumCopies = %d\n", header->NumCopies);
98 fprintf(stderr, "DEBUG: Orientation = %d\n", header->Orientation);
99 fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", header->PageSize[0], header->PageSize[1]);
100 fprintf(stderr, "DEBUG: cupsWidth = %d\n", header->cupsWidth);
101 fprintf(stderr, "DEBUG: cupsHeight = %d\n", header->cupsHeight);
102 fprintf(stderr, "DEBUG: cupsMediaType = %d\n", header->cupsMediaType);
103 fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", header->cupsBitsPerColor);
104 fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", header->cupsBitsPerPixel);
105 fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", header->cupsBytesPerLine);
106 fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", header->cupsColorOrder);
107 fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", header->cupsColorSpace);
108 fprintf(stderr, "DEBUG: cupsCompression = %d\n", header->cupsCompression);
109
110 /*
111 * Setup printer/job attributes...
112 */
113
114 Duplex = header->Duplex;
115 ColorBits = header->cupsBitsPerColor;
116
117 if ((!Duplex || (Page & 1)) && header->MediaPosition)
118 printf("\033&l%dH", /* Set media position */
119 header->MediaPosition);
120
121 if (Duplex && ppd && ppd->model_number == 2)
122 {
123 /*
124 * Handle duplexing on new DeskJet printers...
125 */
126
127 printf("\033&l-2H"); /* Load media */
128
129 if (Page & 1)
130 printf("\033&l2S"); /* Set duplex mode */
131 }
132
133 if (!Duplex || (Page & 1) || (ppd && ppd->model_number == 2))
134 {
135 /*
136 * Set the media size...
137 */
138
139 printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */
140 printf("\033&l0O"); /* Set portrait orientation */
141
142 switch (header->PageSize[1])
143 {
144 case 540 : /* Monarch Envelope */
145 printf("\033&l80A"); /* Set page size */
146 break;
147
148 case 595 : /* A5 */
149 printf("\033&l25A"); /* Set page size */
150 break;
151
152 case 624 : /* DL Envelope */
153 printf("\033&l90A"); /* Set page size */
154 break;
155
156 case 649 : /* C5 Envelope */
157 printf("\033&l91A"); /* Set page size */
158 break;
159
160 case 684 : /* COM-10 Envelope */
161 printf("\033&l81A"); /* Set page size */
162 break;
163
164 case 709 : /* B5 Envelope */
165 printf("\033&l100A"); /* Set page size */
166 break;
167
168 case 756 : /* Executive */
169 printf("\033&l1A"); /* Set page size */
170 break;
171
172 case 792 : /* Letter */
173 printf("\033&l2A"); /* Set page size */
174 break;
175
176 case 842 : /* A4 */
177 printf("\033&l26A"); /* Set page size */
178 break;
179
180 case 1008 : /* Legal */
181 printf("\033&l3A"); /* Set page size */
182 break;
183
184 case 1191 : /* A3 */
185 printf("\033&l27A"); /* Set page size */
186 break;
187
188 case 1224 : /* Tabloid */
189 printf("\033&l6A"); /* Set page size */
190 break;
191 }
192
193 printf("\033&l%dP", /* Set page length */
194 header->PageSize[1] / 12);
195 printf("\033&l0E"); /* Set top margin to 0 */
196 }
197
198 if (!Duplex || (Page & 1))
199 {
200 /*
201 * Set other job options...
202 */
203
204 printf("\033&l%dX", header->NumCopies); /* Set number copies */
205
206 if (header->cupsMediaType &&
207 (!ppd || ppd->model_number != 2 || header->HWResolution[0] == 600))
208 printf("\033&l%dM", /* Set media type */
209 header->cupsMediaType);
210
211 if (!ppd || ppd->model_number != 2)
212 {
213 int mode = Duplex ? 1 + header->Tumble != 0 : 0;
214
215 printf("\033&l%dS", mode); /* Set duplex mode */
216 printf("\033&l0L"); /* Turn off perforation skip */
217 }
218 }
219 else if (!ppd || ppd->model_number != 2)
220 printf("\033&a2G"); /* Set back side */
221
222 /*
223 * Set graphics mode...
224 */
225
226 if (ppd && ppd->model_number == 2)
227 {
228 /*
229 * Figure out the number of color planes...
230 */
231
232 if (header->cupsColorSpace == CUPS_CSPACE_KCMY)
233 NumPlanes = 4;
234 else
235 NumPlanes = 1;
236
237 /*
238 * Set the resolution and top-of-form...
239 */
240
241 printf("\033&u%dD", header->HWResolution[0]);
242 /* Resolution */
243 printf("\033&l0e0L"); /* Reset top and don't skip */
244 printf("\033*p0Y\033*p0X"); /* Set top of form */
245
246 /*
247 * Send 26-byte configure image data command with horizontal and
248 * vertical resolutions as well as a color count...
249 */
250
251 printf("\033*g26W");
252 putchar(2); /* Format 2 */
253 putchar((int)NumPlanes); /* Output planes */
254
255 putchar((int)(header->HWResolution[0] >> 8));/* Black resolution */
256 putchar((int)header->HWResolution[0]);
257 putchar((int)(header->HWResolution[1] >> 8));
258 putchar((int)header->HWResolution[1]);
259 putchar(0);
260 putchar(1 << ColorBits); /* # of black levels */
261
262 putchar((int)(header->HWResolution[0] >> 8));/* Cyan resolution */
263 putchar((int)header->HWResolution[0]);
264 putchar((int)(header->HWResolution[1] >> 8));
265 putchar((int)header->HWResolution[1]);
266 putchar(0);
267 putchar(1 << ColorBits); /* # of cyan levels */
268
269 putchar((int)(header->HWResolution[0] >> 8));/* Magenta resolution */
270 putchar((int)header->HWResolution[0]);
271 putchar((int)(header->HWResolution[1] >> 8));
272 putchar((int)header->HWResolution[1]);
273 putchar(0);
274 putchar(1 << ColorBits); /* # of magenta levels */
275
276 putchar((int)(header->HWResolution[0] >> 8));/* Yellow resolution */
277 putchar((int)header->HWResolution[0]);
278 putchar((int)(header->HWResolution[1] >> 8));
279 putchar((int)header->HWResolution[1]);
280 putchar(0);
281 putchar(1 << ColorBits); /* # of yellow levels */
282
283 printf("\033&l0H"); /* Set media position */
284 }
285 else
286 {
287 printf("\033*t%uR", header->HWResolution[0]);
288 /* Set resolution */
289
290 if (header->cupsColorSpace == CUPS_CSPACE_KCMY)
291 {
292 NumPlanes = 4;
293 printf("\033*r-4U"); /* Set KCMY graphics */
294 }
295 else if (header->cupsColorSpace == CUPS_CSPACE_CMY)
296 {
297 NumPlanes = 3;
298 printf("\033*r-3U"); /* Set CMY graphics */
299 }
300 else
301 NumPlanes = 1; /* Black&white graphics */
302
303 /*
304 * Set size and position of graphics...
305 */
306
307 printf("\033*r%uS", header->cupsWidth); /* Set width */
308 printf("\033*r%uT", header->cupsHeight); /* Set height */
309
310 printf("\033&a0H"); /* Set horizontal position */
311
312 if (ppd)
313 printf("\033&a%.0fV", /* Set vertical position */
314 10.0 * (ppd->sizes[0].length - ppd->sizes[0].top));
315 else
316 printf("\033&a0V"); /* Set top-of-page */
317 }
318
319 printf("\033*r1A"); /* Start graphics */
320
321 if (header->cupsCompression)
322 printf("\033*b%uM", /* Set compression */
323 header->cupsCompression);
324
325 Feed = 0; /* No blank lines yet */
326
327 /*
328 * Allocate memory for a line of graphics...
329 */
330
331 if ((Planes[0] = malloc(header->cupsBytesPerLine + NumPlanes)) == NULL)
332 {
333 fputs("ERROR: Unable to allocate memory\n", stderr);
334 exit(1);
335 }
336
337 for (plane = 1; plane < NumPlanes; plane ++)
338 Planes[plane] = Planes[0] + plane * header->cupsBytesPerLine / NumPlanes;
339
340 if (ColorBits > 1)
341 BitBuffer = malloc(ColorBits * ((header->cupsWidth + 7) / 8));
342 else
343 BitBuffer = NULL;
344
345 if (header->cupsCompression)
346 CompBuffer = malloc(header->cupsBytesPerLine * 2 + 2);
347 else
348 CompBuffer = NULL;
349 }
350
351
352 /*
353 * 'EndPage()' - Finish a page of graphics.
354 */
355
356 void
EndPage(void)357 EndPage(void)
358 {
359 /*
360 * Eject the current page...
361 */
362
363 if (NumPlanes > 1)
364 {
365 printf("\033*rC"); /* End color GFX */
366
367 if (!(Duplex && (Page & 1)))
368 printf("\033&l0H"); /* Eject current page */
369 }
370 else
371 {
372 printf("\033*r0B"); /* End GFX */
373
374 if (!(Duplex && (Page & 1)))
375 printf("\014"); /* Eject current page */
376 }
377
378 fflush(stdout);
379
380 /*
381 * Free memory...
382 */
383
384 free(Planes[0]);
385
386 if (BitBuffer)
387 free(BitBuffer);
388
389 if (CompBuffer)
390 free(CompBuffer);
391 }
392
393
394 /*
395 * 'Shutdown()' - Shutdown the printer.
396 */
397
398 void
Shutdown(void)399 Shutdown(void)
400 {
401 /*
402 * Send a PCL reset sequence.
403 */
404
405 putchar(0x1b);
406 putchar('E');
407 }
408
409
410 /*
411 * 'CancelJob()' - Cancel the current job...
412 */
413
414 void
CancelJob(int sig)415 CancelJob(int sig) /* I - Signal */
416 {
417 (void)sig;
418
419 Canceled = 1;
420 }
421
422
423 /*
424 * 'CompressData()' - Compress a line of graphics.
425 */
426
427 void
CompressData(unsigned char * line,unsigned length,unsigned plane,unsigned type)428 CompressData(unsigned char *line, /* I - Data to compress */
429 unsigned length, /* I - Number of bytes */
430 unsigned plane, /* I - Color plane */
431 unsigned type) /* I - Type of compression */
432 {
433 unsigned char *line_ptr, /* Current byte pointer */
434 *line_end, /* End-of-line byte pointer */
435 *comp_ptr, /* Pointer into compression buffer */
436 *start; /* Start of compression sequence */
437 unsigned count; /* Count of bytes for output */
438
439
440 switch (type)
441 {
442 default :
443 /*
444 * Do no compression...
445 */
446
447 line_ptr = line;
448 line_end = line + length;
449 break;
450
451 case 1 :
452 /*
453 * Do run-length encoding...
454 */
455
456 line_end = line + length;
457 for (line_ptr = line, comp_ptr = CompBuffer;
458 line_ptr < line_end;
459 comp_ptr += 2, line_ptr += count)
460 {
461 for (count = 1;
462 (line_ptr + count) < line_end &&
463 line_ptr[0] == line_ptr[count] &&
464 count < 256;
465 count ++);
466
467 comp_ptr[0] = (unsigned char)(count - 1);
468 comp_ptr[1] = line_ptr[0];
469 }
470
471 line_ptr = CompBuffer;
472 line_end = comp_ptr;
473 break;
474
475 case 2 :
476 /*
477 * Do TIFF pack-bits encoding...
478 */
479
480 line_ptr = line;
481 line_end = line + length;
482 comp_ptr = CompBuffer;
483
484 while (line_ptr < line_end)
485 {
486 if ((line_ptr + 1) >= line_end)
487 {
488 /*
489 * Single byte on the end...
490 */
491
492 *comp_ptr++ = 0x00;
493 *comp_ptr++ = *line_ptr++;
494 }
495 else if (line_ptr[0] == line_ptr[1])
496 {
497 /*
498 * Repeated sequence...
499 */
500
501 line_ptr ++;
502 count = 2;
503
504 while (line_ptr < (line_end - 1) &&
505 line_ptr[0] == line_ptr[1] &&
506 count < 127)
507 {
508 line_ptr ++;
509 count ++;
510 }
511
512 *comp_ptr++ = (unsigned char)(257 - count);
513 *comp_ptr++ = *line_ptr++;
514 }
515 else
516 {
517 /*
518 * Non-repeated sequence...
519 */
520
521 start = line_ptr;
522 line_ptr ++;
523 count = 1;
524
525 while (line_ptr < (line_end - 1) &&
526 line_ptr[0] != line_ptr[1] &&
527 count < 127)
528 {
529 line_ptr ++;
530 count ++;
531 }
532
533 *comp_ptr++ = (unsigned char)(count - 1);
534
535 memcpy(comp_ptr, start, count);
536 comp_ptr += count;
537 }
538 }
539
540 line_ptr = CompBuffer;
541 line_end = comp_ptr;
542 break;
543 }
544
545 /*
546 * Set the length of the data and write a raster plane...
547 */
548
549 printf("\033*b%d%c", (int)(line_end - line_ptr), plane);
550 fwrite(line_ptr, (size_t)(line_end - line_ptr), 1, stdout);
551 }
552
553
554 /*
555 * 'OutputLine()' - Output a line of graphics.
556 */
557
558 void
OutputLine(cups_page_header2_t * header)559 OutputLine(cups_page_header2_t *header) /* I - Page header */
560 {
561 unsigned plane, /* Current plane */
562 bytes, /* Bytes to write */
563 count; /* Bytes to convert */
564 unsigned char bit, /* Current plane data */
565 bit0, /* Current low bit data */
566 bit1, /* Current high bit data */
567 *plane_ptr, /* Pointer into Planes */
568 *bit_ptr; /* Pointer into BitBuffer */
569
570
571 /*
572 * Output whitespace as needed...
573 */
574
575 if (Feed > 0)
576 {
577 printf("\033*b%dY", Feed);
578 Feed = 0;
579 }
580
581 /*
582 * Write bitmap data as needed...
583 */
584
585 bytes = (header->cupsWidth + 7) / 8;
586
587 for (plane = 0; plane < NumPlanes; plane ++)
588 if (ColorBits == 1)
589 {
590 /*
591 * Send bits as-is...
592 */
593
594 CompressData(Planes[plane], bytes, plane < (NumPlanes - 1) ? 'V' : 'W',
595 header->cupsCompression);
596 }
597 else
598 {
599 /*
600 * Separate low and high bit data into separate buffers.
601 */
602
603 for (count = header->cupsBytesPerLine / NumPlanes,
604 plane_ptr = Planes[plane], bit_ptr = BitBuffer;
605 count > 0;
606 count -= 2, plane_ptr += 2, bit_ptr ++)
607 {
608 bit = plane_ptr[0];
609
610 bit0 = (unsigned char)(((bit & 64) << 1) | ((bit & 16) << 2) | ((bit & 4) << 3) | ((bit & 1) << 4));
611 bit1 = (unsigned char)((bit & 128) | ((bit & 32) << 1) | ((bit & 8) << 2) | ((bit & 2) << 3));
612
613 if (count > 1)
614 {
615 bit = plane_ptr[1];
616
617 bit0 |= (unsigned char)((bit & 1) | ((bit & 4) >> 1) | ((bit & 16) >> 2) | ((bit & 64) >> 3));
618 bit1 |= (unsigned char)(((bit & 2) >> 1) | ((bit & 8) >> 2) | ((bit & 32) >> 3) | ((bit & 128) >> 4));
619 }
620
621 bit_ptr[0] = bit0;
622 bit_ptr[bytes] = bit1;
623 }
624
625 /*
626 * Send low and high bits...
627 */
628
629 CompressData(BitBuffer, bytes, 'V', header->cupsCompression);
630 CompressData(BitBuffer + bytes, bytes, plane < (NumPlanes - 1) ? 'V' : 'W',
631 header->cupsCompression);
632 }
633
634 fflush(stdout);
635 }
636
637
638 /*
639 * 'main()' - Main entry and processing of driver.
640 */
641
642 int /* O - Exit status */
main(int argc,char * argv[])643 main(int argc, /* I - Number of command-line arguments */
644 char *argv[]) /* I - Command-line arguments */
645 {
646 int fd; /* File descriptor */
647 cups_raster_t *ras; /* Raster stream for printing */
648 cups_page_header2_t header; /* Page header from file */
649 unsigned y; /* Current line */
650 ppd_file_t *ppd; /* PPD file */
651 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
652 struct sigaction action; /* Actions for POSIX signals */
653 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
654
655
656 /*
657 * Make sure status messages are not buffered...
658 */
659
660 setbuf(stderr, NULL);
661
662 /*
663 * Check command-line...
664 */
665
666 if (argc < 6 || argc > 7)
667 {
668 /*
669 * We don't have the correct number of arguments; write an error message
670 * and return.
671 */
672
673 _cupsLangPrintFilter(stderr, "ERROR",
674 _("%s job-id user title copies options [file]"),
675 "rastertohp");
676 return (1);
677 }
678
679 /*
680 * Open the page stream...
681 */
682
683 if (argc == 7)
684 {
685 if ((fd = open(argv[6], O_RDONLY)) == -1)
686 {
687 _cupsLangPrintError("ERROR", _("Unable to open raster file"));
688 sleep(1);
689 return (1);
690 }
691 }
692 else
693 fd = 0;
694
695 ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
696
697 /*
698 * Register a signal handler to eject the current page if the
699 * job is cancelled.
700 */
701
702 Canceled = 0;
703
704 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
705 sigset(SIGTERM, CancelJob);
706 #elif defined(HAVE_SIGACTION)
707 memset(&action, 0, sizeof(action));
708
709 sigemptyset(&action.sa_mask);
710 action.sa_handler = CancelJob;
711 sigaction(SIGTERM, &action, NULL);
712 #else
713 signal(SIGTERM, CancelJob);
714 #endif /* HAVE_SIGSET */
715
716 /*
717 * Initialize the print device...
718 */
719
720 ppd = ppdOpenFile(getenv("PPD"));
721 if (!ppd)
722 {
723 ppd_status_t status; /* PPD error */
724 int linenum; /* Line number */
725
726 _cupsLangPrintFilter(stderr, "ERROR",
727 _("The PPD file could not be opened."));
728
729 status = ppdLastError(&linenum);
730
731 fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum);
732
733 return (1);
734 }
735
736 Setup();
737
738 /*
739 * Process pages as needed...
740 */
741
742 Page = 0;
743
744 while (cupsRasterReadHeader2(ras, &header))
745 {
746 /*
747 * Write a status message with the page number and number of copies.
748 */
749
750 if (Canceled)
751 break;
752
753 Page ++;
754
755 fprintf(stderr, "PAGE: %d %d\n", Page, header.NumCopies);
756 _cupsLangPrintFilter(stderr, "INFO", _("Starting page %d."), Page);
757
758 /*
759 * Start the page...
760 */
761
762 StartPage(ppd, &header);
763
764 /*
765 * Loop for each line on the page...
766 */
767
768 for (y = 0; y < header.cupsHeight; y ++)
769 {
770 /*
771 * Let the user know how far we have progressed...
772 */
773
774 if (Canceled)
775 break;
776
777 if ((y & 127) == 0)
778 {
779 _cupsLangPrintFilter(stderr, "INFO",
780 _("Printing page %d, %u%% complete."),
781 Page, 100 * y / header.cupsHeight);
782 fprintf(stderr, "ATTR: job-media-progress=%u\n",
783 100 * y / header.cupsHeight);
784 }
785
786 /*
787 * Read a line of graphics...
788 */
789
790 if (cupsRasterReadPixels(ras, Planes[0], header.cupsBytesPerLine) < 1)
791 break;
792
793 /*
794 * See if the line is blank; if not, write it to the printer...
795 */
796
797 if (Planes[0][0] ||
798 memcmp(Planes[0], Planes[0] + 1, header.cupsBytesPerLine - 1))
799 OutputLine(&header);
800 else
801 Feed ++;
802 }
803
804 /*
805 * Eject the page...
806 */
807
808 _cupsLangPrintFilter(stderr, "INFO", _("Finished page %d."), Page);
809
810 EndPage();
811
812 if (Canceled)
813 break;
814 }
815
816 /*
817 * Shutdown the printer...
818 */
819
820 Shutdown();
821
822 if (ppd)
823 ppdClose(ppd);
824
825 /*
826 * Close the raster stream...
827 */
828
829 cupsRasterClose(ras);
830 if (fd != 0)
831 close(fd);
832
833 /*
834 * If no pages were printed, send an error message...
835 */
836
837 if (Page == 0)
838 {
839 _cupsLangPrintFilter(stderr, "ERROR", _("No pages were found."));
840 return (1);
841 }
842 else
843 return (0);
844 }
845