Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
