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