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