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