4 * Hewlett-Packard Page Control Language filter for CUPS.
6 * Copyright 2007-2015 by Apple Inc.
7 * Copyright 1993-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * Include necessary headers...
22 #include <cups/cups.h>
24 #include <cups/string-private.h>
25 #include <cups/language-private.h>
26 #include <cups/raster.h>
36 unsigned char *Planes
[4], /* Output buffers */
37 *CompBuffer
, /* Compression buffer */
38 *BitBuffer
; /* Buffer for output bits */
39 unsigned NumPlanes
, /* Number of color planes */
40 ColorBits
, /* Number of bits per color */
41 Feed
; /* Number of lines to skip */
42 int Duplex
, /* Current duplex mode */
43 Page
, /* Current page number */
44 Canceled
; /* Has the current job been canceled? */
52 void StartPage(ppd_file_t
*ppd
, cups_page_header2_t
*header
);
56 void CancelJob(int sig
);
57 void CompressData(unsigned char *line
, unsigned length
, unsigned plane
, unsigned type
);
58 void OutputLine(cups_page_header2_t
*header
);
62 * 'Setup()' - Prepare the printer for printing.
69 * Send a PCL reset sequence.
78 * 'StartPage()' - Start a page of graphics.
82 StartPage(ppd_file_t
*ppd
, /* I - PPD file */
83 cups_page_header2_t
*header
) /* I - Page header */
85 unsigned plane
; /* Looping var */
89 * Show page device dictionary...
92 fprintf(stderr
, "DEBUG: StartPage...\n");
93 fprintf(stderr
, "DEBUG: Duplex = %d\n", header
->Duplex
);
94 fprintf(stderr
, "DEBUG: HWResolution = [ %d %d ]\n", header
->HWResolution
[0], header
->HWResolution
[1]);
95 fprintf(stderr
, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n", header
->ImagingBoundingBox
[0], header
->ImagingBoundingBox
[1], header
->ImagingBoundingBox
[2], header
->ImagingBoundingBox
[3]);
96 fprintf(stderr
, "DEBUG: Margins = [ %d %d ]\n", header
->Margins
[0], header
->Margins
[1]);
97 fprintf(stderr
, "DEBUG: ManualFeed = %d\n", header
->ManualFeed
);
98 fprintf(stderr
, "DEBUG: MediaPosition = %d\n", header
->MediaPosition
);
99 fprintf(stderr
, "DEBUG: NumCopies = %d\n", header
->NumCopies
);
100 fprintf(stderr
, "DEBUG: Orientation = %d\n", header
->Orientation
);
101 fprintf(stderr
, "DEBUG: PageSize = [ %d %d ]\n", header
->PageSize
[0], header
->PageSize
[1]);
102 fprintf(stderr
, "DEBUG: cupsWidth = %d\n", header
->cupsWidth
);
103 fprintf(stderr
, "DEBUG: cupsHeight = %d\n", header
->cupsHeight
);
104 fprintf(stderr
, "DEBUG: cupsMediaType = %d\n", header
->cupsMediaType
);
105 fprintf(stderr
, "DEBUG: cupsBitsPerColor = %d\n", header
->cupsBitsPerColor
);
106 fprintf(stderr
, "DEBUG: cupsBitsPerPixel = %d\n", header
->cupsBitsPerPixel
);
107 fprintf(stderr
, "DEBUG: cupsBytesPerLine = %d\n", header
->cupsBytesPerLine
);
108 fprintf(stderr
, "DEBUG: cupsColorOrder = %d\n", header
->cupsColorOrder
);
109 fprintf(stderr
, "DEBUG: cupsColorSpace = %d\n", header
->cupsColorSpace
);
110 fprintf(stderr
, "DEBUG: cupsCompression = %d\n", header
->cupsCompression
);
113 * Setup printer/job attributes...
116 Duplex
= header
->Duplex
;
117 ColorBits
= header
->cupsBitsPerColor
;
119 if ((!Duplex
|| (Page
& 1)) && header
->MediaPosition
)
120 printf("\033&l%dH", /* Set media position */
121 header
->MediaPosition
);
123 if (Duplex
&& ppd
&& ppd
->model_number
== 2)
126 * Handle duplexing on new DeskJet printers...
129 printf("\033&l-2H"); /* Load media */
132 printf("\033&l2S"); /* Set duplex mode */
135 if (!Duplex
|| (Page
& 1) || (ppd
&& ppd
->model_number
== 2))
138 * Set the media size...
141 printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */
142 printf("\033&l0O"); /* Set portrait orientation */
144 switch (header
->PageSize
[1])
146 case 540 : /* Monarch Envelope */
147 printf("\033&l80A"); /* Set page size */
151 printf("\033&l25A"); /* Set page size */
154 case 624 : /* DL Envelope */
155 printf("\033&l90A"); /* Set page size */
158 case 649 : /* C5 Envelope */
159 printf("\033&l91A"); /* Set page size */
162 case 684 : /* COM-10 Envelope */
163 printf("\033&l81A"); /* Set page size */
166 case 709 : /* B5 Envelope */
167 printf("\033&l100A"); /* Set page size */
170 case 756 : /* Executive */
171 printf("\033&l1A"); /* Set page size */
174 case 792 : /* Letter */
175 printf("\033&l2A"); /* Set page size */
179 printf("\033&l26A"); /* Set page size */
182 case 1008 : /* Legal */
183 printf("\033&l3A"); /* Set page size */
187 printf("\033&l27A"); /* Set page size */
190 case 1224 : /* Tabloid */
191 printf("\033&l6A"); /* Set page size */
195 printf("\033&l%dP", /* Set page length */
196 header
->PageSize
[1] / 12);
197 printf("\033&l0E"); /* Set top margin to 0 */
200 if (!Duplex
|| (Page
& 1))
203 * Set other job options...
206 printf("\033&l%dX", header
->NumCopies
); /* Set number copies */
208 if (header
->cupsMediaType
&&
209 (!ppd
|| ppd
->model_number
!= 2 || header
->HWResolution
[0] == 600))
210 printf("\033&l%dM", /* Set media type */
211 header
->cupsMediaType
);
213 if (!ppd
|| ppd
->model_number
!= 2)
215 int mode
= Duplex
? 1 + header
->Tumble
!= 0 : 0;
217 printf("\033&l%dS", mode
); /* Set duplex mode */
218 printf("\033&l0L"); /* Turn off perforation skip */
221 else if (!ppd
|| ppd
->model_number
!= 2)
222 printf("\033&a2G"); /* Set back side */
225 * Set graphics mode...
228 if (ppd
&& ppd
->model_number
== 2)
231 * Figure out the number of color planes...
234 if (header
->cupsColorSpace
== CUPS_CSPACE_KCMY
)
240 * Set the resolution and top-of-form...
243 printf("\033&u%dD", header
->HWResolution
[0]);
245 printf("\033&l0e0L"); /* Reset top and don't skip */
246 printf("\033*p0Y\033*p0X"); /* Set top of form */
249 * Send 26-byte configure image data command with horizontal and
250 * vertical resolutions as well as a color count...
254 putchar(2); /* Format 2 */
255 putchar((int)NumPlanes
); /* Output planes */
257 putchar((int)(header
->HWResolution
[0] >> 8));/* Black resolution */
258 putchar((int)header
->HWResolution
[0]);
259 putchar((int)(header
->HWResolution
[1] >> 8));
260 putchar((int)header
->HWResolution
[1]);
262 putchar(1 << ColorBits
); /* # of black levels */
264 putchar((int)(header
->HWResolution
[0] >> 8));/* Cyan resolution */
265 putchar((int)header
->HWResolution
[0]);
266 putchar((int)(header
->HWResolution
[1] >> 8));
267 putchar((int)header
->HWResolution
[1]);
269 putchar(1 << ColorBits
); /* # of cyan levels */
271 putchar((int)(header
->HWResolution
[0] >> 8));/* Magenta resolution */
272 putchar((int)header
->HWResolution
[0]);
273 putchar((int)(header
->HWResolution
[1] >> 8));
274 putchar((int)header
->HWResolution
[1]);
276 putchar(1 << ColorBits
); /* # of magenta levels */
278 putchar((int)(header
->HWResolution
[0] >> 8));/* Yellow resolution */
279 putchar((int)header
->HWResolution
[0]);
280 putchar((int)(header
->HWResolution
[1] >> 8));
281 putchar((int)header
->HWResolution
[1]);
283 putchar(1 << ColorBits
); /* # of yellow levels */
285 printf("\033&l0H"); /* Set media position */
289 printf("\033*t%uR", header
->HWResolution
[0]);
292 if (header
->cupsColorSpace
== CUPS_CSPACE_KCMY
)
295 printf("\033*r-4U"); /* Set KCMY graphics */
297 else if (header
->cupsColorSpace
== CUPS_CSPACE_CMY
)
300 printf("\033*r-3U"); /* Set CMY graphics */
303 NumPlanes
= 1; /* Black&white graphics */
306 * Set size and position of graphics...
309 printf("\033*r%uS", header
->cupsWidth
); /* Set width */
310 printf("\033*r%uT", header
->cupsHeight
); /* Set height */
312 printf("\033&a0H"); /* Set horizontal position */
315 printf("\033&a%.0fV", /* Set vertical position */
316 10.0 * (ppd
->sizes
[0].length
- ppd
->sizes
[0].top
));
318 printf("\033&a0V"); /* Set top-of-page */
321 printf("\033*r1A"); /* Start graphics */
323 if (header
->cupsCompression
)
324 printf("\033*b%uM", /* Set compression */
325 header
->cupsCompression
);
327 Feed
= 0; /* No blank lines yet */
330 * Allocate memory for a line of graphics...
333 if ((Planes
[0] = malloc(header
->cupsBytesPerLine
+ NumPlanes
)) == NULL
)
335 fputs("ERROR: Unable to allocate memory\n", stderr
);
339 for (plane
= 1; plane
< NumPlanes
; plane
++)
340 Planes
[plane
] = Planes
[0] + plane
* header
->cupsBytesPerLine
/ NumPlanes
;
343 BitBuffer
= malloc(ColorBits
* ((header
->cupsWidth
+ 7) / 8));
347 if (header
->cupsCompression
)
348 CompBuffer
= malloc(header
->cupsBytesPerLine
* 2 + 2);
355 * 'EndPage()' - Finish a page of graphics.
362 * Eject the current page...
367 printf("\033*rC"); /* End color GFX */
369 if (!(Duplex
&& (Page
& 1)))
370 printf("\033&l0H"); /* Eject current page */
374 printf("\033*r0B"); /* End GFX */
376 if (!(Duplex
&& (Page
& 1)))
377 printf("\014"); /* Eject current page */
397 * 'Shutdown()' - Shutdown the printer.
404 * Send a PCL reset sequence.
413 * 'CancelJob()' - Cancel the current job...
417 CancelJob(int sig
) /* I - Signal */
426 * 'CompressData()' - Compress a line of graphics.
430 CompressData(unsigned char *line
, /* I - Data to compress */
431 unsigned length
, /* I - Number of bytes */
432 unsigned plane
, /* I - Color plane */
433 unsigned type
) /* I - Type of compression */
435 unsigned char *line_ptr
, /* Current byte pointer */
436 *line_end
, /* End-of-line byte pointer */
437 *comp_ptr
, /* Pointer into compression buffer */
438 *start
; /* Start of compression sequence */
439 unsigned count
; /* Count of bytes for output */
446 * Do no compression...
450 line_end
= line
+ length
;
455 * Do run-length encoding...
458 line_end
= line
+ length
;
459 for (line_ptr
= line
, comp_ptr
= CompBuffer
;
461 comp_ptr
+= 2, line_ptr
+= count
)
464 (line_ptr
+ count
) < line_end
&&
465 line_ptr
[0] == line_ptr
[count
] &&
469 comp_ptr
[0] = (unsigned char)(count
- 1);
470 comp_ptr
[1] = line_ptr
[0];
473 line_ptr
= CompBuffer
;
479 * Do TIFF pack-bits encoding...
483 line_end
= line
+ length
;
484 comp_ptr
= CompBuffer
;
486 while (line_ptr
< line_end
)
488 if ((line_ptr
+ 1) >= line_end
)
491 * Single byte on the end...
495 *comp_ptr
++ = *line_ptr
++;
497 else if (line_ptr
[0] == line_ptr
[1])
500 * Repeated sequence...
506 while (line_ptr
< (line_end
- 1) &&
507 line_ptr
[0] == line_ptr
[1] &&
514 *comp_ptr
++ = (unsigned char)(257 - count
);
515 *comp_ptr
++ = *line_ptr
++;
520 * Non-repeated sequence...
527 while (line_ptr
< (line_end
- 1) &&
528 line_ptr
[0] != line_ptr
[1] &&
535 *comp_ptr
++ = (unsigned char)(count
- 1);
537 memcpy(comp_ptr
, start
, count
);
542 line_ptr
= CompBuffer
;
548 * Set the length of the data and write a raster plane...
551 printf("\033*b%d%c", (int)(line_end
- line_ptr
), plane
);
552 fwrite(line_ptr
, (size_t)(line_end
- line_ptr
), 1, stdout
);
557 * 'OutputLine()' - Output a line of graphics.
561 OutputLine(cups_page_header2_t
*header
) /* I - Page header */
563 unsigned plane
, /* Current plane */
564 bytes
, /* Bytes to write */
565 count
; /* Bytes to convert */
566 unsigned char bit
, /* Current plane data */
567 bit0
, /* Current low bit data */
568 bit1
, /* Current high bit data */
569 *plane_ptr
, /* Pointer into Planes */
570 *bit_ptr
; /* Pointer into BitBuffer */
574 * Output whitespace as needed...
579 printf("\033*b%dY", Feed
);
584 * Write bitmap data as needed...
587 bytes
= (header
->cupsWidth
+ 7) / 8;
589 for (plane
= 0; plane
< NumPlanes
; plane
++)
596 CompressData(Planes
[plane
], bytes
, plane
< (NumPlanes
- 1) ? 'V' : 'W',
597 header
->cupsCompression
);
602 * Separate low and high bit data into separate buffers.
605 for (count
= header
->cupsBytesPerLine
/ NumPlanes
,
606 plane_ptr
= Planes
[plane
], bit_ptr
= BitBuffer
;
608 count
-= 2, plane_ptr
+= 2, bit_ptr
++)
612 bit0
= (unsigned char)(((bit
& 64) << 1) | ((bit
& 16) << 2) | ((bit
& 4) << 3) | ((bit
& 1) << 4));
613 bit1
= (unsigned char)((bit
& 128) | ((bit
& 32) << 1) | ((bit
& 8) << 2) | ((bit
& 2) << 3));
619 bit0
|= (unsigned char)((bit
& 1) | ((bit
& 4) >> 1) | ((bit
& 16) >> 2) | ((bit
& 64) >> 3));
620 bit1
|= (unsigned char)(((bit
& 2) >> 1) | ((bit
& 8) >> 2) | ((bit
& 32) >> 3) | ((bit
& 128) >> 4));
624 bit_ptr
[bytes
] = bit1
;
628 * Send low and high bits...
631 CompressData(BitBuffer
, bytes
, 'V', header
->cupsCompression
);
632 CompressData(BitBuffer
+ bytes
, bytes
, plane
< (NumPlanes
- 1) ? 'V' : 'W',
633 header
->cupsCompression
);
641 * 'main()' - Main entry and processing of driver.
644 int /* O - Exit status */
645 main(int argc
, /* I - Number of command-line arguments */
646 char *argv
[]) /* I - Command-line arguments */
648 int fd
; /* File descriptor */
649 cups_raster_t
*ras
; /* Raster stream for printing */
650 cups_page_header2_t header
; /* Page header from file */
651 unsigned y
; /* Current line */
652 ppd_file_t
*ppd
; /* PPD file */
653 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
654 struct sigaction action
; /* Actions for POSIX signals */
655 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
659 * Make sure status messages are not buffered...
662 setbuf(stderr
, NULL
);
665 * Check command-line...
668 if (argc
< 6 || argc
> 7)
671 * We don't have the correct number of arguments; write an error message
675 _cupsLangPrintFilter(stderr
, "ERROR",
676 _("%s job-id user title copies options [file]"),
682 * Open the page stream...
687 if ((fd
= open(argv
[6], O_RDONLY
)) == -1)
689 _cupsLangPrintError("ERROR", _("Unable to open raster file"));
697 ras
= cupsRasterOpen(fd
, CUPS_RASTER_READ
);
700 * Register a signal handler to eject the current page if the
706 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
707 sigset(SIGTERM
, CancelJob
);
708 #elif defined(HAVE_SIGACTION)
709 memset(&action
, 0, sizeof(action
));
711 sigemptyset(&action
.sa_mask
);
712 action
.sa_handler
= CancelJob
;
713 sigaction(SIGTERM
, &action
, NULL
);
715 signal(SIGTERM
, CancelJob
);
716 #endif /* HAVE_SIGSET */
719 * Initialize the print device...
722 ppd
= ppdOpenFile(getenv("PPD"));
725 ppd_status_t status
; /* PPD error */
726 int linenum
; /* Line number */
728 _cupsLangPrintFilter(stderr
, "ERROR",
729 _("The PPD file could not be opened."));
731 status
= ppdLastError(&linenum
);
733 fprintf(stderr
, "DEBUG: %s on line %d.\n", ppdErrorString(status
), linenum
);
741 * Process pages as needed...
746 while (cupsRasterReadHeader2(ras
, &header
))
749 * Write a status message with the page number and number of copies.
757 fprintf(stderr
, "PAGE: %d %d\n", Page
, header
.NumCopies
);
758 _cupsLangPrintFilter(stderr
, "INFO", _("Starting page %d."), Page
);
764 StartPage(ppd
, &header
);
767 * Loop for each line on the page...
770 for (y
= 0; y
< header
.cupsHeight
; y
++)
773 * Let the user know how far we have progressed...
781 _cupsLangPrintFilter(stderr
, "INFO",
782 _("Printing page %d, %u%% complete."),
783 Page
, 100 * y
/ header
.cupsHeight
);
784 fprintf(stderr
, "ATTR: job-media-progress=%u\n",
785 100 * y
/ header
.cupsHeight
);
789 * Read a line of graphics...
792 if (cupsRasterReadPixels(ras
, Planes
[0], header
.cupsBytesPerLine
) < 1)
796 * See if the line is blank; if not, write it to the printer...
800 memcmp(Planes
[0], Planes
[0] + 1, header
.cupsBytesPerLine
- 1))
810 _cupsLangPrintFilter(stderr
, "INFO", _("Finished page %d."), Page
);
819 * Shutdown the printer...
828 * Close the raster stream...
831 cupsRasterClose(ras
);
836 * If no pages were printed, send an error message...
841 _cupsLangPrintFilter(stderr
, "ERROR", _("No pages were found."));