4 * Advanced HP Page Control Language and Raster Transfer Language
7 * Copyright 2007-2011 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...
32 #include <cupsfilters/driver.h>
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 ppdEmit(ppd, stdout, PPD_ORDER_JCL);
533 if (ppd->model_number & PCL_PJL_HPGL2)
534 pjl_enter_language("HPGL2");
535 else if (ppd->model_number & PCL_PJL_PCL3GUI)
536 pjl_enter_language("PCL3GUI");
538 pjl_enter_language("PCL");
546 if (ppd->model_number & PCL_PJL_HPGL2)
551 * HP-GL/2 initialization...
555 printf("MG\"%d %s %s\";", job_id, user, title);
559 * Set media size, position, type, etc...
563 printf("PS%.0f,%.0f;",
564 header->cupsHeight * 1016.0 / header->HWResolution[1],
565 header->cupsWidth * 1016.0 / header->HWResolution[0]);
569 printf("MT%d;", header->cupsMediaType);
571 if (header->CutMedia == CUPS_CUT_PAGE)
577 * Set graphics mode...
581 pcl_set_negative_motion();
586 * Set media size, position, type, etc...
589 if (!header->Duplex || (Page & 1))
591 pcl_set_media_size(ppd, header->PageSize[0], header->PageSize[1]);
593 if (header->MediaPosition)
594 pcl_set_media_source(header->MediaPosition);
596 pcl_set_media_type(header->cupsMediaType);
598 if (ppdFindAttr(ppd, "cupsPJL", "Duplex") == NULL)
599 pcl_set_duplex(header->Duplex, header->Tumble);
602 * Set the number of copies...
605 if (!ppd->manual_copies)
606 pcl_set_copies(header->NumCopies);
609 * Set the output order/bin...
612 if (ppdFindAttr(ppd, "cupsPJL", "Jog") == NULL && header->Jog)
613 printf("\033&l%dG", header->Jog);
618 * Print on the back side...
624 if (header->Duplex && (ppd->model_number & PCL_RASTER_CRD))
627 * Reload the media...
630 pcl_set_media_source(-2);
634 * Set the units for cursor positioning and go to the top of the form.
637 printf("\033&u%dD", header->HWResolution[0]);
638 printf("\033*p0Y\033*p0X");
641 if ((attr = cupsFindAttr(ppd, "cupsPCLQuality", colormodel,
642 header->MediaType, resolution, spec,
643 sizeof(spec))) != NULL)
646 * Set the print quality...
649 if (ppd->model_number & PCL_PJL_HPGL2)
650 printf("QM%d", atoi(attr->value));
652 printf("\033*o%dM", atoi(attr->value));
656 * Enter graphics mode...
659 if (ppd->model_number & PCL_RASTER_CRD)
662 * Use configure raster data command...
665 if (OutputMode == OUTPUT_RGB)
668 * Send 12-byte configure raster data command with horizontal and
669 * vertical resolutions as well as a color count...
672 if ((attr = cupsFindAttr(ppd, "cupsPCLCRDMode", colormodel,
673 header->MediaType, resolution, spec,
674 sizeof(spec))) != NULL)
675 i = atoi(attr->value);
680 putchar(6); /* Format 6 */
681 putchar(i); /* Set pen mode */
682 putchar(0x00); /* Number components */
683 putchar(0x01); /* (1 for RGB) */
685 putchar(header->HWResolution[0] >> 8);
686 putchar(header->HWResolution[0]);
687 putchar(header->HWResolution[1] >> 8);
688 putchar(header->HWResolution[1]);
690 putchar(header->cupsCompression); /* Compression mode 3 or 10 */
691 putchar(0x01); /* Portrait orientation */
692 putchar(0x20); /* Bits per pixel (32 = RGB) */
693 putchar(0x01); /* Planes per pixel (1 = chunky RGB) */
698 * Send the configure raster data command with horizontal and
699 * vertical resolutions as well as a color count...
702 printf("\033*g%dW", PrinterPlanes * 6 + 2);
703 putchar(2); /* Format 2 */
704 putchar(PrinterPlanes); /* Output planes */
706 order = ColorOrders[PrinterPlanes - 1];
708 for (i = 0; i < PrinterPlanes; i ++)
712 putchar(header->HWResolution[0] >> 8);
713 putchar(header->HWResolution[0]);
714 putchar(header->HWResolution[1] >> 8);
715 putchar(header->HWResolution[1]);
717 putchar(1 << DotBits[plane]);
721 else if ((ppd->model_number & PCL_RASTER_CID) && OutputMode == OUTPUT_RGB)
724 * Use configure image data command...
727 pcl_set_simple_resolution(header->HWResolution[0]);
728 /* Set output resolution */
730 cupsWritePrintData("\033*v6W\0\3\0\10\10\10", 11);
736 * Use simple raster commands...
739 pcl_set_simple_resolution(header->HWResolution[0]);
740 /* Set output resolution */
742 if (PrinterPlanes == 3)
743 pcl_set_simple_cmy();
744 else if (PrinterPlanes == 4)
745 pcl_set_simple_kcmy();
748 if ((attr = ppdFindAttr(ppd, "cupsPCLOrigin", "X")) != NULL)
749 xorigin = atoi(attr->value);
753 if ((attr = ppdFindAttr(ppd, "cupsPCLOrigin", "Y")) != NULL)
754 yorigin = atoi(attr->value);
758 printf("\033&a%dH\033&a%dV", xorigin, yorigin);
759 printf("\033*r%dS", header->cupsWidth);
760 printf("\033*r%dT", header->cupsHeight);
763 if (header->cupsCompression && header->cupsCompression != 10)
764 printf("\033*b%dM", header->cupsCompression);
769 * Allocate memory for the page...
772 PixelBuffer = malloc(header->cupsBytesPerLine);
774 if (OutputMode == OUTPUT_DITHERED)
776 InputBuffer = malloc(header->cupsWidth * PrinterPlanes * 2);
777 OutputBuffers[0] = malloc(PrinterPlanes * header->cupsWidth);
779 for (i = 1; i < PrinterPlanes; i ++)
780 OutputBuffers[i] = OutputBuffers[0] + i * header->cupsWidth;
783 CMYKBuffer = malloc(header->cupsWidth * PrinterPlanes);
785 for (plane = 0, DotBufferSize = 0; plane < PrinterPlanes; plane ++)
787 DotBufferSizes[plane] = (header->cupsWidth + 7) / 8 * DotBits[plane];
788 DotBufferSize += DotBufferSizes[plane];
791 DotBuffers[0] = malloc(DotBufferSize);
792 for (plane = 1; plane < PrinterPlanes; plane ++)
793 DotBuffers[plane] = DotBuffers[plane - 1] + DotBufferSizes[plane - 1];
796 if (header->cupsCompression)
797 CompBuffer = malloc(DotBufferSize * 4);
799 if (header->cupsCompression >= 3)
800 SeedBuffer = malloc(DotBufferSize);
804 fprintf(stderr, "BlankValue=%d\n", BlankValue);
809 * 'EndPage()' - Finish a page of graphics.
813 EndPage(ppd_file_t *ppd, /* I - PPD file */
814 cups_page_header2_t *header) /* I - Page header */
816 int plane; /* Current plane */
820 * End graphics mode...
823 if (ppd->model_number & PCL_RASTER_END_COLOR)
824 printf("\033*rC"); /* End color GFX */
826 printf("\033*r0B"); /* End B&W GFX */
829 * Output a page eject sequence...
832 if (ppd->model_number & PCL_PJL_HPGL2)
834 pcl_set_hpgl_mode(0); /* Back to HP-GL/2 mode */
835 printf("PG;"); /* Eject the current page */
837 else if (!(header->Duplex && (Page & 1)))
838 printf("\014"); /* Eject current page */
841 * Free memory for the page...
846 if (OutputMode == OUTPUT_DITHERED)
848 for (plane = 0; plane < PrinterPlanes; plane ++)
850 cupsDitherDelete(DitherStates[plane]);
851 cupsLutDelete(DitherLuts[plane]);
856 free(OutputBuffers[0]);
858 cupsCMYKDelete(CMYK);
867 if (header->cupsCompression)
870 if (header->cupsCompression >= 3)
876 * 'Shutdown()' - Shutdown a printer.
880 Shutdown(ppd_file_t *ppd, /* I - PPD file */
881 int job_id, /* I - Job ID */
882 const char *user, /* I - User printing job */
883 const char *title, /* I - Title of job */
884 int num_options,/* I - Number of command-line options */
885 cups_option_t *options) /* I - Command-line options */
887 ppd_attr_t *attr; /* Attribute from PPD file */
890 if ((attr = ppdFindAttr(ppd, "cupsPCL", "EndJob")) != NULL)
893 * Tell the printer how many pages were in the job...
897 printf(attr->value, Page);
902 * Return the printer to the default state...
908 if (ppd->model_number & PCL_PJL)
912 if ((attr = ppdFindAttr(ppd, "cupsPJL", "EndJob")) != NULL)
913 pjl_write(ppd, attr->value, NULL, job_id, user, title, num_options,
916 printf("@PJL EOJ\r\n");
924 * 'CancelJob()' - Cancel the current job...
928 CancelJob(int sig) /* I - Signal */
937 * 'CompressData()' - Compress a line of graphics.
941 CompressData(unsigned char *line, /* I - Data to compress */
942 int length, /* I - Number of bytes */
943 int plane, /* I - Color plane */
944 int pend, /* I - End character for data */
945 int type) /* I - Type of compression */
947 unsigned char *line_ptr, /* Current byte pointer */
948 *line_end, /* End-of-line byte pointer */
949 *comp_ptr, /* Pointer into compression buffer */
950 *start, /* Start of compression sequence */
951 *seed; /* Seed buffer pointer */
952 int count, /* Count of bytes for output */
953 offset, /* Offset of bytes for output */
954 temp; /* Temporary count */
955 int r, g, b; /* RGB deltas for mode 10 compression */
962 * Do no compression; with a mode-0 only printer, we can compress blank
968 if (cupsCheckBytes(line, length))
969 line_end = line; /* Blank line */
971 line_end = line + length; /* Non-blank line */
976 * Do run-length encoding...
979 line_end = line + length;
980 for (line_ptr = line, comp_ptr = CompBuffer;
982 comp_ptr += 2, line_ptr += count)
985 (line_ptr + count) < line_end &&
986 line_ptr[0] == line_ptr[count] &&
990 comp_ptr[0] = count - 1;
991 comp_ptr[1] = line_ptr[0];
994 line_ptr = CompBuffer;
1000 * Do TIFF pack-bits encoding...
1004 line_end = line + length;
1005 comp_ptr = CompBuffer;
1007 while (line_ptr < line_end)
1009 if ((line_ptr + 1) >= line_end)
1012 * Single byte on the end...
1016 *comp_ptr++ = *line_ptr++;
1018 else if (line_ptr[0] == line_ptr[1])
1021 * Repeated sequence...
1027 while (line_ptr < (line_end - 1) &&
1028 line_ptr[0] == line_ptr[1] &&
1035 *comp_ptr++ = 257 - count;
1036 *comp_ptr++ = *line_ptr++;
1041 * Non-repeated sequence...
1048 while (line_ptr < (line_end - 1) &&
1049 line_ptr[0] != line_ptr[1] &&
1056 *comp_ptr++ = count - 1;
1058 memcpy(comp_ptr, start, count);
1063 line_ptr = CompBuffer;
1064 line_end = comp_ptr;
1069 * Do delta-row compression...
1073 line_end = line + length;
1075 comp_ptr = CompBuffer;
1076 seed = SeedBuffer + plane * length;
1078 while (line_ptr < line_end)
1081 * Find the next non-matching sequence...
1089 * The seed buffer is invalid, so do the next 8 bytes, max...
1094 if ((count = line_end - line_ptr) > 8)
1102 * The seed buffer is valid, so compare against it...
1105 while (*line_ptr == *seed &&
1106 line_ptr < line_end)
1112 if (line_ptr == line_end)
1115 offset = line_ptr - start;
1118 * Find up to 8 non-matching bytes...
1123 while (*line_ptr != *seed &&
1124 line_ptr < line_end &&
1134 * Place mode 3 compression data in the buffer; see HP manuals
1141 * Output multi-byte offset...
1144 *comp_ptr++ = ((count - 1) << 5) | 31;
1147 while (offset >= 255)
1153 *comp_ptr++ = offset;
1158 * Output single-byte offset...
1161 *comp_ptr++ = ((count - 1) << 5) | offset;
1164 memcpy(comp_ptr, start, count);
1168 line_ptr = CompBuffer;
1169 line_end = comp_ptr;
1171 memcpy(SeedBuffer + plane * length, line, length);
1176 * Mode 10 "near lossless" RGB compression...
1180 line_end = line + length;
1182 comp_ptr = CompBuffer;
1185 if (PrinterPlanes == 1)
1188 * Do grayscale compression to RGB...
1191 while (line_ptr < line_end)
1194 * Find the next non-matching sequence...
1198 while (line_ptr < line_end &&
1205 if (line_ptr == line_end)
1208 offset = line_ptr - start;
1211 * Find non-matching grayscale pixels...
1215 while (line_ptr < line_end &&
1222 count = line_ptr - start;
1225 fprintf(stderr, "DEBUG: offset=%d, count=%d, comp_ptr=%p(%d of %d)...\n",
1226 offset, count, comp_ptr, comp_ptr - CompBuffer,
1231 * Place mode 10 compression data in the buffer; each sequence
1232 * starts with a command byte that looks like:
1234 * CMD SRC SRC OFF OFF CNT CNT CNT
1236 * For the purpose of this driver, CMD and SRC are always 0.
1238 * If the offset >= 3 then additional offset bytes follow the
1239 * first command byte, each byte == 255 until the last one.
1241 * If the count >= 7, then additional count bytes follow each
1242 * group of pixels, each byte == 255 until the last one.
1244 * The offset and count are in RGB tuples (not bytes, as for
1251 * Output multi-byte offset...
1257 *comp_ptr++ = 0x18 | (count - 1);
1260 while (offset >= 255)
1266 *comp_ptr++ = offset;
1271 * Output single-byte offset...
1275 *comp_ptr++ = (offset << 3) | 0x07;
1277 *comp_ptr++ = (offset << 3) | (count - 1);
1288 * This is exceedingly lame... The replacement counts
1289 * are intermingled with the data...
1301 * Get difference between current and see pixels...
1306 b = ((*start & 0xfe) - (*seed & 0xfe)) / 2;
1308 if (r < -16 || r > 15 || g < -16 || g > 15 || b < -16 || b > 15)
1311 * Pack 24-bit RGB into 23 bits... Lame...
1316 *comp_ptr++ = g >> 1;
1319 *comp_ptr++ = 0x80 | (g >> 1);
1321 *comp_ptr++ = g >> 1;
1324 *comp_ptr++ = 0x80 | (g >> 1);
1326 *comp_ptr++ = g >> 1;
1331 * Pack 15-bit RGB difference...
1334 *comp_ptr++ = 0x80 | ((r << 2) & 0x7c) | ((g >> 3) & 0x03);
1335 *comp_ptr++ = ((g << 5) & 0xe0) | (b & 0x1f);
1344 * Make sure we have the ending count if the replacement count
1345 * was exactly 8 + 255n...
1355 * Do RGB compression...
1358 while (line_ptr < line_end)
1361 * Find the next non-matching sequence...
1365 while (line_ptr[0] == seed[0] &&
1366 line_ptr[1] == seed[1] &&
1367 line_ptr[2] == seed[2] &&
1368 (line_ptr + 2) < line_end)
1374 if (line_ptr == line_end)
1377 offset = (line_ptr - start) / 3;
1380 * Find non-matching RGB tuples...
1384 while ((line_ptr[0] != seed[0] ||
1385 line_ptr[1] != seed[1] ||
1386 line_ptr[2] != seed[2]) &&
1387 (line_ptr + 2) < line_end)
1393 count = (line_ptr - start) / 3;
1396 * Place mode 10 compression data in the buffer; each sequence
1397 * starts with a command byte that looks like:
1399 * CMD SRC SRC OFF OFF CNT CNT CNT
1401 * For the purpose of this driver, CMD and SRC are always 0.
1403 * If the offset >= 3 then additional offset bytes follow the
1404 * first command byte, each byte == 255 until the last one.
1406 * If the count >= 7, then additional count bytes follow each
1407 * group of pixels, each byte == 255 until the last one.
1409 * The offset and count are in RGB tuples (not bytes, as for
1416 * Output multi-byte offset...
1422 *comp_ptr++ = 0x18 | (count - 1);
1425 while (offset >= 255)
1431 *comp_ptr++ = offset;
1436 * Output single-byte offset...
1440 *comp_ptr++ = (offset << 3) | 0x07;
1442 *comp_ptr++ = (offset << 3) | (count - 1);
1453 * This is exceedingly lame... The replacement counts
1454 * are intermingled with the data...
1466 * Get difference between current and see pixels...
1469 r = start[0] - seed[0];
1470 g = start[1] - seed[1];
1471 b = ((start[2] & 0xfe) - (seed[2] & 0xfe)) / 2;
1473 if (r < -16 || r > 15 || g < -16 || g > 15 || b < -16 || b > 15)
1476 * Pack 24-bit RGB into 23 bits... Lame...
1479 *comp_ptr++ = start[0] >> 1;
1482 *comp_ptr++ = 0x80 | (start[1] >> 1);
1484 *comp_ptr++ = start[1] >> 1;
1487 *comp_ptr++ = 0x80 | (start[2] >> 1);
1489 *comp_ptr++ = start[2] >> 1;
1494 * Pack 15-bit RGB difference...
1497 *comp_ptr++ = 0x80 | ((r << 2) & 0x7c) | ((g >> 3) & 0x03);
1498 *comp_ptr++ = ((g << 5) & 0xe0) | (b & 0x1f);
1507 * Make sure we have the ending count if the replacement count
1508 * was exactly 8 + 255n...
1516 line_ptr = CompBuffer;
1517 line_end = comp_ptr;
1519 memcpy(SeedBuffer, line, length);
1524 * Set the length of the data and write a raster plane...
1527 printf("\033*b%d%c", (int)(line_end - line_ptr), pend);
1528 cupsWritePrintData(line_ptr, line_end - line_ptr);
1533 * 'OutputLine()' - Output the specified number of lines of graphics.
1537 OutputLine(ppd_file_t *ppd, /* I - PPD file */
1538 cups_page_header2_t *header) /* I - Page header */
1540 int i, j; /* Looping vars */
1541 int plane; /* Current plane */
1542 unsigned char bit; /* Current bit */
1543 int bytes; /* Number of bytes/plane */
1544 int width; /* Width of line in pixels */
1545 const int *order; /* Order to use */
1546 unsigned char *ptr; /* Pointer into buffer */
1550 * Output whitespace as needed...
1555 if (header->cupsCompression < 3)
1558 * Send blank raster lines...
1561 while (OutputFeed > 0)
1570 * Send Y offset command and invalidate the seed buffer...
1573 printf("\033*b%dY", OutputFeed);
1580 * Write bitmap data as needed...
1585 case OUTPUT_BITMAP : /* Send 1-bit bitmap data... */
1586 order = ColorOrders[PrinterPlanes - 1];
1587 bytes = header->cupsBytesPerLine / PrinterPlanes;
1589 for (i = 0; i < PrinterPlanes; i ++)
1593 CompressData(PixelBuffer + i * bytes, bytes, plane,
1594 (i < (PrinterPlanes - 1)) ? 'V' : 'W',
1595 header->cupsCompression);
1599 case OUTPUT_INVERBIT : /* Send inverted 1-bit bitmap data... */
1600 order = ColorOrders[PrinterPlanes - 1];
1601 bytes = header->cupsBytesPerLine / PrinterPlanes;
1603 for (i = header->cupsBytesPerLine, ptr = PixelBuffer;
1608 for (i = 0; i < PrinterPlanes; i ++)
1612 CompressData(PixelBuffer + i * bytes, bytes, plane,
1613 (i < (PrinterPlanes - 1)) ? 'V' : 'W',
1614 header->cupsCompression);
1618 case OUTPUT_RGB : /* Send 24-bit RGB data... */
1619 if (PrinterPlanes == 1 && !BlankValue)
1622 * Invert black to grayscale...
1625 for (i = header->cupsBytesPerLine, ptr = PixelBuffer;
1632 * Compress the output...
1635 CompressData(PixelBuffer, header->cupsBytesPerLine, 0, 'W',
1636 header->cupsCompression);
1640 order = ColorOrders[PrinterPlanes - 1];
1641 width = header->cupsWidth;
1643 for (i = 0, j = 0; i < PrinterPlanes; i ++)
1646 bytes = DotBufferSizes[plane] / DotBits[plane];
1648 for (bit = 1, ptr = DotBuffers[plane];
1649 bit <= DotBits[plane];
1650 bit <<= 1, ptr += bytes, j ++)
1652 cupsPackHorizontalBit(OutputBuffers[plane], DotBuffers[plane],
1654 CompressData(ptr, bytes, j,
1655 i == (PrinterPlanes - 1) &&
1656 bit == DotBits[plane] ? 'W' : 'V',
1657 header->cupsCompression);
1664 * The seed buffer, if any, now should contain valid data...
1672 * 'ReadLine()' - Read graphics from the page stream.
1675 int /* O - Number of lines (0 if blank) */
1676 ReadLine(cups_raster_t *ras, /* I - Raster stream */
1677 cups_page_header2_t *header) /* I - Page header */
1679 int plane, /* Current color plane */
1680 width; /* Width of line */
1684 * Read raster data...
1687 cupsRasterReadPixels(ras, PixelBuffer, header->cupsBytesPerLine);
1690 * See if it is blank; if so, return right away...
1693 if (cupsCheckValue(PixelBuffer, header->cupsBytesPerLine, BlankValue))
1697 * If we aren't dithering, return immediately...
1700 if (OutputMode != OUTPUT_DITHERED)
1704 * Perform the color separation...
1707 width = header->cupsWidth;
1709 switch (header->cupsColorSpace)
1711 case CUPS_CSPACE_W :
1714 cupsRGBDoGray(RGB, PixelBuffer, CMYKBuffer, width);
1716 if (RGB->num_channels == 1)
1717 cupsCMYKDoBlack(CMYK, CMYKBuffer, InputBuffer, width);
1719 cupsCMYKDoCMYK(CMYK, CMYKBuffer, InputBuffer, width);
1722 cupsCMYKDoGray(CMYK, PixelBuffer, InputBuffer, width);
1725 case CUPS_CSPACE_K :
1726 cupsCMYKDoBlack(CMYK, PixelBuffer, InputBuffer, width);
1730 case CUPS_CSPACE_RGB :
1733 cupsRGBDoRGB(RGB, PixelBuffer, CMYKBuffer, width);
1735 if (RGB->num_channels == 1)
1736 cupsCMYKDoBlack(CMYK, CMYKBuffer, InputBuffer, width);
1738 cupsCMYKDoCMYK(CMYK, CMYKBuffer, InputBuffer, width);
1741 cupsCMYKDoRGB(CMYK, PixelBuffer, InputBuffer, width);
1744 case CUPS_CSPACE_CMYK :
1745 cupsCMYKDoCMYK(CMYK, PixelBuffer, InputBuffer, width);
1750 * Dither the pixels...
1753 for (plane = 0; plane < PrinterPlanes; plane ++)
1754 cupsDitherLine(DitherStates[plane], DitherLuts[plane], InputBuffer + plane,
1755 PrinterPlanes, OutputBuffers[plane]);
1758 * Return 1 to indicate that we have non-blank output...
1766 * 'main()' - Main entry and processing of driver.
1769 int /* O - Exit status */
1770 main(int argc, /* I - Number of command-line arguments */
1771 char *argv[]) /* I - Command-line arguments */
1773 int fd; /* File descriptor */
1774 cups_raster_t *ras; /* Raster stream for printing */
1775 cups_page_header2_t header; /* Page header from file */
1776 int y; /* Current line */
1777 ppd_file_t *ppd; /* PPD file */
1778 int job_id; /* Job ID */
1779 int num_options; /* Number of options */
1780 cups_option_t *options; /* Options */
1781 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1782 struct sigaction action; /* Actions for POSIX signals */
1783 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1787 * Make sure status messages are not buffered...
1790 setbuf(stderr, NULL);
1793 * Check command-line...
1796 if (argc < 6 || argc > 7)
1798 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
1803 num_options = cupsParseOptions(argv[5], 0, &options);
1806 * Open the PPD file...
1809 ppd = ppdOpenFile(getenv("PPD"));
1813 ppd_status_t status; /* PPD error */
1814 int linenum; /* Line number */
1816 fputs("ERROR: The PPD file could not be opened.\n", stderr);
1818 status = ppdLastError(&linenum);
1820 fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum);
1825 ppdMarkDefaults(ppd);
1826 cupsMarkOptions(ppd, num_options, options);
1829 * Open the page stream...
1834 if ((fd = open(argv[6], O_RDONLY)) == -1)
1836 perror("ERROR: Unable to open raster file");
1843 ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
1846 * Register a signal handler to eject the current page if the
1852 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1853 sigset(SIGTERM, CancelJob);
1854 #elif defined(HAVE_SIGACTION)
1855 memset(&action, 0, sizeof(action));
1857 sigemptyset(&action.sa_mask);
1858 action.sa_handler = CancelJob;
1859 sigaction(SIGTERM, &action, NULL);
1861 signal(SIGTERM, CancelJob);
1862 #endif /* HAVE_SIGSET */
1865 * Process pages as needed...
1868 job_id = atoi(argv[1]);
1872 while (cupsRasterReadHeader2(ras, &header))
1875 * Write a status message with the page number and number of copies.
1883 fprintf(stderr, "PAGE: %d %d\n", Page, header.NumCopies);
1884 fprintf(stderr, "INFO: Starting page %d.\n", Page);
1886 StartPage(ppd, &header, atoi(argv[1]), argv[2], argv[3],
1887 num_options, options);
1889 for (y = 0; y < (int)header.cupsHeight; y ++)
1892 * Let the user know how far we have progressed...
1900 fprintf(stderr, "INFO: Printing page %d, %d%% complete.\n",
1901 Page, 100 * y / header.cupsHeight);
1902 fprintf(stderr, "ATTR: job-media-progress=%d\n",
1903 100 * y / header.cupsHeight);
1907 * Read and write a line of graphics or whitespace...
1910 if (ReadLine(ras, &header))
1911 OutputLine(ppd, &header);
1920 fprintf(stderr, "INFO: Finished page %d.\n", Page);
1922 EndPage(ppd, &header);
1928 Shutdown(ppd, job_id, argv[2], argv[3], num_options, options);
1930 cupsFreeOptions(num_options, options);
1932 cupsRasterClose(ras);