comparison mupdf-source/source/fitz/output-pcl.c @ 2:b50eed0cc0ef upstream

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