]> git.ipfire.org Git - thirdparty/cups.git/blame - test/ippevepcl.c
Save more work - JPEG, PDF, PostScript, and raster support for PS command.
[thirdparty/cups.git] / test / ippevepcl.c
CommitLineData
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
22static 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
27static 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
35static void pcl_end_page(cups_page_header2_t *header, unsigned page);
36static void pcl_start_page(cups_page_header2_t *header, unsigned page);
de4912b2 37static int pcl_to_pcl(const char *filename);
dd43b7f7 38static void pcl_write_line(cups_page_header2_t *header, unsigned y, const unsigned char *line);
de4912b2 39static int raster_to_pcl(const char *filename);
6641bd0d
MS
40
41
42/*
43 * 'main()' - Main entry for PCL printer command.
44 */
45
46int /* O - Exit status */
47main(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
87static void
88pcl_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
118static void
119pcl_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
251static int /* O - Exit status */
252pcl_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
298static void
299pcl_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
450static int /* O - Exit status */
451raster_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}