4 * Advanced HP Page Control Language and Raster Transfer Language
7 * Copyright 2007-2010 by Apple Inc.
8 * Copyright 1993-2005 by Easy Software Products
10 * These coded instructions, statements, and computer programs are the
11 * property of Apple Inc. and are protected by Federal copyright
12 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
13 * which should have been included with this file. If this file is
14 * file is missing or damaged, see the license at "http://www.cups.org/".
18 * StartPage() - Start a page of graphics.
19 * EndPage() - Finish a page of graphics.
20 * Shutdown() - Shutdown a printer.
21 * CancelJob() - Cancel the current job...
22 * CompressData() - Compress a line of graphics.
23 * OutputLine() - Output the specified number of lines of graphics.
24 * ReadLine() - Read graphics from the page stream.
25 * main() - Main entry and processing of driver.
29 * Include necessary headers...
33 #include <cups/language-private.h>
34 #include <cups/string-private.h>
35 #include "pcl-common.h"
45 OUTPUT_BITMAP
, /* Output bitmap data from RIP */
46 OUTPUT_INVERBIT
, /* Output inverted bitmap data */
47 OUTPUT_RGB
, /* Output 24-bit RGB data from RIP */
48 OUTPUT_DITHERED
/* Output dithered data */
56 cups_rgb_t
*RGB
; /* RGB color separation data */
57 cups_cmyk_t
*CMYK
; /* CMYK color separation data */
58 unsigned char *PixelBuffer
, /* Pixel buffer */
59 *CMYKBuffer
, /* CMYK buffer */
60 *OutputBuffers
[6], /* Output buffers */
61 *DotBuffers
[6], /* Bit buffers */
62 *CompBuffer
, /* Compression buffer */
63 *SeedBuffer
, /* Mode 3 seed buffers */
64 BlankValue
; /* The blank value */
65 short *InputBuffer
; /* Color separation buffer */
66 cups_lut_t
*DitherLuts
[6]; /* Lookup tables for dithering */
67 cups_dither_t
*DitherStates
[6]; /* Dither state tables */
68 int PrinterPlanes
, /* Number of color planes */
69 SeedInvalid
, /* Contents of seed buffer invalid? */
70 DotBits
[6], /* Number of bits per color */
71 DotBufferSizes
[6], /* Size of one row of color dots */
72 DotBufferSize
, /* Size of complete line */
73 OutputFeed
, /* Number of lines to skip */
74 Page
; /* Current page number */
75 pcl_output_t OutputMode
; /* Output mode - see OUTPUT_ consts */
76 const int ColorOrders
[7][7] = /* Order of color planes */
78 { 0, 0, 0, 0, 0, 0, 0 }, /* Black */
79 { 0, 0, 0, 0, 0, 0, 0 },
80 { 0, 1, 2, 0, 0, 0, 0 }, /* CMY */
81 { 3, 0, 1, 2, 0, 0, 0 }, /* KCMY */
82 { 0, 0, 0, 0, 0, 0, 0 },
83 { 5, 0, 1, 2, 3, 4, 0 }, /* KCMYcm */
84 { 5, 0, 1, 2, 3, 4, 6 } /* KCMYcmk */
86 int Canceled
; /* Is the job canceled? */
93 void StartPage(ppd_file_t
*ppd
, cups_page_header2_t
*header
, int job_id
,
94 const char *user
, const char *title
, int num_options
,
95 cups_option_t
*options
);
96 void EndPage(ppd_file_t
*ppd
, cups_page_header2_t
*header
);
97 void Shutdown(ppd_file_t
*ppd
, int job_id
, const char *user
,
98 const char *title
, int num_options
, cups_option_t
*options
);
100 void CancelJob(int sig
);
101 void CompressData(unsigned char *line
, int length
, int plane
, int pend
,
103 void OutputLine(ppd_file_t
*ppd
, cups_page_header2_t
*header
);
104 int ReadLine(cups_raster_t
*ras
, cups_page_header2_t
*header
);
108 * 'StartPage()' - Start a page of graphics.
112 StartPage(ppd_file_t
*ppd
, /* I - PPD file */
113 cups_page_header2_t
*header
, /* I - Page header */
114 int job_id
, /* I - Job ID */
115 const char *user
, /* I - User printing job */
116 const char *title
, /* I - Title of job */
118 /* I - Number of command-line options */
119 cups_option_t
*options
) /* I - Command-line options */
121 int i
; /* Temporary/looping var */
122 int plane
; /* Current plane */
123 char s
[255]; /* Temporary value */
124 const char *colormodel
; /* Color model string */
125 char resolution
[PPD_MAX_NAME
],
126 /* Resolution string */
127 spec
[PPD_MAX_NAME
]; /* PPD attribute name */
128 ppd_attr_t
*attr
; /* Attribute from PPD file */
129 ppd_choice_t
*choice
; /* Selected option */
130 const int *order
; /* Order to use */
131 int xorigin
, /* X origin of page */
132 yorigin
; /* Y origin of page */
133 static const float default_lut
[2] = /* Default dithering lookup table */
144 fprintf(stderr
, "DEBUG: StartPage...\n");
145 fprintf(stderr
, "DEBUG: MediaClass = \"%s\"\n", header
->MediaClass
);
146 fprintf(stderr
, "DEBUG: MediaColor = \"%s\"\n", header
->MediaColor
);
147 fprintf(stderr
, "DEBUG: MediaType = \"%s\"\n", header
->MediaType
);
148 fprintf(stderr
, "DEBUG: OutputType = \"%s\"\n", header
->OutputType
);
150 fprintf(stderr
, "DEBUG: AdvanceDistance = %d\n", header
->AdvanceDistance
);
151 fprintf(stderr
, "DEBUG: AdvanceMedia = %d\n", header
->AdvanceMedia
);
152 fprintf(stderr
, "DEBUG: Collate = %d\n", header
->Collate
);
153 fprintf(stderr
, "DEBUG: CutMedia = %d\n", header
->CutMedia
);
154 fprintf(stderr
, "DEBUG: Duplex = %d\n", header
->Duplex
);
155 fprintf(stderr
, "DEBUG: HWResolution = [ %d %d ]\n", header
->HWResolution
[0],
156 header
->HWResolution
[1]);
157 fprintf(stderr
, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n",
158 header
->ImagingBoundingBox
[0], header
->ImagingBoundingBox
[1],
159 header
->ImagingBoundingBox
[2], header
->ImagingBoundingBox
[3]);
160 fprintf(stderr
, "DEBUG: InsertSheet = %d\n", header
->InsertSheet
);
161 fprintf(stderr
, "DEBUG: Jog = %d\n", header
->Jog
);
162 fprintf(stderr
, "DEBUG: LeadingEdge = %d\n", header
->LeadingEdge
);
163 fprintf(stderr
, "DEBUG: Margins = [ %d %d ]\n", header
->Margins
[0],
165 fprintf(stderr
, "DEBUG: ManualFeed = %d\n", header
->ManualFeed
);
166 fprintf(stderr
, "DEBUG: MediaPosition = %d\n", header
->MediaPosition
);
167 fprintf(stderr
, "DEBUG: MediaWeight = %d\n", header
->MediaWeight
);
168 fprintf(stderr
, "DEBUG: MirrorPrint = %d\n", header
->MirrorPrint
);
169 fprintf(stderr
, "DEBUG: NegativePrint = %d\n", header
->NegativePrint
);
170 fprintf(stderr
, "DEBUG: NumCopies = %d\n", header
->NumCopies
);
171 fprintf(stderr
, "DEBUG: Orientation = %d\n", header
->Orientation
);
172 fprintf(stderr
, "DEBUG: OutputFaceUp = %d\n", header
->OutputFaceUp
);
173 fprintf(stderr
, "DEBUG: PageSize = [ %d %d ]\n", header
->PageSize
[0],
174 header
->PageSize
[1]);
175 fprintf(stderr
, "DEBUG: Separations = %d\n", header
->Separations
);
176 fprintf(stderr
, "DEBUG: TraySwitch = %d\n", header
->TraySwitch
);
177 fprintf(stderr
, "DEBUG: Tumble = %d\n", header
->Tumble
);
178 fprintf(stderr
, "DEBUG: cupsWidth = %d\n", header
->cupsWidth
);
179 fprintf(stderr
, "DEBUG: cupsHeight = %d\n", header
->cupsHeight
);
180 fprintf(stderr
, "DEBUG: cupsMediaType = %d\n", header
->cupsMediaType
);
181 fprintf(stderr
, "DEBUG: cupsBitsPerColor = %d\n", header
->cupsBitsPerColor
);
182 fprintf(stderr
, "DEBUG: cupsBitsPerPixel = %d\n", header
->cupsBitsPerPixel
);
183 fprintf(stderr
, "DEBUG: cupsBytesPerLine = %d\n", header
->cupsBytesPerLine
);
184 fprintf(stderr
, "DEBUG: cupsColorOrder = %d\n", header
->cupsColorOrder
);
185 fprintf(stderr
, "DEBUG: cupsColorSpace = %d\n", header
->cupsColorSpace
);
186 fprintf(stderr
, "DEBUG: cupsCompression = %d\n", header
->cupsCompression
);
190 * MacOS X 10.2.x doesn't set most of the page device attributes, so check
191 * the options and set them accordingly...
194 if (ppdIsMarked(ppd
, "Duplex", "DuplexNoTumble"))
196 header
->Duplex
= CUPS_TRUE
;
197 header
->Tumble
= CUPS_FALSE
;
199 else if (ppdIsMarked(ppd
, "Duplex", "DuplexTumble"))
201 header
->Duplex
= CUPS_TRUE
;
202 header
->Tumble
= CUPS_TRUE
;
205 fprintf(stderr
, "DEBUG: num_options=%d\n", num_options
);
207 for (i
= 0; i
< num_options
; i
++)
208 fprintf(stderr
, "DEBUG: options[%d]=[\"%s\" \"%s\"]\n", i
,
209 options
[i
].name
, options
[i
].value
);
210 #endif /* __APPLE__ */
213 * Figure out the color model and spec strings...
216 switch (header
->cupsColorSpace
)
219 colormodel
= "Black";
225 case CUPS_CSPACE_RGB
:
228 case CUPS_CSPACE_CMY
:
231 case CUPS_CSPACE_CMYK
:
236 if (header
->HWResolution
[0] != header
->HWResolution
[1])
237 snprintf(resolution
, sizeof(resolution
), "%dx%ddpi",
238 header
->HWResolution
[0], header
->HWResolution
[1]);
240 snprintf(resolution
, sizeof(resolution
), "%ddpi",
241 header
->HWResolution
[0]);
243 if (!header
->MediaType
[0])
244 strcpy(header
->MediaType
, "PLAIN");
247 * Get the dithering parameters...
252 if (header
->cupsBitsPerColor
== 1)
255 * Use raw bitmap mode...
258 switch (header
->cupsColorSpace
)
261 OutputMode
= OUTPUT_BITMAP
;
265 OutputMode
= OUTPUT_INVERBIT
;
269 case CUPS_CSPACE_RGB
:
270 OutputMode
= OUTPUT_INVERBIT
;
273 case CUPS_CSPACE_CMY
:
274 OutputMode
= OUTPUT_BITMAP
;
277 case CUPS_CSPACE_CMYK
:
278 OutputMode
= OUTPUT_BITMAP
;
283 if (OutputMode
== OUTPUT_INVERBIT
)
286 DotBufferSize
= header
->cupsBytesPerLine
;
288 memset(DitherLuts
, 0, sizeof(DitherLuts
));
289 memset(DitherStates
, 0, sizeof(DitherStates
));
291 else if (header
->cupsColorSpace
== CUPS_CSPACE_RGB
&&
292 (ppd
->model_number
& PCL_RASTER_RGB24
))
295 * Use 24-bit RGB output mode...
298 OutputMode
= OUTPUT_RGB
;
300 DotBufferSize
= header
->cupsBytesPerLine
;
302 if (header
->cupsCompression
== 10)
305 memset(DitherLuts
, 0, sizeof(DitherLuts
));
306 memset(DitherStates
, 0, sizeof(DitherStates
));
308 else if ((header
->cupsColorSpace
== CUPS_CSPACE_K
||
309 header
->cupsColorSpace
== CUPS_CSPACE_W
) &&
310 (ppd
->model_number
& PCL_RASTER_RGB24
) &&
311 header
->cupsCompression
== 10)
314 * Use 24-bit RGB output mode for grayscale/black output...
317 OutputMode
= OUTPUT_RGB
;
319 DotBufferSize
= header
->cupsBytesPerLine
;
321 if (header
->cupsColorSpace
== CUPS_CSPACE_W
)
324 memset(DitherLuts
, 0, sizeof(DitherLuts
));
325 memset(DitherStates
, 0, sizeof(DitherStates
));
330 * Use dithered output mode...
333 OutputMode
= OUTPUT_DITHERED
;
336 * Load the appropriate color profiles...
342 fputs("DEBUG: Attempting to load color profiles using the following values:\n", stderr
);
343 fprintf(stderr
, "DEBUG: ColorModel = %s\n", colormodel
);
344 fprintf(stderr
, "DEBUG: MediaType = %s\n", header
->MediaType
);
345 fprintf(stderr
, "DEBUG: Resolution = %s\n", resolution
);
347 if (header
->cupsColorSpace
== CUPS_CSPACE_RGB
||
348 header
->cupsColorSpace
== CUPS_CSPACE_W
)
349 RGB
= cupsRGBLoad(ppd
, colormodel
, header
->MediaType
, resolution
);
351 CMYK
= cupsCMYKLoad(ppd
, colormodel
, header
->MediaType
, resolution
);
354 fputs("DEBUG: Loaded RGB separation from PPD.\n", stderr
);
357 fputs("DEBUG: Loaded CMYK separation from PPD.\n", stderr
);
360 fputs("DEBUG: Loading default K separation.\n", stderr
);
361 CMYK
= cupsCMYKNew(1);
364 PrinterPlanes
= CMYK
->num_channels
;
367 * Use dithered mode...
370 switch (PrinterPlanes
)
373 DitherLuts
[0] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
374 resolution
, "Black");
378 DitherLuts
[0] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
380 DitherLuts
[1] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
381 resolution
, "Magenta");
382 DitherLuts
[2] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
383 resolution
, "Yellow");
387 DitherLuts
[0] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
389 DitherLuts
[1] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
390 resolution
, "Magenta");
391 DitherLuts
[2] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
392 resolution
, "Yellow");
393 DitherLuts
[3] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
394 resolution
, "Black");
397 case 6 : /* CcMmYK */
398 DitherLuts
[0] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
400 DitherLuts
[1] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
401 resolution
, "LightCyan");
402 DitherLuts
[2] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
403 resolution
, "Magenta");
404 DitherLuts
[3] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
405 resolution
, "LightMagenta");
406 DitherLuts
[4] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
407 resolution
, "Yellow");
408 DitherLuts
[5] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
409 resolution
, "Black");
413 for (plane
= 0; plane
< PrinterPlanes
; plane
++)
415 if (!DitherLuts
[plane
])
416 DitherLuts
[plane
] = cupsLutNew(2, default_lut
);
418 if (DitherLuts
[plane
][4095].pixel
> 1)
423 DitherStates
[plane
] = cupsDitherNew(header
->cupsWidth
);
425 if (!DitherLuts
[plane
])
426 DitherLuts
[plane
] = cupsLutNew(2, default_lut
);
430 fprintf(stderr
, "DEBUG: PrinterPlanes = %d\n", PrinterPlanes
);
433 * Initialize the printer...
436 if ((attr
= ppdFindAttr(ppd
, "cupsInitialNulls", NULL
)) != NULL
)
437 for (i
= atoi(attr
->value
); i
> 0; i
--)
440 if (Page
== 1 && (ppd
->model_number
& PCL_PJL
))
448 pjl_set_job(job_id
, user
, title
);
450 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "StartJob")) != NULL
)
451 pjl_write(ppd
, attr
->value
, NULL
, job_id
, user
, title
, num_options
,
454 snprintf(spec
, sizeof(spec
), "RENDERMODE.%s", colormodel
);
455 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", spec
)) != NULL
)
456 printf("@PJL SET RENDERMODE=%s\r\n", attr
->value
);
458 snprintf(spec
, sizeof(spec
), "COLORSPACE.%s", colormodel
);
459 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", spec
)) != NULL
)
460 printf("@PJL SET COLORSPACE=%s\r\n", attr
->value
);
462 snprintf(spec
, sizeof(spec
), "RENDERINTENT.%s", colormodel
);
463 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", spec
)) != NULL
)
464 printf("@PJL SET RENDERINTENT=%s\r\n", attr
->value
);
466 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "Duplex")) != NULL
)
468 sprintf(s
, "%d", header
->Duplex
);
469 pjl_write(ppd
, attr
->value
, s
, job_id
, user
, title
, num_options
, options
);
472 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "Tumble")) != NULL
)
474 sprintf(s
, "%d", header
->Tumble
);
475 pjl_write(ppd
, attr
->value
, s
, job_id
, user
, title
, num_options
, options
);
478 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "MediaClass")) != NULL
)
479 pjl_write(ppd
, attr
->value
, header
->MediaClass
, job_id
, user
, title
,
480 num_options
, options
);
482 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "MediaColor")) != NULL
)
483 pjl_write(ppd
, attr
->value
, header
->MediaColor
, job_id
, user
, title
,
484 num_options
, options
);
486 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "MediaType")) != NULL
)
487 pjl_write(ppd
, attr
->value
, header
->MediaType
, job_id
, user
, title
,
488 num_options
, options
);
490 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "OutputType")) != NULL
)
491 pjl_write(ppd
, attr
->value
, header
->OutputType
, job_id
, user
, title
,
492 num_options
, options
);
494 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "cupsBooklet")) != NULL
&&
495 (choice
= ppdFindMarkedChoice(ppd
, "cupsBooklet")) != NULL
)
496 pjl_write(ppd
, attr
->value
, choice
->choice
, job_id
, user
, title
,
497 num_options
, options
);
499 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "Jog")) != NULL
)
501 sprintf(s
, "%d", header
->Jog
);
502 pjl_write(ppd
, attr
->value
, s
, job_id
, user
, title
, num_options
, options
);
505 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "cupsPunch")) != NULL
&&
506 (choice
= ppdFindMarkedChoice(ppd
, "cupsPunch")) != NULL
)
507 pjl_write(ppd
, attr
->value
, choice
->choice
, job_id
, user
, title
,
508 num_options
, options
);
510 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "cupsStaple")) != NULL
&&
511 (choice
= ppdFindMarkedChoice(ppd
, "cupsStaple")) != NULL
)
512 pjl_write(ppd
, attr
->value
, choice
->choice
, job_id
, user
, title
,
513 num_options
, options
);
515 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "cupsRET")) != NULL
&&
516 (choice
= ppdFindMarkedChoice(ppd
, "cupsRET")) != NULL
)
517 pjl_write(ppd
, attr
->value
, choice
->choice
, job_id
, user
, title
,
518 num_options
, options
);
520 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "cupsTonerSave")) != NULL
&&
521 (choice
= ppdFindMarkedChoice(ppd
, "cupsTonerSave")) != NULL
)
522 pjl_write(ppd
, attr
->value
, choice
->choice
, job_id
, user
, title
,
523 num_options
, options
);
525 if (ppd
->model_number
& PCL_PJL_PAPERWIDTH
)
527 printf("@PJL SET PAPERLENGTH=%d\r\n", header
->PageSize
[1] * 10);
528 printf("@PJL SET PAPERWIDTH=%d\r\n", header
->PageSize
[0] * 10);
531 if (ppd
->model_number
& PCL_PJL_RESOLUTION
)
532 printf("@PJL SET RESOLUTION=%d\r\n", header
->HWResolution
[0]);
534 if (ppd
->model_number
& PCL_PJL_HPGL2
)
535 pjl_enter_language("HPGL2");
536 else if (ppd
->model_number
& PCL_PJL_PCL3GUI
)
537 pjl_enter_language("PCL3GUI");
539 pjl_enter_language("PCL");
547 if (ppd
->model_number
& PCL_PJL_HPGL2
)
552 * HP-GL/2 initialization...
556 printf("MG\"%d %s %s\";", job_id
, user
, title
);
560 * Set media size, position, type, etc...
564 printf("PS%.0f,%.0f;",
565 header
->cupsHeight
* 1016.0 / header
->HWResolution
[1],
566 header
->cupsWidth
* 1016.0 / header
->HWResolution
[0]);
570 printf("MT%d;", header
->cupsMediaType
);
572 if (header
->CutMedia
== CUPS_CUT_PAGE
)
578 * Set graphics mode...
582 pcl_set_negative_motion();
587 * Set media size, position, type, etc...
590 if (!header
->Duplex
|| (Page
& 1))
592 pcl_set_media_size(ppd
, header
->PageSize
[0], header
->PageSize
[1]);
594 if (header
->MediaPosition
)
595 pcl_set_media_source(header
->MediaPosition
);
597 pcl_set_media_type(header
->cupsMediaType
);
599 if (ppdFindAttr(ppd
, "cupsPJL", "Duplex") == NULL
)
600 pcl_set_duplex(header
->Duplex
, header
->Tumble
);
603 * Set the number of copies...
606 if (!ppd
->manual_copies
)
607 pcl_set_copies(header
->NumCopies
);
610 * Set the output order/bin...
613 if (ppdFindAttr(ppd
, "cupsPJL", "Jog") == NULL
&& header
->Jog
)
614 printf("\033&l%dG", header
->Jog
);
619 * Print on the back side...
625 if (header
->Duplex
&& (ppd
->model_number
& PCL_RASTER_CRD
))
628 * Reload the media...
631 pcl_set_media_source(-2);
635 * Set the units for cursor positioning and go to the top of the form.
638 printf("\033&u%dD", header
->HWResolution
[0]);
639 printf("\033*p0Y\033*p0X");
642 if ((attr
= cupsFindAttr(ppd
, "cupsPCLQuality", colormodel
,
643 header
->MediaType
, resolution
, spec
,
644 sizeof(spec
))) != NULL
)
647 * Set the print quality...
650 if (ppd
->model_number
& PCL_PJL_HPGL2
)
651 printf("QM%d", atoi(attr
->value
));
653 printf("\033*o%dM", atoi(attr
->value
));
657 * Enter graphics mode...
660 if (ppd
->model_number
& PCL_RASTER_CRD
)
663 * Use configure raster data command...
666 if (OutputMode
== OUTPUT_RGB
)
669 * Send 12-byte configure raster data command with horizontal and
670 * vertical resolutions as well as a color count...
673 if ((attr
= cupsFindAttr(ppd
, "cupsPCLCRDMode", colormodel
,
674 header
->MediaType
, resolution
, spec
,
675 sizeof(spec
))) != NULL
)
676 i
= atoi(attr
->value
);
681 putchar(6); /* Format 6 */
682 putchar(i
); /* Set pen mode */
683 putchar(0x00); /* Number components */
684 putchar(0x01); /* (1 for RGB) */
686 putchar(header
->HWResolution
[0] >> 8);
687 putchar(header
->HWResolution
[0]);
688 putchar(header
->HWResolution
[1] >> 8);
689 putchar(header
->HWResolution
[1]);
691 putchar(header
->cupsCompression
); /* Compression mode 3 or 10 */
692 putchar(0x01); /* Portrait orientation */
693 putchar(0x20); /* Bits per pixel (32 = RGB) */
694 putchar(0x01); /* Planes per pixel (1 = chunky RGB) */
699 * Send the configure raster data command with horizontal and
700 * vertical resolutions as well as a color count...
703 printf("\033*g%dW", PrinterPlanes
* 6 + 2);
704 putchar(2); /* Format 2 */
705 putchar(PrinterPlanes
); /* Output planes */
707 order
= ColorOrders
[PrinterPlanes
- 1];
709 for (i
= 0; i
< PrinterPlanes
; i
++)
713 putchar(header
->HWResolution
[0] >> 8);
714 putchar(header
->HWResolution
[0]);
715 putchar(header
->HWResolution
[1] >> 8);
716 putchar(header
->HWResolution
[1]);
718 putchar(1 << DotBits
[plane
]);
722 else if ((ppd
->model_number
& PCL_RASTER_CID
) && OutputMode
== OUTPUT_RGB
)
725 * Use configure image data command...
728 pcl_set_simple_resolution(header
->HWResolution
[0]);
729 /* Set output resolution */
731 cupsWritePrintData("\033*v6W\0\3\0\10\10\10", 11);
737 * Use simple raster commands...
740 pcl_set_simple_resolution(header
->HWResolution
[0]);
741 /* Set output resolution */
743 if (PrinterPlanes
== 3)
744 pcl_set_simple_cmy();
745 else if (PrinterPlanes
== 4)
746 pcl_set_simple_kcmy();
749 if ((attr
= ppdFindAttr(ppd
, "cupsPCLOrigin", "X")) != NULL
)
750 xorigin
= atoi(attr
->value
);
754 if ((attr
= ppdFindAttr(ppd
, "cupsPCLOrigin", "Y")) != NULL
)
755 yorigin
= atoi(attr
->value
);
759 printf("\033&a%dH\033&a%dV", xorigin
, yorigin
);
760 printf("\033*r%dS", header
->cupsWidth
);
761 printf("\033*r%dT", header
->cupsHeight
);
764 if (header
->cupsCompression
&& header
->cupsCompression
!= 10)
765 printf("\033*b%dM", header
->cupsCompression
);
770 * Allocate memory for the page...
773 PixelBuffer
= malloc(header
->cupsBytesPerLine
);
775 if (OutputMode
== OUTPUT_DITHERED
)
777 InputBuffer
= malloc(header
->cupsWidth
* PrinterPlanes
* 2);
778 OutputBuffers
[0] = malloc(PrinterPlanes
* header
->cupsWidth
);
780 for (i
= 1; i
< PrinterPlanes
; i
++)
781 OutputBuffers
[i
] = OutputBuffers
[0] + i
* header
->cupsWidth
;
784 CMYKBuffer
= malloc(header
->cupsWidth
* PrinterPlanes
);
786 for (plane
= 0, DotBufferSize
= 0; plane
< PrinterPlanes
; plane
++)
788 DotBufferSizes
[plane
] = (header
->cupsWidth
+ 7) / 8 * DotBits
[plane
];
789 DotBufferSize
+= DotBufferSizes
[plane
];
792 DotBuffers
[0] = malloc(DotBufferSize
);
793 for (plane
= 1; plane
< PrinterPlanes
; plane
++)
794 DotBuffers
[plane
] = DotBuffers
[plane
- 1] + DotBufferSizes
[plane
- 1];
797 if (header
->cupsCompression
)
798 CompBuffer
= malloc(DotBufferSize
* 4);
800 if (header
->cupsCompression
>= 3)
801 SeedBuffer
= malloc(DotBufferSize
);
805 fprintf(stderr
, "BlankValue=%d\n", BlankValue
);
810 * 'EndPage()' - Finish a page of graphics.
814 EndPage(ppd_file_t
*ppd
, /* I - PPD file */
815 cups_page_header2_t
*header
) /* I - Page header */
817 int plane
; /* Current plane */
821 * End graphics mode...
824 if (ppd
->model_number
& PCL_RASTER_END_COLOR
)
825 printf("\033*rC"); /* End color GFX */
827 printf("\033*r0B"); /* End B&W GFX */
830 * Output a page eject sequence...
833 if (ppd
->model_number
& PCL_PJL_HPGL2
)
835 pcl_set_hpgl_mode(0); /* Back to HP-GL/2 mode */
836 printf("PG;"); /* Eject the current page */
838 else if (!(header
->Duplex
&& (Page
& 1)))
839 printf("\014"); /* Eject current page */
842 * Free memory for the page...
847 if (OutputMode
== OUTPUT_DITHERED
)
849 for (plane
= 0; plane
< PrinterPlanes
; plane
++)
851 cupsDitherDelete(DitherStates
[plane
]);
852 cupsLutDelete(DitherLuts
[plane
]);
857 free(OutputBuffers
[0]);
859 cupsCMYKDelete(CMYK
);
868 if (header
->cupsCompression
)
871 if (header
->cupsCompression
>= 3)
877 * 'Shutdown()' - Shutdown a printer.
881 Shutdown(ppd_file_t
*ppd
, /* I - PPD file */
882 int job_id
, /* I - Job ID */
883 const char *user
, /* I - User printing job */
884 const char *title
, /* I - Title of job */
885 int num_options
,/* I - Number of command-line options */
886 cups_option_t
*options
) /* I - Command-line options */
888 ppd_attr_t
*attr
; /* Attribute from PPD file */
891 if ((attr
= ppdFindAttr(ppd
, "cupsPCL", "EndJob")) != NULL
)
894 * Tell the printer how many pages were in the job...
898 printf(attr
->value
, Page
);
903 * Return the printer to the default state...
909 if (ppd
->model_number
& PCL_PJL
)
913 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "EndJob")) != NULL
)
914 pjl_write(ppd
, attr
->value
, NULL
, job_id
, user
, title
, num_options
,
917 printf("@PJL EOJ\r\n");
925 * 'CancelJob()' - Cancel the current job...
929 CancelJob(int sig
) /* I - Signal */
938 * 'CompressData()' - Compress a line of graphics.
942 CompressData(unsigned char *line
, /* I - Data to compress */
943 int length
, /* I - Number of bytes */
944 int plane
, /* I - Color plane */
945 int pend
, /* I - End character for data */
946 int type
) /* I - Type of compression */
948 unsigned char *line_ptr
, /* Current byte pointer */
949 *line_end
, /* End-of-line byte pointer */
950 *comp_ptr
, /* Pointer into compression buffer */
951 *start
, /* Start of compression sequence */
952 *seed
; /* Seed buffer pointer */
953 int count
, /* Count of bytes for output */
954 offset
, /* Offset of bytes for output */
955 temp
; /* Temporary count */
956 int r
, g
, b
; /* RGB deltas for mode 10 compression */
963 * Do no compression; with a mode-0 only printer, we can compress blank
969 if (cupsCheckBytes(line
, length
))
970 line_end
= line
; /* Blank line */
972 line_end
= line
+ length
; /* Non-blank line */
977 * Do run-length encoding...
980 line_end
= line
+ length
;
981 for (line_ptr
= line
, comp_ptr
= CompBuffer
;
983 comp_ptr
+= 2, line_ptr
+= count
)
986 (line_ptr
+ count
) < line_end
&&
987 line_ptr
[0] == line_ptr
[count
] &&
991 comp_ptr
[0] = count
- 1;
992 comp_ptr
[1] = line_ptr
[0];
995 line_ptr
= CompBuffer
;
1001 * Do TIFF pack-bits encoding...
1005 line_end
= line
+ length
;
1006 comp_ptr
= CompBuffer
;
1008 while (line_ptr
< line_end
)
1010 if ((line_ptr
+ 1) >= line_end
)
1013 * Single byte on the end...
1017 *comp_ptr
++ = *line_ptr
++;
1019 else if (line_ptr
[0] == line_ptr
[1])
1022 * Repeated sequence...
1028 while (line_ptr
< (line_end
- 1) &&
1029 line_ptr
[0] == line_ptr
[1] &&
1036 *comp_ptr
++ = 257 - count
;
1037 *comp_ptr
++ = *line_ptr
++;
1042 * Non-repeated sequence...
1049 while (line_ptr
< (line_end
- 1) &&
1050 line_ptr
[0] != line_ptr
[1] &&
1057 *comp_ptr
++ = count
- 1;
1059 memcpy(comp_ptr
, start
, count
);
1064 line_ptr
= CompBuffer
;
1065 line_end
= comp_ptr
;
1070 * Do delta-row compression...
1074 line_end
= line
+ length
;
1076 comp_ptr
= CompBuffer
;
1077 seed
= SeedBuffer
+ plane
* length
;
1079 while (line_ptr
< line_end
)
1082 * Find the next non-matching sequence...
1090 * The seed buffer is invalid, so do the next 8 bytes, max...
1095 if ((count
= line_end
- line_ptr
) > 8)
1103 * The seed buffer is valid, so compare against it...
1106 while (*line_ptr
== *seed
&&
1107 line_ptr
< line_end
)
1113 if (line_ptr
== line_end
)
1116 offset
= line_ptr
- start
;
1119 * Find up to 8 non-matching bytes...
1124 while (*line_ptr
!= *seed
&&
1125 line_ptr
< line_end
&&
1135 * Place mode 3 compression data in the buffer; see HP manuals
1142 * Output multi-byte offset...
1145 *comp_ptr
++ = ((count
- 1) << 5) | 31;
1148 while (offset
>= 255)
1154 *comp_ptr
++ = offset
;
1159 * Output single-byte offset...
1162 *comp_ptr
++ = ((count
- 1) << 5) | offset
;
1165 memcpy(comp_ptr
, start
, count
);
1169 line_ptr
= CompBuffer
;
1170 line_end
= comp_ptr
;
1172 memcpy(SeedBuffer
+ plane
* length
, line
, length
);
1177 * Mode 10 "near lossless" RGB compression...
1181 line_end
= line
+ length
;
1183 comp_ptr
= CompBuffer
;
1186 if (PrinterPlanes
== 1)
1189 * Do grayscale compression to RGB...
1192 while (line_ptr
< line_end
)
1195 * Find the next non-matching sequence...
1199 while (line_ptr
< line_end
&&
1206 if (line_ptr
== line_end
)
1209 offset
= line_ptr
- start
;
1212 * Find non-matching grayscale pixels...
1216 while (line_ptr
< line_end
&&
1223 count
= line_ptr
- start
;
1226 fprintf(stderr
, "DEBUG: offset=%d, count=%d, comp_ptr=%p(%d of %d)...\n",
1227 offset
, count
, comp_ptr
, comp_ptr
- CompBuffer
,
1232 * Place mode 10 compression data in the buffer; each sequence
1233 * starts with a command byte that looks like:
1235 * CMD SRC SRC OFF OFF CNT CNT CNT
1237 * For the purpose of this driver, CMD and SRC are always 0.
1239 * If the offset >= 3 then additional offset bytes follow the
1240 * first command byte, each byte == 255 until the last one.
1242 * If the count >= 7, then additional count bytes follow each
1243 * group of pixels, each byte == 255 until the last one.
1245 * The offset and count are in RGB tuples (not bytes, as for
1252 * Output multi-byte offset...
1258 *comp_ptr
++ = 0x18 | (count
- 1);
1261 while (offset
>= 255)
1267 *comp_ptr
++ = offset
;
1272 * Output single-byte offset...
1276 *comp_ptr
++ = (offset
<< 3) | 0x07;
1278 *comp_ptr
++ = (offset
<< 3) | (count
- 1);
1289 * This is exceedingly lame... The replacement counts
1290 * are intermingled with the data...
1302 * Get difference between current and see pixels...
1307 b
= ((*start
& 0xfe) - (*seed
& 0xfe)) / 2;
1309 if (r
< -16 || r
> 15 || g
< -16 || g
> 15 || b
< -16 || b
> 15)
1312 * Pack 24-bit RGB into 23 bits... Lame...
1317 *comp_ptr
++ = g
>> 1;
1320 *comp_ptr
++ = 0x80 | (g
>> 1);
1322 *comp_ptr
++ = g
>> 1;
1325 *comp_ptr
++ = 0x80 | (g
>> 1);
1327 *comp_ptr
++ = g
>> 1;
1332 * Pack 15-bit RGB difference...
1335 *comp_ptr
++ = 0x80 | ((r
<< 2) & 0x7c) | ((g
>> 3) & 0x03);
1336 *comp_ptr
++ = ((g
<< 5) & 0xe0) | (b
& 0x1f);
1345 * Make sure we have the ending count if the replacement count
1346 * was exactly 8 + 255n...
1356 * Do RGB compression...
1359 while (line_ptr
< line_end
)
1362 * Find the next non-matching sequence...
1366 while (line_ptr
[0] == seed
[0] &&
1367 line_ptr
[1] == seed
[1] &&
1368 line_ptr
[2] == seed
[2] &&
1369 (line_ptr
+ 2) < line_end
)
1375 if (line_ptr
== line_end
)
1378 offset
= (line_ptr
- start
) / 3;
1381 * Find non-matching RGB tuples...
1385 while ((line_ptr
[0] != seed
[0] ||
1386 line_ptr
[1] != seed
[1] ||
1387 line_ptr
[2] != seed
[2]) &&
1388 (line_ptr
+ 2) < line_end
)
1394 count
= (line_ptr
- start
) / 3;
1397 * Place mode 10 compression data in the buffer; each sequence
1398 * starts with a command byte that looks like:
1400 * CMD SRC SRC OFF OFF CNT CNT CNT
1402 * For the purpose of this driver, CMD and SRC are always 0.
1404 * If the offset >= 3 then additional offset bytes follow the
1405 * first command byte, each byte == 255 until the last one.
1407 * If the count >= 7, then additional count bytes follow each
1408 * group of pixels, each byte == 255 until the last one.
1410 * The offset and count are in RGB tuples (not bytes, as for
1417 * Output multi-byte offset...
1423 *comp_ptr
++ = 0x18 | (count
- 1);
1426 while (offset
>= 255)
1432 *comp_ptr
++ = offset
;
1437 * Output single-byte offset...
1441 *comp_ptr
++ = (offset
<< 3) | 0x07;
1443 *comp_ptr
++ = (offset
<< 3) | (count
- 1);
1454 * This is exceedingly lame... The replacement counts
1455 * are intermingled with the data...
1467 * Get difference between current and see pixels...
1470 r
= start
[0] - seed
[0];
1471 g
= start
[1] - seed
[1];
1472 b
= ((start
[2] & 0xfe) - (seed
[2] & 0xfe)) / 2;
1474 if (r
< -16 || r
> 15 || g
< -16 || g
> 15 || b
< -16 || b
> 15)
1477 * Pack 24-bit RGB into 23 bits... Lame...
1480 *comp_ptr
++ = start
[0] >> 1;
1483 *comp_ptr
++ = 0x80 | (start
[1] >> 1);
1485 *comp_ptr
++ = start
[1] >> 1;
1488 *comp_ptr
++ = 0x80 | (start
[2] >> 1);
1490 *comp_ptr
++ = start
[2] >> 1;
1495 * Pack 15-bit RGB difference...
1498 *comp_ptr
++ = 0x80 | ((r
<< 2) & 0x7c) | ((g
>> 3) & 0x03);
1499 *comp_ptr
++ = ((g
<< 5) & 0xe0) | (b
& 0x1f);
1508 * Make sure we have the ending count if the replacement count
1509 * was exactly 8 + 255n...
1517 line_ptr
= CompBuffer
;
1518 line_end
= comp_ptr
;
1520 memcpy(SeedBuffer
, line
, length
);
1525 * Set the length of the data and write a raster plane...
1528 printf("\033*b%d%c", (int)(line_end
- line_ptr
), pend
);
1529 cupsWritePrintData(line_ptr
, line_end
- line_ptr
);
1534 * 'OutputLine()' - Output the specified number of lines of graphics.
1538 OutputLine(ppd_file_t
*ppd
, /* I - PPD file */
1539 cups_page_header2_t
*header
) /* I - Page header */
1541 int i
, j
; /* Looping vars */
1542 int plane
; /* Current plane */
1543 unsigned char bit
; /* Current bit */
1544 int bytes
; /* Number of bytes/plane */
1545 int width
; /* Width of line in pixels */
1546 const int *order
; /* Order to use */
1547 unsigned char *ptr
; /* Pointer into buffer */
1551 * Output whitespace as needed...
1556 if (header
->cupsCompression
< 3)
1559 * Send blank raster lines...
1562 while (OutputFeed
> 0)
1571 * Send Y offset command and invalidate the seed buffer...
1574 printf("\033*b%dY", OutputFeed
);
1581 * Write bitmap data as needed...
1586 case OUTPUT_BITMAP
: /* Send 1-bit bitmap data... */
1587 order
= ColorOrders
[PrinterPlanes
- 1];
1588 bytes
= header
->cupsBytesPerLine
/ PrinterPlanes
;
1590 for (i
= 0; i
< PrinterPlanes
; i
++)
1594 CompressData(PixelBuffer
+ i
* bytes
, bytes
, plane
,
1595 (i
< (PrinterPlanes
- 1)) ? 'V' : 'W',
1596 header
->cupsCompression
);
1600 case OUTPUT_INVERBIT
: /* Send inverted 1-bit bitmap data... */
1601 order
= ColorOrders
[PrinterPlanes
- 1];
1602 bytes
= header
->cupsBytesPerLine
/ PrinterPlanes
;
1604 for (i
= header
->cupsBytesPerLine
, ptr
= PixelBuffer
;
1609 for (i
= 0; i
< PrinterPlanes
; i
++)
1613 CompressData(PixelBuffer
+ i
* bytes
, bytes
, plane
,
1614 (i
< (PrinterPlanes
- 1)) ? 'V' : 'W',
1615 header
->cupsCompression
);
1619 case OUTPUT_RGB
: /* Send 24-bit RGB data... */
1620 if (PrinterPlanes
== 1 && !BlankValue
)
1623 * Invert black to grayscale...
1626 for (i
= header
->cupsBytesPerLine
, ptr
= PixelBuffer
;
1633 * Compress the output...
1636 CompressData(PixelBuffer
, header
->cupsBytesPerLine
, 0, 'W',
1637 header
->cupsCompression
);
1641 order
= ColorOrders
[PrinterPlanes
- 1];
1642 width
= header
->cupsWidth
;
1644 for (i
= 0, j
= 0; i
< PrinterPlanes
; i
++)
1647 bytes
= DotBufferSizes
[plane
] / DotBits
[plane
];
1649 for (bit
= 1, ptr
= DotBuffers
[plane
];
1650 bit
<= DotBits
[plane
];
1651 bit
<<= 1, ptr
+= bytes
, j
++)
1653 cupsPackHorizontalBit(OutputBuffers
[plane
], DotBuffers
[plane
],
1655 CompressData(ptr
, bytes
, j
,
1656 i
== (PrinterPlanes
- 1) &&
1657 bit
== DotBits
[plane
] ? 'W' : 'V',
1658 header
->cupsCompression
);
1665 * The seed buffer, if any, now should contain valid data...
1673 * 'ReadLine()' - Read graphics from the page stream.
1676 int /* O - Number of lines (0 if blank) */
1677 ReadLine(cups_raster_t
*ras
, /* I - Raster stream */
1678 cups_page_header2_t
*header
) /* I - Page header */
1680 int plane
, /* Current color plane */
1681 width
; /* Width of line */
1685 * Read raster data...
1688 cupsRasterReadPixels(ras
, PixelBuffer
, header
->cupsBytesPerLine
);
1691 * See if it is blank; if so, return right away...
1694 if (cupsCheckValue(PixelBuffer
, header
->cupsBytesPerLine
, BlankValue
))
1698 * If we aren't dithering, return immediately...
1701 if (OutputMode
!= OUTPUT_DITHERED
)
1705 * Perform the color separation...
1708 width
= header
->cupsWidth
;
1710 switch (header
->cupsColorSpace
)
1712 case CUPS_CSPACE_W
:
1715 cupsRGBDoGray(RGB
, PixelBuffer
, CMYKBuffer
, width
);
1717 if (RGB
->num_channels
== 1)
1718 cupsCMYKDoBlack(CMYK
, CMYKBuffer
, InputBuffer
, width
);
1720 cupsCMYKDoCMYK(CMYK
, CMYKBuffer
, InputBuffer
, width
);
1723 cupsCMYKDoGray(CMYK
, PixelBuffer
, InputBuffer
, width
);
1726 case CUPS_CSPACE_K
:
1727 cupsCMYKDoBlack(CMYK
, PixelBuffer
, InputBuffer
, width
);
1731 case CUPS_CSPACE_RGB
:
1734 cupsRGBDoRGB(RGB
, PixelBuffer
, CMYKBuffer
, width
);
1736 if (RGB
->num_channels
== 1)
1737 cupsCMYKDoBlack(CMYK
, CMYKBuffer
, InputBuffer
, width
);
1739 cupsCMYKDoCMYK(CMYK
, CMYKBuffer
, InputBuffer
, width
);
1742 cupsCMYKDoRGB(CMYK
, PixelBuffer
, InputBuffer
, width
);
1745 case CUPS_CSPACE_CMYK
:
1746 cupsCMYKDoCMYK(CMYK
, PixelBuffer
, InputBuffer
, width
);
1751 * Dither the pixels...
1754 for (plane
= 0; plane
< PrinterPlanes
; plane
++)
1755 cupsDitherLine(DitherStates
[plane
], DitherLuts
[plane
], InputBuffer
+ plane
,
1756 PrinterPlanes
, OutputBuffers
[plane
]);
1759 * Return 1 to indicate that we have non-blank output...
1767 * 'main()' - Main entry and processing of driver.
1770 int /* O - Exit status */
1771 main(int argc
, /* I - Number of command-line arguments */
1772 char *argv
[]) /* I - Command-line arguments */
1774 int fd
; /* File descriptor */
1775 cups_raster_t
*ras
; /* Raster stream for printing */
1776 cups_page_header2_t header
; /* Page header from file */
1777 int y
; /* Current line */
1778 ppd_file_t
*ppd
; /* PPD file */
1779 int job_id
; /* Job ID */
1780 int num_options
; /* Number of options */
1781 cups_option_t
*options
; /* Options */
1782 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1783 struct sigaction action
; /* Actions for POSIX signals */
1784 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1788 * Make sure status messages are not buffered...
1791 setbuf(stderr
, NULL
);
1794 * Check command-line...
1797 if (argc
< 6 || argc
> 7)
1799 _cupsLangPrintFilter(stderr
, "ERROR",
1800 _("%s job-id user title copies options [file]"),
1805 num_options
= cupsParseOptions(argv
[5], 0, &options
);
1808 * Open the PPD file...
1811 ppd
= ppdOpenFile(getenv("PPD"));
1815 ppd_status_t status
; /* PPD error */
1816 int linenum
; /* Line number */
1818 _cupsLangPrintFilter(stderr
, "ERROR",
1819 _("The PPD file could not be opened."));
1821 status
= ppdLastError(&linenum
);
1823 fprintf(stderr
, "DEBUG: %s on line %d.\n", ppdErrorString(status
), linenum
);
1828 ppdMarkDefaults(ppd
);
1829 cupsMarkOptions(ppd
, num_options
, options
);
1832 * Open the page stream...
1837 if ((fd
= open(argv
[6], O_RDONLY
)) == -1)
1839 _cupsLangPrintError("ERROR", _("Unable to open raster file"));
1846 ras
= cupsRasterOpen(fd
, CUPS_RASTER_READ
);
1849 * Register a signal handler to eject the current page if the
1855 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1856 sigset(SIGTERM
, CancelJob
);
1857 #elif defined(HAVE_SIGACTION)
1858 memset(&action
, 0, sizeof(action
));
1860 sigemptyset(&action
.sa_mask
);
1861 action
.sa_handler
= CancelJob
;
1862 sigaction(SIGTERM
, &action
, NULL
);
1864 signal(SIGTERM
, CancelJob
);
1865 #endif /* HAVE_SIGSET */
1868 * Process pages as needed...
1871 job_id
= atoi(argv
[1]);
1875 while (cupsRasterReadHeader2(ras
, &header
))
1878 * Write a status message with the page number and number of copies.
1886 fprintf(stderr
, "PAGE: %d %d\n", Page
, header
.NumCopies
);
1887 _cupsLangPrintFilter(stderr
, "INFO", _("Starting page %d."), Page
);
1889 StartPage(ppd
, &header
, atoi(argv
[1]), argv
[2], argv
[3],
1890 num_options
, options
);
1892 for (y
= 0; y
< (int)header
.cupsHeight
; y
++)
1895 * Let the user know how far we have progressed...
1903 _cupsLangPrintFilter(stderr
, "INFO",
1904 _("Printing page %d, %d%% complete."),
1905 Page
, 100 * y
/ header
.cupsHeight
);
1906 fprintf(stderr
, "ATTR: job-media-progress=%d\n",
1907 100 * y
/ header
.cupsHeight
);
1911 * Read and write a line of graphics or whitespace...
1914 if (ReadLine(ras
, &header
))
1915 OutputLine(ppd
, &header
);
1924 _cupsLangPrintFilter(stderr
, "INFO", _("Finished page %d."), Page
);
1926 EndPage(ppd
, &header
);
1932 Shutdown(ppd
, job_id
, argv
[2], argv
[3], num_options
, options
);
1934 cupsFreeOptions(num_options
, options
);
1936 cupsRasterClose(ras
);
1943 _cupsLangPrintFilter(stderr
, "ERROR", _("No pages were found."));
1948 _cupsLangPrintFilter(stderr
, "INFO", _("Ready to print."));