Tizen 2.0 Release
[platform/upstream/cups-filters.git] / filter / rastertopclx.c
1 /*
2  * "$Id$"
3  *
4  *   Advanced HP Page Control Language and Raster Transfer Language
5  *   filter for CUPS.
6  *
7  *   Copyright 2007-2011 by Apple Inc.
8  *   Copyright 1993-2005 by Easy Software Products
9  *
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/".
15  *
16  * Contents:
17  *
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.
26  */
27
28 /*
29  * Include necessary headers...
30  */
31
32 #include <cupsfilters/driver.h>
33 #include "pcl-common.h"
34 #include <signal.h>
35
36
37 /*
38  * Output modes...
39  */
40
41 typedef enum
42 {
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 */
47 } pcl_output_t;
48
49
50 /*
51  * Globals...
52  */
53
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 */
75                 {
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 */
83                 };
84 int             Canceled;               /* Is the job canceled? */
85
86
87 /*
88  * Prototypes...
89  */
90
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);
97
98 void    CancelJob(int sig);
99 void    CompressData(unsigned char *line, int length, int plane, int pend,
100                      int type);
101 void    OutputLine(ppd_file_t *ppd, cups_page_header2_t *header);
102 int     ReadLine(cups_raster_t *ras, cups_page_header2_t *header);
103
104
105 /*
106  * 'StartPage()' - Start a page of graphics.
107  */
108
109 void
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 */
115           int                num_options,
116                                         /* I - Number of command-line options */
117           cups_option_t      *options)  /* I - Command-line options */
118 {
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 */
132                 {
133                   0.0,
134                   1.0
135                 };
136
137
138  /*
139   * Debug info...
140   */
141
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);
147
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],
162           header->Margins[1]);
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);
185
186 #ifdef __APPLE__
187  /*
188   * MacOS X 10.2.x doesn't set most of the page device attributes, so check
189   * the options and set them accordingly...
190   */
191
192   if (ppdIsMarked(ppd, "Duplex", "DuplexNoTumble"))
193   {
194     header->Duplex = CUPS_TRUE;
195     header->Tumble = CUPS_FALSE;
196   }
197   else if (ppdIsMarked(ppd, "Duplex", "DuplexTumble"))
198   {
199     header->Duplex = CUPS_TRUE;
200     header->Tumble = CUPS_TRUE;
201   }
202
203   fprintf(stderr, "DEBUG: num_options=%d\n", num_options);
204
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__ */
209
210  /*
211   * Figure out the color model and spec strings...
212   */
213
214   switch (header->cupsColorSpace)
215   {
216     case CUPS_CSPACE_K :
217         colormodel = "Black";
218         break;
219     case CUPS_CSPACE_W :
220         colormodel = "Gray";
221         break;
222     default :
223     case CUPS_CSPACE_RGB :
224         colormodel = "RGB";
225         break;
226     case CUPS_CSPACE_CMY :
227         colormodel = "CMY";
228         break;
229     case CUPS_CSPACE_CMYK :
230         colormodel = "CMYK";
231         break;
232   }
233
234   if (header->HWResolution[0] != header->HWResolution[1])
235     snprintf(resolution, sizeof(resolution), "%dx%ddpi",
236              header->HWResolution[0], header->HWResolution[1]);
237   else
238     snprintf(resolution, sizeof(resolution), "%ddpi",
239              header->HWResolution[0]);
240
241   if (!header->MediaType[0])
242     strcpy(header->MediaType, "PLAIN");
243
244  /*
245   * Get the dithering parameters...
246   */
247
248   BlankValue = 0x00;
249
250   if (header->cupsBitsPerColor == 1)
251   {
252    /*
253     * Use raw bitmap mode...
254     */
255
256     switch (header->cupsColorSpace)
257     {
258       case CUPS_CSPACE_K :
259           OutputMode    = OUTPUT_BITMAP;
260           PrinterPlanes = 1;
261           break;
262       case CUPS_CSPACE_W :
263           OutputMode    = OUTPUT_INVERBIT;
264           PrinterPlanes = 1;
265           break;
266       default :
267       case CUPS_CSPACE_RGB :
268           OutputMode    = OUTPUT_INVERBIT;
269           PrinterPlanes = 3;
270           break;
271       case CUPS_CSPACE_CMY :
272           OutputMode    = OUTPUT_BITMAP;
273           PrinterPlanes = 3;
274           break;
275       case CUPS_CSPACE_CMYK :
276           OutputMode    = OUTPUT_BITMAP;
277           PrinterPlanes = 4;
278           break;
279     }
280
281     if (OutputMode == OUTPUT_INVERBIT)
282       BlankValue = 0xff;
283
284     DotBufferSize = header->cupsBytesPerLine;
285
286     memset(DitherLuts, 0, sizeof(DitherLuts));
287     memset(DitherStates, 0, sizeof(DitherStates));
288   }
289   else if (header->cupsColorSpace == CUPS_CSPACE_RGB &&
290            (ppd->model_number & PCL_RASTER_RGB24))
291   {
292    /*
293     * Use 24-bit RGB output mode...
294     */
295
296     OutputMode    = OUTPUT_RGB;
297     PrinterPlanes = 3;
298     DotBufferSize = header->cupsBytesPerLine;
299
300     if (header->cupsCompression == 10)
301       BlankValue = 0xff;
302
303     memset(DitherLuts, 0, sizeof(DitherLuts));
304     memset(DitherStates, 0, sizeof(DitherStates));
305   }
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)
310   {
311    /*
312     * Use 24-bit RGB output mode for grayscale/black output...
313     */
314
315     OutputMode    = OUTPUT_RGB;
316     PrinterPlanes = 1;
317     DotBufferSize = header->cupsBytesPerLine;
318
319     if (header->cupsColorSpace == CUPS_CSPACE_W)
320       BlankValue = 0xff;
321
322     memset(DitherLuts, 0, sizeof(DitherLuts));
323     memset(DitherStates, 0, sizeof(DitherStates));
324   }
325   else
326   {
327    /*
328     * Use dithered output mode...
329     */
330
331     OutputMode = OUTPUT_DITHERED;
332
333    /*
334     * Load the appropriate color profiles...
335     */
336
337     RGB  = NULL;
338     CMYK = NULL;
339
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);
344
345     if (header->cupsColorSpace == CUPS_CSPACE_RGB ||
346         header->cupsColorSpace == CUPS_CSPACE_W)
347       RGB = cupsRGBLoad(ppd, colormodel, header->MediaType, resolution);
348
349     CMYK = cupsCMYKLoad(ppd, colormodel, header->MediaType, resolution);
350
351     if (RGB)
352       fputs("DEBUG: Loaded RGB separation from PPD.\n", stderr);
353
354     if (CMYK)
355       fputs("DEBUG: Loaded CMYK separation from PPD.\n", stderr);
356     else
357     {
358       fputs("DEBUG: Loading default K separation.\n", stderr);
359       CMYK = cupsCMYKNew(1);
360     }
361
362     PrinterPlanes = CMYK->num_channels;
363
364    /*
365     * Use dithered mode...
366     */
367
368     switch (PrinterPlanes)
369     {
370       case 1 : /* K */
371           DitherLuts[0] = cupsLutLoad(ppd, colormodel, header->MediaType,
372                                       resolution, "Black");
373           break;
374
375       case 3 : /* CMY */
376           DitherLuts[0] = cupsLutLoad(ppd, colormodel, header->MediaType,
377                                       resolution, "Cyan");
378           DitherLuts[1] = cupsLutLoad(ppd, colormodel, header->MediaType,
379                                       resolution, "Magenta");
380           DitherLuts[2] = cupsLutLoad(ppd, colormodel, header->MediaType,
381                                       resolution, "Yellow");
382           break;
383
384       case 4 : /* CMYK */
385           DitherLuts[0] = cupsLutLoad(ppd, colormodel, header->MediaType,
386                                       resolution, "Cyan");
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");
393           break;
394
395       case 6 : /* CcMmYK */
396           DitherLuts[0] = cupsLutLoad(ppd, colormodel, header->MediaType,
397                                       resolution, "Cyan");
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");
408           break;
409     }
410
411     for (plane = 0; plane < PrinterPlanes; plane ++)
412     {
413       if (!DitherLuts[plane])
414         DitherLuts[plane] = cupsLutNew(2, default_lut);
415
416       if (DitherLuts[plane][4095].pixel > 1)
417         DotBits[plane] = 2;
418       else
419         DotBits[plane] = 1;
420
421       DitherStates[plane] = cupsDitherNew(header->cupsWidth);
422
423       if (!DitherLuts[plane])
424         DitherLuts[plane] = cupsLutNew(2, default_lut);
425     }
426   }
427
428   fprintf(stderr, "DEBUG: PrinterPlanes = %d\n", PrinterPlanes);
429
430  /*
431   * Initialize the printer...
432   */
433
434   if ((attr = ppdFindAttr(ppd, "cupsInitialNulls", NULL)) != NULL)
435     for (i = atoi(attr->value); i > 0; i --)
436       putchar(0);
437
438   if (Page == 1 && (ppd->model_number & PCL_PJL))
439   {
440     pjl_escape();
441
442    /*
443     * PJL job setup...
444     */
445
446     pjl_set_job(job_id, user, title);
447
448     if ((attr = ppdFindAttr(ppd, "cupsPJL", "StartJob")) != NULL)
449       pjl_write(ppd, attr->value, NULL, job_id, user, title, num_options,
450                 options);
451
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);
455
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);
459
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);
463
464     if ((attr = ppdFindAttr(ppd, "cupsPJL", "Duplex")) != NULL)
465     {
466       sprintf(s, "%d", header->Duplex);
467       pjl_write(ppd, attr->value, s, job_id, user, title, num_options, options);
468     }
469
470     if ((attr = ppdFindAttr(ppd, "cupsPJL", "Tumble")) != NULL)
471     {
472       sprintf(s, "%d", header->Tumble);
473       pjl_write(ppd, attr->value, s, job_id, user, title, num_options, options);
474     }
475
476     if ((attr = ppdFindAttr(ppd, "cupsPJL", "MediaClass")) != NULL)
477       pjl_write(ppd, attr->value, header->MediaClass, job_id, user, title,
478                 num_options, options);
479
480     if ((attr = ppdFindAttr(ppd, "cupsPJL", "MediaColor")) != NULL)
481       pjl_write(ppd, attr->value, header->MediaColor, job_id, user, title,
482                 num_options, options);
483
484     if ((attr = ppdFindAttr(ppd, "cupsPJL", "MediaType")) != NULL)
485       pjl_write(ppd, attr->value, header->MediaType, job_id, user, title,
486                 num_options, options);
487
488     if ((attr = ppdFindAttr(ppd, "cupsPJL", "OutputType")) != NULL)
489       pjl_write(ppd, attr->value, header->OutputType, job_id, user, title,
490                 num_options, options);
491
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);
496
497     if ((attr = ppdFindAttr(ppd, "cupsPJL", "Jog")) != NULL)
498     {
499       sprintf(s, "%d", header->Jog);
500       pjl_write(ppd, attr->value, s, job_id, user, title, num_options, options);
501     }
502
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);
507
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);
512
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);
517
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);
522
523     if (ppd->model_number & PCL_PJL_PAPERWIDTH)
524     {
525       printf("@PJL SET PAPERLENGTH=%d\r\n", header->PageSize[1] * 10);
526       printf("@PJL SET PAPERWIDTH=%d\r\n", header->PageSize[0] * 10);
527     }
528
529     if (ppd->model_number & PCL_PJL_RESOLUTION)
530       printf("@PJL SET RESOLUTION=%d\r\n", header->HWResolution[0]);
531
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");
537     else
538       pjl_enter_language("PCL");
539   }
540
541   if (Page == 1)
542   {
543     pcl_reset();
544   }
545
546   if (ppd->model_number & PCL_PJL_HPGL2)
547   {
548     if (Page == 1)
549     {
550      /*
551       * HP-GL/2 initialization...
552       */
553
554       printf("IN;");
555       printf("MG\"%d %s %s\";", job_id, user, title);
556     }
557
558    /*
559     * Set media size, position, type, etc...
560     */
561
562     printf("BP5,0;");
563     printf("PS%.0f,%.0f;",
564            header->cupsHeight * 1016.0 / header->HWResolution[1],
565            header->cupsWidth * 1016.0 / header->HWResolution[0]);
566     printf("PU;");
567     printf("PA0,0");
568
569     printf("MT%d;", header->cupsMediaType);
570
571     if (header->CutMedia == CUPS_CUT_PAGE)
572       printf("EC;");
573     else
574       printf("EC0;");
575
576    /*
577     * Set graphics mode...
578     */
579
580     pcl_set_pcl_mode(0);
581     pcl_set_negative_motion();
582   }
583   else
584   {
585    /*
586     * Set media size, position, type, etc...
587     */
588
589     if (!header->Duplex || (Page & 1))
590     {
591       pcl_set_media_size(ppd, header->PageSize[0], header->PageSize[1]);
592
593       if (header->MediaPosition)
594         pcl_set_media_source(header->MediaPosition);
595
596       pcl_set_media_type(header->cupsMediaType);
597
598       if (ppdFindAttr(ppd, "cupsPJL", "Duplex") == NULL)
599         pcl_set_duplex(header->Duplex, header->Tumble);
600
601      /*
602       * Set the number of copies...
603       */
604
605       if (!ppd->manual_copies)
606         pcl_set_copies(header->NumCopies);
607
608      /*
609       * Set the output order/bin...
610       */
611
612       if (ppdFindAttr(ppd, "cupsPJL", "Jog") == NULL && header->Jog)
613         printf("\033&l%dG", header->Jog);
614     }
615     else
616     {
617      /*
618       * Print on the back side...
619       */
620
621       printf("\033&a2G");
622     }
623
624     if (header->Duplex && (ppd->model_number & PCL_RASTER_CRD))
625     {
626      /*
627       * Reload the media...
628       */
629
630       pcl_set_media_source(-2);
631     }
632
633    /*
634     * Set the units for cursor positioning and go to the top of the form.
635     */
636
637     printf("\033&u%dD", header->HWResolution[0]);
638     printf("\033*p0Y\033*p0X");
639   }
640
641   if ((attr = cupsFindAttr(ppd, "cupsPCLQuality", colormodel,
642                            header->MediaType, resolution, spec,
643                            sizeof(spec))) != NULL)
644   {
645    /*
646     * Set the print quality...
647     */
648
649     if (ppd->model_number & PCL_PJL_HPGL2)
650       printf("QM%d", atoi(attr->value));
651     else
652       printf("\033*o%dM", atoi(attr->value));
653   }
654
655  /*
656   * Enter graphics mode...
657   */
658
659   if (ppd->model_number & PCL_RASTER_CRD)
660   {
661    /*
662     * Use configure raster data command...
663     */
664
665     if (OutputMode == OUTPUT_RGB)
666     {
667      /*
668       * Send 12-byte configure raster data command with horizontal and
669       * vertical resolutions as well as a color count...
670       */
671
672       if ((attr = cupsFindAttr(ppd, "cupsPCLCRDMode", colormodel,
673                                header->MediaType, resolution, spec,
674                                sizeof(spec))) != NULL)
675         i = atoi(attr->value);
676       else
677         i = 31;
678
679       printf("\033*g12W");
680       putchar(6);                       /* Format 6 */
681       putchar(i);                       /* Set pen mode */
682       putchar(0x00);                    /* Number components */
683       putchar(0x01);                    /* (1 for RGB) */
684
685       putchar(header->HWResolution[0] >> 8);
686       putchar(header->HWResolution[0]);
687       putchar(header->HWResolution[1] >> 8);
688       putchar(header->HWResolution[1]);
689
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) */
694     }
695     else
696     {
697      /*
698       * Send the configure raster data command with horizontal and
699       * vertical resolutions as well as a color count...
700       */
701
702       printf("\033*g%dW", PrinterPlanes * 6 + 2);
703       putchar(2);                       /* Format 2 */
704       putchar(PrinterPlanes);           /* Output planes */
705
706       order = ColorOrders[PrinterPlanes - 1];
707
708       for (i = 0; i < PrinterPlanes; i ++)
709       {
710         plane = order[i];
711
712         putchar(header->HWResolution[0] >> 8);
713         putchar(header->HWResolution[0]);
714         putchar(header->HWResolution[1] >> 8);
715         putchar(header->HWResolution[1]);
716         putchar(0);
717         putchar(1 << DotBits[plane]);
718       }
719     }
720   }
721   else if ((ppd->model_number & PCL_RASTER_CID) && OutputMode == OUTPUT_RGB)
722   {
723    /*
724     * Use configure image data command...
725     */
726
727     pcl_set_simple_resolution(header->HWResolution[0]);
728                                         /* Set output resolution */
729
730     cupsWritePrintData("\033*v6W\0\3\0\10\10\10", 11);
731                                         /* 24-bit RGB */
732   }
733   else
734   {
735    /*
736     * Use simple raster commands...
737     */
738
739     pcl_set_simple_resolution(header->HWResolution[0]);
740                                         /* Set output resolution */
741
742     if (PrinterPlanes == 3)
743       pcl_set_simple_cmy();
744     else if (PrinterPlanes == 4)
745       pcl_set_simple_kcmy();
746   }
747
748   if ((attr = ppdFindAttr(ppd, "cupsPCLOrigin", "X")) != NULL)
749     xorigin = atoi(attr->value);
750   else
751     xorigin = 0;
752
753   if ((attr = ppdFindAttr(ppd, "cupsPCLOrigin", "Y")) != NULL)
754     yorigin = atoi(attr->value);
755   else
756     yorigin = 120;
757
758   printf("\033&a%dH\033&a%dV", xorigin, yorigin);
759   printf("\033*r%dS", header->cupsWidth);
760   printf("\033*r%dT", header->cupsHeight);
761   printf("\033*r1A");
762
763   if (header->cupsCompression && header->cupsCompression != 10)
764     printf("\033*b%dM", header->cupsCompression);
765
766   OutputFeed = 0;
767
768  /*
769   * Allocate memory for the page...
770   */
771
772   PixelBuffer = malloc(header->cupsBytesPerLine);
773
774   if (OutputMode == OUTPUT_DITHERED)
775   {
776     InputBuffer      = malloc(header->cupsWidth * PrinterPlanes * 2);
777     OutputBuffers[0] = malloc(PrinterPlanes * header->cupsWidth);
778
779     for (i = 1; i < PrinterPlanes; i ++)
780       OutputBuffers[i] = OutputBuffers[0] + i * header->cupsWidth;
781
782     if (RGB)
783       CMYKBuffer = malloc(header->cupsWidth * PrinterPlanes);
784
785     for (plane = 0, DotBufferSize = 0; plane < PrinterPlanes; plane ++)
786     {
787       DotBufferSizes[plane] = (header->cupsWidth + 7) / 8 * DotBits[plane];
788       DotBufferSize         += DotBufferSizes[plane];
789     }
790
791     DotBuffers[0] = malloc(DotBufferSize);
792     for (plane = 1; plane < PrinterPlanes; plane ++)
793       DotBuffers[plane] = DotBuffers[plane - 1] + DotBufferSizes[plane - 1];
794   }
795
796   if (header->cupsCompression)
797     CompBuffer = malloc(DotBufferSize * 4);
798
799   if (header->cupsCompression >= 3)
800     SeedBuffer = malloc(DotBufferSize);
801
802   SeedInvalid = 1;
803
804   fprintf(stderr, "BlankValue=%d\n", BlankValue);
805 }
806
807
808 /*
809  * 'EndPage()' - Finish a page of graphics.
810  */
811
812 void
813 EndPage(ppd_file_t         *ppd,        /* I - PPD file */
814         cups_page_header2_t *header)    /* I - Page header */
815 {
816   int   plane;                          /* Current plane */
817
818
819  /*
820   * End graphics mode...
821   */
822
823   if (ppd->model_number & PCL_RASTER_END_COLOR)
824     printf("\033*rC");                  /* End color GFX */
825   else
826     printf("\033*r0B");                 /* End B&W GFX */
827
828  /*
829   * Output a page eject sequence...
830   */
831
832   if (ppd->model_number & PCL_PJL_HPGL2)
833   {
834      pcl_set_hpgl_mode(0);              /* Back to HP-GL/2 mode */
835      printf("PG;");                     /* Eject the current page */
836   }
837   else if (!(header->Duplex && (Page & 1)))
838     printf("\014");                     /* Eject current page */
839
840  /*
841   * Free memory for the page...
842   */
843
844   free(PixelBuffer);
845
846   if (OutputMode == OUTPUT_DITHERED)
847   {
848     for (plane = 0; plane < PrinterPlanes; plane ++)
849     {
850       cupsDitherDelete(DitherStates[plane]);
851       cupsLutDelete(DitherLuts[plane]);
852     }
853
854     free(DotBuffers[0]);
855     free(InputBuffer);
856     free(OutputBuffers[0]);
857
858     cupsCMYKDelete(CMYK);
859
860     if (RGB)
861     {
862       cupsRGBDelete(RGB);
863       free(CMYKBuffer);
864     }
865   }
866
867   if (header->cupsCompression)
868     free(CompBuffer);
869
870   if (header->cupsCompression >= 3)
871     free(SeedBuffer);
872 }
873
874
875 /*
876  * 'Shutdown()' - Shutdown a printer.
877  */
878
879 void
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 */
886 {
887   ppd_attr_t    *attr;                  /* Attribute from PPD file */
888
889
890   if ((attr = ppdFindAttr(ppd, "cupsPCL", "EndJob")) != NULL)
891   {
892    /*
893     * Tell the printer how many pages were in the job...
894     */
895
896     putchar(0x1b);
897     printf(attr->value, Page);
898   }
899   else
900   {
901    /*
902     * Return the printer to the default state...
903     */
904
905     pcl_reset();
906   }
907
908   if (ppd->model_number & PCL_PJL)
909   {
910     pjl_escape();
911
912     if ((attr = ppdFindAttr(ppd, "cupsPJL", "EndJob")) != NULL)
913       pjl_write(ppd, attr->value, NULL, job_id, user, title, num_options,
914                 options);
915     else
916       printf("@PJL EOJ\r\n");
917
918     pjl_escape();
919   }
920 }
921
922
923 /*
924  * 'CancelJob()' - Cancel the current job...
925  */
926
927 void
928 CancelJob(int sig)                      /* I - Signal */
929 {
930   (void)sig;
931
932   Canceled = 1;
933 }
934
935
936 /*
937  * 'CompressData()' - Compress a line of graphics.
938  */
939
940 void
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 */
946 {
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 */
956
957
958   switch (type)
959   {
960     default :
961        /*
962         * Do no compression; with a mode-0 only printer, we can compress blank
963         * lines...
964         */
965
966         line_ptr = line;
967
968         if (cupsCheckBytes(line, length))
969           line_end = line;              /* Blank line */
970         else
971           line_end = line + length;     /* Non-blank line */
972         break;
973
974     case 1 :
975        /*
976         * Do run-length encoding...
977         */
978
979         line_end = line + length;
980         for (line_ptr = line, comp_ptr = CompBuffer;
981              line_ptr < line_end;
982              comp_ptr += 2, line_ptr += count)
983         {
984           for (count = 1;
985                (line_ptr + count) < line_end &&
986                    line_ptr[0] == line_ptr[count] &&
987                    count < 256;
988                count ++);
989
990           comp_ptr[0] = count - 1;
991           comp_ptr[1] = line_ptr[0];
992         }
993
994         line_ptr = CompBuffer;
995         line_end = comp_ptr;
996         break;
997
998     case 2 :
999        /*
1000         * Do TIFF pack-bits encoding...
1001         */
1002
1003         line_ptr = line;
1004         line_end = line + length;
1005         comp_ptr = CompBuffer;
1006
1007         while (line_ptr < line_end)
1008         {
1009           if ((line_ptr + 1) >= line_end)
1010           {
1011            /*
1012             * Single byte on the end...
1013             */
1014
1015             *comp_ptr++ = 0x00;
1016             *comp_ptr++ = *line_ptr++;
1017           }
1018           else if (line_ptr[0] == line_ptr[1])
1019           {
1020            /*
1021             * Repeated sequence...
1022             */
1023
1024             line_ptr ++;
1025             count = 2;
1026
1027             while (line_ptr < (line_end - 1) &&
1028                    line_ptr[0] == line_ptr[1] &&
1029                    count < 127)
1030             {
1031               line_ptr ++;
1032               count ++;
1033             }
1034
1035             *comp_ptr++ = 257 - count;
1036             *comp_ptr++ = *line_ptr++;
1037           }
1038           else
1039           {
1040            /*
1041             * Non-repeated sequence...
1042             */
1043
1044             start    = line_ptr;
1045             line_ptr ++;
1046             count    = 1;
1047
1048             while (line_ptr < (line_end - 1) &&
1049                    line_ptr[0] != line_ptr[1] &&
1050                    count < 127)
1051             {
1052               line_ptr ++;
1053               count ++;
1054             }
1055
1056             *comp_ptr++ = count - 1;
1057
1058             memcpy(comp_ptr, start, count);
1059             comp_ptr += count;
1060           }
1061         }
1062
1063         line_ptr = CompBuffer;
1064         line_end = comp_ptr;
1065         break;
1066
1067     case 3 :
1068        /*
1069         * Do delta-row compression...
1070         */
1071
1072         line_ptr = line;
1073         line_end = line + length;
1074
1075         comp_ptr = CompBuffer;
1076         seed     = SeedBuffer + plane * length;
1077
1078         while (line_ptr < line_end)
1079         {
1080          /*
1081           * Find the next non-matching sequence...
1082           */
1083
1084           start = line_ptr;
1085
1086           if (SeedInvalid)
1087           {
1088            /*
1089             * The seed buffer is invalid, so do the next 8 bytes, max...
1090             */
1091
1092             offset = 0;
1093
1094             if ((count = line_end - line_ptr) > 8)
1095               count = 8;
1096
1097             line_ptr += count;
1098           }
1099           else
1100           {
1101            /*
1102             * The seed buffer is valid, so compare against it...
1103             */
1104
1105             while (*line_ptr == *seed &&
1106                    line_ptr < line_end)
1107             {
1108               line_ptr ++;
1109               seed ++;
1110             }
1111
1112             if (line_ptr == line_end)
1113               break;
1114
1115             offset = line_ptr - start;
1116
1117            /*
1118             * Find up to 8 non-matching bytes...
1119             */
1120
1121             start = line_ptr;
1122             count = 0;
1123             while (*line_ptr != *seed &&
1124                    line_ptr < line_end &&
1125                    count < 8)
1126             {
1127               line_ptr ++;
1128               seed ++;
1129               count ++;
1130             }
1131           }
1132
1133          /*
1134           * Place mode 3 compression data in the buffer; see HP manuals
1135           * for details...
1136           */
1137
1138           if (offset >= 31)
1139           {
1140            /*
1141             * Output multi-byte offset...
1142             */
1143
1144             *comp_ptr++ = ((count - 1) << 5) | 31;
1145
1146             offset -= 31;
1147             while (offset >= 255)
1148             {
1149               *comp_ptr++ = 255;
1150               offset    -= 255;
1151             }
1152
1153             *comp_ptr++ = offset;
1154           }
1155           else
1156           {
1157            /*
1158             * Output single-byte offset...
1159             */
1160
1161             *comp_ptr++ = ((count - 1) << 5) | offset;
1162           }
1163
1164           memcpy(comp_ptr, start, count);
1165           comp_ptr += count;
1166         }
1167
1168         line_ptr = CompBuffer;
1169         line_end = comp_ptr;
1170
1171         memcpy(SeedBuffer + plane * length, line, length);
1172         break;
1173
1174     case 10 :
1175        /*
1176         * Mode 10 "near lossless" RGB compression...
1177         */
1178
1179         line_ptr = line;
1180         line_end = line + length;
1181
1182         comp_ptr = CompBuffer;
1183         seed     = SeedBuffer;
1184
1185         if (PrinterPlanes == 1)
1186         {
1187          /*
1188           * Do grayscale compression to RGB...
1189           */
1190
1191           while (line_ptr < line_end)
1192           {
1193            /*
1194             * Find the next non-matching sequence...
1195             */
1196
1197             start = line_ptr;
1198             while (line_ptr < line_end &&
1199                    *line_ptr == *seed)
1200             {
1201               line_ptr ++;
1202               seed ++;
1203             }
1204
1205             if (line_ptr == line_end)
1206               break;
1207
1208             offset = line_ptr - start;
1209
1210            /*
1211             * Find non-matching grayscale pixels...
1212             */
1213
1214             start = line_ptr;
1215             while (line_ptr < line_end &&
1216                    *line_ptr != *seed)
1217             {
1218               line_ptr ++;
1219               seed ++;
1220             }
1221
1222             count = line_ptr - start;
1223
1224 #if 0
1225             fprintf(stderr, "DEBUG: offset=%d, count=%d, comp_ptr=%p(%d of %d)...\n",
1226                     offset, count, comp_ptr, comp_ptr - CompBuffer,
1227                     BytesPerLine * 5);
1228 #endif /* 0 */
1229
1230            /*
1231             * Place mode 10 compression data in the buffer; each sequence
1232             * starts with a command byte that looks like:
1233             *
1234             *     CMD SRC SRC OFF OFF CNT CNT CNT
1235             *
1236             * For the purpose of this driver, CMD and SRC are always 0.
1237             *
1238             * If the offset >= 3 then additional offset bytes follow the
1239             * first command byte, each byte == 255 until the last one.
1240             *
1241             * If the count >= 7, then additional count bytes follow each
1242             * group of pixels, each byte == 255 until the last one.
1243             *
1244             * The offset and count are in RGB tuples (not bytes, as for
1245             * Mode 3 and 9)...
1246             */
1247
1248             if (offset >= 3)
1249             {
1250              /*
1251               * Output multi-byte offset...
1252               */
1253
1254               if (count > 7)
1255                 *comp_ptr++ = 0x1f;
1256               else
1257                 *comp_ptr++ = 0x18 | (count - 1);
1258
1259               offset -= 3;
1260               while (offset >= 255)
1261               {
1262                 *comp_ptr++ = 255;
1263                 offset      -= 255;
1264               }
1265
1266               *comp_ptr++ = offset;
1267             }
1268             else
1269             {
1270              /*
1271               * Output single-byte offset...
1272               */
1273
1274               if (count > 7)
1275                 *comp_ptr++ = (offset << 3) | 0x07;
1276               else
1277                 *comp_ptr++ = (offset << 3) | (count - 1);
1278             }
1279
1280             temp = count - 8;
1281             seed -= count;
1282
1283             while (count > 0)
1284             {
1285               if (count <= temp)
1286               {
1287                /*
1288                 * This is exceedingly lame...  The replacement counts
1289                 * are intermingled with the data...
1290                 */
1291
1292                 if (temp >= 255)
1293                   *comp_ptr++ = 255;
1294                 else
1295                   *comp_ptr++ = temp;
1296
1297                 temp -= 255;
1298               }
1299
1300              /*
1301               * Get difference between current and see pixels...
1302               */
1303
1304               r = *start - *seed;
1305               g = r;
1306               b = ((*start & 0xfe) - (*seed & 0xfe)) / 2;
1307
1308               if (r < -16 || r > 15 || g < -16 || g > 15 || b < -16 || b > 15)
1309               {
1310                /*
1311                 * Pack 24-bit RGB into 23 bits...  Lame...
1312                 */
1313
1314                 g = *start;
1315
1316                 *comp_ptr++ = g >> 1;
1317
1318                 if (g & 1)
1319                   *comp_ptr++ = 0x80 | (g >> 1);
1320                 else
1321                   *comp_ptr++ = g >> 1;
1322
1323                 if (g & 1)
1324                   *comp_ptr++ = 0x80 | (g >> 1);
1325                 else
1326                   *comp_ptr++ = g >> 1;
1327               }
1328               else
1329               {
1330                /*
1331                 * Pack 15-bit RGB difference...
1332                 */
1333
1334                 *comp_ptr++ = 0x80 | ((r << 2) & 0x7c) | ((g >> 3) & 0x03);
1335                 *comp_ptr++ = ((g << 5) & 0xe0) | (b & 0x1f);
1336               }
1337
1338               count --;
1339               start ++;
1340               seed ++;
1341             }
1342
1343            /*
1344             * Make sure we have the ending count if the replacement count
1345             * was exactly 8 + 255n...
1346             */
1347
1348             if (temp == 0)
1349               *comp_ptr++ = 0;
1350           }
1351         }
1352         else
1353         {
1354          /*
1355           * Do RGB compression...
1356           */
1357
1358           while (line_ptr < line_end)
1359           {
1360            /*
1361             * Find the next non-matching sequence...
1362             */
1363
1364             start = line_ptr;
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)
1369             {
1370               line_ptr += 3;
1371               seed += 3;
1372             }
1373
1374             if (line_ptr == line_end)
1375               break;
1376
1377             offset = (line_ptr - start) / 3;
1378
1379            /*
1380             * Find non-matching RGB tuples...
1381             */
1382
1383             start = line_ptr;
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)
1388             {
1389               line_ptr += 3;
1390               seed += 3;
1391             }
1392
1393             count = (line_ptr - start) / 3;
1394
1395            /*
1396             * Place mode 10 compression data in the buffer; each sequence
1397             * starts with a command byte that looks like:
1398             *
1399             *     CMD SRC SRC OFF OFF CNT CNT CNT
1400             *
1401             * For the purpose of this driver, CMD and SRC are always 0.
1402             *
1403             * If the offset >= 3 then additional offset bytes follow the
1404             * first command byte, each byte == 255 until the last one.
1405             *
1406             * If the count >= 7, then additional count bytes follow each
1407             * group of pixels, each byte == 255 until the last one.
1408             *
1409             * The offset and count are in RGB tuples (not bytes, as for
1410             * Mode 3 and 9)...
1411             */
1412
1413             if (offset >= 3)
1414             {
1415              /*
1416               * Output multi-byte offset...
1417               */
1418
1419               if (count > 7)
1420                 *comp_ptr++ = 0x1f;
1421               else
1422                 *comp_ptr++ = 0x18 | (count - 1);
1423
1424               offset -= 3;
1425               while (offset >= 255)
1426               {
1427                 *comp_ptr++ = 255;
1428                 offset      -= 255;
1429               }
1430
1431               *comp_ptr++ = offset;
1432             }
1433             else
1434             {
1435              /*
1436               * Output single-byte offset...
1437               */
1438
1439               if (count > 7)
1440                 *comp_ptr++ = (offset << 3) | 0x07;
1441               else
1442                 *comp_ptr++ = (offset << 3) | (count - 1);
1443             }
1444
1445             temp = count - 8;
1446             seed -= count * 3;
1447
1448             while (count > 0)
1449             {
1450               if (count <= temp)
1451               {
1452                /*
1453                 * This is exceedingly lame...  The replacement counts
1454                 * are intermingled with the data...
1455                 */
1456
1457                 if (temp >= 255)
1458                   *comp_ptr++ = 255;
1459                 else
1460                   *comp_ptr++ = temp;
1461
1462                 temp -= 255;
1463               }
1464
1465              /*
1466               * Get difference between current and see pixels...
1467               */
1468
1469               r = start[0] - seed[0];
1470               g = start[1] - seed[1];
1471               b = ((start[2] & 0xfe) - (seed[2] & 0xfe)) / 2;
1472
1473               if (r < -16 || r > 15 || g < -16 || g > 15 || b < -16 || b > 15)
1474               {
1475                /*
1476                 * Pack 24-bit RGB into 23 bits...  Lame...
1477                 */
1478
1479                 *comp_ptr++ = start[0] >> 1;
1480
1481                 if (start[0] & 1)
1482                   *comp_ptr++ = 0x80 | (start[1] >> 1);
1483                 else
1484                   *comp_ptr++ = start[1] >> 1;
1485
1486                 if (start[1] & 1)
1487                   *comp_ptr++ = 0x80 | (start[2] >> 1);
1488                 else
1489                   *comp_ptr++ = start[2] >> 1;
1490               }
1491               else
1492               {
1493                /*
1494                 * Pack 15-bit RGB difference...
1495                 */
1496
1497                 *comp_ptr++ = 0x80 | ((r << 2) & 0x7c) | ((g >> 3) & 0x03);
1498                 *comp_ptr++ = ((g << 5) & 0xe0) | (b & 0x1f);
1499               }
1500
1501               count --;
1502               start += 3;
1503               seed += 3;
1504             }
1505
1506            /*
1507             * Make sure we have the ending count if the replacement count
1508             * was exactly 8 + 255n...
1509             */
1510
1511             if (temp == 0)
1512               *comp_ptr++ = 0;
1513           }
1514         }
1515
1516         line_ptr = CompBuffer;
1517         line_end = comp_ptr;
1518
1519         memcpy(SeedBuffer, line, length);
1520         break;
1521   }
1522
1523  /*
1524   * Set the length of the data and write a raster plane...
1525   */
1526
1527   printf("\033*b%d%c", (int)(line_end - line_ptr), pend);
1528   cupsWritePrintData(line_ptr, line_end - line_ptr);
1529 }
1530
1531
1532 /*
1533  * 'OutputLine()' - Output the specified number of lines of graphics.
1534  */
1535
1536 void
1537 OutputLine(ppd_file_t         *ppd,     /* I - PPD file */
1538            cups_page_header2_t *header) /* I - Page header */
1539 {
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 */
1547
1548
1549  /*
1550   * Output whitespace as needed...
1551   */
1552
1553   if (OutputFeed > 0)
1554   {
1555     if (header->cupsCompression < 3)
1556     {
1557      /*
1558       * Send blank raster lines...
1559       */
1560
1561       while (OutputFeed > 0)
1562       {
1563         printf("\033*b0W");
1564         OutputFeed --;
1565       }
1566     }
1567     else
1568     {
1569      /*
1570       * Send Y offset command and invalidate the seed buffer...
1571       */
1572
1573       printf("\033*b%dY", OutputFeed);
1574       OutputFeed  = 0;
1575       SeedInvalid = 1;
1576     }
1577   }
1578
1579  /*
1580   * Write bitmap data as needed...
1581   */
1582
1583   switch (OutputMode)
1584   {
1585     case OUTPUT_BITMAP :                /* Send 1-bit bitmap data... */
1586         order = ColorOrders[PrinterPlanes - 1];
1587         bytes = header->cupsBytesPerLine / PrinterPlanes;
1588
1589         for (i = 0; i < PrinterPlanes; i ++)
1590         {
1591           plane = order[i];
1592
1593           CompressData(PixelBuffer + i * bytes, bytes, plane,
1594                        (i < (PrinterPlanes - 1)) ? 'V' : 'W',
1595                        header->cupsCompression);
1596         }
1597         break;
1598
1599     case OUTPUT_INVERBIT :              /* Send inverted 1-bit bitmap data... */
1600         order = ColorOrders[PrinterPlanes - 1];
1601         bytes = header->cupsBytesPerLine / PrinterPlanes;
1602
1603         for (i = header->cupsBytesPerLine, ptr = PixelBuffer;
1604              i > 0;
1605              i --, ptr ++)
1606           *ptr = ~*ptr;
1607
1608         for (i = 0; i < PrinterPlanes; i ++)
1609         {
1610           plane = order[i];
1611
1612           CompressData(PixelBuffer + i * bytes, bytes, plane,
1613                        (i < (PrinterPlanes - 1)) ? 'V' : 'W',
1614                        header->cupsCompression);
1615         }
1616         break;
1617
1618     case OUTPUT_RGB :                   /* Send 24-bit RGB data... */
1619         if (PrinterPlanes == 1 && !BlankValue)
1620         {
1621          /*
1622           * Invert black to grayscale...
1623           */
1624
1625           for (i = header->cupsBytesPerLine, ptr = PixelBuffer;
1626                i > 0;
1627                i --, ptr ++)
1628             *ptr = ~*ptr;
1629         }
1630
1631        /*
1632         * Compress the output...
1633         */
1634
1635         CompressData(PixelBuffer, header->cupsBytesPerLine, 0, 'W',
1636                      header->cupsCompression);
1637         break;
1638
1639     default :
1640         order = ColorOrders[PrinterPlanes - 1];
1641         width = header->cupsWidth;
1642
1643         for (i = 0, j = 0; i < PrinterPlanes; i ++)
1644         {
1645           plane = order[i];
1646           bytes = DotBufferSizes[plane] / DotBits[plane];
1647
1648           for (bit = 1, ptr = DotBuffers[plane];
1649                bit <= DotBits[plane];
1650                bit <<= 1, ptr += bytes, j ++)
1651           {
1652             cupsPackHorizontalBit(OutputBuffers[plane], DotBuffers[plane],
1653                                   width, 0, bit);
1654             CompressData(ptr, bytes, j,
1655                          i == (PrinterPlanes - 1) &&
1656                              bit == DotBits[plane] ? 'W' : 'V',
1657                          header->cupsCompression);
1658           }
1659         }
1660         break;
1661   }
1662
1663  /*
1664   * The seed buffer, if any, now should contain valid data...
1665   */
1666
1667   SeedInvalid = 0;
1668 }
1669
1670
1671 /*
1672  * 'ReadLine()' - Read graphics from the page stream.
1673  */
1674
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 */
1678 {
1679   int   plane,                          /* Current color plane */
1680         width;                          /* Width of line */
1681
1682
1683  /*
1684   * Read raster data...
1685   */
1686
1687   cupsRasterReadPixels(ras, PixelBuffer, header->cupsBytesPerLine);
1688
1689  /*
1690   * See if it is blank; if so, return right away...
1691   */
1692
1693   if (cupsCheckValue(PixelBuffer, header->cupsBytesPerLine, BlankValue))
1694     return (0);
1695
1696  /*
1697   * If we aren't dithering, return immediately...
1698   */
1699
1700   if (OutputMode != OUTPUT_DITHERED)
1701     return (1);
1702
1703  /*
1704   * Perform the color separation...
1705   */
1706
1707   width = header->cupsWidth;
1708
1709   switch (header->cupsColorSpace)
1710   {
1711     case CUPS_CSPACE_W :
1712         if (RGB)
1713         {
1714           cupsRGBDoGray(RGB, PixelBuffer, CMYKBuffer, width);
1715
1716           if (RGB->num_channels == 1)
1717             cupsCMYKDoBlack(CMYK, CMYKBuffer, InputBuffer, width);
1718           else
1719             cupsCMYKDoCMYK(CMYK, CMYKBuffer, InputBuffer, width);
1720         }
1721         else
1722           cupsCMYKDoGray(CMYK, PixelBuffer, InputBuffer, width);
1723         break;
1724
1725     case CUPS_CSPACE_K :
1726         cupsCMYKDoBlack(CMYK, PixelBuffer, InputBuffer, width);
1727         break;
1728
1729     default :
1730     case CUPS_CSPACE_RGB :
1731         if (RGB)
1732         {
1733           cupsRGBDoRGB(RGB, PixelBuffer, CMYKBuffer, width);
1734
1735           if (RGB->num_channels == 1)
1736             cupsCMYKDoBlack(CMYK, CMYKBuffer, InputBuffer, width);
1737           else
1738             cupsCMYKDoCMYK(CMYK, CMYKBuffer, InputBuffer, width);
1739         }
1740         else
1741           cupsCMYKDoRGB(CMYK, PixelBuffer, InputBuffer, width);
1742         break;
1743
1744     case CUPS_CSPACE_CMYK :
1745         cupsCMYKDoCMYK(CMYK, PixelBuffer, InputBuffer, width);
1746         break;
1747   }
1748
1749  /*
1750   * Dither the pixels...
1751   */
1752
1753   for (plane = 0; plane < PrinterPlanes; plane ++)
1754     cupsDitherLine(DitherStates[plane], DitherLuts[plane], InputBuffer + plane,
1755                    PrinterPlanes, OutputBuffers[plane]);
1756
1757  /*
1758   * Return 1 to indicate that we have non-blank output...
1759   */
1760
1761   return (1);
1762 }
1763
1764
1765 /*
1766  * 'main()' - Main entry and processing of driver.
1767  */
1768
1769 int                                     /* O - Exit status */
1770 main(int  argc,                         /* I - Number of command-line arguments */
1771      char *argv[])                      /* I - Command-line arguments */
1772 {
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 */
1784
1785
1786  /*
1787   * Make sure status messages are not buffered...
1788   */
1789
1790   setbuf(stderr, NULL);
1791
1792  /*
1793   * Check command-line...
1794   */
1795
1796   if (argc < 6 || argc > 7)
1797   {
1798     fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
1799             "rastertopclx");
1800     return (1);
1801   }
1802
1803   num_options = cupsParseOptions(argv[5], 0, &options);
1804
1805  /*
1806   * Open the PPD file...
1807   */
1808
1809   ppd = ppdOpenFile(getenv("PPD"));
1810
1811   if (!ppd)
1812   {
1813     ppd_status_t        status;         /* PPD error */
1814     int                 linenum;        /* Line number */
1815
1816     fputs("ERROR: The PPD file could not be opened.\n", stderr);
1817
1818     status = ppdLastError(&linenum);
1819
1820     fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum);
1821
1822     return (1);
1823   }
1824
1825   ppdMarkDefaults(ppd);
1826   cupsMarkOptions(ppd, num_options, options);
1827
1828  /*
1829   * Open the page stream...
1830   */
1831
1832   if (argc == 7)
1833   {
1834     if ((fd = open(argv[6], O_RDONLY)) == -1)
1835     {
1836       perror("ERROR: Unable to open raster file");
1837       return (1);
1838     }
1839   }
1840   else
1841     fd = 0;
1842
1843   ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
1844
1845  /*
1846   * Register a signal handler to eject the current page if the
1847   * job is cancelled.
1848   */
1849
1850   Canceled = 0;
1851
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));
1856
1857   sigemptyset(&action.sa_mask);
1858   action.sa_handler = CancelJob;
1859   sigaction(SIGTERM, &action, NULL);
1860 #else
1861   signal(SIGTERM, CancelJob);
1862 #endif /* HAVE_SIGSET */
1863
1864  /*
1865   * Process pages as needed...
1866   */
1867
1868   job_id = atoi(argv[1]);
1869
1870   Page = 0;
1871
1872   while (cupsRasterReadHeader2(ras, &header))
1873   {
1874    /*
1875     * Write a status message with the page number and number of copies.
1876     */
1877
1878     if (Canceled)
1879       break;
1880
1881     Page ++;
1882
1883     fprintf(stderr, "PAGE: %d %d\n", Page, header.NumCopies);
1884     fprintf(stderr, "INFO: Starting page %d.\n", Page);
1885
1886     StartPage(ppd, &header, atoi(argv[1]), argv[2], argv[3],
1887               num_options, options);
1888
1889     for (y = 0; y < (int)header.cupsHeight; y ++)
1890     {
1891      /*
1892       * Let the user know how far we have progressed...
1893       */
1894
1895       if (Canceled)
1896         break;
1897
1898       if ((y & 127) == 0)
1899       {
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);
1904       }
1905
1906      /*
1907       * Read and write a line of graphics or whitespace...
1908       */
1909
1910       if (ReadLine(ras, &header))
1911         OutputLine(ppd, &header);
1912       else
1913         OutputFeed ++;
1914     }
1915
1916    /*
1917     * Eject the page...
1918     */
1919
1920     fprintf(stderr, "INFO: Finished page %d.\n", Page);
1921
1922     EndPage(ppd, &header);
1923
1924     if (Canceled)
1925       break;
1926   }
1927
1928   Shutdown(ppd, job_id, argv[2], argv[3], num_options, options);
1929
1930   cupsFreeOptions(num_options, options);
1931
1932   cupsRasterClose(ras);
1933
1934   if (fd != 0)
1935     close(fd);
1936
1937   return (Page == 0);
1938 }
1939
1940
1941 /*
1942  * End of "$Id$".
1943  */