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