Coverage Report

Created: 2024-05-20 06:23

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