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