1/*
2 * Hewlett-Packard Page Control Language filter for CUPS.
3 *
4 * Copyright 2007-2015 by Apple Inc.
5 * Copyright 1993-2007 by Easy Software Products.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file.  If this file is
11 * missing or damaged, see the license at "http://www.cups.org/".
12 *
13 * This file is subject to the Apple OS-Developed Software exception.
14 */
15
16/*
17 * Include necessary headers...
18 */
19
20#include <cups/cups.h>
21#include <cups/ppd.h>
22#include <cups/string-private.h>
23#include <cups/language-private.h>
24#include <cups/raster.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <signal.h>
28
29
30/*
31 * Globals...
32 */
33
34unsigned char	*Planes[4],		/* Output buffers */
35		*CompBuffer,		/* Compression buffer */
36		*BitBuffer;		/* Buffer for output bits */
37unsigned 	NumPlanes,		/* Number of color planes */
38		ColorBits,		/* Number of bits per color */
39		Feed;			/* Number of lines to skip */
40int		Duplex,			/* Current duplex mode */
41		Page,			/* Current page number */
42		Canceled;		/* Has the current job been canceled? */
43
44
45/*
46 * Prototypes...
47 */
48
49void	Setup(void);
50void	StartPage(ppd_file_t *ppd, cups_page_header2_t *header);
51void	EndPage(void);
52void	Shutdown(void);
53
54void	CancelJob(int sig);
55void	CompressData(unsigned char *line, unsigned length, unsigned plane, unsigned type);
56void	OutputLine(cups_page_header2_t *header);
57
58
59/*
60 * 'Setup()' - Prepare the printer for printing.
61 */
62
63void
64Setup(void)
65{
66 /*
67  * Send a PCL reset sequence.
68  */
69
70  putchar(0x1b);
71  putchar('E');
72}
73
74
75/*
76 * 'StartPage()' - Start a page of graphics.
77 */
78
79void
80StartPage(ppd_file_t         *ppd,	/* I - PPD file */
81          cups_page_header2_t *header)	/* I - Page header */
82{
83  unsigned	plane;			/* Looping var */
84
85
86 /*
87  * Show page device dictionary...
88  */
89
90  fprintf(stderr, "DEBUG: StartPage...\n");
91  fprintf(stderr, "DEBUG: Duplex = %d\n", header->Duplex);
92  fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", header->HWResolution[0], header->HWResolution[1]);
93  fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n", header->ImagingBoundingBox[0], header->ImagingBoundingBox[1], header->ImagingBoundingBox[2], header->ImagingBoundingBox[3]);
94  fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", header->Margins[0], header->Margins[1]);
95  fprintf(stderr, "DEBUG: ManualFeed = %d\n", header->ManualFeed);
96  fprintf(stderr, "DEBUG: MediaPosition = %d\n", header->MediaPosition);
97  fprintf(stderr, "DEBUG: NumCopies = %d\n", header->NumCopies);
98  fprintf(stderr, "DEBUG: Orientation = %d\n", header->Orientation);
99  fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", header->PageSize[0], header->PageSize[1]);
100  fprintf(stderr, "DEBUG: cupsWidth = %d\n", header->cupsWidth);
101  fprintf(stderr, "DEBUG: cupsHeight = %d\n", header->cupsHeight);
102  fprintf(stderr, "DEBUG: cupsMediaType = %d\n", header->cupsMediaType);
103  fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", header->cupsBitsPerColor);
104  fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", header->cupsBitsPerPixel);
105  fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", header->cupsBytesPerLine);
106  fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", header->cupsColorOrder);
107  fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", header->cupsColorSpace);
108  fprintf(stderr, "DEBUG: cupsCompression = %d\n", header->cupsCompression);
109
110 /*
111  * Setup printer/job attributes...
112  */
113
114  Duplex    = header->Duplex;
115  ColorBits = header->cupsBitsPerColor;
116
117  if ((!Duplex || (Page & 1)) && header->MediaPosition)
118    printf("\033&l%dH",				/* Set media position */
119           header->MediaPosition);
120
121  if (Duplex && ppd && ppd->model_number == 2)
122  {
123   /*
124    * Handle duplexing on new DeskJet printers...
125    */
126
127    printf("\033&l-2H");			/* Load media */
128
129    if (Page & 1)
130      printf("\033&l2S");			/* Set duplex mode */
131  }
132
133  if (!Duplex || (Page & 1) || (ppd && ppd->model_number == 2))
134  {
135   /*
136    * Set the media size...
137    */
138
139    printf("\033&l6D\033&k12H");		/* Set 6 LPI, 10 CPI */
140    printf("\033&l0O");				/* Set portrait orientation */
141
142    switch (header->PageSize[1])
143    {
144      case 540 : /* Monarch Envelope */
145          printf("\033&l80A");			/* Set page size */
146	  break;
147
148      case 595 : /* A5 */
149          printf("\033&l25A");			/* Set page size */
150	  break;
151
152      case 624 : /* DL Envelope */
153          printf("\033&l90A");			/* Set page size */
154	  break;
155
156      case 649 : /* C5 Envelope */
157          printf("\033&l91A");			/* Set page size */
158	  break;
159
160      case 684 : /* COM-10 Envelope */
161          printf("\033&l81A");			/* Set page size */
162	  break;
163
164      case 709 : /* B5 Envelope */
165          printf("\033&l100A");			/* Set page size */
166	  break;
167
168      case 756 : /* Executive */
169          printf("\033&l1A");			/* Set page size */
170	  break;
171
172      case 792 : /* Letter */
173          printf("\033&l2A");			/* Set page size */
174	  break;
175
176      case 842 : /* A4 */
177          printf("\033&l26A");			/* Set page size */
178	  break;
179
180      case 1008 : /* Legal */
181          printf("\033&l3A");			/* Set page size */
182	  break;
183
184      case 1191 : /* A3 */
185          printf("\033&l27A");			/* Set page size */
186	  break;
187
188      case 1224 : /* Tabloid */
189          printf("\033&l6A");			/* Set page size */
190	  break;
191    }
192
193    printf("\033&l%dP",				/* Set page length */
194           header->PageSize[1] / 12);
195    printf("\033&l0E");				/* Set top margin to 0 */
196  }
197
198  if (!Duplex || (Page & 1))
199  {
200   /*
201    * Set other job options...
202    */
203
204    printf("\033&l%dX", header->NumCopies);	/* Set number copies */
205
206    if (header->cupsMediaType &&
207        (!ppd || ppd->model_number != 2 || header->HWResolution[0] == 600))
208      printf("\033&l%dM",			/* Set media type */
209             header->cupsMediaType);
210
211    if (!ppd || ppd->model_number != 2)
212    {
213      int mode = Duplex ? 1 + header->Tumble != 0 : 0;
214
215      printf("\033&l%dS", mode);		/* Set duplex mode */
216      printf("\033&l0L");			/* Turn off perforation skip */
217    }
218  }
219  else if (!ppd || ppd->model_number != 2)
220    printf("\033&a2G");				/* Set back side */
221
222 /*
223  * Set graphics mode...
224  */
225
226  if (ppd && ppd->model_number == 2)
227  {
228   /*
229    * Figure out the number of color planes...
230    */
231
232    if (header->cupsColorSpace == CUPS_CSPACE_KCMY)
233      NumPlanes = 4;
234    else
235      NumPlanes = 1;
236
237   /*
238    * Set the resolution and top-of-form...
239    */
240
241    printf("\033&u%dD", header->HWResolution[0]);
242						/* Resolution */
243    printf("\033&l0e0L");			/* Reset top and don't skip */
244    printf("\033*p0Y\033*p0X");			/* Set top of form */
245
246   /*
247    * Send 26-byte configure image data command with horizontal and
248    * vertical resolutions as well as a color count...
249    */
250
251    printf("\033*g26W");
252    putchar(2);					/* Format 2 */
253    putchar((int)NumPlanes);			/* Output planes */
254
255    putchar((int)(header->HWResolution[0] >> 8));/* Black resolution */
256    putchar((int)header->HWResolution[0]);
257    putchar((int)(header->HWResolution[1] >> 8));
258    putchar((int)header->HWResolution[1]);
259    putchar(0);
260    putchar(1 << ColorBits);			/* # of black levels */
261
262    putchar((int)(header->HWResolution[0] >> 8));/* Cyan resolution */
263    putchar((int)header->HWResolution[0]);
264    putchar((int)(header->HWResolution[1] >> 8));
265    putchar((int)header->HWResolution[1]);
266    putchar(0);
267    putchar(1 << ColorBits);			/* # of cyan levels */
268
269    putchar((int)(header->HWResolution[0] >> 8));/* Magenta resolution */
270    putchar((int)header->HWResolution[0]);
271    putchar((int)(header->HWResolution[1] >> 8));
272    putchar((int)header->HWResolution[1]);
273    putchar(0);
274    putchar(1 << ColorBits);			/* # of magenta levels */
275
276    putchar((int)(header->HWResolution[0] >> 8));/* Yellow resolution */
277    putchar((int)header->HWResolution[0]);
278    putchar((int)(header->HWResolution[1] >> 8));
279    putchar((int)header->HWResolution[1]);
280    putchar(0);
281    putchar(1 << ColorBits);			/* # of yellow levels */
282
283    printf("\033&l0H");				/* Set media position */
284  }
285  else
286  {
287    printf("\033*t%uR", header->HWResolution[0]);
288						/* Set resolution */
289
290    if (header->cupsColorSpace == CUPS_CSPACE_KCMY)
291    {
292      NumPlanes = 4;
293      printf("\033*r-4U");			/* Set KCMY graphics */
294    }
295    else if (header->cupsColorSpace == CUPS_CSPACE_CMY)
296    {
297      NumPlanes = 3;
298      printf("\033*r-3U");			/* Set CMY graphics */
299    }
300    else
301      NumPlanes = 1;				/* Black&white graphics */
302
303   /*
304    * Set size and position of graphics...
305    */
306
307    printf("\033*r%uS", header->cupsWidth);	/* Set width */
308    printf("\033*r%uT", header->cupsHeight);	/* Set height */
309
310    printf("\033&a0H");				/* Set horizontal position */
311
312    if (ppd)
313      printf("\033&a%.0fV", 			/* Set vertical position */
314             10.0 * (ppd->sizes[0].length - ppd->sizes[0].top));
315    else
316      printf("\033&a0V");			/* Set top-of-page */
317  }
318
319  printf("\033*r1A");				/* Start graphics */
320
321  if (header->cupsCompression)
322    printf("\033*b%uM",				/* Set compression */
323           header->cupsCompression);
324
325  Feed = 0;					/* No blank lines yet */
326
327 /*
328  * Allocate memory for a line of graphics...
329  */
330
331  if ((Planes[0] = malloc(header->cupsBytesPerLine + NumPlanes)) == NULL)
332  {
333    fputs("ERROR: Unable to allocate memory\n", stderr);
334    exit(1);
335  }
336
337  for (plane = 1; plane < NumPlanes; plane ++)
338    Planes[plane] = Planes[0] + plane * header->cupsBytesPerLine / NumPlanes;
339
340  if (ColorBits > 1)
341    BitBuffer = malloc(ColorBits * ((header->cupsWidth + 7) / 8));
342  else
343    BitBuffer = NULL;
344
345  if (header->cupsCompression)
346    CompBuffer = malloc(header->cupsBytesPerLine * 2 + 2);
347  else
348    CompBuffer = NULL;
349}
350
351
352/*
353 * 'EndPage()' - Finish a page of graphics.
354 */
355
356void
357EndPage(void)
358{
359 /*
360  * Eject the current page...
361  */
362
363  if (NumPlanes > 1)
364  {
365     printf("\033*rC");			/* End color GFX */
366
367     if (!(Duplex && (Page & 1)))
368       printf("\033&l0H");		/* Eject current page */
369  }
370  else
371  {
372     printf("\033*r0B");		/* End GFX */
373
374     if (!(Duplex && (Page & 1)))
375       printf("\014");			/* Eject current page */
376  }
377
378  fflush(stdout);
379
380 /*
381  * Free memory...
382  */
383
384  free(Planes[0]);
385
386  if (BitBuffer)
387    free(BitBuffer);
388
389  if (CompBuffer)
390    free(CompBuffer);
391}
392
393
394/*
395 * 'Shutdown()' - Shutdown the printer.
396 */
397
398void
399Shutdown(void)
400{
401 /*
402  * Send a PCL reset sequence.
403  */
404
405  putchar(0x1b);
406  putchar('E');
407}
408
409
410/*
411 * 'CancelJob()' - Cancel the current job...
412 */
413
414void
415CancelJob(int sig)			/* I - Signal */
416{
417  (void)sig;
418
419  Canceled = 1;
420}
421
422
423/*
424 * 'CompressData()' - Compress a line of graphics.
425 */
426
427void
428CompressData(unsigned char *line,	/* I - Data to compress */
429             unsigned      length,	/* I - Number of bytes */
430	     unsigned      plane,	/* I - Color plane */
431	     unsigned      type)	/* I - Type of compression */
432{
433  unsigned char	*line_ptr,		/* Current byte pointer */
434        	*line_end,		/* End-of-line byte pointer */
435        	*comp_ptr,		/* Pointer into compression buffer */
436        	*start;			/* Start of compression sequence */
437  unsigned	count;			/* Count of bytes for output */
438
439
440  switch (type)
441  {
442    default :
443       /*
444	* Do no compression...
445	*/
446
447	line_ptr = line;
448	line_end = line + length;
449	break;
450
451    case 1 :
452       /*
453        * Do run-length encoding...
454        */
455
456	line_end = line + length;
457	for (line_ptr = line, comp_ptr = CompBuffer;
458	     line_ptr < line_end;
459	     comp_ptr += 2, line_ptr += count)
460	{
461	  for (count = 1;
462               (line_ptr + count) < line_end &&
463	           line_ptr[0] == line_ptr[count] &&
464        	   count < 256;
465               count ++);
466
467	  comp_ptr[0] = (unsigned char)(count - 1);
468	  comp_ptr[1] = line_ptr[0];
469	}
470
471        line_ptr = CompBuffer;
472        line_end = comp_ptr;
473	break;
474
475    case 2 :
476       /*
477        * Do TIFF pack-bits encoding...
478        */
479
480	line_ptr = line;
481	line_end = line + length;
482	comp_ptr = CompBuffer;
483
484	while (line_ptr < line_end)
485	{
486	  if ((line_ptr + 1) >= line_end)
487	  {
488	   /*
489	    * Single byte on the end...
490	    */
491
492	    *comp_ptr++ = 0x00;
493	    *comp_ptr++ = *line_ptr++;
494	  }
495	  else if (line_ptr[0] == line_ptr[1])
496	  {
497	   /*
498	    * Repeated sequence...
499	    */
500
501	    line_ptr ++;
502	    count = 2;
503
504	    while (line_ptr < (line_end - 1) &&
505        	   line_ptr[0] == line_ptr[1] &&
506        	   count < 127)
507	    {
508              line_ptr ++;
509              count ++;
510	    }
511
512	    *comp_ptr++ = (unsigned char)(257 - count);
513	    *comp_ptr++ = *line_ptr++;
514	  }
515	  else
516	  {
517	   /*
518	    * Non-repeated sequence...
519	    */
520
521	    start    = line_ptr;
522	    line_ptr ++;
523	    count    = 1;
524
525	    while (line_ptr < (line_end - 1) &&
526        	   line_ptr[0] != line_ptr[1] &&
527        	   count < 127)
528	    {
529              line_ptr ++;
530              count ++;
531	    }
532
533	    *comp_ptr++ = (unsigned char)(count - 1);
534
535	    memcpy(comp_ptr, start, count);
536	    comp_ptr += count;
537	  }
538	}
539
540        line_ptr = CompBuffer;
541        line_end = comp_ptr;
542	break;
543  }
544
545 /*
546  * Set the length of the data and write a raster plane...
547  */
548
549  printf("\033*b%d%c", (int)(line_end - line_ptr), plane);
550  fwrite(line_ptr, (size_t)(line_end - line_ptr), 1, stdout);
551}
552
553
554/*
555 * 'OutputLine()' - Output a line of graphics.
556 */
557
558void
559OutputLine(cups_page_header2_t *header)	/* I - Page header */
560{
561  unsigned	plane,			/* Current plane */
562		bytes,			/* Bytes to write */
563		count;			/* Bytes to convert */
564  unsigned char	bit,			/* Current plane data */
565		bit0,			/* Current low bit data */
566		bit1,			/* Current high bit data */
567		*plane_ptr,		/* Pointer into Planes */
568		*bit_ptr;		/* Pointer into BitBuffer */
569
570
571 /*
572  * Output whitespace as needed...
573  */
574
575  if (Feed > 0)
576  {
577    printf("\033*b%dY", Feed);
578    Feed = 0;
579  }
580
581 /*
582  * Write bitmap data as needed...
583  */
584
585  bytes = (header->cupsWidth + 7) / 8;
586
587  for (plane = 0; plane < NumPlanes; plane ++)
588    if (ColorBits == 1)
589    {
590     /*
591      * Send bits as-is...
592      */
593
594      CompressData(Planes[plane], bytes, plane < (NumPlanes - 1) ? 'V' : 'W',
595		   header->cupsCompression);
596    }
597    else
598    {
599     /*
600      * Separate low and high bit data into separate buffers.
601      */
602
603      for (count = header->cupsBytesPerLine / NumPlanes,
604               plane_ptr = Planes[plane], bit_ptr = BitBuffer;
605	   count > 0;
606	   count -= 2, plane_ptr += 2, bit_ptr ++)
607      {
608        bit = plane_ptr[0];
609
610        bit0 = (unsigned char)(((bit & 64) << 1) | ((bit & 16) << 2) | ((bit & 4) << 3) | ((bit & 1) << 4));
611        bit1 = (unsigned char)((bit & 128) | ((bit & 32) << 1) | ((bit & 8) << 2) | ((bit & 2) << 3));
612
613        if (count > 1)
614	{
615	  bit = plane_ptr[1];
616
617          bit0 |= (unsigned char)((bit & 1) | ((bit & 4) >> 1) | ((bit & 16) >> 2) | ((bit & 64) >> 3));
618          bit1 |= (unsigned char)(((bit & 2) >> 1) | ((bit & 8) >> 2) | ((bit & 32) >> 3) | ((bit & 128) >> 4));
619	}
620
621        bit_ptr[0]     = bit0;
622	bit_ptr[bytes] = bit1;
623      }
624
625     /*
626      * Send low and high bits...
627      */
628
629      CompressData(BitBuffer, bytes, 'V', header->cupsCompression);
630      CompressData(BitBuffer + bytes, bytes, plane < (NumPlanes - 1) ? 'V' : 'W',
631		   header->cupsCompression);
632    }
633
634  fflush(stdout);
635}
636
637
638/*
639 * 'main()' - Main entry and processing of driver.
640 */
641
642int					/* O - Exit status */
643main(int  argc,				/* I - Number of command-line arguments */
644     char *argv[])			/* I - Command-line arguments */
645{
646  int			fd;		/* File descriptor */
647  cups_raster_t		*ras;		/* Raster stream for printing */
648  cups_page_header2_t	header;		/* Page header from file */
649  unsigned		y;		/* Current line */
650  ppd_file_t		*ppd;		/* PPD file */
651#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
652  struct sigaction action;		/* Actions for POSIX signals */
653#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
654
655
656 /*
657  * Make sure status messages are not buffered...
658  */
659
660  setbuf(stderr, NULL);
661
662 /*
663  * Check command-line...
664  */
665
666  if (argc < 6 || argc > 7)
667  {
668   /*
669    * We don't have the correct number of arguments; write an error message
670    * and return.
671    */
672
673    _cupsLangPrintFilter(stderr, "ERROR",
674                         _("%s job-id user title copies options [file]"),
675			 "rastertohp");
676    return (1);
677  }
678
679 /*
680  * Open the page stream...
681  */
682
683  if (argc == 7)
684  {
685    if ((fd = open(argv[6], O_RDONLY)) == -1)
686    {
687      _cupsLangPrintError("ERROR", _("Unable to open raster file"));
688      sleep(1);
689      return (1);
690    }
691  }
692  else
693    fd = 0;
694
695  ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
696
697 /*
698  * Register a signal handler to eject the current page if the
699  * job is cancelled.
700  */
701
702  Canceled = 0;
703
704#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
705  sigset(SIGTERM, CancelJob);
706#elif defined(HAVE_SIGACTION)
707  memset(&action, 0, sizeof(action));
708
709  sigemptyset(&action.sa_mask);
710  action.sa_handler = CancelJob;
711  sigaction(SIGTERM, &action, NULL);
712#else
713  signal(SIGTERM, CancelJob);
714#endif /* HAVE_SIGSET */
715
716 /*
717  * Initialize the print device...
718  */
719
720  ppd = ppdOpenFile(getenv("PPD"));
721  if (!ppd)
722  {
723    ppd_status_t	status;		/* PPD error */
724    int			linenum;	/* Line number */
725
726    _cupsLangPrintFilter(stderr, "ERROR",
727                         _("The PPD file could not be opened."));
728
729    status = ppdLastError(&linenum);
730
731    fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum);
732
733    return (1);
734  }
735
736  Setup();
737
738 /*
739  * Process pages as needed...
740  */
741
742  Page = 0;
743
744  while (cupsRasterReadHeader2(ras, &header))
745  {
746   /*
747    * Write a status message with the page number and number of copies.
748    */
749
750    if (Canceled)
751      break;
752
753    Page ++;
754
755    fprintf(stderr, "PAGE: %d %d\n", Page, header.NumCopies);
756    _cupsLangPrintFilter(stderr, "INFO", _("Starting page %d."), Page);
757
758   /*
759    * Start the page...
760    */
761
762    StartPage(ppd, &header);
763
764   /*
765    * Loop for each line on the page...
766    */
767
768    for (y = 0; y < header.cupsHeight; y ++)
769    {
770     /*
771      * Let the user know how far we have progressed...
772      */
773
774      if (Canceled)
775	break;
776
777      if ((y & 127) == 0)
778      {
779        _cupsLangPrintFilter(stderr, "INFO",
780	                     _("Printing page %d, %u%% complete."),
781			     Page, 100 * y / header.cupsHeight);
782        fprintf(stderr, "ATTR: job-media-progress=%u\n",
783		100 * y / header.cupsHeight);
784      }
785
786     /*
787      * Read a line of graphics...
788      */
789
790      if (cupsRasterReadPixels(ras, Planes[0], header.cupsBytesPerLine) < 1)
791        break;
792
793     /*
794      * See if the line is blank; if not, write it to the printer...
795      */
796
797      if (Planes[0][0] ||
798          memcmp(Planes[0], Planes[0] + 1, header.cupsBytesPerLine - 1))
799        OutputLine(&header);
800      else
801        Feed ++;
802    }
803
804   /*
805    * Eject the page...
806    */
807
808    _cupsLangPrintFilter(stderr, "INFO", _("Finished page %d."), Page);
809
810    EndPage();
811
812    if (Canceled)
813      break;
814  }
815
816 /*
817  * Shutdown the printer...
818  */
819
820  Shutdown();
821
822  if (ppd)
823    ppdClose(ppd);
824
825 /*
826  * Close the raster stream...
827  */
828
829  cupsRasterClose(ras);
830  if (fd != 0)
831    close(fd);
832
833 /*
834  * If no pages were printed, send an error message...
835  */
836
837  if (Page == 0)
838  {
839    _cupsLangPrintFilter(stderr, "ERROR", _("No pages were found."));
840    return (1);
841  }
842  else
843    return (0);
844}
845