Home | History | Annotate | Download | only in filter
      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
     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
     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
    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
    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
    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
    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
    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 */
    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