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