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