1 #include "mupdf/fitz.h"
2 
3 #include <limits.h>
4 #include <stdlib.h>
5 #include <string.h>
6 
7 /* Lifted from ghostscript gdevjlm.h */
8 /*
9  * The notion that there is such a thing as a "PCL printer" is a fiction: no
10  * two "PCL" printers, even at the same PCL level, have identical command
11  * sets. (The H-P documentation isn't fully accurate either; for example,
12  * it doesn't reveal that the DeskJet printers implement anything beyond PCL
13  * 3.)
14  *
15  * This file contains feature definitions for a generic monochrome PCL
16  * driver (gdevdljm.c), and the specific feature values for all such
17  * printers that Ghostscript currently supports.
18  */
19 
20 /* Printer spacing capabilities. Include at most one of these. */
21 #define PCL_NO_SPACING	0	/* no vertical spacing capability, must be 0 */
22 #define PCL3_SPACING	1	/* <ESC>*p+<n>Y (PCL 3) */
23 #define PCL4_SPACING	2	/* <ESC>*b<n>Y (PCL 4) */
24 #define PCL5_SPACING	4	/* <ESC>*b<n>Y and clear seed row (PCL 5) */
25 /* The following is only used internally. */
26 #define PCL_ANY_SPACING \
27 	(PCL3_SPACING | PCL4_SPACING | PCL5_SPACING)
28 
29 /* Individual printer properties. Any subset of these may be included. */
30 #define PCL_MODE_2_COMPRESSION		8	/* compression mode 2 supported */
31 						/* (PCL 4) */
32 #define PCL_MODE_3_COMPRESSION		16	/* compression modes 2 & 3 supported */
33 						/* (PCL 5) */
34 #define PCL_END_GRAPHICS_DOES_RESET	32	/* <esc>*rB resets all parameters */
35 #define PCL_HAS_DUPLEX			64	/* <esc>&l<duplex>S supported */
36 #define PCL_CAN_SET_PAPER_SIZE		128	/* <esc>&l<sizecode>A supported */
37 #define PCL_CAN_PRINT_COPIES		256	/* <esc>&l<copies>X supported */
38 #define HACK__IS_A_LJET4PJL		512
39 #define HACK__IS_A_OCE9050		1024
40 #define PCL_HAS_ORIENTATION             2048
41 #define PCL_CAN_SET_CUSTOM_PAPER_SIZE   4096
42 #define PCL_HAS_RICOH_PAPER_SIZES       8192
43 
44 /* Shorthands for the most common spacing/compression combinations. */
45 #define PCL_MODE0 PCL3_SPACING
46 #define PCL_MODE0NS PCL_NO_SPACING
47 #define PCL_MODE2 (PCL4_SPACING | PCL_MODE_2_COMPRESSION)
48 #define PCL_MODE2P (PCL_NO_SPACING | PCL_MODE_2_COMPRESSION)
49 #define PCL_MODE3 (PCL5_SPACING | PCL_MODE_3_COMPRESSION)
50 #define PCL_MODE3NS (PCL_NO_SPACING | PCL_MODE_3_COMPRESSION)
51 
52 #define MIN_SKIP_LINES 7
53 static const char *const from2to3 = "\033*b3M";
54 static const char *const from3to2 = "\033*b2M";
55 static const int penalty_from2to3 = 5; /* strlen(from2to3); */
56 static const int penalty_from3to2 = 5; /* strlen(from3to2); */
57 
58 /* Generic */
59 static const fz_pcl_options fz_pcl_options_generic =
60 {
61 	(PCL_MODE2 | PCL_END_GRAPHICS_DOES_RESET | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_SET_CUSTOM_PAPER_SIZE),
62 	"\033&k1W\033*b2M",
63 	"\033&k1W\033*b2M"
64 };
65 
66 /* H-P DeskJet */
67 static const fz_pcl_options fz_pcl_options_ljet4 =
68 {
69 	(PCL_MODE2 | PCL_END_GRAPHICS_DOES_RESET | PCL_CAN_SET_PAPER_SIZE),
70 	"\033&k1W\033*b2M",
71 	"\033&k1W\033*b2M"
72 };
73 
74 /* H-P DeskJet 500 */
75 static const fz_pcl_options fz_pcl_options_dj500 =
76 {
77 	(PCL_MODE3 | PCL_END_GRAPHICS_DOES_RESET | PCL_CAN_SET_PAPER_SIZE),
78 	"\033&k1W",
79 	"\033&k1W"
80 };
81 
82 /* Kyocera FS-600 */
83 static const fz_pcl_options fz_pcl_options_fs600 =
84 {
85 	(PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES),
86 	"\033*r0F\033&u%dD",
87 	"\033*r0F\033&u%dD"
88 };
89 
90 /* H-P original LaserJet */
91 /* H-P LaserJet Plus */
92 static const fz_pcl_options fz_pcl_options_lj =
93 {
94 	(PCL_MODE0),
95 	"\033*b0M",
96 	"\033*b0M"
97 };
98 
99 /* H-P LaserJet IIp, IId */
100 static const fz_pcl_options fz_pcl_options_lj2 =
101 {
102 	(PCL_MODE2P | PCL_CAN_SET_PAPER_SIZE),
103 	"\033*r0F\033*b2M",
104 	"\033*r0F\033*b2M"
105 };
106 
107 /* H-P LaserJet III* */
108 static const fz_pcl_options fz_pcl_options_lj3 =
109 {
110 	(PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES),
111 	"\033&l-180u36Z\033*r0F",
112 	"\033&l-180u36Z\033*r0F"
113 };
114 
115 /* H-P LaserJet IIId */
116 static const fz_pcl_options fz_pcl_options_lj3d =
117 {
118 	(PCL_MODE3 | PCL_HAS_DUPLEX | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES),
119 	"\033&l-180u36Z\033*r0F",
120 	"\033&l180u36Z\033*r0F"
121 };
122 
123 /* H-P LaserJet 4 */
124 static const fz_pcl_options fz_pcl_options_lj4 =
125 {
126 	(PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES),
127 	"\033&l-180u36Z\033*r0F\033&u%dD",
128 	"\033&l-180u36Z\033*r0F\033&u%dD"
129 };
130 
131 /* H-P LaserJet 4 PL */
132 static const fz_pcl_options fz_pcl_options_lj4pl =
133 {
134 	(PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES | HACK__IS_A_LJET4PJL),
135 	"\033&l-180u36Z\033*r0F\033&u%dD",
136 	"\033&l-180u36Z\033*r0F\033&u%dD"
137 };
138 
139 /* H-P LaserJet 4d */
140 static const fz_pcl_options fz_pcl_options_lj4d =
141 {
142 	(PCL_MODE3 | PCL_HAS_DUPLEX | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES),
143 	"\033&l-180u36Z\033*r0F\033&u%dD",
144 	"\033&l180u36Z\033*r0F\033&u%dD"
145 };
146 
147 /* H-P 2563B line printer */
148 static const fz_pcl_options fz_pcl_options_lp2563b =
149 {
150 	(PCL_MODE0NS | PCL_CAN_SET_PAPER_SIZE),
151 	"\033*b0M",
152 	"\033*b0M"
153 };
154 
155 /* OCE 9050 line printer */
156 static const fz_pcl_options fz_pcl_options_oce9050 =
157 {
158 	(PCL_MODE3NS | PCL_CAN_SET_PAPER_SIZE | HACK__IS_A_OCE9050),
159 	"\033*b0M",
160 	"\033*b0M"
161 };
162 
163 enum {
164 	eLetterPaper = 0,
165 	eLegalPaper,
166 	eA4Paper,
167 	eExecPaper,
168 	eLedgerPaper,
169 	eA3Paper,
170 	eCOM10Envelope,
171 	eMonarchEnvelope,
172 	eC5Envelope,
173 	eDLEnvelope,
174 	eJB4Paper,
175 	eJB5Paper,
176 	eB5Envelope,
177 	eB5Paper,                   /* 2.1 */
178 	eJPostcard,
179 	eJDoublePostcard,
180 	eA5Paper,
181 	eA6Paper,                   /* 2.0 */
182 	eJB6Paper,                  /* 2.0 */
183 	eJIS8K,                     /* 2.1 */
184 	eJIS16K,                    /* 2.1 */
185 	eJISExec,                   /* 2.1 */
186 	eDefaultPaperSize = 96,     /* 2.1 */
187 	eCustomPaperSize = 101,
188 	eB6JIS = 201,               /* non-standard, Ricoh printers */
189 	eC6Envelope = 202,          /* non-standard, Ricoh printers */
190 	e8Kai  = 203,               /* non-standard, Ricoh printers */
191 	e16Kai = 204,               /* non-standard, Ricoh printers */
192 	e12x18 = 205,               /* non-standard, Ricoh printers */
193 	e13x19_2 = 212,             /* non-standard, Ricoh printers */
194 	e13x19 = 213,               /* non-standard, Ricoh printers */
195 	e12_6x19_2 = 214,           /* non-standard, Ricoh printers */
196 	e12_6x18_5 = 215,           /* non-standard, Ricoh printers */
197 	e13x18  = 216,              /* non-standard, Ricoh printers */
198 	eSRA3 = 217,                /* non-standard, Ricoh printers */
199 	eSRA4 = 218,                /* non-standard, Ricoh printers */
200 	e226x310 = 219,             /* non-standard, Ricoh printers */
201 	e310x432 = 220,             /* non-standard, Ricoh printers */
202 	eEngQuatro = 221,           /* non-standard, Ricoh printers */
203 	e11x14 = 222,               /* non-standard, Ricoh printers */
204 	e11x15 = 223,               /* non-standard, Ricoh printers */
205 	e10x14 = 224,               /* non-standard, Ricoh printers */
206 };
207 
copy_opts(fz_pcl_options * dst,const fz_pcl_options * src)208 static void copy_opts(fz_pcl_options *dst, const fz_pcl_options *src)
209 {
210 	if (dst)
211 		*dst = *src;
212 }
213 
214 const char *fz_pcl_write_options_usage =
215 	"PCL output options:\n"
216 	"\tcolorspace=mono: render 1-bit black and white page\n"
217 	"\tcolorspace=rgb: render full color page\n"
218 	"\tpreset=generic|ljet4|dj500|fs600|lj|lj2|lj3|lj3d|lj4|lj4pl|lj4d|lp2563b|oce9050\n"
219 	"\tspacing=0: No vertical spacing capability\n"
220 	"\tspacing=1: PCL 3 spacing (<ESC>*p+<n>Y)\n"
221 	"\tspacing=2: PCL 4 spacing (<ESC>*b<n>Y)\n"
222 	"\tspacing=3: PCL 5 spacing (<ESC>*b<n>Y and clear seed row)\n"
223 	"\tmode2: Enable mode 2 graphics compression\n"
224 	"\tmode3: Enable mode 3 graphics compression\n"
225 	"\teog_reset: End of graphics (<ESC>*rB) resets all parameters\n"
226 	"\thas_duplex: Duplex supported (<ESC>&l<duplex>S)\n"
227 	"\thas_papersize: Papersize setting supported (<ESC>&l<sizecode>A)\n"
228 	"\thas_copies: Number of copies supported (<ESC>&l<copies>X)\n"
229 	"\tis_ljet4pjl: Disable/Enable HP 4PJL model-specific output\n"
230 	"\tis_oce9050: Disable/Enable Oce 9050 model-specific output\n"
231 	"\n";
232 
fz_pcl_preset(fz_context * ctx,fz_pcl_options * opts,const char * preset)233 void fz_pcl_preset(fz_context *ctx, fz_pcl_options *opts, const char *preset)
234 {
235 	if (preset == NULL || *preset == 0 || !strcmp(preset, "generic"))
236 		copy_opts(opts, &fz_pcl_options_generic);
237 	else if (!strcmp(preset, "ljet4"))
238 		copy_opts(opts, &fz_pcl_options_ljet4);
239 	else if (!strcmp(preset, "dj500"))
240 		copy_opts(opts, &fz_pcl_options_dj500);
241 	else if (!strcmp(preset, "fs600"))
242 		copy_opts(opts, &fz_pcl_options_fs600);
243 	else if (!strcmp(preset, "lj"))
244 		copy_opts(opts, &fz_pcl_options_lj);
245 	else if (!strcmp(preset, "lj2"))
246 		copy_opts(opts, &fz_pcl_options_lj2);
247 	else if (!strcmp(preset, "lj3"))
248 		copy_opts(opts, &fz_pcl_options_lj3);
249 	else if (!strcmp(preset, "lj3d"))
250 		copy_opts(opts, &fz_pcl_options_lj3d);
251 	else if (!strcmp(preset, "lj4"))
252 		copy_opts(opts, &fz_pcl_options_lj4);
253 	else if (!strcmp(preset, "lj4pl"))
254 		copy_opts(opts, &fz_pcl_options_lj4pl);
255 	else if (!strcmp(preset, "lj4d"))
256 		copy_opts(opts, &fz_pcl_options_lj4d);
257 	else if (!strcmp(preset, "lp2563b"))
258 		copy_opts(opts, &fz_pcl_options_lp2563b);
259 	else if (!strcmp(preset, "oce9050"))
260 		copy_opts(opts, &fz_pcl_options_oce9050);
261 	else
262 		fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown preset '%s'", preset);
263 }
264 
265 fz_pcl_options *
fz_parse_pcl_options(fz_context * ctx,fz_pcl_options * opts,const char * args)266 fz_parse_pcl_options(fz_context *ctx, fz_pcl_options *opts, const char *args)
267 {
268 	const char *val;
269 
270 	memset(opts, 0, sizeof *opts);
271 
272 	if (fz_has_option(ctx, args, "preset", &val))
273 		fz_pcl_preset(ctx, opts, val);
274 	else
275 		fz_pcl_preset(ctx, opts, "generic");
276 
277 	if (fz_has_option(ctx, args, "spacing", &val))
278 	{
279 		switch (atoi(val))
280 		{
281 		case 0: opts->features &= ~PCL_ANY_SPACING; break;
282 		case 1: opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL3_SPACING; break;
283 		case 2: opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL4_SPACING; break;
284 		case 3: opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL5_SPACING; break;
285 		default: fz_throw(ctx, FZ_ERROR_GENERIC, "Unsupported PCL spacing %d (0-3 only)", atoi(val));
286 		}
287 	}
288 	if (fz_has_option(ctx, args, "mode2", &val))
289 	{
290 		if (fz_option_eq(val, "no"))
291 			opts->features &= ~PCL_MODE_2_COMPRESSION;
292 		else if (fz_option_eq(val, "yes"))
293 			opts->features |= PCL_MODE_2_COMPRESSION;
294 		else
295 			fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for mode2 value");
296 	}
297 	if (fz_has_option(ctx, args, "mode3", &val))
298 	{
299 		if (fz_option_eq(val, "no"))
300 			opts->features &= ~PCL_MODE_3_COMPRESSION;
301 		else if (fz_option_eq(val, "yes"))
302 			opts->features |= PCL_MODE_3_COMPRESSION;
303 		else
304 			fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for mode3 value");
305 	}
306 	if (fz_has_option(ctx, args, "eog_reset", &val))
307 	{
308 		if (fz_option_eq(val, "no"))
309 			opts->features &= ~PCL_END_GRAPHICS_DOES_RESET;
310 		else if (fz_option_eq(val, "yes"))
311 			opts->features |= PCL_END_GRAPHICS_DOES_RESET;
312 		else
313 			fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for eog_reset value");
314 	}
315 	if (fz_has_option(ctx, args, "has_duplex", &val))
316 	{
317 		if (fz_option_eq(val, "no"))
318 			opts->features &= ~PCL_HAS_DUPLEX;
319 		else if (fz_option_eq(val, "yes"))
320 			opts->features |= PCL_HAS_DUPLEX;
321 		else
322 			fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for has_duplex value");
323 	}
324 	if (fz_has_option(ctx, args, "has_papersize", &val))
325 	{
326 		if (fz_option_eq(val, "no"))
327 			opts->features &= ~PCL_CAN_SET_PAPER_SIZE;
328 		else if (fz_option_eq(val, "yes"))
329 			opts->features |= PCL_CAN_SET_PAPER_SIZE;
330 		else
331 			fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for has_papersize value");
332 	}
333 	if (fz_has_option(ctx, args, "has_copies", &val))
334 	{
335 		if (fz_option_eq(val, "no"))
336 			opts->features &= ~PCL_CAN_PRINT_COPIES;
337 		else if (fz_option_eq(val, "yes"))
338 			opts->features |= PCL_CAN_PRINT_COPIES;
339 		else
340 			fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for has_papersize value");
341 	}
342 	if (fz_has_option(ctx, args, "is_ljet4pjl", &val))
343 	{
344 		if (fz_option_eq(val, "no"))
345 			opts->features &= ~HACK__IS_A_LJET4PJL;
346 		else if (fz_option_eq(val, "yes"))
347 			opts->features |= HACK__IS_A_LJET4PJL;
348 		else
349 			fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for is_ljet4pjl value");
350 	}
351 	if (fz_has_option(ctx, args, "is_oce9050", &val))
352 	{
353 		if (fz_option_eq(val, "no"))
354 			opts->features &= ~HACK__IS_A_OCE9050;
355 		else if (fz_option_eq(val, "yes"))
356 			opts->features |= HACK__IS_A_OCE9050;
357 		else
358 			fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for is_oce9050 value");
359 	}
360 
361 	return opts;
362 }
363 
364 static void
make_init(fz_pcl_options * pcl,char * buf,unsigned long len,const char * str,int res)365 make_init(fz_pcl_options *pcl, char *buf, unsigned long len, const char *str, int res)
366 {
367 	int paper_source = -1;
368 
369 	fz_snprintf(buf, len, str, res);
370 
371 	if (pcl->manual_feed_set && pcl->manual_feed)
372 		paper_source = 2;
373 	else if (pcl->media_position_set && pcl->media_position >= 0)
374 		paper_source = pcl->media_position;
375 	if (paper_source >= 0)
376 	{
377 		char buf2[40];
378 		fz_snprintf(buf2, sizeof(buf2), "\033&l%dH", paper_source);
379 		strncat(buf, buf2, len);
380 	}
381 }
382 
383 static void
pcl_header(fz_context * ctx,fz_output * out,fz_pcl_options * pcl,int num_copies,int xres,int yres,int w,int h)384 pcl_header(fz_context *ctx, fz_output *out, fz_pcl_options *pcl, int num_copies, int xres, int yres, int w, int h)
385 {
386 	char odd_page_init[80];
387 	char even_page_init[80];
388 
389 	make_init(pcl, odd_page_init, sizeof(odd_page_init), pcl->odd_page_init, xres);
390 	make_init(pcl, even_page_init, sizeof(even_page_init), pcl->even_page_init, xres);
391 
392 	if (pcl->page_count == 0)
393 	{
394 		if (pcl->features & HACK__IS_A_LJET4PJL)
395 			fz_write_string(ctx, out, "\033%-12345X@PJL\r\n@PJL ENTER LANGUAGE = PCL\r\n");
396 		fz_write_string(ctx, out, "\033E"); /* reset printer */
397 		/* Reset the margins */
398 		fz_write_string(ctx, out, "\033&10e-180u36Z");
399 		/* If the printer supports it, set orientation */
400 		if (pcl->features & PCL_HAS_ORIENTATION)
401 		{
402 			fz_write_printf(ctx, out, "\033&l%dO", pcl->orientation);
403 		}
404 		/* If the printer supports it, set the paper size */
405 		/* based on the actual requested size. */
406 		if (pcl->features & PCL_CAN_SET_PAPER_SIZE)
407 		{
408 			/* It probably never hurts to define the page explicitly */
409 			{
410 				int decipointw = (w * 720 + (xres>>1)) / xres;
411 				int decipointh = (h * 720 + (yres>>1)) / yres;
412 
413 				fz_write_printf(ctx, out, "\033&f%dI", decipointw);
414 				fz_write_printf(ctx, out, "\033&f%dJ", decipointh);
415 			}
416 			fz_write_printf(ctx, out, "\033&l%dA", pcl->paper_size);
417 		}
418 		/* If printer can duplex, set duplex mode appropriately. */
419 		if (pcl->features & PCL_HAS_DUPLEX)
420 		{
421 			if (pcl->duplex_set)
422 			{
423 				if (pcl->duplex)
424 				{
425 					if (!pcl->tumble)
426 						fz_write_string(ctx, out, "\033&l1S");
427 					else
428 						fz_write_string(ctx, out, "\033&l2S");
429 				}
430 				else
431 					fz_write_string(ctx, out, "\033&l0S");
432 			}
433 			else
434 			{
435 				/* default to duplex for this printer */
436 				fz_write_string(ctx, out, "\033&l1S");
437 			}
438 		}
439 	}
440 
441 	/* Put out per-page initialization. */
442 	/* In duplex mode the sheet is already in process, so there are some
443 	 * commands which must not be sent to the printer for the 2nd page,
444 	 * as these commands will cause the printer to eject the sheet with
445 	 * only the 1st page printed. These commands are:
446 	 * \033&l%dA (setting paper size)
447 	 * \033&l%dH (setting paper tray)
448 	 * in simplex mode we set these parameters for each page,
449 	 * in duplex mode we set these parameters for each odd page
450 	 */
451 
452 	if ((pcl->features & PCL_HAS_DUPLEX) && pcl->duplex_set && pcl->duplex)
453 	{
454 		/* We are printing duplex, so change margins as needed */
455 		if (((pcl->page_count/num_copies)%2) == 0)
456 		{
457 			if (pcl->page_count != 0 && (pcl->features & PCL_CAN_SET_PAPER_SIZE))
458 			{
459 				fz_write_printf(ctx, out, "\033&l%dA", pcl->paper_size);
460 			}
461 			fz_write_string(ctx, out, "\033&l0o0l0E");
462 			fz_write_string(ctx, out, pcl->odd_page_init);
463 		}
464 		else
465 			fz_write_string(ctx, out, pcl->even_page_init);
466 	}
467 	else
468 	{
469 		if (pcl->features & PCL_CAN_SET_PAPER_SIZE)
470 		{
471 			fz_write_printf(ctx, out, "\033&l%dA", pcl->paper_size);
472 		}
473 		fz_write_string(ctx, out, "\033&l0o0l0E");
474 		fz_write_string(ctx, out, pcl->odd_page_init);
475 	}
476 
477 	fz_write_printf(ctx, out, "\033&l%dX", num_copies); /* # of copies */
478 
479 	/* End raster graphics, position cursor at top. */
480 	fz_write_string(ctx, out, "\033*rB\033*p0x0Y");
481 
482 	/* The DeskJet and DeskJet Plus reset everything upon */
483 	/* receiving \033*rB, so we must reinitialize graphics mode. */
484 	if (pcl->features & PCL_END_GRAPHICS_DOES_RESET)
485 	{
486 		fz_write_string(ctx, out, pcl->odd_page_init); /* Assume this does the right thing */
487 		fz_write_printf(ctx, out, "\033&l%dX", num_copies); /* # of copies */
488 	}
489 
490 	/* Set resolution. */
491 	fz_write_printf(ctx, out, "\033*t%dR", xres);
492 
493 	/* Raster units */
494 	/* 96,100,120,144,150,160,180,200,225,240,288,300,360,400,450,480,600,720,800,900,1200,1440,1800,2400,3600,7200 */
495 	/* FIXME: xres vs yres */
496 	fz_write_printf(ctx, out, "\033&u%dD", xres);
497 
498 	pcl->page_count++;
499 }
500 
501 typedef struct pcl_papersize_s
502 {
503 	int code;
504 	const char *text;
505 	int width;
506 	int height;
507 } pcl_papersize;
508 
509 static const pcl_papersize papersizes[] =
510 {
511 	{ eLetterPaper,      "letter",       2550, 3300},
512 	{ eLegalPaper,       "legal",        2550, 4200},
513 	{ eA4Paper,          "a4",           2480, 3507},
514 	{ eExecPaper,        "executive",    2175, 3150},
515 	{ eLedgerPaper,      "ledger",       3300, 5100},
516 	{ eA3Paper,          "a3",           3507, 4960},
517 	{ eCOM10Envelope,    "com10",        1237, 2850},
518 	{ eMonarchEnvelope,  "monarch",      1162, 2250},
519 	{ eC5Envelope,       "c5",           1913, 2704},
520 	{ eDLEnvelope,       "dl",           1299, 2598},
521 	{ eJB4Paper,         "jisb4",        3035, 4299},
522 	{ eJB4Paper,         "jis b4",       3035, 4299},
523 	{ eJB5Paper,         "jisb5",        2150, 3035},
524 	{ eJB5Paper,         "jis b5",       2150, 3035},
525 	{ eB5Envelope,       "b5",           2078, 2952},
526 	{ eB5Paper,          "b5paper",      2150, 3035},
527 	{ eJPostcard,        "jpost",        1181, 1748},
528 	{ eJDoublePostcard,  "jpostd",       2362, 1748},
529 	{ eA5Paper,          "a5",           1748, 2480},
530 	{ eA6Paper,          "a6",           1240, 1748},
531 	{ eJB6Paper,         "jisb6",        1512, 2150},
532 	{ eJIS8K,            "jis8K",        3154, 4606},
533 	{ eJIS16K,           "jis16K",       2303, 3154},
534 	{ eJISExec,          "jisexec",      2551, 3898},
535 	{ eB6JIS,            "B6 (JIS)",     1512, 2150},
536 	{ eC6Envelope,       "C6",           1345, 1912},
537 	{ e8Kai,             "8Kai",         3154, 4608},
538 	{ e16Kai,            "16Kai",        2304, 3154},
539 	{ e12x18,            "12x18",        3600, 5400},
540 	{ e13x19_2,          "13x19.2",      3900, 5758},
541 	{ e13x19,            "13x19",        3900, 5700},
542 	{ e12_6x19_2,        "12.6x19.2",    3779, 5758},
543 	{ e12_6x18_5,        "12.6x18.5",    3779, 5550},
544 	{ e13x18,            "13x18",        3900, 5400},
545 	{ eSRA3,             "SRA3",         3779, 5316},
546 	{ eSRA4,             "SRA4",         2658, 3779},
547 	{ e226x310,          "226x310",      2670, 3662},
548 	{ e310x432,          "310x432",      3662, 5104},
549 	{ eEngQuatro,        "EngQuatro",    2400, 3000},
550 	{ e11x14,            "11x14",        3300, 4200},
551 	{ e11x15,            "11x15",        3300, 4500},
552 	{ e10x14,            "10x14",        3000, 4200}
553 };
554 
555 #define num_elems(X) (sizeof(X)/sizeof(*X))
556 
guess_paper_size(fz_pcl_options * pcl,int w,int h,int xres,int yres)557 static void guess_paper_size(fz_pcl_options *pcl, int w, int h, int xres, int yres)
558 {
559 	int size;
560 	int rotated = 0;
561 
562 	/* If we've been given a paper size, live with it */
563 	if (pcl->paper_size != 0)
564 		return;
565 
566 	w = w * 300 / xres;
567 	h = h * 300 / xres;
568 
569 	/* Look for an exact match */
570 	for (size = 0; size < (int)num_elems(papersizes); size++)
571 	{
572 		if (papersizes[size].code > eCustomPaperSize && (pcl->features & PCL_HAS_RICOH_PAPER_SIZES) == 0)
573 			continue;
574 		if (w == papersizes[size].width && h == papersizes[size].height)
575 			break;
576 		if ((pcl->features & PCL_HAS_ORIENTATION) && w == papersizes[size].height && h == papersizes[size].width)
577 		{
578 			rotated = 1;
579 			break;
580 		}
581 	}
582 
583 	/* If we didn't find an exact match, find the smallest one that's
584 	 * larger. Consider orientation if our printer supports it. */
585 	if (size == num_elems(papersizes))
586 	{
587 		if ((pcl->features & PCL_CAN_SET_CUSTOM_PAPER_SIZE) != 0)
588 		{
589 			/* Send it as a custom size */
590 			size = eCustomPaperSize;
591 		}
592 		else
593 		{
594 			/* Send the next larger one (minimise waste) */
595 			int i;
596 			int best_waste = INT_MAX;
597 			for (i = 0; i < (int)num_elems(papersizes); i++)
598 			{
599 				int waste;
600 				if (papersizes[i].code > eCustomPaperSize && (pcl->features & PCL_HAS_RICOH_PAPER_SIZES) == 0)
601 					continue;
602 				waste = papersizes[i].width * papersizes[i].height - w * h;
603 				if (waste > best_waste)
604 					continue;
605 				if (w <= papersizes[i].width && h <= papersizes[i].height)
606 				{
607 					best_waste = waste;
608 					rotated = 0;
609 					size = i;
610 				}
611 				if ((pcl->features & PCL_HAS_ORIENTATION) && w <= papersizes[i].height && h <= papersizes[i].width)
612 				{
613 					best_waste = waste;
614 					rotated = 1;
615 					size = i;
616 				}
617 			}
618 		}
619 	}
620 
621 	/* Now, size = The best size we have (or num_elems(papersizes)) if it's too big */
622 
623 	if (size < (int)num_elems(papersizes))
624 		pcl->paper_size = papersizes[size].code;
625 	else
626 		pcl->paper_size = eCustomPaperSize; /* Custom */
627 
628 	pcl->orientation = rotated;
629 }
630 
631 /* Copy a line, returning true if the line was blank. */
632 static int
line_is_blank(unsigned char * dst,const unsigned char * sp,int w)633 line_is_blank(unsigned char *dst, const unsigned char *sp, int w)
634 {
635 	int zero = 0;
636 
637 	while (w-- > 0)
638 	{
639 		zero |= (*dst++ = *sp++);
640 		zero |= (*dst++ = *sp++);
641 		zero |= (*dst++ = *sp++);
642 	}
643 
644 	return zero == 0;
645 }
646 
647 static int
delta_compression(unsigned char * curr,unsigned char * prev,unsigned char * comp,int ds,int space)648 delta_compression(unsigned char *curr, unsigned char *prev, unsigned char *comp, int ds, int space)
649 {
650 	int left = space;
651 	int x = ds;
652 
653 	while (x > 0)
654 	{
655 		/* Count matching bytes */
656 		int match = 0;
657 		int diff = 0;
658 		while (x > 0 && *curr == *prev)
659 		{
660 			curr++;
661 			prev++;
662 			match++;
663 			x--;
664 		}
665 
666 		/* Count different bytes */
667 		while (x > 0 && *curr != *prev)
668 		{
669 			curr++;
670 			prev++;
671 			diff++;
672 			x--;
673 		}
674 
675 		while (diff > 0)
676 		{
677 			int exts;
678 			int mini_diff = diff;
679 			if (mini_diff > 8)
680 				mini_diff = 8;
681 
682 			exts = (match+255-31)/255;
683 			left -= 1 + mini_diff + exts;
684 			if (left < 0)
685 				return 0;
686 			*comp++ = ((mini_diff-1)<<5) | (match < 31 ? match : 31);
687 			if (exts > 0)
688 			{
689 				match -= 31;
690 				while (--exts)
691 				{
692 					*comp++ = 255;
693 					match -= 255;
694 				}
695 				*comp++ = match;
696 			}
697 			memcpy(comp, curr-diff, mini_diff);
698 			comp += mini_diff;
699 
700 			match = 0;
701 			diff -= mini_diff;
702 		}
703 	}
704 	return space - left;
705 }
706 
707 void
fz_write_pixmap_as_pcl(fz_context * ctx,fz_output * out,const fz_pixmap * pixmap,const fz_pcl_options * pcl)708 fz_write_pixmap_as_pcl(fz_context *ctx, fz_output *out, const fz_pixmap *pixmap, const fz_pcl_options *pcl)
709 {
710 	fz_band_writer *writer;
711 
712 	if (!pixmap || !out)
713 		return;
714 
715 	writer = fz_new_color_pcl_band_writer(ctx, out, pcl);
716 	fz_try(ctx)
717 	{
718 		fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace, pixmap->seps);
719 		fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
720 	}
721 	fz_always(ctx)
722 		fz_drop_band_writer(ctx, writer);
723 	fz_catch(ctx)
724 		fz_rethrow(ctx);
725 }
726 
727 typedef struct color_pcl_band_writer_s
728 {
729 	fz_band_writer super;
730 	fz_pcl_options options;
731 	unsigned char *linebuf;
732 	unsigned char compbuf[32768];
733 	unsigned char compbuf2[32768];
734 } color_pcl_band_writer;
735 
736 static void
color_pcl_write_header(fz_context * ctx,fz_band_writer * writer_,fz_colorspace * cs)737 color_pcl_write_header(fz_context *ctx, fz_band_writer *writer_, fz_colorspace *cs)
738 {
739 	color_pcl_band_writer *writer = (color_pcl_band_writer *)writer_;
740 	fz_output *out = writer->super.out;
741 	int w = writer->super.w;
742 	int h = writer->super.h;
743 	int n = writer->super.n;
744 	int s = writer->super.s;
745 	int a = writer->super.alpha;
746 	int xres = writer->super.xres;
747 	int yres = writer->super.yres;
748 
749 	if (a != 0)
750 		fz_throw(ctx, FZ_ERROR_GENERIC, "color PCL cannot write alpha channel");
751 	if (s != 0)
752 		fz_throw(ctx, FZ_ERROR_GENERIC, "color PCL cannot write spot colors");
753 	if (n != 3)
754 		fz_throw(ctx, FZ_ERROR_GENERIC, "color PCL must be RGB");
755 
756 	writer->linebuf = Memento_label(fz_malloc(ctx, w * 3 * 2), "color_pcl_linebuf");
757 
758 	guess_paper_size(&writer->options, w, h, xres, yres);
759 
760 	pcl_header(ctx, out, &writer->options, 1, xres, yres, w, h);
761 
762 	/* Raster presentation */
763 	/* Print in orientation of the logical page */
764 	fz_write_string(ctx, out, "\033&r0F");
765 
766 	/* Set color mode */
767 	fz_write_data(ctx, out, "\033*v6W"
768 		"\000"	/* Colorspace 0 = Device RGB */
769 		"\003"	/* Pixel encoding mode: 3 = Direct by Pixel*/
770 		"\000"	/* Bits per index: 0 = no palette */
771 		"\010"	/* Red bits */
772 		"\010"	/* Green bits */
773 		"\010",	/* Blue bits */
774 		11
775 		);
776 
777 	/* Raster resolution */
778 	/* Supposed to be strictly 75, 100, 150, 200, 300, 600 */
779 	/* FIXME: xres vs yres */
780 	fz_write_printf(ctx, out, "\033*t%dR", xres);
781 }
782 
flush_if_not_room(fz_context * ctx,fz_output * out,const unsigned char * comp,int * fill,int len)783 static void flush_if_not_room(fz_context *ctx, fz_output *out, const unsigned char *comp, int *fill, int len)
784 {
785 	if (len + *fill >= 32767)
786 	{
787 		/* Can't fit any data, so flush */
788 		fz_write_printf(ctx, out, "\033*b%dW", *fill);
789 		fz_write_data(ctx, out, comp, *fill);
790 		*fill = 0;
791 	}
792 }
793 
794 static void
color_pcl_compress_column(fz_context * ctx,color_pcl_band_writer * writer,const unsigned char * sp,int w,int h,int stride)795 color_pcl_compress_column(fz_context *ctx, color_pcl_band_writer *writer, const unsigned char *sp, int w, int h, int stride)
796 {
797 	fz_output *out = writer->super.out;
798 	int ss = w * 3;
799 	int seed_valid = 0;
800 	int fill = 0;
801 	int y = 0;
802 	unsigned char *prev = writer->linebuf + w * 3;
803 	unsigned char *curr = writer->linebuf;
804 	unsigned char *comp = writer->compbuf;
805 	unsigned char *comp2 = writer->compbuf2;
806 
807 	while (y < h)
808 	{
809 		/* Skip over multiple blank lines */
810 		int blanks;
811 		do
812 		{
813 			blanks = 0;
814 			while (blanks < 32767 && y < h)
815 			{
816 				if (!line_is_blank(curr, sp, w))
817 					break;
818 				blanks++;
819 				y++;
820 			}
821 
822 			if (blanks)
823 			{
824 				flush_if_not_room(ctx, out, comp, &fill, 3);
825 				comp[fill++] = 4; /* Empty row */
826 				comp[fill++] = blanks>>8;
827 				comp[fill++] = blanks & 0xFF;
828 				seed_valid = 0;
829 			}
830 		}
831 		while (blanks == 32767);
832 
833 		if (y == h)
834 			break;
835 
836 		/* So, at least 1 more line to copy, and it's in curr */
837 		if (seed_valid && memcmp(curr, prev, ss) == 0)
838 		{
839 			int count = 1;
840 			sp += stride;
841 			y++;
842 			while (count < 32767 && y < h)
843 			{
844 				if (memcmp(sp-stride, sp, ss) != 0)
845 					break;
846 				count++;
847 				sp += stride;
848 				y++;
849 			}
850 			flush_if_not_room(ctx, out, comp, &fill, 3);
851 			comp[fill++] = 5; /* Duplicate row */
852 			comp[fill++] = count>>8;
853 			comp[fill++] = count & 0xFF;
854 		}
855 		else
856 		{
857 			unsigned char *tmp;
858 			int len = 0;
859 
860 			/* Compress the line into our fixed buffer. */
861 			if (seed_valid)
862 				len = delta_compression(curr, prev, comp2, ss, fz_mini(ss-1, 32767-3));
863 
864 			if (len > 0)
865 			{
866 				/* Delta compression */
867 				flush_if_not_room(ctx, out, comp, &fill, len+3);
868 				comp[fill++] = 3; /* Delta compression */
869 				comp[fill++] = len>>8;
870 				comp[fill++] = len & 0xFF;
871 				memcpy(&comp[fill], comp2, len);
872 				fill += len;
873 			}
874 			else
875 			{
876 				flush_if_not_room(ctx, out, comp, &fill, 3 + ss);
877 
878 				/* PCL requires that all rows MUST fit in at most 1 block, so
879 				 * we are carefully sending columns that are only so wide. */
880 
881 				/* Unencoded */
882 				/* Transfer Raster Data: ss+3 bytes, 0 = Unencoded, count high, count low */
883 				comp[fill++] = 0;
884 				comp[fill++] = ss>>8;
885 				comp[fill++] = ss & 0xFF;
886 				memcpy(&comp[fill], curr, ss);
887 				fill += ss;
888 				seed_valid = 1;
889 			}
890 
891 			/* curr becomes prev */
892 			tmp = prev; prev = curr; curr = tmp;
893 			sp += stride;
894 			y++;
895 		}
896 	}
897 	/* And flush */
898 	if (fill) {
899 		fz_write_printf(ctx, out, "\033*b%dW", fill);
900 		fz_write_data(ctx, out, comp, fill);
901 	}
902 
903 	/* End Raster Graphics */
904 	fz_write_string(ctx, out, "\033*rC");
905 }
906 
907 static void
color_pcl_write_band(fz_context * ctx,fz_band_writer * writer_,int stride,int band_start,int band_height,const unsigned char * sp)908 color_pcl_write_band(fz_context *ctx, fz_band_writer *writer_, int stride, int band_start, int band_height, const unsigned char *sp)
909 {
910 	color_pcl_band_writer *writer = (color_pcl_band_writer *)writer_;
911 	fz_output *out = writer->super.out;
912 	int w = writer->super.w;
913 	int h = writer->super.h;
914 	int xres = writer->super.xres;
915 	int cw;
916 	int x;
917 
918 	if (!out)
919 		return;
920 
921 	if (band_start+band_height >= h)
922 		band_height = h - band_start;
923 
924 	/* We have to specify image output size in decipoints (720dpi).
925 	 * Most usual PCL resolutions are a multiple of 75.
926 	 * Pick our maximum column size to be 10800 = 15*720 = 144*75
927 	 * to give us good results. 10800 * 3 = 32400 < 32760 */
928 	cw = 10800; /* Limited by how much rowdata we can send at once */
929 	if (cw > w)
930 		cw = w;
931 
932 	for (x = 0; x*cw < w; x++)
933 	{
934 		int col_w = w - cw*x;
935 		if (col_w > cw)
936 			col_w = cw;
937 
938 		/* Top left corner */
939 		fz_write_printf(ctx, out, "\033*p%dx%dY", x*cw, band_start);
940 
941 		/* Raster height */
942 		fz_write_printf(ctx, out, "\033*r%dT", band_height);
943 
944 		/* Raster width */
945 		fz_write_printf(ctx, out, "\033*r%dS", col_w);
946 
947 		/* Destination height */
948 		fz_write_printf(ctx, out, "\033*t%dV", band_height*720/xres);
949 
950 		/* Destination width */
951 		fz_write_printf(ctx, out, "\033*t%dH", col_w*720/xres);
952 
953 		/* start raster graphics */
954 		/* 1 = start at cursor position */
955 		fz_write_string(ctx, out, "\033*r3A");
956 
957 		/* Now output the actual bitmap */
958 		/* Adaptive Compression */
959 		fz_write_string(ctx, out, "\033*b5M");
960 
961 		color_pcl_compress_column(ctx, writer, sp + x * cw * 3, col_w, band_height, stride);
962 	}
963 }
964 
965 static void
color_pcl_write_trailer(fz_context * ctx,fz_band_writer * writer_)966 color_pcl_write_trailer(fz_context *ctx, fz_band_writer *writer_)
967 {
968 }
969 
970 static void
color_pcl_drop_band_writer(fz_context * ctx,fz_band_writer * writer_)971 color_pcl_drop_band_writer(fz_context *ctx, fz_band_writer *writer_)
972 {
973 	color_pcl_band_writer *writer = (color_pcl_band_writer *)writer_;
974 	fz_free(ctx, writer->linebuf);
975 }
976 
fz_new_color_pcl_band_writer(fz_context * ctx,fz_output * out,const fz_pcl_options * options)977 fz_band_writer *fz_new_color_pcl_band_writer(fz_context *ctx, fz_output *out, const fz_pcl_options *options)
978 {
979 	color_pcl_band_writer *writer = fz_new_band_writer(ctx, color_pcl_band_writer, out);
980 
981 	writer->super.header = color_pcl_write_header;
982 	writer->super.band = color_pcl_write_band;
983 	writer->super.trailer = color_pcl_write_trailer;
984 	writer->super.drop = color_pcl_drop_band_writer;
985 
986 	if (options)
987 		writer->options = *options;
988 	else
989 		fz_pcl_preset(ctx, &writer->options, "generic");
990 
991 	return &writer->super;
992 }
993 
994 /*
995  * Mode 2 Row compression routine for the HP DeskJet & LaserJet IIp.
996  * Compresses data from row up to end_row, storing the result
997  * starting at out. Returns the number of bytes stored.
998  * Runs of K<=127 literal bytes are encoded as K-1 followed by
999  * the bytes; runs of 2<=K<=127 identical bytes are encoded as
1000  * 257-K followed by the byte.
1001  * In the worst case, the result is N+(N/127)+1 bytes long,
1002  * where N is the original byte count (end_row - row).
1003  */
1004 static int
mode2compress(unsigned char * out,const unsigned char * in,int in_len)1005 mode2compress(unsigned char *out, const unsigned char *in, int in_len)
1006 {
1007 	int x;
1008 	int out_len = 0;
1009 	int run;
1010 
1011 	for (x = 0; x < in_len; x += run)
1012 	{
1013 		/* How far do we have to look to find a value that isn't repeated? */
1014 		for (run = 1; run < 127 && x+run < in_len; run++)
1015 			if (in[0] != in[run])
1016 				break;
1017 		if (run > 1)
1018 		{
1019 			/* We have a run of matching bytes */
1020 			out[out_len++] = 257-run;
1021 			out[out_len++] = in[0];
1022 		}
1023 		else
1024 		{
1025 			/* Now copy as many literals as possible. We only
1026 			 * break the run at a length of 127, at the end,
1027 			 * or where we have 3 repeated values. */
1028 			int i;
1029 
1030 			/* How many literals do we need to copy? */
1031 			for (; run < 127 && x+run+2 < in_len; run++)
1032 				if (in[run] == in[run+1] && in[run] == in[run+2])
1033 					break;
1034 			/* Don't leave stragglers at the end */
1035 			if (x + run + 2 >= in_len)
1036 			{
1037 				run = in_len - x;
1038 				if (run > 127)
1039 					run = 127;
1040 			}
1041 			out[out_len++] = run-1;
1042 			for (i = 0; i < run; i++)
1043 			{
1044 				out[out_len++] = in[i];
1045 			}
1046 		}
1047 		in += run;
1048 	}
1049 
1050 	return out_len;
1051 }
1052 
1053 /*
1054  * Mode 3 compression routine for the HP LaserJet III family.
1055  * Compresses bytecount bytes starting at current, storing the result
1056  * in compressed, comparing against and updating previous.
1057  * Returns the number of bytes stored.	In the worst case,
1058  * the number of bytes is bytecount+(bytecount/8)+1.
1059  */
1060 static int
mode3compress(unsigned char * out,const unsigned char * in,unsigned char * prev,int in_len)1061 mode3compress(unsigned char *out, const unsigned char *in, unsigned char *prev, int in_len)
1062 {
1063 	unsigned char *compressed = out;
1064 	const unsigned char *cur = in;
1065 	const unsigned char *end = in + in_len;
1066 
1067 	while (cur < end) {		/* Detect a maximum run of unchanged bytes. */
1068 		const unsigned char *run = cur;
1069 		const unsigned char *diff;
1070 		const unsigned char *stop;
1071 		int offset, cbyte;
1072 
1073 		while (cur < end && *cur == *prev) {
1074 			cur++, prev++;
1075 		}
1076 		if (cur == end)
1077 			break;		/* rest of row is unchanged */
1078 		/* Detect a run of up to 8 changed bytes. */
1079 		/* We know that *cur != *prev. */
1080 		diff = cur;
1081 		stop = (end - cur > 8 ? cur + 8 : end);
1082 		do
1083 		{
1084 			*prev++ = *cur++;
1085 		}
1086 		while (cur < stop && *cur != *prev);
1087 		/* Now [run..diff) are unchanged, and */
1088 		/* [diff..cur) are changed. */
1089 		/* Generate the command byte(s). */
1090 		offset = diff - run;
1091 		cbyte = (cur - diff - 1) << 5;
1092 		if (offset < 31)
1093 			*out++ = cbyte + offset;
1094 		else {
1095 			*out++ = cbyte + 31;
1096 			offset -= 31;
1097 			while (offset >= 255)
1098 				*out++ = 255, offset -= 255;
1099 			*out++ = offset;
1100 		}
1101 		/* Copy the changed data. */
1102 		while (diff < cur)
1103 			*out++ = *diff++;
1104 	}
1105 	return out - compressed;
1106 }
1107 
1108 void
fz_write_bitmap_as_pcl(fz_context * ctx,fz_output * out,const fz_bitmap * bitmap,const fz_pcl_options * pcl)1109 fz_write_bitmap_as_pcl(fz_context *ctx, fz_output *out, const fz_bitmap *bitmap, const fz_pcl_options *pcl)
1110 {
1111 	fz_band_writer *writer;
1112 
1113 	if (!bitmap || !out)
1114 		return;
1115 
1116 	writer = fz_new_mono_pcl_band_writer(ctx, out, pcl);
1117 	fz_try(ctx)
1118 	{
1119 		fz_write_header(ctx, writer, bitmap->w, bitmap->h, 1, 0, bitmap->xres, bitmap->yres, 0, NULL, NULL);
1120 		fz_write_band(ctx, writer, bitmap->stride, bitmap->h, bitmap->samples);
1121 	}
1122 	fz_always(ctx)
1123 		fz_drop_band_writer(ctx, writer);
1124 	fz_catch(ctx)
1125 		fz_rethrow(ctx);
1126 }
1127 
1128 typedef struct mono_pcl_band_writer_s
1129 {
1130 	fz_band_writer super;
1131 	fz_pcl_options options;
1132 	unsigned char *prev;
1133 	unsigned char *mode2buf;
1134 	unsigned char *mode3buf;
1135 	int top_of_page;
1136 	int num_blank_lines;
1137 } mono_pcl_band_writer;
1138 
1139 static void
mono_pcl_write_header(fz_context * ctx,fz_band_writer * writer_,fz_colorspace * cs)1140 mono_pcl_write_header(fz_context *ctx, fz_band_writer *writer_, fz_colorspace *cs)
1141 {
1142 	mono_pcl_band_writer *writer = (mono_pcl_band_writer *)writer_;
1143 	fz_output *out = writer->super.out;
1144 	int w = writer->super.w;
1145 	int h = writer->super.h;
1146 	int xres = writer->super.xres;
1147 	int yres = writer->super.yres;
1148 	int line_size;
1149 	int max_mode_2_size;
1150 	int max_mode_3_size;
1151 
1152 	if (writer->super.alpha != 0)
1153 		fz_throw(ctx, FZ_ERROR_GENERIC, "mono PCL cannot write alpha channel");
1154 	if (writer->super.s != 0)
1155 		fz_throw(ctx, FZ_ERROR_GENERIC, "mono PCL cannot write spot colors");
1156 	if (writer->super.n != 1)
1157 		fz_throw(ctx, FZ_ERROR_GENERIC, "mono PCL must be grayscale");
1158 
1159 	line_size = (w + 7)/8;
1160 	max_mode_2_size = line_size + (line_size/127) + 1;
1161 	max_mode_3_size = line_size + (line_size/8) + 1;
1162 
1163 	writer->prev = fz_calloc(ctx, line_size, sizeof(unsigned char));
1164 	writer->mode2buf = fz_calloc(ctx, max_mode_2_size, sizeof(unsigned char));
1165 	writer->mode3buf = fz_calloc(ctx, max_mode_3_size, sizeof(unsigned char));
1166 	writer->num_blank_lines = 0;
1167 	writer->top_of_page = 1;
1168 
1169 	guess_paper_size(&writer->options, w, h, xres, yres);
1170 
1171 	if (writer->options.features & HACK__IS_A_OCE9050)
1172 	{
1173 		/* Enter HPGL/2 mode, begin plot, Initialise (start plot), Enter PCL mode */
1174 		fz_write_string(ctx, out, "\033%1BBPIN;\033%1A");
1175 	}
1176 
1177 	pcl_header(ctx, out, &writer->options, 1, xres, yres, w, h);
1178 }
1179 
1180 static void
mono_pcl_write_band(fz_context * ctx,fz_band_writer * writer_,int ss,int band_start,int band_height,const unsigned char * data)1181 mono_pcl_write_band(fz_context *ctx, fz_band_writer *writer_, int ss, int band_start, int band_height, const unsigned char *data)
1182 {
1183 	mono_pcl_band_writer *writer = (mono_pcl_band_writer *)writer_;
1184 	fz_output *out = writer->super.out;
1185 	int w = writer->super.w;
1186 	int yres = writer->super.yres;
1187 	const unsigned char *out_data;
1188 	int y, rmask, line_size;
1189 	int num_blank_lines;
1190 	int compression = -1;
1191 	unsigned char *prev = NULL;
1192 	unsigned char *mode2buf = NULL;
1193 	unsigned char *mode3buf = NULL;
1194 	int out_count;
1195 	const fz_pcl_options *pcl;
1196 
1197 	if (!out)
1198 		return;
1199 
1200 	num_blank_lines = writer->num_blank_lines;
1201 	rmask = ~0 << (-w & 7);
1202 	line_size = (w + 7)/8;
1203 	prev = writer->prev;
1204 	mode2buf = writer->mode2buf;
1205 	mode3buf = writer->mode3buf;
1206 	pcl = &writer->options;
1207 
1208 	/* Transfer raster graphics. */
1209 	for (y = 0; y < band_height; y++, data += ss)
1210 	{
1211 		const unsigned char *end_data = data + line_size;
1212 
1213 		if ((end_data[-1] & rmask) == 0)
1214 		{
1215 			end_data--;
1216 			while (end_data > data && end_data[-1] == 0)
1217 				end_data--;
1218 		}
1219 		if (end_data == data)
1220 		{
1221 			/* Blank line */
1222 			num_blank_lines++;
1223 			continue;
1224 		}
1225 
1226 		/* We've reached a non-blank line. */
1227 		/* Put out a spacing command if necessary. */
1228 		if (writer->top_of_page)
1229 		{
1230 			writer->top_of_page = 0;
1231 			/* We're at the top of a page. */
1232 			if (pcl->features & PCL_ANY_SPACING)
1233 			{
1234 				if (num_blank_lines > 0)
1235 					fz_write_printf(ctx, out, "\033*p+%dY", num_blank_lines);
1236 				/* Start raster graphics. */
1237 				fz_write_string(ctx, out, "\033*r1A");
1238 			}
1239 			else if (pcl->features & PCL_MODE_3_COMPRESSION)
1240 			{
1241 				/* Start raster graphics. */
1242 				fz_write_string(ctx, out, "\033*r1A");
1243 				for (; num_blank_lines; num_blank_lines--)
1244 					fz_write_string(ctx, out, "\033*b0W");
1245 			}
1246 			else
1247 			{
1248 				/* Start raster graphics. */
1249 				fz_write_string(ctx, out, "\033*r1A");
1250 				for (; num_blank_lines; num_blank_lines--)
1251 					fz_write_string(ctx, out, "\033*bW");
1252 			}
1253 		}
1254 
1255 		/* Skip blank lines if any */
1256 		else if (num_blank_lines != 0)
1257 		{
1258 			/* Moving down from current position causes head
1259 			 * motion on the DeskJet, so if the number of lines
1260 			 * is small, we're better off printing blanks.
1261 			 *
1262 			 * For Canon LBP4i and some others, <ESC>*b<n>Y
1263 			 * doesn't properly clear the seed row if we are in
1264 			 * compression mode 3.
1265 			 */
1266 			if ((num_blank_lines < MIN_SKIP_LINES && compression != 3) ||
1267 					!(pcl->features & PCL_ANY_SPACING))
1268 			{
1269 				int mode_3ns = ((pcl->features & PCL_MODE_3_COMPRESSION) && !(pcl->features & PCL_ANY_SPACING));
1270 				if (mode_3ns && compression != 2)
1271 				{
1272 					/* Switch to mode 2 */
1273 					fz_write_string(ctx, out, from3to2);
1274 					compression = 2;
1275 				}
1276 				if (pcl->features & PCL_MODE_3_COMPRESSION)
1277 				{
1278 					/* Must clear the seed row. */
1279 					fz_write_string(ctx, out, "\033*b1Y");
1280 					num_blank_lines--;
1281 				}
1282 				if (mode_3ns)
1283 				{
1284 					for (; num_blank_lines; num_blank_lines--)
1285 						fz_write_string(ctx, out, "\033*b0W");
1286 				}
1287 				else
1288 				{
1289 					for (; num_blank_lines; num_blank_lines--)
1290 						fz_write_string(ctx, out, "\033*bW");
1291 				}
1292 			}
1293 			else if (pcl->features & PCL3_SPACING)
1294 				fz_write_printf(ctx, out, "\033*p+%dY", num_blank_lines * yres);
1295 			else
1296 				fz_write_printf(ctx, out, "\033*b%dY", num_blank_lines);
1297 			/* Clear the seed row (only matters for mode 3 compression). */
1298 			memset(prev, 0, line_size);
1299 		}
1300 		num_blank_lines = 0;
1301 
1302 		/* Choose the best compression mode for this particular line. */
1303 		if (pcl->features & PCL_MODE_3_COMPRESSION)
1304 		{
1305 			/* Compression modes 2 and 3 are both available. Try
1306 			 * both and see which produces the least output data.
1307 			 */
1308 			int count3 = mode3compress(mode3buf, data, prev, line_size);
1309 			int count2 = mode2compress(mode2buf, data, line_size);
1310 			int penalty3 = (compression == 3 ? 0 : penalty_from2to3);
1311 			int penalty2 = (compression == 2 ? 0 : penalty_from3to2);
1312 
1313 			if (count3 + penalty3 < count2 + penalty2)
1314 			{
1315 				if (compression != 3)
1316 					fz_write_string(ctx, out, from2to3);
1317 				compression = 3;
1318 				out_data = (unsigned char *)mode3buf;
1319 				out_count = count3;
1320 			}
1321 			else
1322 			{
1323 				if (compression != 2)
1324 					fz_write_string(ctx, out, from3to2);
1325 				compression = 2;
1326 				out_data = (unsigned char *)mode2buf;
1327 				out_count = count2;
1328 			}
1329 		}
1330 		else if (pcl->features & PCL_MODE_2_COMPRESSION)
1331 		{
1332 			out_data = mode2buf;
1333 			out_count = mode2compress(mode2buf, data, line_size);
1334 		}
1335 		else
1336 		{
1337 			out_data = data;
1338 			out_count = line_size;
1339 		}
1340 
1341 		/* Transfer the data */
1342 		fz_write_printf(ctx, out, "\033*b%dW", out_count);
1343 		fz_write_data(ctx, out, out_data, out_count);
1344 	}
1345 
1346 	writer->num_blank_lines = num_blank_lines;
1347 }
1348 
1349 static void
mono_pcl_write_trailer(fz_context * ctx,fz_band_writer * writer_)1350 mono_pcl_write_trailer(fz_context *ctx, fz_band_writer *writer_)
1351 {
1352 	mono_pcl_band_writer *writer = (mono_pcl_band_writer *)writer_;
1353 	fz_output *out = writer->super.out;
1354 
1355 	/* end raster graphics and eject page */
1356 	fz_write_string(ctx, out, "\033*rB\f");
1357 
1358 	if (writer->options.features & HACK__IS_A_OCE9050)
1359 	{
1360 		/* Pen up, pen select, advance full page, reset */
1361 		fz_write_string(ctx, out, "\033%1BPUSP0PG;\033E");
1362 	}
1363 }
1364 
1365 static void
mono_pcl_drop_band_writer(fz_context * ctx,fz_band_writer * writer_)1366 mono_pcl_drop_band_writer(fz_context *ctx, fz_band_writer *writer_)
1367 {
1368 	mono_pcl_band_writer *writer = (mono_pcl_band_writer *)writer_;
1369 
1370 	fz_free(ctx, writer->prev);
1371 	fz_free(ctx, writer->mode2buf);
1372 	fz_free(ctx, writer->mode3buf);
1373 }
1374 
fz_new_mono_pcl_band_writer(fz_context * ctx,fz_output * out,const fz_pcl_options * options)1375 fz_band_writer *fz_new_mono_pcl_band_writer(fz_context *ctx, fz_output *out, const fz_pcl_options *options)
1376 {
1377 	mono_pcl_band_writer *writer = fz_new_band_writer(ctx, mono_pcl_band_writer, out);
1378 
1379 	writer->super.header = mono_pcl_write_header;
1380 	writer->super.band = mono_pcl_write_band;
1381 	writer->super.trailer = mono_pcl_write_trailer;
1382 	writer->super.drop = mono_pcl_drop_band_writer;
1383 
1384 	if (options)
1385 		writer->options = *options;
1386 	else
1387 		fz_pcl_preset(ctx, &writer->options, "generic");
1388 
1389 	return &writer->super;
1390 }
1391 
1392 void
fz_save_pixmap_as_pcl(fz_context * ctx,fz_pixmap * pixmap,char * filename,int append,const fz_pcl_options * pcl)1393 fz_save_pixmap_as_pcl(fz_context *ctx, fz_pixmap *pixmap, char *filename, int append, const fz_pcl_options *pcl)
1394 {
1395 	fz_output *out = fz_new_output_with_path(ctx, filename, append);
1396 	fz_try(ctx)
1397 	{
1398 		fz_write_pixmap_as_pcl(ctx, out, pixmap, pcl);
1399 		fz_close_output(ctx, out);
1400 	}
1401 	fz_always(ctx)
1402 		fz_drop_output(ctx, out);
1403 	fz_catch(ctx)
1404 		fz_rethrow(ctx);
1405 }
1406 
1407 void
fz_save_bitmap_as_pcl(fz_context * ctx,fz_bitmap * bitmap,char * filename,int append,const fz_pcl_options * pcl)1408 fz_save_bitmap_as_pcl(fz_context *ctx, fz_bitmap *bitmap, char *filename, int append, const fz_pcl_options *pcl)
1409 {
1410 	fz_output *out = fz_new_output_with_path(ctx, filename, append);
1411 	fz_try(ctx)
1412 	{
1413 		fz_write_bitmap_as_pcl(ctx, out, bitmap, pcl);
1414 		fz_close_output(ctx, out);
1415 	}
1416 	fz_always(ctx)
1417 		fz_drop_output(ctx, out);
1418 	fz_catch(ctx)
1419 		fz_rethrow(ctx);
1420 }
1421 
1422 /* High-level document writer interface */
1423 
1424 typedef struct
1425 {
1426 	fz_document_writer super;
1427 	fz_draw_options draw;
1428 	fz_pcl_options pcl;
1429 	fz_pixmap *pixmap;
1430 	int mono;
1431 	fz_output *out;
1432 } fz_pcl_writer;
1433 
1434 static fz_device *
pcl_begin_page(fz_context * ctx,fz_document_writer * wri_,fz_rect mediabox)1435 pcl_begin_page(fz_context *ctx, fz_document_writer *wri_, fz_rect mediabox)
1436 {
1437 	fz_pcl_writer *wri = (fz_pcl_writer*)wri_;
1438 	return fz_new_draw_device_with_options(ctx, &wri->draw, mediabox, &wri->pixmap);
1439 }
1440 
1441 static void
pcl_end_page(fz_context * ctx,fz_document_writer * wri_,fz_device * dev)1442 pcl_end_page(fz_context *ctx, fz_document_writer *wri_, fz_device *dev)
1443 {
1444 	fz_pcl_writer *wri = (fz_pcl_writer*)wri_;
1445 	fz_bitmap *bitmap = NULL;
1446 
1447 	fz_var(bitmap);
1448 
1449 	fz_try(ctx)
1450 	{
1451 		fz_close_device(ctx, dev);
1452 		if (wri->mono)
1453 		{
1454 			bitmap = fz_new_bitmap_from_pixmap(ctx, wri->pixmap, NULL);
1455 			fz_write_bitmap_as_pcl(ctx, wri->out, bitmap, &wri->pcl);
1456 		}
1457 		else
1458 		{
1459 			fz_write_pixmap_as_pcl(ctx, wri->out, wri->pixmap, &wri->pcl);
1460 		}
1461 	}
1462 	fz_always(ctx)
1463 	{
1464 		fz_drop_device(ctx, dev);
1465 		fz_drop_bitmap(ctx, bitmap);
1466 		fz_drop_pixmap(ctx, wri->pixmap);
1467 		wri->pixmap = NULL;
1468 	}
1469 	fz_catch(ctx)
1470 		fz_rethrow(ctx);
1471 }
1472 
1473 static void
pcl_close_writer(fz_context * ctx,fz_document_writer * wri_)1474 pcl_close_writer(fz_context *ctx, fz_document_writer *wri_)
1475 {
1476 	fz_pcl_writer *wri = (fz_pcl_writer*)wri_;
1477 	fz_close_output(ctx, wri->out);
1478 }
1479 
1480 static void
pcl_drop_writer(fz_context * ctx,fz_document_writer * wri_)1481 pcl_drop_writer(fz_context *ctx, fz_document_writer *wri_)
1482 {
1483 	fz_pcl_writer *wri = (fz_pcl_writer*)wri_;
1484 	fz_drop_pixmap(ctx, wri->pixmap);
1485 	fz_drop_output(ctx, wri->out);
1486 }
1487 
1488 fz_document_writer *
fz_new_pcl_writer_with_output(fz_context * ctx,fz_output * out,const char * options)1489 fz_new_pcl_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
1490 {
1491 	fz_pcl_writer *wri = fz_new_derived_document_writer(ctx, fz_pcl_writer, pcl_begin_page, pcl_end_page, pcl_close_writer, pcl_drop_writer);
1492 	const char *val;
1493 
1494 	fz_try(ctx)
1495 	{
1496 		fz_parse_draw_options(ctx, &wri->draw, options);
1497 		fz_parse_pcl_options(ctx, &wri->pcl, options);
1498 		if (fz_has_option(ctx, options, "colorspace", &val))
1499 			if (fz_option_eq(val, "mono"))
1500 				wri->mono = 1;
1501 		wri->out = out;
1502 	}
1503 	fz_catch(ctx)
1504 	{
1505 		fz_free(ctx, wri);
1506 		fz_rethrow(ctx);
1507 	}
1508 
1509 	return (fz_document_writer*)wri;
1510 }
1511 
1512 fz_document_writer *
fz_new_pcl_writer(fz_context * ctx,const char * path,const char * options)1513 fz_new_pcl_writer(fz_context *ctx, const char *path, const char *options)
1514 {
1515 	fz_output *out = fz_new_output_with_path(ctx, path ? path : "out.pcl", 0);
1516 	fz_document_writer *wri = NULL;
1517 	fz_try(ctx)
1518 		wri = fz_new_pcl_writer_with_output(ctx, out, options);
1519 	fz_catch(ctx)
1520 	{
1521 		fz_drop_output(ctx, out);
1522 		fz_rethrow(ctx);
1523 	}
1524 	return wri;
1525 }
1526