4 * Advanced HP Page Control Language and Raster Transfer Language
7 * Copyright 2007-2008 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 "pcl-common.h"
43 OUTPUT_BITMAP
, /* Output bitmap data from RIP */
44 OUTPUT_INVERBIT
, /* Output inverted bitmap data */
45 OUTPUT_RGB
, /* Output 24-bit RGB data from RIP */
46 OUTPUT_DITHERED
/* Output dithered data */
54 cups_rgb_t
*RGB
; /* RGB color separation data */
55 cups_cmyk_t
*CMYK
; /* CMYK color separation data */
56 unsigned char *PixelBuffer
, /* Pixel buffer */
57 *CMYKBuffer
, /* CMYK buffer */
58 *OutputBuffers
[6], /* Output buffers */
59 *DotBuffers
[6], /* Bit buffers */
60 *CompBuffer
, /* Compression buffer */
61 *SeedBuffer
, /* Mode 3 seed buffers */
62 BlankValue
; /* The blank value */
63 short *InputBuffer
; /* Color separation buffer */
64 cups_lut_t
*DitherLuts
[6]; /* Lookup tables for dithering */
65 cups_dither_t
*DitherStates
[6]; /* Dither state tables */
66 int PrinterPlanes
, /* Number of color planes */
67 SeedInvalid
, /* Contents of seed buffer invalid? */
68 DotBits
[6], /* Number of bits per color */
69 DotBufferSizes
[6], /* Size of one row of color dots */
70 DotBufferSize
, /* Size of complete line */
71 OutputFeed
, /* Number of lines to skip */
72 Page
; /* Current page number */
73 pcl_output_t OutputMode
; /* Output mode - see OUTPUT_ consts */
74 const int ColorOrders
[7][7] = /* Order of color planes */
76 { 0, 0, 0, 0, 0, 0, 0 }, /* Black */
77 { 0, 0, 0, 0, 0, 0, 0 },
78 { 0, 1, 2, 0, 0, 0, 0 }, /* CMY */
79 { 3, 0, 1, 2, 0, 0, 0 }, /* KCMY */
80 { 0, 0, 0, 0, 0, 0, 0 },
81 { 5, 0, 1, 2, 3, 4, 0 }, /* KCMYcm */
82 { 5, 0, 1, 2, 3, 4, 6 } /* KCMYcmk */
84 int Canceled
; /* Is the job canceled? */
91 void StartPage(ppd_file_t
*ppd
, cups_page_header2_t
*header
, int job_id
,
92 const char *user
, const char *title
, int num_options
,
93 cups_option_t
*options
);
94 void EndPage(ppd_file_t
*ppd
, cups_page_header2_t
*header
);
95 void Shutdown(ppd_file_t
*ppd
, int job_id
, const char *user
,
96 const char *title
, int num_options
, cups_option_t
*options
);
98 void CancelJob(int sig
);
99 void CompressData(unsigned char *line
, int length
, int plane
, int pend
,
101 void OutputLine(ppd_file_t
*ppd
, cups_page_header2_t
*header
);
102 int ReadLine(cups_raster_t
*ras
, cups_page_header2_t
*header
);
106 * 'StartPage()' - Start a page of graphics.
110 StartPage(ppd_file_t
*ppd
, /* I - PPD file */
111 cups_page_header2_t
*header
, /* I - Page header */
112 int job_id
, /* I - Job ID */
113 const char *user
, /* I - User printing job */
114 const char *title
, /* I - Title of job */
116 /* I - Number of command-line options */
117 cups_option_t
*options
) /* I - Command-line options */
119 int i
; /* Temporary/looping var */
120 int plane
; /* Current plane */
121 char s
[255]; /* Temporary value */
122 const char *colormodel
; /* Color model string */
123 char resolution
[PPD_MAX_NAME
],
124 /* Resolution string */
125 spec
[PPD_MAX_NAME
]; /* PPD attribute name */
126 ppd_attr_t
*attr
; /* Attribute from PPD file */
127 ppd_choice_t
*choice
; /* Selected option */
128 const int *order
; /* Order to use */
129 int xorigin
, /* X origin of page */
130 yorigin
; /* Y origin of page */
131 static const float default_lut
[2] = /* Default dithering lookup table */
142 fprintf(stderr
, "DEBUG: StartPage...\n");
143 fprintf(stderr
, "DEBUG: MediaClass = \"%s\"\n", header
->MediaClass
);
144 fprintf(stderr
, "DEBUG: MediaColor = \"%s\"\n", header
->MediaColor
);
145 fprintf(stderr
, "DEBUG: MediaType = \"%s\"\n", header
->MediaType
);
146 fprintf(stderr
, "DEBUG: OutputType = \"%s\"\n", header
->OutputType
);
148 fprintf(stderr
, "DEBUG: AdvanceDistance = %d\n", header
->AdvanceDistance
);
149 fprintf(stderr
, "DEBUG: AdvanceMedia = %d\n", header
->AdvanceMedia
);
150 fprintf(stderr
, "DEBUG: Collate = %d\n", header
->Collate
);
151 fprintf(stderr
, "DEBUG: CutMedia = %d\n", header
->CutMedia
);
152 fprintf(stderr
, "DEBUG: Duplex = %d\n", header
->Duplex
);
153 fprintf(stderr
, "DEBUG: HWResolution = [ %d %d ]\n", header
->HWResolution
[0],
154 header
->HWResolution
[1]);
155 fprintf(stderr
, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n",
156 header
->ImagingBoundingBox
[0], header
->ImagingBoundingBox
[1],
157 header
->ImagingBoundingBox
[2], header
->ImagingBoundingBox
[3]);
158 fprintf(stderr
, "DEBUG: InsertSheet = %d\n", header
->InsertSheet
);
159 fprintf(stderr
, "DEBUG: Jog = %d\n", header
->Jog
);
160 fprintf(stderr
, "DEBUG: LeadingEdge = %d\n", header
->LeadingEdge
);
161 fprintf(stderr
, "DEBUG: Margins = [ %d %d ]\n", header
->Margins
[0],
163 fprintf(stderr
, "DEBUG: ManualFeed = %d\n", header
->ManualFeed
);
164 fprintf(stderr
, "DEBUG: MediaPosition = %d\n", header
->MediaPosition
);
165 fprintf(stderr
, "DEBUG: MediaWeight = %d\n", header
->MediaWeight
);
166 fprintf(stderr
, "DEBUG: MirrorPrint = %d\n", header
->MirrorPrint
);
167 fprintf(stderr
, "DEBUG: NegativePrint = %d\n", header
->NegativePrint
);
168 fprintf(stderr
, "DEBUG: NumCopies = %d\n", header
->NumCopies
);
169 fprintf(stderr
, "DEBUG: Orientation = %d\n", header
->Orientation
);
170 fprintf(stderr
, "DEBUG: OutputFaceUp = %d\n", header
->OutputFaceUp
);
171 fprintf(stderr
, "DEBUG: PageSize = [ %d %d ]\n", header
->PageSize
[0],
172 header
->PageSize
[1]);
173 fprintf(stderr
, "DEBUG: Separations = %d\n", header
->Separations
);
174 fprintf(stderr
, "DEBUG: TraySwitch = %d\n", header
->TraySwitch
);
175 fprintf(stderr
, "DEBUG: Tumble = %d\n", header
->Tumble
);
176 fprintf(stderr
, "DEBUG: cupsWidth = %d\n", header
->cupsWidth
);
177 fprintf(stderr
, "DEBUG: cupsHeight = %d\n", header
->cupsHeight
);
178 fprintf(stderr
, "DEBUG: cupsMediaType = %d\n", header
->cupsMediaType
);
179 fprintf(stderr
, "DEBUG: cupsBitsPerColor = %d\n", header
->cupsBitsPerColor
);
180 fprintf(stderr
, "DEBUG: cupsBitsPerPixel = %d\n", header
->cupsBitsPerPixel
);
181 fprintf(stderr
, "DEBUG: cupsBytesPerLine = %d\n", header
->cupsBytesPerLine
);
182 fprintf(stderr
, "DEBUG: cupsColorOrder = %d\n", header
->cupsColorOrder
);
183 fprintf(stderr
, "DEBUG: cupsColorSpace = %d\n", header
->cupsColorSpace
);
184 fprintf(stderr
, "DEBUG: cupsCompression = %d\n", header
->cupsCompression
);
188 * MacOS X 10.2.x doesn't set most of the page device attributes, so check
189 * the options and set them accordingly...
192 if (ppdIsMarked(ppd
, "Duplex", "DuplexNoTumble"))
194 header
->Duplex
= CUPS_TRUE
;
195 header
->Tumble
= CUPS_FALSE
;
197 else if (ppdIsMarked(ppd
, "Duplex", "DuplexTumble"))
199 header
->Duplex
= CUPS_TRUE
;
200 header
->Tumble
= CUPS_TRUE
;
203 fprintf(stderr
, "DEBUG: num_options=%d\n", num_options
);
205 for (i
= 0; i
< num_options
; i
++)
206 fprintf(stderr
, "DEBUG: options[%d]=[\"%s\" \"%s\"]\n", i
,
207 options
[i
].name
, options
[i
].value
);
208 #endif /* __APPLE__ */
211 * Figure out the color model and spec strings...
214 switch (header
->cupsColorSpace
)
217 colormodel
= "Black";
223 case CUPS_CSPACE_RGB
:
226 case CUPS_CSPACE_CMY
:
229 case CUPS_CSPACE_CMYK
:
234 if (header
->HWResolution
[0] != header
->HWResolution
[1])
235 snprintf(resolution
, sizeof(resolution
), "%dx%ddpi",
236 header
->HWResolution
[0], header
->HWResolution
[1]);
238 snprintf(resolution
, sizeof(resolution
), "%ddpi",
239 header
->HWResolution
[0]);
241 if (!header
->MediaType
[0])
242 strcpy(header
->MediaType
, "PLAIN");
245 * Get the dithering parameters...
250 if (header
->cupsBitsPerColor
== 1)
253 * Use raw bitmap mode...
256 switch (header
->cupsColorSpace
)
259 OutputMode
= OUTPUT_BITMAP
;
263 OutputMode
= OUTPUT_INVERBIT
;
267 case CUPS_CSPACE_RGB
:
268 OutputMode
= OUTPUT_INVERBIT
;
271 case CUPS_CSPACE_CMY
:
272 OutputMode
= OUTPUT_BITMAP
;
275 case CUPS_CSPACE_CMYK
:
276 OutputMode
= OUTPUT_BITMAP
;
281 if (OutputMode
== OUTPUT_INVERBIT
)
284 DotBufferSize
= header
->cupsBytesPerLine
;
286 memset(DitherLuts
, 0, sizeof(DitherLuts
));
287 memset(DitherStates
, 0, sizeof(DitherStates
));
289 else if (header
->cupsColorSpace
== CUPS_CSPACE_RGB
&&
290 (ppd
->model_number
& PCL_RASTER_RGB24
))
293 * Use 24-bit RGB output mode...
296 OutputMode
= OUTPUT_RGB
;
298 DotBufferSize
= header
->cupsBytesPerLine
;
300 if (header
->cupsCompression
== 10)
303 memset(DitherLuts
, 0, sizeof(DitherLuts
));
304 memset(DitherStates
, 0, sizeof(DitherStates
));
306 else if ((header
->cupsColorSpace
== CUPS_CSPACE_K
||
307 header
->cupsColorSpace
== CUPS_CSPACE_W
) &&
308 (ppd
->model_number
& PCL_RASTER_RGB24
) &&
309 header
->cupsCompression
== 10)
312 * Use 24-bit RGB output mode for grayscale/black output...
315 OutputMode
= OUTPUT_RGB
;
317 DotBufferSize
= header
->cupsBytesPerLine
;
319 if (header
->cupsColorSpace
== CUPS_CSPACE_W
)
322 memset(DitherLuts
, 0, sizeof(DitherLuts
));
323 memset(DitherStates
, 0, sizeof(DitherStates
));
328 * Use dithered output mode...
331 OutputMode
= OUTPUT_DITHERED
;
334 * Load the appropriate color profiles...
340 fputs("DEBUG: Attempting to load color profiles using the following values:\n", stderr
);
341 fprintf(stderr
, "DEBUG: ColorModel = %s\n", colormodel
);
342 fprintf(stderr
, "DEBUG: MediaType = %s\n", header
->MediaType
);
343 fprintf(stderr
, "DEBUG: Resolution = %s\n", resolution
);
345 if (header
->cupsColorSpace
== CUPS_CSPACE_RGB
||
346 header
->cupsColorSpace
== CUPS_CSPACE_W
)
347 RGB
= cupsRGBLoad(ppd
, colormodel
, header
->MediaType
, resolution
);
349 CMYK
= cupsCMYKLoad(ppd
, colormodel
, header
->MediaType
, resolution
);
352 fputs("DEBUG: Loaded RGB separation from PPD.\n", stderr
);
355 fputs("DEBUG: Loaded CMYK separation from PPD.\n", stderr
);
358 fputs("DEBUG: Loading default K separation.\n", stderr
);
359 CMYK
= cupsCMYKNew(1);
362 PrinterPlanes
= CMYK
->num_channels
;
365 * Use dithered mode...
368 switch (PrinterPlanes
)
371 DitherLuts
[0] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
372 resolution
, "Black");
376 DitherLuts
[0] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
378 DitherLuts
[1] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
379 resolution
, "Magenta");
380 DitherLuts
[2] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
381 resolution
, "Yellow");
385 DitherLuts
[0] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
387 DitherLuts
[1] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
388 resolution
, "Magenta");
389 DitherLuts
[2] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
390 resolution
, "Yellow");
391 DitherLuts
[3] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
392 resolution
, "Black");
395 case 6 : /* CcMmYK */
396 DitherLuts
[0] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
398 DitherLuts
[1] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
399 resolution
, "LightCyan");
400 DitherLuts
[2] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
401 resolution
, "Magenta");
402 DitherLuts
[3] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
403 resolution
, "LightMagenta");
404 DitherLuts
[4] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
405 resolution
, "Yellow");
406 DitherLuts
[5] = cupsLutLoad(ppd
, colormodel
, header
->MediaType
,
407 resolution
, "Black");
411 for (plane
= 0; plane
< PrinterPlanes
; plane
++)
413 if (!DitherLuts
[plane
])
414 DitherLuts
[plane
] = cupsLutNew(2, default_lut
);
416 if (DitherLuts
[plane
][4095].pixel
> 1)
421 DitherStates
[plane
] = cupsDitherNew(header
->cupsWidth
);
423 if (!DitherLuts
[plane
])
424 DitherLuts
[plane
] = cupsLutNew(2, default_lut
);
428 fprintf(stderr
, "DEBUG: PrinterPlanes = %d\n", PrinterPlanes
);
431 * Initialize the printer...
434 if ((attr
= ppdFindAttr(ppd
, "cupsInitialNulls", NULL
)) != NULL
)
435 for (i
= atoi(attr
->value
); i
> 0; i
--)
438 if (Page
== 1 && (ppd
->model_number
& PCL_PJL
))
446 pjl_set_job(job_id
, user
, title
);
448 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "StartJob")) != NULL
)
449 pjl_write(ppd
, attr
->value
, NULL
, job_id
, user
, title
, num_options
,
452 snprintf(spec
, sizeof(spec
), "RENDERMODE.%s", colormodel
);
453 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", spec
)) != NULL
)
454 printf("@PJL SET RENDERMODE=%s\r\n", attr
->value
);
456 snprintf(spec
, sizeof(spec
), "COLORSPACE.%s", colormodel
);
457 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", spec
)) != NULL
)
458 printf("@PJL SET COLORSPACE=%s\r\n", attr
->value
);
460 snprintf(spec
, sizeof(spec
), "RENDERINTENT.%s", colormodel
);
461 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", spec
)) != NULL
)
462 printf("@PJL SET RENDERINTENT=%s\r\n", attr
->value
);
464 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "Duplex")) != NULL
)
466 sprintf(s
, "%d", header
->Duplex
);
467 pjl_write(ppd
, attr
->value
, s
, job_id
, user
, title
, num_options
, options
);
470 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "Tumble")) != NULL
)
472 sprintf(s
, "%d", header
->Tumble
);
473 pjl_write(ppd
, attr
->value
, s
, job_id
, user
, title
, num_options
, options
);
476 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "MediaClass")) != NULL
)
477 pjl_write(ppd
, attr
->value
, header
->MediaClass
, job_id
, user
, title
,
478 num_options
, options
);
480 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "MediaColor")) != NULL
)
481 pjl_write(ppd
, attr
->value
, header
->MediaColor
, job_id
, user
, title
,
482 num_options
, options
);
484 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "MediaType")) != NULL
)
485 pjl_write(ppd
, attr
->value
, header
->MediaType
, job_id
, user
, title
,
486 num_options
, options
);
488 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "OutputType")) != NULL
)
489 pjl_write(ppd
, attr
->value
, header
->OutputType
, job_id
, user
, title
,
490 num_options
, options
);
492 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "cupsBooklet")) != NULL
&&
493 (choice
= ppdFindMarkedChoice(ppd
, "cupsBooklet")) != NULL
)
494 pjl_write(ppd
, attr
->value
, choice
->choice
, job_id
, user
, title
,
495 num_options
, options
);
497 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "Jog")) != NULL
)
499 sprintf(s
, "%d", header
->Jog
);
500 pjl_write(ppd
, attr
->value
, s
, job_id
, user
, title
, num_options
, options
);
503 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "cupsPunch")) != NULL
&&
504 (choice
= ppdFindMarkedChoice(ppd
, "cupsPunch")) != NULL
)
505 pjl_write(ppd
, attr
->value
, choice
->choice
, job_id
, user
, title
,
506 num_options
, options
);
508 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "cupsStaple")) != NULL
&&
509 (choice
= ppdFindMarkedChoice(ppd
, "cupsStaple")) != NULL
)
510 pjl_write(ppd
, attr
->value
, choice
->choice
, job_id
, user
, title
,
511 num_options
, options
);
513 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "cupsRET")) != NULL
&&
514 (choice
= ppdFindMarkedChoice(ppd
, "cupsRET")) != NULL
)
515 pjl_write(ppd
, attr
->value
, choice
->choice
, job_id
, user
, title
,
516 num_options
, options
);
518 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "cupsTonerSave")) != NULL
&&
519 (choice
= ppdFindMarkedChoice(ppd
, "cupsTonerSave")) != NULL
)
520 pjl_write(ppd
, attr
->value
, choice
->choice
, job_id
, user
, title
,
521 num_options
, options
);
523 if (ppd
->model_number
& PCL_PJL_PAPERWIDTH
)
525 printf("@PJL SET PAPERLENGTH=%d\r\n", header
->PageSize
[1] * 10);
526 printf("@PJL SET PAPERWIDTH=%d\r\n", header
->PageSize
[0] * 10);
529 if (ppd
->model_number
& PCL_PJL_RESOLUTION
)
530 printf("@PJL SET RESOLUTION=%d\r\n", header
->HWResolution
[0]);
532 if (ppd
->model_number
& PCL_PJL_HPGL2
)
533 pjl_enter_language("HPGL2");
534 else if (ppd
->model_number
& PCL_PJL_PCL3GUI
)
535 pjl_enter_language("PCL3GUI");
537 pjl_enter_language("PCL");
545 if (ppd
->model_number
& PCL_PJL_HPGL2
)
550 * HP-GL/2 initialization...
554 printf("MG\"%d %s %s\";", job_id
, user
, title
);
558 * Set media size, position, type, etc...
562 printf("PS%.0f,%.0f;",
563 header
->cupsHeight
* 1016.0 / header
->HWResolution
[1],
564 header
->cupsWidth
* 1016.0 / header
->HWResolution
[0]);
568 printf("MT%d;", header
->cupsMediaType
);
570 if (header
->CutMedia
== CUPS_CUT_PAGE
)
576 * Set graphics mode...
580 pcl_set_negative_motion();
585 * Set media size, position, type, etc...
588 if (!header
->Duplex
|| (Page
& 1))
590 pcl_set_media_size(ppd
, header
->PageSize
[0], header
->PageSize
[1]);
592 if (header
->MediaPosition
)
593 pcl_set_media_source(header
->MediaPosition
);
595 pcl_set_media_type(header
->cupsMediaType
);
597 if (ppdFindAttr(ppd
, "cupsPJL", "Duplex") == NULL
)
598 pcl_set_duplex(header
->Duplex
, header
->Tumble
);
601 * Set the number of copies...
604 if (!ppd
->manual_copies
)
605 pcl_set_copies(header
->NumCopies
);
608 * Set the output order/bin...
611 if (ppdFindAttr(ppd
, "cupsPJL", "Jog") == NULL
&& header
->Jog
)
612 printf("\033&l%dG", header
->Jog
);
617 * Print on the back side...
623 if (header
->Duplex
&& (ppd
->model_number
& PCL_RASTER_CRD
))
626 * Reload the media...
629 pcl_set_media_source(-2);
633 * Set the units for cursor positioning and go to the top of the form.
636 printf("\033&u%dD", header
->HWResolution
[0]);
637 printf("\033*p0Y\033*p0X");
640 if ((attr
= cupsFindAttr(ppd
, "cupsPCLQuality", colormodel
,
641 header
->MediaType
, resolution
, spec
,
642 sizeof(spec
))) != NULL
)
645 * Set the print quality...
648 if (ppd
->model_number
& PCL_PJL_HPGL2
)
649 printf("QM%d", atoi(attr
->value
));
651 printf("\033*o%dM", atoi(attr
->value
));
655 * Enter graphics mode...
658 if (ppd
->model_number
& PCL_RASTER_CRD
)
661 * Use configure raster data command...
664 if (OutputMode
== OUTPUT_RGB
)
667 * Send 12-byte configure raster data command with horizontal and
668 * vertical resolutions as well as a color count...
671 if ((attr
= cupsFindAttr(ppd
, "cupsPCLCRDMode", colormodel
,
672 header
->MediaType
, resolution
, spec
,
673 sizeof(spec
))) != NULL
)
674 i
= atoi(attr
->value
);
679 putchar(6); /* Format 6 */
680 putchar(i
); /* Set pen mode */
681 putchar(0x00); /* Number components */
682 putchar(0x01); /* (1 for RGB) */
684 putchar(header
->HWResolution
[0] >> 8);
685 putchar(header
->HWResolution
[0]);
686 putchar(header
->HWResolution
[1] >> 8);
687 putchar(header
->HWResolution
[1]);
689 putchar(header
->cupsCompression
); /* Compression mode 3 or 10 */
690 putchar(0x01); /* Portrait orientation */
691 putchar(0x20); /* Bits per pixel (32 = RGB) */
692 putchar(0x01); /* Planes per pixel (1 = chunky RGB) */
697 * Send the configure raster data command with horizontal and
698 * vertical resolutions as well as a color count...
701 printf("\033*g%dW", PrinterPlanes
* 6 + 2);
702 putchar(2); /* Format 2 */
703 putchar(PrinterPlanes
); /* Output planes */
705 order
= ColorOrders
[PrinterPlanes
- 1];
707 for (i
= 0; i
< PrinterPlanes
; i
++)
711 putchar(header
->HWResolution
[0] >> 8);
712 putchar(header
->HWResolution
[0]);
713 putchar(header
->HWResolution
[1] >> 8);
714 putchar(header
->HWResolution
[1]);
716 putchar(1 << DotBits
[plane
]);
720 else if ((ppd
->model_number
& PCL_RASTER_CID
) && OutputMode
== OUTPUT_RGB
)
723 * Use configure image data command...
726 pcl_set_simple_resolution(header
->HWResolution
[0]);
727 /* Set output resolution */
729 cupsWritePrintData("\033*v6W\0\3\0\10\10\10", 11);
735 * Use simple raster commands...
738 pcl_set_simple_resolution(header
->HWResolution
[0]);
739 /* Set output resolution */
741 if (PrinterPlanes
== 3)
742 pcl_set_simple_cmy();
743 else if (PrinterPlanes
== 4)
744 pcl_set_simple_kcmy();
747 if ((attr
= ppdFindAttr(ppd
, "cupsPCLOrigin", "X")) != NULL
)
748 xorigin
= atoi(attr
->value
);
752 if ((attr
= ppdFindAttr(ppd
, "cupsPCLOrigin", "Y")) != NULL
)
753 yorigin
= atoi(attr
->value
);
757 printf("\033&a%dH\033&a%dV", xorigin
, yorigin
);
758 printf("\033*r%dS", header
->cupsWidth
);
759 printf("\033*r%dT", header
->cupsHeight
);
762 if (header
->cupsCompression
&& header
->cupsCompression
!= 10)
763 printf("\033*b%dM", header
->cupsCompression
);
768 * Allocate memory for the page...
771 PixelBuffer
= malloc(header
->cupsBytesPerLine
);
773 if (OutputMode
== OUTPUT_DITHERED
)
775 InputBuffer
= malloc(header
->cupsWidth
* PrinterPlanes
* 2);
776 OutputBuffers
[0] = malloc(PrinterPlanes
* header
->cupsWidth
);
778 for (i
= 1; i
< PrinterPlanes
; i
++)
779 OutputBuffers
[i
] = OutputBuffers
[0] + i
* header
->cupsWidth
;
782 CMYKBuffer
= malloc(header
->cupsWidth
* PrinterPlanes
);
784 for (plane
= 0, DotBufferSize
= 0; plane
< PrinterPlanes
; plane
++)
786 DotBufferSizes
[plane
] = (header
->cupsWidth
+ 7) / 8 * DotBits
[plane
];
787 DotBufferSize
+= DotBufferSizes
[plane
];
790 DotBuffers
[0] = malloc(DotBufferSize
);
791 for (plane
= 1; plane
< PrinterPlanes
; plane
++)
792 DotBuffers
[plane
] = DotBuffers
[plane
- 1] + DotBufferSizes
[plane
- 1];
795 if (header
->cupsCompression
)
796 CompBuffer
= malloc(DotBufferSize
* 4);
798 if (header
->cupsCompression
>= 3)
799 SeedBuffer
= malloc(DotBufferSize
);
803 fprintf(stderr
, "BlankValue=%d\n", BlankValue
);
808 * 'EndPage()' - Finish a page of graphics.
812 EndPage(ppd_file_t
*ppd
, /* I - PPD file */
813 cups_page_header2_t
*header
) /* I - Page header */
815 int plane
; /* Current plane */
819 * End graphics mode...
822 if (ppd
->model_number
& PCL_RASTER_END_COLOR
)
823 printf("\033*rC"); /* End color GFX */
825 printf("\033*r0B"); /* End B&W GFX */
828 * Output a page eject sequence...
831 if (ppd
->model_number
& PCL_PJL_HPGL2
)
833 pcl_set_hpgl_mode(0); /* Back to HP-GL/2 mode */
834 printf("PG;"); /* Eject the current page */
836 else if (!(header
->Duplex
&& (Page
& 1)))
837 printf("\014"); /* Eject current page */
840 * Free memory for the page...
845 if (OutputMode
== OUTPUT_DITHERED
)
847 for (plane
= 0; plane
< PrinterPlanes
; plane
++)
849 cupsDitherDelete(DitherStates
[plane
]);
850 cupsLutDelete(DitherLuts
[plane
]);
855 free(OutputBuffers
[0]);
857 cupsCMYKDelete(CMYK
);
866 if (header
->cupsCompression
)
869 if (header
->cupsCompression
>= 3)
875 * 'Shutdown()' - Shutdown a printer.
879 Shutdown(ppd_file_t
*ppd
, /* I - PPD file */
880 int job_id
, /* I - Job ID */
881 const char *user
, /* I - User printing job */
882 const char *title
, /* I - Title of job */
883 int num_options
,/* I - Number of command-line options */
884 cups_option_t
*options
) /* I - Command-line options */
886 ppd_attr_t
*attr
; /* Attribute from PPD file */
889 if ((attr
= ppdFindAttr(ppd
, "cupsPCL", "EndJob")) != NULL
)
892 * Tell the printer how many pages were in the job...
896 printf(attr
->value
, Page
);
901 * Return the printer to the default state...
907 if (ppd
->model_number
& PCL_PJL
)
911 if ((attr
= ppdFindAttr(ppd
, "cupsPJL", "EndJob")) != NULL
)
912 pjl_write(ppd
, attr
->value
, NULL
, job_id
, user
, title
, num_options
,
915 printf("@PJL EOJ\r\n");
923 * 'CancelJob()' - Cancel the current job...
927 CancelJob(int sig
) /* I - Signal */
936 * 'CompressData()' - Compress a line of graphics.
940 CompressData(unsigned char *line
, /* I - Data to compress */
941 int length
, /* I - Number of bytes */
942 int plane
, /* I - Color plane */
943 int pend
, /* I - End character for data */
944 int type
) /* I - Type of compression */
946 unsigned char *line_ptr
, /* Current byte pointer */
947 *line_end
, /* End-of-line byte pointer */
948 *comp_ptr
, /* Pointer into compression buffer */
949 *start
, /* Start of compression sequence */
950 *seed
; /* Seed buffer pointer */
951 int count
, /* Count of bytes for output */
952 offset
, /* Offset of bytes for output */
953 temp
; /* Temporary count */
954 int r
, g
, b
; /* RGB deltas for mode 10 compression */
961 * Do no compression; with a mode-0 only printer, we can compress blank
967 if (cupsCheckBytes(line
, length
))
968 line_end
= line
; /* Blank line */
970 line_end
= line
+ length
; /* Non-blank line */
975 * Do run-length encoding...
978 line_end
= line
+ length
;
979 for (line_ptr
= line
, comp_ptr
= CompBuffer
;
981 comp_ptr
+= 2, line_ptr
+= count
)
984 (line_ptr
+ count
) < line_end
&&
985 line_ptr
[0] == line_ptr
[count
] &&
989 comp_ptr
[0] = count
- 1;
990 comp_ptr
[1] = line_ptr
[0];
993 line_ptr
= CompBuffer
;
999 * Do TIFF pack-bits encoding...
1003 line_end
= line
+ length
;
1004 comp_ptr
= CompBuffer
;
1006 while (line_ptr
< line_end
)
1008 if ((line_ptr
+ 1) >= line_end
)
1011 * Single byte on the end...
1015 *comp_ptr
++ = *line_ptr
++;
1017 else if (line_ptr
[0] == line_ptr
[1])
1020 * Repeated sequence...
1026 while (line_ptr
< (line_end
- 1) &&
1027 line_ptr
[0] == line_ptr
[1] &&
1034 *comp_ptr
++ = 257 - count
;
1035 *comp_ptr
++ = *line_ptr
++;
1040 * Non-repeated sequence...
1047 while (line_ptr
< (line_end
- 1) &&
1048 line_ptr
[0] != line_ptr
[1] &&
1055 *comp_ptr
++ = count
- 1;
1057 memcpy(comp_ptr
, start
, count
);
1062 line_ptr
= CompBuffer
;
1063 line_end
= comp_ptr
;
1068 * Do delta-row compression...
1072 line_end
= line
+ length
;
1074 comp_ptr
= CompBuffer
;
1075 seed
= SeedBuffer
+ plane
* length
;
1077 while (line_ptr
< line_end
)
1080 * Find the next non-matching sequence...
1088 * The seed buffer is invalid, so do the next 8 bytes, max...
1093 if ((count
= line_end
- line_ptr
) > 8)
1101 * The seed buffer is valid, so compare against it...
1104 while (*line_ptr
== *seed
&&
1105 line_ptr
< line_end
)
1111 if (line_ptr
== line_end
)
1114 offset
= line_ptr
- start
;
1117 * Find up to 8 non-matching bytes...
1122 while (*line_ptr
!= *seed
&&
1123 line_ptr
< line_end
&&
1133 * Place mode 3 compression data in the buffer; see HP manuals
1140 * Output multi-byte offset...
1143 *comp_ptr
++ = ((count
- 1) << 5) | 31;
1146 while (offset
>= 255)
1152 *comp_ptr
++ = offset
;
1157 * Output single-byte offset...
1160 *comp_ptr
++ = ((count
- 1) << 5) | offset
;
1163 memcpy(comp_ptr
, start
, count
);
1167 line_ptr
= CompBuffer
;
1168 line_end
= comp_ptr
;
1170 memcpy(SeedBuffer
+ plane
* length
, line
, length
);
1175 * Mode 10 "near lossless" RGB compression...
1179 line_end
= line
+ length
;
1181 comp_ptr
= CompBuffer
;
1184 if (PrinterPlanes
== 1)
1187 * Do grayscale compression to RGB...
1190 while (line_ptr
< line_end
)
1193 * Find the next non-matching sequence...
1197 while (line_ptr
< line_end
&&
1204 if (line_ptr
== line_end
)
1207 offset
= line_ptr
- start
;
1210 * Find non-matching grayscale pixels...
1214 while (line_ptr
< line_end
&&
1221 count
= line_ptr
- start
;
1224 fprintf(stderr
, "DEBUG: offset=%d, count=%d, comp_ptr=%p(%d of %d)...\n",
1225 offset
, count
, comp_ptr
, comp_ptr
- CompBuffer
,
1230 * Place mode 10 compression data in the buffer; each sequence
1231 * starts with a command byte that looks like:
1233 * CMD SRC SRC OFF OFF CNT CNT CNT
1235 * For the purpose of this driver, CMD and SRC are always 0.
1237 * If the offset >= 3 then additional offset bytes follow the
1238 * first command byte, each byte == 255 until the last one.
1240 * If the count >= 7, then additional count bytes follow each
1241 * group of pixels, each byte == 255 until the last one.
1243 * The offset and count are in RGB tuples (not bytes, as for
1250 * Output multi-byte offset...
1256 *comp_ptr
++ = 0x18 | (count
- 1);
1259 while (offset
>= 255)
1265 *comp_ptr
++ = offset
;
1270 * Output single-byte offset...
1274 *comp_ptr
++ = (offset
<< 3) | 0x07;
1276 *comp_ptr
++ = (offset
<< 3) | (count
- 1);
1287 * This is exceedingly lame... The replacement counts
1288 * are intermingled with the data...
1300 * Get difference between current and see pixels...
1305 b
= ((*start
& 0xfe) - (*seed
& 0xfe)) / 2;
1307 if (r
< -16 || r
> 15 || g
< -16 || g
> 15 || b
< -16 || b
> 15)
1310 * Pack 24-bit RGB into 23 bits... Lame...
1315 *comp_ptr
++ = g
>> 1;
1318 *comp_ptr
++ = 0x80 | (g
>> 1);
1320 *comp_ptr
++ = g
>> 1;
1323 *comp_ptr
++ = 0x80 | (g
>> 1);
1325 *comp_ptr
++ = g
>> 1;
1330 * Pack 15-bit RGB difference...
1333 *comp_ptr
++ = 0x80 | ((r
<< 2) & 0x7c) | ((g
>> 3) & 0x03);
1334 *comp_ptr
++ = ((g
<< 5) & 0xe0) | (b
& 0x1f);
1343 * Make sure we have the ending count if the replacement count
1344 * was exactly 8 + 255n...
1354 * Do RGB compression...
1357 while (line_ptr
< line_end
)
1360 * Find the next non-matching sequence...
1364 while (line_ptr
[0] == seed
[0] &&
1365 line_ptr
[1] == seed
[1] &&
1366 line_ptr
[2] == seed
[2] &&
1367 (line_ptr
+ 2) < line_end
)
1373 if (line_ptr
== line_end
)
1376 offset
= (line_ptr
- start
) / 3;
1379 * Find non-matching RGB tuples...
1383 while ((line_ptr
[0] != seed
[0] ||
1384 line_ptr
[1] != seed
[1] ||
1385 line_ptr
[2] != seed
[2]) &&
1386 (line_ptr
+ 2) < line_end
)
1392 count
= (line_ptr
- start
) / 3;
1395 * Place mode 10 compression data in the buffer; each sequence
1396 * starts with a command byte that looks like:
1398 * CMD SRC SRC OFF OFF CNT CNT CNT
1400 * For the purpose of this driver, CMD and SRC are always 0.
1402 * If the offset >= 3 then additional offset bytes follow the
1403 * first command byte, each byte == 255 until the last one.
1405 * If the count >= 7, then additional count bytes follow each
1406 * group of pixels, each byte == 255 until the last one.
1408 * The offset and count are in RGB tuples (not bytes, as for
1415 * Output multi-byte offset...
1421 *comp_ptr
++ = 0x18 | (count
- 1);
1424 while (offset
>= 255)
1430 *comp_ptr
++ = offset
;
1435 * Output single-byte offset...
1439 *comp_ptr
++ = (offset
<< 3) | 0x07;
1441 *comp_ptr
++ = (offset
<< 3) | (count
- 1);
1452 * This is exceedingly lame... The replacement counts
1453 * are intermingled with the data...
1465 * Get difference between current and see pixels...
1468 r
= start
[0] - seed
[0];
1469 g
= start
[1] - seed
[1];
1470 b
= ((start
[2] & 0xfe) - (seed
[2] & 0xfe)) / 2;
1472 if (r
< -16 || r
> 15 || g
< -16 || g
> 15 || b
< -16 || b
> 15)
1475 * Pack 24-bit RGB into 23 bits... Lame...
1478 *comp_ptr
++ = start
[0] >> 1;
1481 *comp_ptr
++ = 0x80 | (start
[1] >> 1);
1483 *comp_ptr
++ = start
[1] >> 1;
1486 *comp_ptr
++ = 0x80 | (start
[2] >> 1);
1488 *comp_ptr
++ = start
[2] >> 1;
1493 * Pack 15-bit RGB difference...
1496 *comp_ptr
++ = 0x80 | ((r
<< 2) & 0x7c) | ((g
>> 3) & 0x03);
1497 *comp_ptr
++ = ((g
<< 5) & 0xe0) | (b
& 0x1f);
1506 * Make sure we have the ending count if the replacement count
1507 * was exactly 8 + 255n...
1515 line_ptr
= CompBuffer
;
1516 line_end
= comp_ptr
;
1518 memcpy(SeedBuffer
, line
, length
);
1523 * Set the length of the data and write a raster plane...
1526 printf("\033*b%d%c", (int)(line_end
- line_ptr
), pend
);
1527 cupsWritePrintData(line_ptr
, line_end
- line_ptr
);
1532 * 'OutputLine()' - Output the specified number of lines of graphics.
1536 OutputLine(ppd_file_t
*ppd
, /* I - PPD file */
1537 cups_page_header2_t
*header
) /* I - Page header */
1539 int i
, j
; /* Looping vars */
1540 int plane
; /* Current plane */
1541 unsigned char bit
; /* Current bit */
1542 int bytes
; /* Number of bytes/plane */
1543 int width
; /* Width of line in pixels */
1544 const int *order
; /* Order to use */
1545 unsigned char *ptr
; /* Pointer into buffer */
1549 * Output whitespace as needed...
1554 if (header
->cupsCompression
< 3)
1557 * Send blank raster lines...
1560 while (OutputFeed
> 0)
1569 * Send Y offset command and invalidate the seed buffer...
1572 printf("\033*b%dY", OutputFeed
);
1579 * Write bitmap data as needed...
1584 case OUTPUT_BITMAP
: /* Send 1-bit bitmap data... */
1585 order
= ColorOrders
[PrinterPlanes
- 1];
1586 bytes
= header
->cupsBytesPerLine
/ PrinterPlanes
;
1588 for (i
= 0; i
< PrinterPlanes
; i
++)
1592 CompressData(PixelBuffer
+ i
* bytes
, bytes
, plane
,
1593 (i
< (PrinterPlanes
- 1)) ? 'V' : 'W',
1594 header
->cupsCompression
);
1598 case OUTPUT_INVERBIT
: /* Send inverted 1-bit bitmap data... */
1599 order
= ColorOrders
[PrinterPlanes
- 1];
1600 bytes
= header
->cupsBytesPerLine
/ PrinterPlanes
;
1602 for (i
= header
->cupsBytesPerLine
, ptr
= PixelBuffer
;
1607 for (i
= 0; i
< PrinterPlanes
; i
++)
1611 CompressData(PixelBuffer
+ i
* bytes
, bytes
, plane
,
1612 (i
< (PrinterPlanes
- 1)) ? 'V' : 'W',
1613 header
->cupsCompression
);
1617 case OUTPUT_RGB
: /* Send 24-bit RGB data... */
1618 if (PrinterPlanes
== 1 && !BlankValue
)
1621 * Invert black to grayscale...
1624 for (i
= header
->cupsBytesPerLine
, ptr
= PixelBuffer
;
1631 * Compress the output...
1634 CompressData(PixelBuffer
, header
->cupsBytesPerLine
, 0, 'W',
1635 header
->cupsCompression
);
1639 order
= ColorOrders
[PrinterPlanes
- 1];
1640 width
= header
->cupsWidth
;
1642 for (i
= 0, j
= 0; i
< PrinterPlanes
; i
++)
1645 bytes
= DotBufferSizes
[plane
] / DotBits
[plane
];
1647 for (bit
= 1, ptr
= DotBuffers
[plane
];
1648 bit
<= DotBits
[plane
];
1649 bit
<<= 1, ptr
+= bytes
, j
++)
1651 cupsPackHorizontalBit(OutputBuffers
[plane
], DotBuffers
[plane
],
1653 CompressData(ptr
, bytes
, j
,
1654 i
== (PrinterPlanes
- 1) &&
1655 bit
== DotBits
[plane
] ? 'W' : 'V',
1656 header
->cupsCompression
);
1663 * The seed buffer, if any, now should contain valid data...
1671 * 'ReadLine()' - Read graphics from the page stream.
1674 int /* O - Number of lines (0 if blank) */
1675 ReadLine(cups_raster_t
*ras
, /* I - Raster stream */
1676 cups_page_header2_t
*header
) /* I - Page header */
1678 int plane
, /* Current color plane */
1679 width
; /* Width of line */
1683 * Read raster data...
1686 cupsRasterReadPixels(ras
, PixelBuffer
, header
->cupsBytesPerLine
);
1689 * See if it is blank; if so, return right away...
1692 if (cupsCheckValue(PixelBuffer
, header
->cupsBytesPerLine
, BlankValue
))
1696 * If we aren't dithering, return immediately...
1699 if (OutputMode
!= OUTPUT_DITHERED
)
1703 * Perform the color separation...
1706 width
= header
->cupsWidth
;
1708 switch (header
->cupsColorSpace
)
1710 case CUPS_CSPACE_W
:
1713 cupsRGBDoGray(RGB
, PixelBuffer
, CMYKBuffer
, width
);
1715 if (RGB
->num_channels
== 1)
1716 cupsCMYKDoBlack(CMYK
, CMYKBuffer
, InputBuffer
, width
);
1718 cupsCMYKDoCMYK(CMYK
, CMYKBuffer
, InputBuffer
, width
);
1721 cupsCMYKDoGray(CMYK
, PixelBuffer
, InputBuffer
, width
);
1724 case CUPS_CSPACE_K
:
1725 cupsCMYKDoBlack(CMYK
, PixelBuffer
, InputBuffer
, width
);
1729 case CUPS_CSPACE_RGB
:
1732 cupsRGBDoRGB(RGB
, PixelBuffer
, CMYKBuffer
, width
);
1734 if (RGB
->num_channels
== 1)
1735 cupsCMYKDoBlack(CMYK
, CMYKBuffer
, InputBuffer
, width
);
1737 cupsCMYKDoCMYK(CMYK
, CMYKBuffer
, InputBuffer
, width
);
1740 cupsCMYKDoRGB(CMYK
, PixelBuffer
, InputBuffer
, width
);
1743 case CUPS_CSPACE_CMYK
:
1744 cupsCMYKDoCMYK(CMYK
, PixelBuffer
, InputBuffer
, width
);
1749 * Dither the pixels...
1752 for (plane
= 0; plane
< PrinterPlanes
; plane
++)
1753 cupsDitherLine(DitherStates
[plane
], DitherLuts
[plane
], InputBuffer
+ plane
,
1754 PrinterPlanes
, OutputBuffers
[plane
]);
1757 * Return 1 to indicate that we have non-blank output...
1765 * 'main()' - Main entry and processing of driver.
1768 int /* O - Exit status */
1769 main(int argc
, /* I - Number of command-line arguments */
1770 char *argv
[]) /* I - Command-line arguments */
1772 int fd
; /* File descriptor */
1773 cups_raster_t
*ras
; /* Raster stream for printing */
1774 cups_page_header2_t header
; /* Page header from file */
1775 int y
; /* Current line */
1776 ppd_file_t
*ppd
; /* PPD file */
1777 int job_id
; /* Job ID */
1778 int num_options
; /* Number of options */
1779 cups_option_t
*options
; /* Options */
1780 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1781 struct sigaction action
; /* Actions for POSIX signals */
1782 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1786 * Make sure status messages are not buffered...
1789 setbuf(stderr
, NULL
);
1792 * Check command-line...
1795 if (argc
< 6 || argc
> 7)
1797 fputs("ERROR: rastertopclx job-id user title copies options [file]\n", stderr
);
1801 num_options
= cupsParseOptions(argv
[5], 0, &options
);
1804 * Open the PPD file...
1807 ppd
= ppdOpenFile(getenv("PPD"));
1811 fputs("ERROR: Unable to open PPD file!\n", stderr
);
1815 ppdMarkDefaults(ppd
);
1816 cupsMarkOptions(ppd
, num_options
, options
);
1819 * Open the page stream...
1824 if ((fd
= open(argv
[6], O_RDONLY
)) == -1)
1826 perror("ERROR: Unable to open raster file - ");
1833 ras
= cupsRasterOpen(fd
, CUPS_RASTER_READ
);
1836 * Register a signal handler to eject the current page if the
1842 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1843 sigset(SIGTERM
, CancelJob
);
1844 #elif defined(HAVE_SIGACTION)
1845 memset(&action
, 0, sizeof(action
));
1847 sigemptyset(&action
.sa_mask
);
1848 action
.sa_handler
= CancelJob
;
1849 sigaction(SIGTERM
, &action
, NULL
);
1851 signal(SIGTERM
, CancelJob
);
1852 #endif /* HAVE_SIGSET */
1855 * Process pages as needed...
1858 job_id
= atoi(argv
[1]);
1862 while (cupsRasterReadHeader2(ras
, &header
))
1865 * Write a status message with the page number and number of copies.
1873 fprintf(stderr
, "PAGE: %d %d\n", Page
, header
.NumCopies
);
1874 fprintf(stderr
, "INFO: Starting page %d...\n", Page
);
1876 StartPage(ppd
, &header
, atoi(argv
[1]), argv
[2], argv
[3],
1877 num_options
, options
);
1879 for (y
= 0; y
< (int)header
.cupsHeight
; y
++)
1882 * Let the user know how far we have progressed...
1889 fprintf(stderr
, "INFO: Printing page %d, %d%% complete...\n", Page
,
1890 100 * y
/ header
.cupsHeight
);
1893 * Read and write a line of graphics or whitespace...
1896 if (ReadLine(ras
, &header
))
1897 OutputLine(ppd
, &header
);
1906 fprintf(stderr
, "INFO: Finished page %d...\n", Page
);
1908 EndPage(ppd
, &header
);
1914 Shutdown(ppd
, job_id
, argv
[2], argv
[3], num_options
, options
);
1916 cupsFreeOptions(num_options
, options
);
1918 cupsRasterClose(ras
);
1925 fputs("ERROR: No pages found!\n", stderr
);
1930 fputs("INFO: Ready to print.\n", stderr
);