• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
34 unsigned char	*Planes[4],		/* Output buffers */
35 		*CompBuffer,		/* Compression buffer */
36 		*BitBuffer;		/* Buffer for output bits */
37 unsigned 	NumPlanes,		/* Number of color planes */
38 		ColorBits,		/* Number of bits per color */
39 		Feed;			/* Number of lines to skip */
40 int		Duplex,			/* Current duplex mode */
41 		Page,			/* Current page number */
42 		Canceled;		/* Has the current job been canceled? */
43 
44 
45 /*
46  * Prototypes...
47  */
48 
49 void	Setup(void);
50 void	StartPage(ppd_file_t *ppd, cups_page_header2_t *header);
51 void	EndPage(void);
52 void	Shutdown(void);
53 
54 void	CancelJob(int sig);
55 void	CompressData(unsigned char *line, unsigned length, unsigned plane, unsigned type);
56 void	OutputLine(cups_page_header2_t *header);
57 
58 
59 /*
60  * 'Setup()' - Prepare the printer for printing.
61  */
62 
63 void
Setup(void)64 Setup(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 
79 void
StartPage(ppd_file_t * ppd,cups_page_header2_t * header)80 StartPage(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 
356 void
EndPage(void)357 EndPage(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 
398 void
Shutdown(void)399 Shutdown(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 
414 void
CancelJob(int sig)415 CancelJob(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 
427 void
CompressData(unsigned char * line,unsigned length,unsigned plane,unsigned type)428 CompressData(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 
558 void
OutputLine(cups_page_header2_t * header)559 OutputLine(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 
642 int					/* O - Exit status */
main(int argc,char * argv[])643 main(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