]>
Commit | Line | Data |
---|---|---|
1562b9a1 MS |
1 | /* |
2 | * Generic HP PCL printer command for ippeveprinter/CUPS. | |
3 | * | |
4 | * Copyright © 2019 by Apple Inc. | |
5 | * | |
6 | * Licensed under Apache License v2.0. See the file "LICENSE" for more | |
7 | * information. | |
8 | */ | |
9 | ||
10 | /* | |
11 | * Include necessary headers... | |
12 | */ | |
13 | ||
14 | #include "ippevecommon.h" | |
dd43b7f7 MS |
15 | #include "dither.h" |
16 | ||
17 | ||
18 | /* | |
19 | * Local globals... | |
20 | */ | |
21 | ||
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 */ | |
00be463e MS |
27 | static unsigned char pcl_white, /* White color */ |
28 | *pcl_line, /* Line buffer */ | |
dd43b7f7 MS |
29 | *pcl_comp; /* Compression buffer */ |
30 | ||
31 | /* | |
32 | * Local functions... | |
33 | */ | |
34 | ||
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); | |
de4912b2 | 37 | static int pcl_to_pcl(const char *filename); |
dd43b7f7 | 38 | static void pcl_write_line(cups_page_header2_t *header, unsigned y, const unsigned char *line); |
de4912b2 | 39 | static int raster_to_pcl(const char *filename); |
6641bd0d MS |
40 | |
41 | ||
42 | /* | |
43 | * 'main()' - Main entry for PCL printer command. | |
44 | */ | |
45 | ||
46 | int /* O - Exit status */ | |
47 | main(int argc, /* I - Number of command-line arguments */ | |
48 | char *argv[]) /* I - Command-line arguments */ | |
49 | { | |
dd43b7f7 | 50 | const char *content_type; /* Content type to print */ |
dd43b7f7 MS |
51 | |
52 | ||
53 | /* | |
de4912b2 | 54 | * Print it... |
dd43b7f7 MS |
55 | */ |
56 | ||
de4912b2 | 57 | if (argc > 2) |
dd43b7f7 | 58 | { |
de4912b2 | 59 | fputs("ERROR: Too many arguments supplied, aborting.\n", stderr); |
dd43b7f7 MS |
60 | return (1); |
61 | } | |
de4912b2 | 62 | else if ((content_type = getenv("CONTENT_TYPE")) == NULL) |
dd43b7f7 | 63 | { |
de4912b2 | 64 | fputs("ERROR: CONTENT_TYPE environment variable not set, aborting.\n", stderr); |
dd43b7f7 MS |
65 | return (1); |
66 | } | |
de4912b2 | 67 | else if (!strcasecmp(content_type, "application/vnd.hp-pcl")) |
dd43b7f7 | 68 | { |
de4912b2 | 69 | return (pcl_to_pcl(argv[1])); |
dd43b7f7 | 70 | } |
de4912b2 | 71 | else if (!strcasecmp(content_type, "image/pwg-raster") || !strcasecmp(content_type, "image/urf")) |
dd43b7f7 | 72 | { |
de4912b2 | 73 | return (raster_to_pcl(argv[1])); |
dd43b7f7 MS |
74 | } |
75 | else | |
76 | { | |
de4912b2 | 77 | fprintf(stderr, "ERROR: CONTENT_TYPE %s not supported.\n", content_type); |
dd43b7f7 MS |
78 | return (1); |
79 | } | |
6641bd0d | 80 | } |
dd43b7f7 MS |
81 | |
82 | ||
dd43b7f7 MS |
83 | /* |
84 | * 'pcl_end_page()' - End of PCL page. | |
85 | */ | |
86 | ||
87 | static void | |
88 | pcl_end_page( | |
00be463e | 89 | cups_page_header2_t *header, /* I - Page header */ |
dd43b7f7 MS |
90 | unsigned page) /* I - Current page */ |
91 | { | |
92 | /* | |
93 | * End graphics... | |
94 | */ | |
95 | ||
96 | fputs("\033*r0B", stdout); | |
97 | ||
98 | /* | |
99 | * Formfeed as needed... | |
100 | */ | |
101 | ||
102 | if (!(header->Duplex && (page & 1))) | |
103 | putchar('\f'); | |
104 | ||
105 | /* | |
106 | * Free the output buffers... | |
107 | */ | |
108 | ||
109 | free(pcl_line); | |
110 | free(pcl_comp); | |
111 | } | |
112 | ||
113 | ||
114 | /* | |
115 | * 'pcl_start_page()' - Start a PCL page. | |
116 | */ | |
117 | ||
118 | static void | |
119 | pcl_start_page( | |
120 | cups_page_header2_t *header, /* I - Page header */ | |
121 | unsigned page) /* I - Current page */ | |
122 | { | |
123 | /* | |
124 | * Setup margins to be 1/6" top and bottom and 1/4" or .135" on the | |
125 | * left and right. | |
126 | */ | |
127 | ||
bc69e04b MS |
128 | pcl_top = header->HWResolution[1] / 6; |
129 | pcl_bottom = header->cupsHeight - header->HWResolution[1] / 6 - 1; | |
dd43b7f7 | 130 | |
bc69e04b | 131 | if (header->PageSize[1] == 842) |
dd43b7f7 MS |
132 | { |
133 | /* A4 gets special side margins to expose an 8" print area */ | |
bc69e04b MS |
134 | pcl_left = (header->cupsWidth - 8 * header->HWResolution[0]) / 2; |
135 | pcl_right = pcl_left + 8 * header->HWResolution[0] - 1; | |
dd43b7f7 MS |
136 | } |
137 | else | |
138 | { | |
139 | /* All other sizes get 1/4" margins */ | |
bc69e04b MS |
140 | pcl_left = header->HWResolution[0] / 4; |
141 | pcl_right = header->cupsWidth - header->HWResolution[0] / 4 - 1; | |
dd43b7f7 MS |
142 | } |
143 | ||
bc69e04b | 144 | if (!header->Duplex || (page & 1)) |
dd43b7f7 MS |
145 | { |
146 | /* | |
147 | * Set the media size... | |
148 | */ | |
149 | ||
150 | printf("\033&l12D\033&k12H"); /* Set 12 LPI, 10 CPI */ | |
151 | printf("\033&l0O"); /* Set portrait orientation */ | |
152 | ||
bc69e04b | 153 | switch (header->PageSize[1]) |
dd43b7f7 MS |
154 | { |
155 | case 540 : /* Monarch Envelope */ | |
156 | printf("\033&l80A"); | |
157 | break; | |
158 | ||
159 | case 595 : /* A5 */ | |
160 | printf("\033&l25A"); | |
161 | break; | |
162 | ||
163 | case 624 : /* DL Envelope */ | |
164 | printf("\033&l90A"); | |
165 | break; | |
166 | ||
167 | case 649 : /* C5 Envelope */ | |
168 | printf("\033&l91A"); | |
169 | break; | |
170 | ||
171 | case 684 : /* COM-10 Envelope */ | |
172 | printf("\033&l81A"); | |
173 | break; | |
174 | ||
175 | case 709 : /* B5 Envelope */ | |
176 | printf("\033&l100A"); | |
177 | break; | |
178 | ||
179 | case 756 : /* Executive */ | |
180 | printf("\033&l1A"); | |
181 | break; | |
182 | ||
183 | case 792 : /* Letter */ | |
184 | printf("\033&l2A"); | |
185 | break; | |
186 | ||
187 | case 842 : /* A4 */ | |
188 | printf("\033&l26A"); | |
189 | break; | |
190 | ||
191 | case 1008 : /* Legal */ | |
192 | printf("\033&l3A"); | |
193 | break; | |
194 | ||
195 | case 1191 : /* A3 */ | |
196 | printf("\033&l27A"); | |
197 | break; | |
198 | ||
199 | case 1224 : /* Tabloid */ | |
200 | printf("\033&l6A"); | |
201 | break; | |
202 | } | |
203 | ||
204 | /* | |
205 | * Set top margin and turn off perforation skip... | |
206 | */ | |
207 | ||
bc69e04b | 208 | printf("\033&l%uE\033&l0L", 12 * pcl_top / header->HWResolution[1]); |
dd43b7f7 | 209 | |
bc69e04b | 210 | if (header->Duplex) |
dd43b7f7 | 211 | { |
bc69e04b | 212 | int mode = header->Duplex ? 1 + header->Tumble != 0 : 0; |
dd43b7f7 MS |
213 | |
214 | printf("\033&l%dS", mode); /* Set duplex mode */ | |
215 | } | |
216 | } | |
bc69e04b | 217 | else if (header->Duplex) |
dd43b7f7 MS |
218 | printf("\033&a2G"); /* Print on back side */ |
219 | ||
220 | /* | |
221 | * Set graphics mode... | |
222 | */ | |
223 | ||
bc69e04b | 224 | printf("\033*t%uR", header->HWResolution[0]); |
dd43b7f7 | 225 | /* Set resolution */ |
bc69e04b | 226 | printf("\033*r%uS", pcl_right - pcl_left + 1); |
dd43b7f7 | 227 | /* Set width */ |
bc69e04b | 228 | printf("\033*r%uT", pcl_bottom - pcl_top + 1); |
dd43b7f7 | 229 | /* Set height */ |
bc69e04b | 230 | printf("\033&a0H\033&a%uV", 720 * pcl_top / header->HWResolution[1]); |
dd43b7f7 MS |
231 | /* Set position */ |
232 | ||
233 | printf("\033*b2M"); /* Use PackBits compression */ | |
234 | printf("\033*r1A"); /* Start graphics */ | |
235 | ||
236 | /* | |
237 | * Allocate the output buffers... | |
238 | */ | |
239 | ||
00be463e | 240 | pcl_white = header->cupsBitsPerColor == 1 ? 0 : 255; |
dd43b7f7 MS |
241 | pcl_blanks = 0; |
242 | pcl_line = malloc(header->cupsWidth / 8 + 1); | |
243 | pcl_comp = malloc(2 * header->cupsBytesPerLine + 2); | |
244 | } | |
245 | ||
246 | ||
de4912b2 MS |
247 | /* |
248 | * 'pcl_to_pcl()' - Pass through PCL data. | |
249 | */ | |
250 | ||
251 | static int /* O - Exit status */ | |
252 | pcl_to_pcl(const char *filename) /* I - File to print or NULL for stdin */ | |
253 | { | |
254 | int fd; /* File to read from */ | |
255 | char buffer[65536]; /* Copy buffer */ | |
256 | ssize_t bytes; /* Bytes to write */ | |
257 | ||
258 | ||
259 | /* | |
260 | * Open the input file... | |
261 | */ | |
262 | ||
263 | if (filename) | |
264 | { | |
265 | if ((fd = open(filename, O_RDONLY)) < 0) | |
266 | { | |
267 | fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno)); | |
268 | return (1); | |
269 | } | |
270 | } | |
271 | else | |
272 | { | |
273 | fd = 0; | |
274 | } | |
275 | ||
276 | /* | |
277 | * Copy to stdout... | |
278 | */ | |
279 | ||
280 | while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) | |
281 | write(1, buffer, (size_t)bytes); | |
282 | ||
283 | /* | |
284 | * Close the input file... | |
285 | */ | |
286 | ||
287 | if (fd > 0) | |
288 | close(fd); | |
289 | ||
290 | return (0); | |
291 | } | |
292 | ||
293 | ||
dd43b7f7 MS |
294 | /* |
295 | * 'pcl_write_line()' - Write a line of raster data. | |
296 | */ | |
297 | ||
298 | static void | |
299 | pcl_write_line( | |
00be463e | 300 | cups_page_header2_t *header, /* I - Raster information */ |
dd43b7f7 | 301 | unsigned y, /* I - Line number */ |
bc69e04b | 302 | const unsigned char *line) /* I - Pixels on line */ |
dd43b7f7 MS |
303 | { |
304 | unsigned x; /* Column number */ | |
305 | unsigned char bit, /* Current bit */ | |
306 | byte, /* Current byte */ | |
307 | *outptr, /* Pointer into output buffer */ | |
308 | *outend, /* End of output buffer */ | |
309 | *start, /* Start of sequence */ | |
310 | *compptr; /* Pointer into compression buffer */ | |
311 | unsigned count; /* Count of bytes for output */ | |
312 | const unsigned char *ditherline; /* Pointer into dither table */ | |
313 | ||
314 | ||
00be463e | 315 | if (line[0] == pcl_white && !memcmp(line, line + 1, header->cupsBytesPerLine - 1)) |
dd43b7f7 MS |
316 | { |
317 | /* | |
318 | * Skip blank line... | |
319 | */ | |
320 | ||
bc69e04b | 321 | pcl_blanks ++; |
dd43b7f7 MS |
322 | return; |
323 | } | |
324 | ||
00be463e MS |
325 | if (header->cupsBitsPerPixel == 1) |
326 | { | |
327 | /* | |
328 | * B&W bitmap data can be used directly... | |
329 | */ | |
dd43b7f7 | 330 | |
00be463e MS |
331 | outend = (unsigned char *)line + (pcl_right + 7) / 8; |
332 | outptr = (unsigned char *)line + pcl_left / 8; | |
333 | } | |
334 | else | |
dd43b7f7 | 335 | { |
00be463e MS |
336 | /* |
337 | * Dither 8-bit grayscale to B&W... | |
338 | */ | |
339 | ||
340 | y &= 63; | |
341 | ditherline = threshold[y]; | |
dd43b7f7 | 342 | |
00be463e | 343 | for (x = pcl_left, bit = 128, byte = 0, outptr = pcl_line; x <= pcl_right; x ++, line ++) |
dd43b7f7 | 344 | { |
00be463e MS |
345 | if (*line <= ditherline[x & 63]) |
346 | byte |= bit; | |
347 | ||
348 | if (bit == 1) | |
349 | { | |
350 | *outptr++ = byte; | |
351 | byte = 0; | |
352 | bit = 128; | |
353 | } | |
354 | else | |
355 | bit >>= 1; | |
dd43b7f7 | 356 | } |
dd43b7f7 | 357 | |
00be463e MS |
358 | if (bit != 128) |
359 | *outptr++ = byte; | |
360 | ||
361 | outend = outptr; | |
362 | outptr = pcl_line; | |
363 | } | |
dd43b7f7 MS |
364 | |
365 | /* | |
366 | * Apply compression... | |
367 | */ | |
368 | ||
bc69e04b | 369 | compptr = pcl_comp; |
dd43b7f7 MS |
370 | |
371 | while (outptr < outend) | |
372 | { | |
373 | if ((outptr + 1) >= outend) | |
374 | { | |
375 | /* | |
376 | * Single byte on the end... | |
377 | */ | |
378 | ||
379 | *compptr++ = 0x00; | |
380 | *compptr++ = *outptr++; | |
381 | } | |
382 | else if (outptr[0] == outptr[1]) | |
383 | { | |
384 | /* | |
385 | * Repeated sequence... | |
386 | */ | |
387 | ||
388 | outptr ++; | |
389 | count = 2; | |
390 | ||
391 | while (outptr < (outend - 1) && | |
392 | outptr[0] == outptr[1] && | |
393 | count < 127) | |
394 | { | |
395 | outptr ++; | |
396 | count ++; | |
397 | } | |
398 | ||
399 | *compptr++ = (unsigned char)(257 - count); | |
400 | *compptr++ = *outptr++; | |
401 | } | |
402 | else | |
403 | { | |
404 | /* | |
405 | * Non-repeated sequence... | |
406 | */ | |
407 | ||
408 | start = outptr; | |
409 | outptr ++; | |
410 | count = 1; | |
411 | ||
412 | while (outptr < (outend - 1) && | |
413 | outptr[0] != outptr[1] && | |
414 | count < 127) | |
415 | { | |
416 | outptr ++; | |
417 | count ++; | |
418 | } | |
419 | ||
420 | *compptr++ = (unsigned char)(count - 1); | |
421 | ||
422 | memcpy(compptr, start, count); | |
423 | compptr += count; | |
424 | } | |
425 | } | |
426 | ||
427 | /* | |
428 | * Output the line... | |
429 | */ | |
430 | ||
bc69e04b | 431 | if (pcl_blanks > 0) |
dd43b7f7 MS |
432 | { |
433 | /* | |
434 | * Skip blank lines first... | |
435 | */ | |
436 | ||
bc69e04b MS |
437 | printf("\033*b%dY", pcl_blanks); |
438 | pcl_blanks = 0; | |
dd43b7f7 MS |
439 | } |
440 | ||
bc69e04b MS |
441 | printf("\033*b%dW", (int)(compptr - pcl_comp)); |
442 | fwrite(pcl_comp, 1, (size_t)(compptr - pcl_comp), stdout); | |
dd43b7f7 | 443 | } |
de4912b2 MS |
444 | |
445 | ||
446 | /* | |
447 | * 'raster_to_pcl()' - Convert raster data to PCL. | |
448 | */ | |
449 | ||
450 | static int /* O - Exit status */ | |
451 | raster_to_pcl(const char *filename) /* I - File to print (NULL for stdin) */ | |
452 | { | |
453 | int fd; /* Input file */ | |
454 | cups_raster_t *ras; /* Raster stream */ | |
455 | cups_page_header2_t header; /* Page header */ | |
456 | unsigned page = 0, /* Current page */ | |
457 | y; /* Current line */ | |
458 | unsigned char *line; /* Line buffer */ | |
459 | ||
460 | ||
461 | ||
462 | /* | |
463 | * Open the input file... | |
464 | */ | |
465 | ||
466 | if (filename) | |
467 | { | |
468 | if ((fd = open(filename, O_RDONLY)) < 0) | |
469 | { | |
470 | fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno)); | |
471 | return (1); | |
472 | } | |
473 | } | |
474 | else | |
475 | { | |
476 | fd = 0; | |
477 | } | |
478 | ||
479 | /* | |
480 | * Open the raster stream and send pages... | |
481 | */ | |
482 | ||
483 | if ((ras = cupsRasterOpen(fd, CUPS_RASTER_READ)) == NULL) | |
484 | { | |
485 | fputs("ERROR: Unable to read raster data, aborting.\n", stderr); | |
486 | return (1); | |
487 | } | |
488 | ||
489 | fputs("\033E", stdout); | |
490 | ||
491 | while (cupsRasterReadHeader2(ras, &header)) | |
492 | { | |
493 | page ++; | |
494 | ||
7331c1b6 | 495 | if (header.cupsColorSpace != CUPS_CSPACE_W && header.cupsColorSpace != CUPS_CSPACE_SW && header.cupsColorSpace != CUPS_CSPACE_K) |
de4912b2 MS |
496 | { |
497 | fputs("ERROR: Unsupported color space, aborting.\n", stderr); | |
498 | break; | |
499 | } | |
500 | else if (header.cupsBitsPerColor != 1 && header.cupsBitsPerColor != 8) | |
501 | { | |
502 | fputs("ERROR: Unsupported bit depth, aborting.\n", stderr); | |
503 | break; | |
504 | } | |
505 | ||
506 | line = malloc(header.cupsBytesPerLine); | |
507 | ||
508 | pcl_start_page(&header, page); | |
509 | for (y = 0; y < header.cupsHeight; y ++) | |
510 | { | |
511 | if (cupsRasterReadPixels(ras, line, header.cupsBytesPerLine)) | |
512 | pcl_write_line(&header, y, line); | |
513 | else | |
514 | break; | |
515 | } | |
516 | pcl_end_page(&header, page); | |
517 | ||
518 | free(line); | |
519 | } | |
520 | ||
521 | cupsRasterClose(ras); | |
522 | ||
523 | return (0); | |
524 | } |