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