2 * Generic HP PCL printer command for ippeveprinter/CUPS.
4 * Copyright © 2019 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
11 * Include necessary headers...
14 #include "ippevecommon.h"
22 static unsigned pcl_bottom
, /* Bottom line */
23 pcl_left
, /* Left offset in line */
24 pcl_right
, /* Right offset in line */
25 pcl_top
, /* Top line */
26 pcl_blanks
; /* Number of blank lines to skip */
27 static unsigned char pcl_white
, /* White color */
28 *pcl_line
, /* Line buffer */
29 *pcl_comp
; /* Compression buffer */
35 static void pcl_end_page(cups_page_header2_t
*header
, unsigned page
);
36 static void pcl_start_page(cups_page_header2_t
*header
, unsigned page
);
37 static int pcl_to_pcl(const char *filename
);
38 static void pcl_write_line(cups_page_header2_t
*header
, unsigned y
, const unsigned char *line
);
39 static int raster_to_pcl(const char *filename
);
43 * 'main()' - Main entry for PCL printer command.
46 int /* O - Exit status */
47 main(int argc
, /* I - Number of command-line arguments */
48 char *argv
[]) /* I - Command-line arguments */
50 const char *content_type
; /* Content type to print */
59 fputs("ERROR: Too many arguments supplied, aborting.\n", stderr
);
62 else if ((content_type
= getenv("CONTENT_TYPE")) == NULL
)
64 fputs("ERROR: CONTENT_TYPE environment variable not set, aborting.\n", stderr
);
67 else if (!strcasecmp(content_type
, "application/vnd.hp-pcl"))
69 return (pcl_to_pcl(argv
[1]));
71 else if (!strcasecmp(content_type
, "image/pwg-raster") || !strcasecmp(content_type
, "image/urf"))
73 return (raster_to_pcl(argv
[1]));
77 fprintf(stderr
, "ERROR: CONTENT_TYPE %s not supported.\n", content_type
);
84 * 'pcl_end_page()' - End of PCL page.
89 cups_page_header2_t
*header
, /* I - Page header */
90 unsigned page
) /* I - Current page */
96 fputs("\033*r0B", stdout
);
99 * Formfeed as needed...
102 if (!(header
->Duplex
&& (page
& 1)))
106 * Free the output buffers...
115 * 'pcl_start_page()' - Start a PCL page.
120 cups_page_header2_t
*header
, /* I - Page header */
121 unsigned page
) /* I - Current page */
124 * Setup margins to be 1/6" top and bottom and 1/4" or .135" on the
128 pcl_top
= header
->HWResolution
[1] / 6;
129 pcl_bottom
= header
->cupsHeight
- header
->HWResolution
[1] / 6 - 1;
131 if (header
->PageSize
[1] == 842)
133 /* A4 gets special side margins to expose an 8" print area */
134 pcl_left
= (header
->cupsWidth
- 8 * header
->HWResolution
[0]) / 2;
135 pcl_right
= pcl_left
+ 8 * header
->HWResolution
[0] - 1;
139 /* All other sizes get 1/4" margins */
140 pcl_left
= header
->HWResolution
[0] / 4;
141 pcl_right
= header
->cupsWidth
- header
->HWResolution
[0] / 4 - 1;
144 if (!header
->Duplex
|| (page
& 1))
147 * Set the media size...
150 printf("\033&l12D\033&k12H"); /* Set 12 LPI, 10 CPI */
151 printf("\033&l0O"); /* Set portrait orientation */
153 switch (header
->PageSize
[1])
155 case 540 : /* Monarch Envelope */
163 case 624 : /* DL Envelope */
167 case 649 : /* C5 Envelope */
171 case 684 : /* COM-10 Envelope */
175 case 709 : /* B5 Envelope */
176 printf("\033&l100A");
179 case 756 : /* Executive */
183 case 792 : /* Letter */
191 case 1008 : /* Legal */
199 case 1224 : /* Tabloid */
205 * Set top margin and turn off perforation skip...
208 printf("\033&l%uE\033&l0L", 12 * pcl_top
/ header
->HWResolution
[1]);
212 int mode
= header
->Duplex
? 1 + header
->Tumble
!= 0 : 0;
214 printf("\033&l%dS", mode
); /* Set duplex mode */
217 else if (header
->Duplex
)
218 printf("\033&a2G"); /* Print on back side */
221 * Set graphics mode...
224 printf("\033*t%uR", header
->HWResolution
[0]);
226 printf("\033*r%uS", pcl_right
- pcl_left
+ 1);
228 printf("\033*r%uT", pcl_bottom
- pcl_top
+ 1);
230 printf("\033&a0H\033&a%uV", 720 * pcl_top
/ header
->HWResolution
[1]);
233 printf("\033*b2M"); /* Use PackBits compression */
234 printf("\033*r1A"); /* Start graphics */
237 * Allocate the output buffers...
240 pcl_white
= header
->cupsBitsPerColor
== 1 ? 0 : 255;
242 pcl_line
= malloc(header
->cupsWidth
/ 8 + 1);
243 pcl_comp
= malloc(2 * header
->cupsBytesPerLine
+ 2);
245 fprintf(stderr
, "ATTR: job-impressions-completed=%d\n", page
);
250 * 'pcl_to_pcl()' - Pass through PCL data.
253 static int /* O - Exit status */
254 pcl_to_pcl(const char *filename
) /* I - File to print or NULL for stdin */
256 int fd
; /* File to read from */
257 char buffer
[65536]; /* Copy buffer */
258 ssize_t bytes
; /* Bytes to write */
262 * Open the input file...
267 if ((fd
= open(filename
, O_RDONLY
)) < 0)
269 fprintf(stderr
, "ERROR: Unable to open \"%s\": %s\n", filename
, strerror(errno
));
278 fputs("ATTR: job-impressions=unknown\n", stderr
);
284 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
285 write(1, buffer
, (size_t)bytes
);
288 * Close the input file...
299 * 'pcl_write_line()' - Write a line of raster data.
304 cups_page_header2_t
*header
, /* I - Raster information */
305 unsigned y
, /* I - Line number */
306 const unsigned char *line
) /* I - Pixels on line */
308 unsigned x
; /* Column number */
309 unsigned char bit
, /* Current bit */
310 byte
, /* Current byte */
311 *outptr
, /* Pointer into output buffer */
312 *outend
, /* End of output buffer */
313 *start
, /* Start of sequence */
314 *compptr
; /* Pointer into compression buffer */
315 unsigned count
; /* Count of bytes for output */
316 const unsigned char *ditherline
; /* Pointer into dither table */
319 if (line
[0] == pcl_white
&& !memcmp(line
, line
+ 1, header
->cupsBytesPerLine
- 1))
329 if (header
->cupsBitsPerPixel
== 1)
332 * B&W bitmap data can be used directly...
335 outend
= (unsigned char *)line
+ (pcl_right
+ 7) / 8;
336 outptr
= (unsigned char *)line
+ pcl_left
/ 8;
341 * Dither 8-bit grayscale to B&W...
345 ditherline
= threshold
[y
];
347 for (x
= pcl_left
, bit
= 128, byte
= 0, outptr
= pcl_line
; x
<= pcl_right
; x
++, line
++)
349 if (*line
<= ditherline
[x
& 63])
370 * Apply compression...
375 while (outptr
< outend
)
377 if ((outptr
+ 1) >= outend
)
380 * Single byte on the end...
384 *compptr
++ = *outptr
++;
386 else if (outptr
[0] == outptr
[1])
389 * Repeated sequence...
395 while (outptr
< (outend
- 1) &&
396 outptr
[0] == outptr
[1] &&
403 *compptr
++ = (unsigned char)(257 - count
);
404 *compptr
++ = *outptr
++;
409 * Non-repeated sequence...
416 while (outptr
< (outend
- 1) &&
417 outptr
[0] != outptr
[1] &&
424 *compptr
++ = (unsigned char)(count
- 1);
426 memcpy(compptr
, start
, count
);
438 * Skip blank lines first...
441 printf("\033*b%dY", pcl_blanks
);
445 printf("\033*b%dW", (int)(compptr
- pcl_comp
));
446 fwrite(pcl_comp
, 1, (size_t)(compptr
- pcl_comp
), stdout
);
451 * 'raster_to_pcl()' - Convert raster data to PCL.
454 static int /* O - Exit status */
455 raster_to_pcl(const char *filename
) /* I - File to print (NULL for stdin) */
457 int fd
; /* Input file */
458 cups_raster_t
*ras
; /* Raster stream */
459 cups_page_header2_t header
; /* Page header */
460 unsigned page
= 0, /* Current page */
461 y
; /* Current line */
462 unsigned char *line
; /* Line buffer */
467 * Open the input file...
472 if ((fd
= open(filename
, O_RDONLY
)) < 0)
474 fprintf(stderr
, "ERROR: Unable to open \"%s\": %s\n", filename
, strerror(errno
));
484 * Open the raster stream and send pages...
487 if ((ras
= cupsRasterOpen(fd
, CUPS_RASTER_READ
)) == NULL
)
489 fputs("ERROR: Unable to read raster data, aborting.\n", stderr
);
493 fputs("\033E", stdout
);
495 while (cupsRasterReadHeader2(ras
, &header
))
499 if (header
.cupsColorSpace
!= CUPS_CSPACE_W
&& header
.cupsColorSpace
!= CUPS_CSPACE_SW
&& header
.cupsColorSpace
!= CUPS_CSPACE_K
)
501 fputs("ERROR: Unsupported color space, aborting.\n", stderr
);
504 else if (header
.cupsBitsPerColor
!= 1 && header
.cupsBitsPerColor
!= 8)
506 fputs("ERROR: Unsupported bit depth, aborting.\n", stderr
);
510 line
= malloc(header
.cupsBytesPerLine
);
512 pcl_start_page(&header
, page
);
513 for (y
= 0; y
< header
.cupsHeight
; y
++)
515 if (cupsRasterReadPixels(ras
, line
, header
.cupsBytesPerLine
))
516 pcl_write_line(&header
, y
, line
);
520 pcl_end_page(&header
, page
);
525 cupsRasterClose(ras
);
527 fprintf(stderr
, "ATTR: job-impressions=%d\n", page
);