]>
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); | |
58b64171 MS |
244 | |
245 | fprintf(stderr, "ATTR: job-impressions-completed=%d\n", page); | |
dd43b7f7 MS |
246 | } |
247 | ||
248 | ||
de4912b2 MS |
249 | /* |
250 | * 'pcl_to_pcl()' - Pass through PCL data. | |
251 | */ | |
252 | ||
253 | static int /* O - Exit status */ | |
254 | pcl_to_pcl(const char *filename) /* I - File to print or NULL for stdin */ | |
255 | { | |
256 | int fd; /* File to read from */ | |
257 | char buffer[65536]; /* Copy buffer */ | |
258 | ssize_t bytes; /* Bytes to write */ | |
259 | ||
260 | ||
261 | /* | |
262 | * Open the input file... | |
263 | */ | |
264 | ||
265 | if (filename) | |
266 | { | |
267 | if ((fd = open(filename, O_RDONLY)) < 0) | |
268 | { | |
269 | fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno)); | |
270 | return (1); | |
271 | } | |
272 | } | |
273 | else | |
274 | { | |
275 | fd = 0; | |
276 | } | |
277 | ||
58b64171 MS |
278 | fputs("ATTR: job-impressions=unknown\n", stderr); |
279 | ||
de4912b2 MS |
280 | /* |
281 | * Copy to stdout... | |
282 | */ | |
283 | ||
284 | while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) | |
285 | write(1, buffer, (size_t)bytes); | |
286 | ||
287 | /* | |
288 | * Close the input file... | |
289 | */ | |
290 | ||
291 | if (fd > 0) | |
292 | close(fd); | |
293 | ||
294 | return (0); | |
295 | } | |
296 | ||
297 | ||
dd43b7f7 MS |
298 | /* |
299 | * 'pcl_write_line()' - Write a line of raster data. | |
300 | */ | |
301 | ||
302 | static void | |
303 | pcl_write_line( | |
00be463e | 304 | cups_page_header2_t *header, /* I - Raster information */ |
dd43b7f7 | 305 | unsigned y, /* I - Line number */ |
bc69e04b | 306 | const unsigned char *line) /* I - Pixels on line */ |
dd43b7f7 MS |
307 | { |
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 */ | |
317 | ||
318 | ||
00be463e | 319 | if (line[0] == pcl_white && !memcmp(line, line + 1, header->cupsBytesPerLine - 1)) |
dd43b7f7 MS |
320 | { |
321 | /* | |
322 | * Skip blank line... | |
323 | */ | |
324 | ||
bc69e04b | 325 | pcl_blanks ++; |
dd43b7f7 MS |
326 | return; |
327 | } | |
328 | ||
00be463e MS |
329 | if (header->cupsBitsPerPixel == 1) |
330 | { | |
331 | /* | |
332 | * B&W bitmap data can be used directly... | |
333 | */ | |
dd43b7f7 | 334 | |
00be463e MS |
335 | outend = (unsigned char *)line + (pcl_right + 7) / 8; |
336 | outptr = (unsigned char *)line + pcl_left / 8; | |
337 | } | |
338 | else | |
dd43b7f7 | 339 | { |
00be463e MS |
340 | /* |
341 | * Dither 8-bit grayscale to B&W... | |
342 | */ | |
343 | ||
344 | y &= 63; | |
345 | ditherline = threshold[y]; | |
dd43b7f7 | 346 | |
00be463e | 347 | for (x = pcl_left, bit = 128, byte = 0, outptr = pcl_line; x <= pcl_right; x ++, line ++) |
dd43b7f7 | 348 | { |
00be463e MS |
349 | if (*line <= ditherline[x & 63]) |
350 | byte |= bit; | |
351 | ||
352 | if (bit == 1) | |
353 | { | |
354 | *outptr++ = byte; | |
355 | byte = 0; | |
356 | bit = 128; | |
357 | } | |
358 | else | |
359 | bit >>= 1; | |
dd43b7f7 | 360 | } |
dd43b7f7 | 361 | |
00be463e MS |
362 | if (bit != 128) |
363 | *outptr++ = byte; | |
364 | ||
365 | outend = outptr; | |
366 | outptr = pcl_line; | |
367 | } | |
dd43b7f7 MS |
368 | |
369 | /* | |
370 | * Apply compression... | |
371 | */ | |
372 | ||
bc69e04b | 373 | compptr = pcl_comp; |
dd43b7f7 MS |
374 | |
375 | while (outptr < outend) | |
376 | { | |
377 | if ((outptr + 1) >= outend) | |
378 | { | |
379 | /* | |
380 | * Single byte on the end... | |
381 | */ | |
382 | ||
383 | *compptr++ = 0x00; | |
384 | *compptr++ = *outptr++; | |
385 | } | |
386 | else if (outptr[0] == outptr[1]) | |
387 | { | |
388 | /* | |
389 | * Repeated sequence... | |
390 | */ | |
391 | ||
392 | outptr ++; | |
393 | count = 2; | |
394 | ||
395 | while (outptr < (outend - 1) && | |
396 | outptr[0] == outptr[1] && | |
397 | count < 127) | |
398 | { | |
399 | outptr ++; | |
400 | count ++; | |
401 | } | |
402 | ||
403 | *compptr++ = (unsigned char)(257 - count); | |
404 | *compptr++ = *outptr++; | |
405 | } | |
406 | else | |
407 | { | |
408 | /* | |
409 | * Non-repeated sequence... | |
410 | */ | |
411 | ||
412 | start = outptr; | |
413 | outptr ++; | |
414 | count = 1; | |
415 | ||
416 | while (outptr < (outend - 1) && | |
417 | outptr[0] != outptr[1] && | |
418 | count < 127) | |
419 | { | |
420 | outptr ++; | |
421 | count ++; | |
422 | } | |
423 | ||
424 | *compptr++ = (unsigned char)(count - 1); | |
425 | ||
426 | memcpy(compptr, start, count); | |
427 | compptr += count; | |
428 | } | |
429 | } | |
430 | ||
431 | /* | |
432 | * Output the line... | |
433 | */ | |
434 | ||
bc69e04b | 435 | if (pcl_blanks > 0) |
dd43b7f7 MS |
436 | { |
437 | /* | |
438 | * Skip blank lines first... | |
439 | */ | |
440 | ||
bc69e04b MS |
441 | printf("\033*b%dY", pcl_blanks); |
442 | pcl_blanks = 0; | |
dd43b7f7 MS |
443 | } |
444 | ||
bc69e04b MS |
445 | printf("\033*b%dW", (int)(compptr - pcl_comp)); |
446 | fwrite(pcl_comp, 1, (size_t)(compptr - pcl_comp), stdout); | |
dd43b7f7 | 447 | } |
de4912b2 MS |
448 | |
449 | ||
450 | /* | |
451 | * 'raster_to_pcl()' - Convert raster data to PCL. | |
452 | */ | |
453 | ||
454 | static int /* O - Exit status */ | |
455 | raster_to_pcl(const char *filename) /* I - File to print (NULL for stdin) */ | |
456 | { | |
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 */ | |
463 | ||
464 | ||
465 | ||
466 | /* | |
467 | * Open the input file... | |
468 | */ | |
469 | ||
470 | if (filename) | |
471 | { | |
472 | if ((fd = open(filename, O_RDONLY)) < 0) | |
473 | { | |
474 | fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno)); | |
475 | return (1); | |
476 | } | |
477 | } | |
478 | else | |
479 | { | |
480 | fd = 0; | |
481 | } | |
482 | ||
483 | /* | |
484 | * Open the raster stream and send pages... | |
485 | */ | |
486 | ||
487 | if ((ras = cupsRasterOpen(fd, CUPS_RASTER_READ)) == NULL) | |
488 | { | |
489 | fputs("ERROR: Unable to read raster data, aborting.\n", stderr); | |
490 | return (1); | |
491 | } | |
492 | ||
493 | fputs("\033E", stdout); | |
494 | ||
495 | while (cupsRasterReadHeader2(ras, &header)) | |
496 | { | |
497 | page ++; | |
498 | ||
7331c1b6 | 499 | if (header.cupsColorSpace != CUPS_CSPACE_W && header.cupsColorSpace != CUPS_CSPACE_SW && header.cupsColorSpace != CUPS_CSPACE_K) |
de4912b2 MS |
500 | { |
501 | fputs("ERROR: Unsupported color space, aborting.\n", stderr); | |
502 | break; | |
503 | } | |
504 | else if (header.cupsBitsPerColor != 1 && header.cupsBitsPerColor != 8) | |
505 | { | |
506 | fputs("ERROR: Unsupported bit depth, aborting.\n", stderr); | |
507 | break; | |
508 | } | |
509 | ||
510 | line = malloc(header.cupsBytesPerLine); | |
511 | ||
512 | pcl_start_page(&header, page); | |
513 | for (y = 0; y < header.cupsHeight; y ++) | |
514 | { | |
515 | if (cupsRasterReadPixels(ras, line, header.cupsBytesPerLine)) | |
516 | pcl_write_line(&header, y, line); | |
517 | else | |
518 | break; | |
519 | } | |
520 | pcl_end_page(&header, page); | |
521 | ||
522 | free(line); | |
523 | } | |
524 | ||
525 | cupsRasterClose(ras); | |
526 | ||
58b64171 MS |
527 | fprintf(stderr, "ATTR: job-impressions=%d\n", page); |
528 | ||
de4912b2 MS |
529 | return (0); |
530 | } |