Coverage Report

Created: 2025-12-31 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/fitz/output-pcl.c
Line
Count
Source
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
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
    /* ESC & l # E  =  Top Margin in decipoints */
421
0
    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
0
    fz_write_string(ctx, out, "\033&l-180U");
425
    /* ESC & l # Z  =  Top (Short-Edge) offset registration */
426
0
    fz_write_string(ctx, out, "\033&l0Z");
427
    /* If the printer supports it, set orientation */
428
0
    if (pcl->features & PCL_HAS_ORIENTATION)
429
0
    {
430
0
      fz_write_printf(ctx, out, "\033&l%dO", pcl->orientation);
431
0
    }
432
    /* If the printer supports it, set the paper size */
433
    /* based on the actual requested size. */
434
0
    if (pcl->features & PCL_CAN_SET_PAPER_SIZE)
435
0
    {
436
      /* It probably never hurts to define the page explicitly */
437
0
      {
438
0
        int decipointw = (w * 720 + (xres>>1)) / xres;
439
0
        int decipointh = (h * 720 + (yres>>1)) / yres;
440
441
0
        fz_write_printf(ctx, out, "\033&f%dI", decipointw);
442
0
        fz_write_printf(ctx, out, "\033&f%dJ", decipointh);
443
0
      }
444
0
      fz_write_printf(ctx, out, "\033&l%dA", pcl->paper_size);
445
0
    }
446
    /* If printer can duplex, set duplex mode appropriately. */
447
0
    if (pcl->features & PCL_HAS_DUPLEX)
448
0
    {
449
0
      if (pcl->duplex_set)
450
0
      {
451
0
        if (pcl->duplex)
452
0
        {
453
0
          if (!pcl->tumble)
454
0
            fz_write_string(ctx, out, "\033&l1S");
455
0
          else
456
0
            fz_write_string(ctx, out, "\033&l2S");
457
0
        }
458
0
        else
459
0
          fz_write_string(ctx, out, "\033&l0S");
460
0
      }
461
0
      else
462
0
      {
463
        /* default to duplex for this printer */
464
0
        fz_write_string(ctx, out, "\033&l1S");
465
0
      }
466
0
    }
467
0
  }
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
0
  if ((pcl->features & PCL_HAS_DUPLEX) && pcl->duplex_set && pcl->duplex)
481
0
  {
482
    /* We are printing duplex, so change margins as needed */
483
0
    if (((pcl->page_count/num_copies)%2) == 0)
484
0
    {
485
0
      if (pcl->page_count != 0 && (pcl->features & PCL_CAN_SET_PAPER_SIZE))
486
0
      {
487
0
        fz_write_printf(ctx, out, "\033&l%dA", pcl->paper_size);
488
0
      }
489
0
      fz_write_string(ctx, out, "\033&l0o0l0E");
490
0
      fz_write_string(ctx, out, pcl->odd_page_init);
491
0
    }
492
0
    else
493
0
      fz_write_string(ctx, out, pcl->even_page_init);
494
0
  }
495
0
  else
496
0
  {
497
0
    if (pcl->features & PCL_CAN_SET_PAPER_SIZE)
498
0
    {
499
0
      fz_write_printf(ctx, out, "\033&l%dA", pcl->paper_size);
500
0
    }
501
0
    fz_write_string(ctx, out, "\033&l0o0l0E");
502
0
    fz_write_string(ctx, out, pcl->odd_page_init);
503
0
  }
504
505
0
  fz_write_printf(ctx, out, "\033&l%dX", num_copies); /* # of copies */
506
507
  /* End raster graphics, position cursor at top. */
508
0
  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
0
  if (pcl->features & PCL_END_GRAPHICS_DOES_RESET)
513
0
  {
514
0
    fz_write_string(ctx, out, pcl->odd_page_init); /* Assume this does the right thing */
515
0
    fz_write_printf(ctx, out, "\033&l%dX", num_copies); /* # of copies */
516
0
  }
517
518
  /* Set resolution. */
519
0
  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
0
  fz_write_printf(ctx, out, "\033&u%dD", xres);
525
526
0
  pcl->page_count++;
527
0
}
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
0
{
585
0
  int size;
586
0
  int rotated = 0;
587
588
  /* If we've been given a paper size, live with it */
589
0
  if (pcl->paper_size != 0)
590
0
    return;
591
592
0
  w = w * 300 / xres;
593
0
  h = h * 300 / xres;
594
595
  /* Look for an exact match */
596
0
  for (size = 0; size < (int)nelem(papersizes); size++)
597
0
  {
598
0
    if (papersizes[size].code > eCustomPaperSize && (pcl->features & PCL_HAS_RICOH_PAPER_SIZES) == 0)
599
0
      continue;
600
0
    if (w == papersizes[size].width && h == papersizes[size].height)
601
0
      break;
602
0
    if ((pcl->features & PCL_HAS_ORIENTATION) && w == papersizes[size].height && h == papersizes[size].width)
603
0
    {
604
0
      rotated = 1;
605
0
      break;
606
0
    }
607
0
  }
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
0
  if (size == nelem(papersizes))
612
0
  {
613
0
    if ((pcl->features & PCL_CAN_SET_CUSTOM_PAPER_SIZE) != 0)
614
0
    {
615
      /* Send it as a custom size */
616
0
      size = eCustomPaperSize;
617
0
    }
618
0
    else
619
0
    {
620
      /* Send the next larger one (minimise waste) */
621
0
      int i;
622
0
      int best_waste = INT_MAX;
623
0
      for (i = 0; i < (int)nelem(papersizes); i++)
624
0
      {
625
0
        int waste;
626
0
        if (papersizes[i].code > eCustomPaperSize && (pcl->features & PCL_HAS_RICOH_PAPER_SIZES) == 0)
627
0
          continue;
628
0
        waste = papersizes[i].width * papersizes[i].height - w * h;
629
0
        if (waste > best_waste)
630
0
          continue;
631
0
        if (w <= papersizes[i].width && h <= papersizes[i].height)
632
0
        {
633
0
          best_waste = waste;
634
0
          rotated = 0;
635
0
          size = i;
636
0
        }
637
0
        if ((pcl->features & PCL_HAS_ORIENTATION) && w <= papersizes[i].height && h <= papersizes[i].width)
638
0
        {
639
0
          best_waste = waste;
640
0
          rotated = 1;
641
0
          size = i;
642
0
        }
643
0
      }
644
0
    }
645
0
  }
646
647
  /* Now, size = The best size we have (or nelem(papersizes)) if it's too big */
648
649
0
  if (size < (int)nelem(papersizes))
650
0
    pcl->paper_size = papersizes[size].code;
651
0
  else
652
0
    pcl->paper_size = eCustomPaperSize; /* Custom */
653
654
0
  pcl->orientation = rotated;
655
0
}
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
0
{
661
0
  int zero = 0;
662
663
0
  while (w-- > 0)
664
0
  {
665
0
    zero |= (*dst++ = *sp++);
666
0
    zero |= (*dst++ = *sp++);
667
0
    zero |= (*dst++ = *sp++);
668
0
  }
669
670
0
  return zero == 0;
671
0
}
672
673
static int
674
delta_compression(unsigned char *curr, unsigned char *prev, unsigned char *comp, int ds, int space)
675
0
{
676
0
  int left = space;
677
0
  int x = ds;
678
679
0
  while (x > 0)
680
0
  {
681
    /* Count matching bytes */
682
0
    int match = 0;
683
0
    int diff = 0;
684
0
    while (x > 0 && *curr == *prev)
685
0
    {
686
0
      curr++;
687
0
      prev++;
688
0
      match++;
689
0
      x--;
690
0
    }
691
692
    /* Count different bytes */
693
0
    while (x > 0 && *curr != *prev)
694
0
    {
695
0
      curr++;
696
0
      prev++;
697
0
      diff++;
698
0
      x--;
699
0
    }
700
701
0
    while (diff > 0)
702
0
    {
703
0
      int exts;
704
0
      int mini_diff = diff;
705
0
      if (mini_diff > 8)
706
0
        mini_diff = 8;
707
708
0
      exts = (match+255-31)/255;
709
0
      left -= 1 + mini_diff + exts;
710
0
      if (left < 0)
711
0
        return 0;
712
0
      *comp++ = ((mini_diff-1)<<5) | (match < 31 ? match : 31);
713
0
      if (exts > 0)
714
0
      {
715
0
        match -= 31;
716
0
        while (--exts)
717
0
        {
718
0
          *comp++ = 255;
719
0
          match -= 255;
720
0
        }
721
0
        *comp++ = match;
722
0
      }
723
0
      memcpy(comp, curr-diff, mini_diff);
724
0
      comp += mini_diff;
725
726
0
      match = 0;
727
0
      diff -= mini_diff;
728
0
    }
729
0
  }
730
0
  return space - left;
731
0
}
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
0
{
736
0
  fz_band_writer *writer;
737
738
0
  if (!pixmap || !out)
739
0
    return;
740
741
0
  writer = fz_new_color_pcl_band_writer(ctx, out, pcl);
742
0
  fz_try(ctx)
743
0
  {
744
0
    fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace, pixmap->seps);
745
0
    fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
746
0
    fz_close_band_writer(ctx, writer);
747
0
  }
748
0
  fz_always(ctx)
749
0
    fz_drop_band_writer(ctx, writer);
750
0
  fz_catch(ctx)
751
0
    fz_rethrow(ctx);
752
0
}
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
0
{
766
0
  color_pcl_band_writer *writer = (color_pcl_band_writer *)writer_;
767
0
  fz_output *out = writer->super.out;
768
0
  int w = writer->super.w;
769
0
  int h = writer->super.h;
770
0
  int n = writer->super.n;
771
0
  int s = writer->super.s;
772
0
  int a = writer->super.alpha;
773
0
  int xres = writer->super.xres;
774
0
  int yres = writer->super.yres;
775
776
0
  if (a != 0)
777
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "color PCL cannot write alpha channel");
778
0
  if (s != 0)
779
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "color PCL cannot write spot colors");
780
0
  if (n != 3)
781
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "color PCL must be RGB");
782
783
0
  writer->linebuf = Memento_label(fz_malloc(ctx, w * 3 * 2), "color_pcl_linebuf");
784
785
0
  guess_paper_size(&writer->options, w, h, xres, yres);
786
787
0
  pcl_header(ctx, out, &writer->options, 1, xres, yres, w, h);
788
789
  /* Raster presentation */
790
  /* Print in orientation of the logical page */
791
0
  fz_write_string(ctx, out, "\033&r0F");
792
793
  /* Set color mode */
794
0
  fz_write_data(ctx, out, "\033*v6W"
795
0
    "\000"  /* Colorspace 0 = Device RGB */
796
0
    "\003"  /* Pixel encoding mode: 3 = Direct by Pixel*/
797
0
    "\000"  /* Bits per index: 0 = no palette */
798
0
    "\010"  /* Red bits */
799
0
    "\010"  /* Green bits */
800
0
    "\010", /* Blue bits */
801
0
    11
802
0
    );
803
804
  /* Raster resolution */
805
  /* Supposed to be strictly 75, 100, 150, 200, 300, 600 */
806
  /* FIXME: xres vs yres */
807
0
  fz_write_printf(ctx, out, "\033*t%dR", xres);
808
0
}
809
810
static void flush_if_not_room(fz_context *ctx, fz_output *out, const unsigned char *comp, int *fill, int len)
811
0
{
812
0
  if (len + *fill >= 32767)
813
0
  {
814
    /* Can't fit any data, so flush */
815
0
    fz_write_printf(ctx, out, "\033*b%dW", *fill);
816
0
    fz_write_data(ctx, out, comp, *fill);
817
0
    *fill = 0;
818
0
  }
819
0
}
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
0
{
824
0
  fz_output *out = writer->super.out;
825
0
  int ss = w * 3;
826
0
  int seed_valid = 0;
827
0
  int fill = 0;
828
0
  int y = 0;
829
0
  unsigned char *prev = writer->linebuf + w * 3;
830
0
  unsigned char *curr = writer->linebuf;
831
0
  unsigned char *comp = writer->compbuf;
832
0
  unsigned char *comp2 = writer->compbuf2;
833
834
0
  while (y < h)
835
0
  {
836
    /* Skip over multiple blank lines */
837
0
    int blanks;
838
0
    do
839
0
    {
840
0
      blanks = 0;
841
0
      while (blanks < 32767 && y < h)
842
0
      {
843
0
        if (!line_is_blank(curr, sp, w))
844
0
          break;
845
0
        blanks++;
846
0
        y++;
847
0
        sp += stride;
848
0
      }
849
850
0
      if (blanks)
851
0
      {
852
0
        flush_if_not_room(ctx, out, comp, &fill, 3);
853
0
        comp[fill++] = 4; /* Empty row */
854
0
        comp[fill++] = blanks>>8;
855
0
        comp[fill++] = blanks & 0xFF;
856
0
        seed_valid = 0;
857
0
      }
858
0
    }
859
0
    while (blanks == 32767);
860
861
0
    if (y == h)
862
0
      break;
863
864
    /* So, at least 1 more line to copy, and it's in curr */
865
0
    if (seed_valid && memcmp(curr, prev, ss) == 0)
866
0
    {
867
0
      int count = 1;
868
0
      sp += stride;
869
0
      y++;
870
0
      while (count < 32767 && y < h)
871
0
      {
872
0
        if (memcmp(sp-stride, sp, ss) != 0)
873
0
          break;
874
0
        count++;
875
0
        sp += stride;
876
0
        y++;
877
0
      }
878
0
      flush_if_not_room(ctx, out, comp, &fill, 3);
879
0
      comp[fill++] = 5; /* Duplicate row */
880
0
      comp[fill++] = count>>8;
881
0
      comp[fill++] = count & 0xFF;
882
0
    }
883
0
    else
884
0
    {
885
0
      unsigned char *tmp;
886
0
      int len = 0;
887
888
      /* Compress the line into our fixed buffer. */
889
0
      if (seed_valid)
890
0
        len = delta_compression(curr, prev, comp2, ss, fz_mini(ss-1, 32767-3));
891
892
0
      if (len > 0)
893
0
      {
894
        /* Delta compression */
895
0
        flush_if_not_room(ctx, out, comp, &fill, len+3);
896
0
        comp[fill++] = 3; /* Delta compression */
897
0
        comp[fill++] = len>>8;
898
0
        comp[fill++] = len & 0xFF;
899
0
        memcpy(&comp[fill], comp2, len);
900
0
        fill += len;
901
0
      }
902
0
      else
903
0
      {
904
0
        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
0
        comp[fill++] = 0;
912
0
        comp[fill++] = ss>>8;
913
0
        comp[fill++] = ss & 0xFF;
914
0
        memcpy(&comp[fill], curr, ss);
915
0
        fill += ss;
916
0
        seed_valid = 1;
917
0
      }
918
919
      /* curr becomes prev */
920
0
      tmp = prev; prev = curr; curr = tmp;
921
0
      sp += stride;
922
0
      y++;
923
0
    }
924
0
  }
925
  /* And flush */
926
0
  if (fill) {
927
0
    fz_write_printf(ctx, out, "\033*b%dW", fill);
928
0
    fz_write_data(ctx, out, comp, fill);
929
0
  }
930
931
  /* End Raster Graphics */
932
0
  fz_write_string(ctx, out, "\033*rC");
933
0
}
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
0
{
938
0
  color_pcl_band_writer *writer = (color_pcl_band_writer *)writer_;
939
0
  fz_output *out = writer->super.out;
940
0
  int w = writer->super.w;
941
0
  int h = writer->super.h;
942
0
  int xres = writer->super.xres;
943
0
  int cw;
944
0
  int x;
945
946
0
  if (!out)
947
0
    return;
948
949
0
  if (band_start+band_height >= h)
950
0
    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
0
  cw = 10800; /* Limited by how much rowdata we can send at once */
957
0
  if (cw > w)
958
0
    cw = w;
959
960
0
  for (x = 0; x*cw < w; x++)
961
0
  {
962
0
    int col_w = w - cw*x;
963
0
    if (col_w > cw)
964
0
      col_w = cw;
965
966
    /* Top left corner */
967
0
    fz_write_printf(ctx, out, "\033*p%dx%dY", x*cw, band_start);
968
969
    /* Raster height */
970
0
    fz_write_printf(ctx, out, "\033*r%dT", band_height);
971
972
    /* Raster width */
973
0
    fz_write_printf(ctx, out, "\033*r%dS", col_w);
974
975
    /* Destination height */
976
0
    fz_write_printf(ctx, out, "\033*t%dV", band_height*720/xres);
977
978
    /* Destination width */
979
0
    fz_write_printf(ctx, out, "\033*t%dH", col_w*720/xres);
980
981
    /* start raster graphics */
982
    /* 1 = start at cursor position */
983
0
    fz_write_string(ctx, out, "\033*r3A");
984
985
    /* Now output the actual bitmap */
986
    /* Adaptive Compression */
987
0
    fz_write_string(ctx, out, "\033*b5M");
988
989
0
    color_pcl_compress_column(ctx, writer, sp + x * cw * 3, col_w, band_height, stride);
990
0
  }
991
0
}
992
993
static void
994
color_pcl_write_trailer(fz_context *ctx, fz_band_writer *writer_)
995
0
{
996
0
}
997
998
static void
999
color_pcl_drop_band_writer(fz_context *ctx, fz_band_writer *writer_)
1000
0
{
1001
0
  color_pcl_band_writer *writer = (color_pcl_band_writer *)writer_;
1002
0
  fz_free(ctx, writer->linebuf);
1003
0
}
1004
1005
fz_band_writer *fz_new_color_pcl_band_writer(fz_context *ctx, fz_output *out, const fz_pcl_options *options)
1006
0
{
1007
0
  color_pcl_band_writer *writer = fz_new_band_writer(ctx, color_pcl_band_writer, out);
1008
1009
0
  writer->super.header = color_pcl_write_header;
1010
0
  writer->super.band = color_pcl_write_band;
1011
0
  writer->super.trailer = color_pcl_write_trailer;
1012
0
  writer->super.drop = color_pcl_drop_band_writer;
1013
1014
0
  if (options)
1015
0
    writer->options = *options;
1016
0
  else
1017
0
    fz_pcl_preset(ctx, &writer->options, "generic");
1018
1019
0
  return &writer->super;
1020
0
}
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
0
{
1035
0
  int x;
1036
0
  int out_len = 0;
1037
0
  int run;
1038
1039
0
  for (x = 0; x < in_len; x += run)
1040
0
  {
1041
    /* How far do we have to look to find a value that isn't repeated? */
1042
0
    for (run = 1; run < 127 && x+run < in_len; run++)
1043
0
      if (in[0] != in[run])
1044
0
        break;
1045
0
    if (run > 1)
1046
0
    {
1047
      /* We have a run of matching bytes */
1048
0
      out[out_len++] = 257-run;
1049
0
      out[out_len++] = in[0];
1050
0
    }
1051
0
    else
1052
0
    {
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
0
      int i;
1057
1058
      /* How many literals do we need to copy? */
1059
0
      for (; run < 127 && x+run+2 < in_len; run++)
1060
0
        if (in[run] == in[run+1] && in[run] == in[run+2])
1061
0
          break;
1062
      /* Don't leave stragglers at the end */
1063
0
      if (x + run + 2 >= in_len)
1064
0
      {
1065
0
        run = in_len - x;
1066
0
        if (run > 127)
1067
0
          run = 127;
1068
0
      }
1069
0
      out[out_len++] = run-1;
1070
0
      for (i = 0; i < run; i++)
1071
0
      {
1072
0
        out[out_len++] = in[i];
1073
0
      }
1074
0
    }
1075
0
    in += run;
1076
0
  }
1077
1078
0
  return out_len;
1079
0
}
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
0
{
1091
0
  unsigned char *compressed = out;
1092
0
  const unsigned char *cur = in;
1093
0
  const unsigned char *end = in + in_len;
1094
1095
0
  while (cur < end) {   /* Detect a maximum run of unchanged bytes. */
1096
0
    const unsigned char *run = cur;
1097
0
    const unsigned char *diff;
1098
0
    const unsigned char *stop;
1099
0
    int offset, cbyte;
1100
1101
0
    while (cur < end && *cur == *prev) {
1102
0
      cur++, prev++;
1103
0
    }
1104
0
    if (cur == end)
1105
0
      break;   /* rest of row is unchanged */
1106
    /* Detect a run of up to 8 changed bytes. */
1107
    /* We know that *cur != *prev. */
1108
0
    diff = cur;
1109
0
    stop = (end - cur > 8 ? cur + 8 : end);
1110
0
    do
1111
0
    {
1112
0
      *prev++ = *cur++;
1113
0
    }
1114
0
    while (cur < stop && *cur != *prev);
1115
    /* Now [run..diff) are unchanged, and */
1116
    /* [diff..cur) are changed. */
1117
    /* Generate the command byte(s). */
1118
0
    offset = diff - run;
1119
0
    cbyte = (cur - diff - 1) << 5;
1120
0
    if (offset < 31)
1121
0
      *out++ = cbyte + offset;
1122
0
    else {
1123
0
      *out++ = cbyte + 31;
1124
0
      offset -= 31;
1125
0
      while (offset >= 255)
1126
0
        *out++ = 255, offset -= 255;
1127
0
      *out++ = offset;
1128
0
    }
1129
    /* Copy the changed data. */
1130
0
    while (diff < cur)
1131
0
      *out++ = *diff++;
1132
0
  }
1133
0
  return out - compressed;
1134
0
}
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
0
{
1139
0
  fz_band_writer *writer;
1140
1141
0
  if (!bitmap || !out)
1142
0
    return;
1143
1144
0
  writer = fz_new_mono_pcl_band_writer(ctx, out, pcl);
1145
0
  fz_try(ctx)
1146
0
  {
1147
0
    fz_write_header(ctx, writer, bitmap->w, bitmap->h, 1, 0, bitmap->xres, bitmap->yres, 0, NULL, NULL);
1148
0
    fz_write_band(ctx, writer, bitmap->stride, bitmap->h, bitmap->samples);
1149
0
    fz_close_band_writer(ctx, writer);
1150
0
  }
1151
0
  fz_always(ctx)
1152
0
    fz_drop_band_writer(ctx, writer);
1153
0
  fz_catch(ctx)
1154
0
    fz_rethrow(ctx);
1155
0
}
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
0
{
1171
0
  mono_pcl_band_writer *writer = (mono_pcl_band_writer *)writer_;
1172
0
  fz_output *out = writer->super.out;
1173
0
  int w = writer->super.w;
1174
0
  int h = writer->super.h;
1175
0
  int xres = writer->super.xres;
1176
0
  int yres = writer->super.yres;
1177
0
  int line_size;
1178
0
  int max_mode_2_size;
1179
0
  int max_mode_3_size;
1180
1181
0
  if (writer->super.alpha != 0)
1182
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "mono PCL cannot write alpha channel");
1183
0
  if (writer->super.s != 0)
1184
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "mono PCL cannot write spot colors");
1185
0
  if (writer->super.n != 1)
1186
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "mono PCL must be grayscale");
1187
1188
0
  line_size = (w + 7)/8;
1189
0
  max_mode_2_size = line_size + (line_size/127) + 1;
1190
0
  max_mode_3_size = line_size + (line_size/8) + 1;
1191
1192
0
  writer->prev = fz_calloc(ctx, line_size, sizeof(unsigned char));
1193
0
  writer->mode2buf = fz_calloc(ctx, max_mode_2_size, sizeof(unsigned char));
1194
0
  writer->mode3buf = fz_calloc(ctx, max_mode_3_size, sizeof(unsigned char));
1195
0
  writer->num_blank_lines = 0;
1196
0
  writer->top_of_page = 1;
1197
1198
0
  guess_paper_size(&writer->options, w, h, xres, yres);
1199
1200
0
  if (writer->options.features & HACK__IS_A_OCE9050)
1201
0
  {
1202
    /* Enter HPGL/2 mode, begin plot, Initialise (start plot), Enter PCL mode */
1203
0
    fz_write_string(ctx, out, "\033%1BBPIN;\033%1A");
1204
0
  }
1205
1206
0
  pcl_header(ctx, out, &writer->options, 1, xres, yres, w, h);
1207
0
}
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
0
{
1212
0
  mono_pcl_band_writer *writer = (mono_pcl_band_writer *)writer_;
1213
0
  fz_output *out = writer->super.out;
1214
0
  int w = writer->super.w;
1215
0
  int yres = writer->super.yres;
1216
0
  const unsigned char *out_data;
1217
0
  int y, rmask, line_size;
1218
0
  int num_blank_lines;
1219
0
  int compression = -1;
1220
0
  unsigned char *prev = NULL;
1221
0
  unsigned char *mode2buf = NULL;
1222
0
  unsigned char *mode3buf = NULL;
1223
0
  int out_count;
1224
0
  const fz_pcl_options *pcl;
1225
1226
0
  if (!out)
1227
0
    return;
1228
1229
0
  num_blank_lines = writer->num_blank_lines;
1230
0
  rmask = ~0 << (-w & 7);
1231
0
  line_size = (w + 7)/8;
1232
0
  prev = writer->prev;
1233
0
  mode2buf = writer->mode2buf;
1234
0
  mode3buf = writer->mode3buf;
1235
0
  pcl = &writer->options;
1236
1237
  /* Transfer raster graphics. */
1238
0
  for (y = 0; y < band_height; y++, data += ss)
1239
0
  {
1240
0
    const unsigned char *end_data = data + line_size;
1241
1242
0
    if ((end_data[-1] & rmask) == 0)
1243
0
    {
1244
0
      end_data--;
1245
0
      while (end_data > data && end_data[-1] == 0)
1246
0
        end_data--;
1247
0
    }
1248
0
    if (end_data == data)
1249
0
    {
1250
      /* Blank line */
1251
0
      num_blank_lines++;
1252
0
      continue;
1253
0
    }
1254
1255
    /* We've reached a non-blank line. */
1256
    /* Put out a spacing command if necessary. */
1257
0
    if (writer->top_of_page)
1258
0
    {
1259
0
      writer->top_of_page = 0;
1260
      /* We're at the top of a page. */
1261
0
      if (pcl->features & PCL_ANY_SPACING)
1262
0
      {
1263
0
        if (num_blank_lines > 0)
1264
0
          fz_write_printf(ctx, out, "\033*p+%dY", num_blank_lines);
1265
        /* Start raster graphics. */
1266
0
        fz_write_string(ctx, out, "\033*r1A");
1267
0
      }
1268
0
      else if (pcl->features & PCL_MODE_3_COMPRESSION)
1269
0
      {
1270
        /* Start raster graphics. */
1271
0
        fz_write_string(ctx, out, "\033*r1A");
1272
0
        for (; num_blank_lines; num_blank_lines--)
1273
0
          fz_write_string(ctx, out, "\033*b0W");
1274
0
      }
1275
0
      else
1276
0
      {
1277
        /* Start raster graphics. */
1278
0
        fz_write_string(ctx, out, "\033*r1A");
1279
0
        for (; num_blank_lines; num_blank_lines--)
1280
0
          fz_write_string(ctx, out, "\033*bW");
1281
0
      }
1282
0
    }
1283
1284
    /* Skip blank lines if any */
1285
0
    else if (num_blank_lines != 0)
1286
0
    {
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
0
      if ((num_blank_lines < MIN_SKIP_LINES && compression != 3) ||
1296
0
          !(pcl->features & PCL_ANY_SPACING))
1297
0
      {
1298
0
        int mode_3ns = ((pcl->features & PCL_MODE_3_COMPRESSION) && !(pcl->features & PCL_ANY_SPACING));
1299
0
        if (mode_3ns && compression != 2)
1300
0
        {
1301
          /* Switch to mode 2 */
1302
0
          fz_write_string(ctx, out, from3to2);
1303
0
          compression = 2;
1304
0
        }
1305
0
        if (pcl->features & PCL_MODE_3_COMPRESSION)
1306
0
        {
1307
          /* Must clear the seed row. */
1308
0
          fz_write_string(ctx, out, "\033*b1Y");
1309
0
          num_blank_lines--;
1310
0
        }
1311
0
        if (mode_3ns)
1312
0
        {
1313
0
          for (; num_blank_lines; num_blank_lines--)
1314
0
            fz_write_string(ctx, out, "\033*b0W");
1315
0
        }
1316
0
        else
1317
0
        {
1318
0
          for (; num_blank_lines; num_blank_lines--)
1319
0
            fz_write_string(ctx, out, "\033*bW");
1320
0
        }
1321
0
      }
1322
0
      else if (pcl->features & PCL3_SPACING)
1323
0
        fz_write_printf(ctx, out, "\033*p+%dY", num_blank_lines * yres);
1324
0
      else
1325
0
        fz_write_printf(ctx, out, "\033*b%dY", num_blank_lines);
1326
      /* Clear the seed row (only matters for mode 3 compression). */
1327
0
      memset(prev, 0, line_size);
1328
0
    }
1329
0
    num_blank_lines = 0;
1330
1331
    /* Choose the best compression mode for this particular line. */
1332
0
    if (pcl->features & PCL_MODE_3_COMPRESSION)
1333
0
    {
1334
      /* Compression modes 2 and 3 are both available. Try
1335
       * both and see which produces the least output data.
1336
       */
1337
0
      int count3 = mode3compress(mode3buf, data, prev, line_size);
1338
0
      int count2 = mode2compress(mode2buf, data, line_size);
1339
0
      int penalty3 = (compression == 3 ? 0 : penalty_from2to3);
1340
0
      int penalty2 = (compression == 2 ? 0 : penalty_from3to2);
1341
1342
0
      if (count3 + penalty3 < count2 + penalty2)
1343
0
      {
1344
0
        if (compression != 3)
1345
0
          fz_write_string(ctx, out, from2to3);
1346
0
        compression = 3;
1347
0
        out_data = (unsigned char *)mode3buf;
1348
0
        out_count = count3;
1349
0
      }
1350
0
      else
1351
0
      {
1352
0
        if (compression != 2)
1353
0
          fz_write_string(ctx, out, from3to2);
1354
0
        compression = 2;
1355
0
        out_data = (unsigned char *)mode2buf;
1356
0
        out_count = count2;
1357
0
      }
1358
0
    }
1359
0
    else if (pcl->features & PCL_MODE_2_COMPRESSION)
1360
0
    {
1361
0
      out_data = mode2buf;
1362
0
      out_count = mode2compress(mode2buf, data, line_size);
1363
0
    }
1364
0
    else
1365
0
    {
1366
0
      out_data = data;
1367
0
      out_count = line_size;
1368
0
    }
1369
1370
    /* Transfer the data */
1371
0
    fz_write_printf(ctx, out, "\033*b%dW", out_count);
1372
0
    fz_write_data(ctx, out, out_data, out_count);
1373
0
  }
1374
1375
0
  writer->num_blank_lines = num_blank_lines;
1376
0
}
1377
1378
static void
1379
mono_pcl_write_trailer(fz_context *ctx, fz_band_writer *writer_)
1380
0
{
1381
0
  mono_pcl_band_writer *writer = (mono_pcl_band_writer *)writer_;
1382
0
  fz_output *out = writer->super.out;
1383
1384
  /* end raster graphics and eject page */
1385
0
  fz_write_string(ctx, out, "\033*rB\f");
1386
1387
0
  if (writer->options.features & HACK__IS_A_OCE9050)
1388
0
  {
1389
    /* Pen up, pen select, advance full page, reset */
1390
0
    fz_write_string(ctx, out, "\033%1BPUSP0PG;\033E");
1391
0
  }
1392
0
}
1393
1394
static void
1395
mono_pcl_drop_band_writer(fz_context *ctx, fz_band_writer *writer_)
1396
0
{
1397
0
  mono_pcl_band_writer *writer = (mono_pcl_band_writer *)writer_;
1398
1399
0
  fz_free(ctx, writer->prev);
1400
0
  fz_free(ctx, writer->mode2buf);
1401
0
  fz_free(ctx, writer->mode3buf);
1402
0
}
1403
1404
fz_band_writer *fz_new_mono_pcl_band_writer(fz_context *ctx, fz_output *out, const fz_pcl_options *options)
1405
0
{
1406
0
  mono_pcl_band_writer *writer = fz_new_band_writer(ctx, mono_pcl_band_writer, out);
1407
1408
0
  writer->super.header = mono_pcl_write_header;
1409
0
  writer->super.band = mono_pcl_write_band;
1410
0
  writer->super.trailer = mono_pcl_write_trailer;
1411
0
  writer->super.drop = mono_pcl_drop_band_writer;
1412
1413
0
  if (options)
1414
0
    writer->options = *options;
1415
0
  else
1416
0
    fz_pcl_preset(ctx, &writer->options, "generic");
1417
1418
0
  return &writer->super;
1419
0
}
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
0
{
1424
0
  fz_output *out = fz_new_output_with_path(ctx, filename, append);
1425
0
  fz_try(ctx)
1426
0
  {
1427
0
    fz_write_pixmap_as_pcl(ctx, out, pixmap, pcl);
1428
0
    fz_close_output(ctx, out);
1429
0
  }
1430
0
  fz_always(ctx)
1431
0
    fz_drop_output(ctx, out);
1432
0
  fz_catch(ctx)
1433
0
    fz_rethrow(ctx);
1434
0
}
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
0
{
1439
0
  fz_output *out = fz_new_output_with_path(ctx, filename, append);
1440
0
  fz_try(ctx)
1441
0
  {
1442
0
    fz_write_bitmap_as_pcl(ctx, out, bitmap, pcl);
1443
0
    fz_close_output(ctx, out);
1444
0
  }
1445
0
  fz_always(ctx)
1446
0
    fz_drop_output(ctx, out);
1447
0
  fz_catch(ctx)
1448
0
    fz_rethrow(ctx);
1449
0
}
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
0
{
1466
0
  fz_pcl_writer *wri = (fz_pcl_writer*)wri_;
1467
0
  return fz_new_draw_device_with_options(ctx, &wri->draw, mediabox, &wri->pixmap);
1468
0
}
1469
1470
static void
1471
pcl_end_page(fz_context *ctx, fz_document_writer *wri_, fz_device *dev)
1472
0
{
1473
0
  fz_pcl_writer *wri = (fz_pcl_writer*)wri_;
1474
0
  fz_bitmap *bitmap = NULL;
1475
1476
0
  fz_var(bitmap);
1477
1478
0
  fz_try(ctx)
1479
0
  {
1480
0
    fz_close_device(ctx, dev);
1481
0
    if (wri->mono)
1482
0
    {
1483
0
      bitmap = fz_new_bitmap_from_pixmap(ctx, wri->pixmap, NULL);
1484
0
      fz_write_bitmap_as_pcl(ctx, wri->out, bitmap, &wri->pcl);
1485
0
    }
1486
0
    else
1487
0
    {
1488
0
      fz_write_pixmap_as_pcl(ctx, wri->out, wri->pixmap, &wri->pcl);
1489
0
    }
1490
0
  }
1491
0
  fz_always(ctx)
1492
0
  {
1493
0
    fz_drop_device(ctx, dev);
1494
0
    fz_drop_bitmap(ctx, bitmap);
1495
0
    fz_drop_pixmap(ctx, wri->pixmap);
1496
0
    wri->pixmap = NULL;
1497
0
  }
1498
0
  fz_catch(ctx)
1499
0
    fz_rethrow(ctx);
1500
0
}
1501
1502
static void
1503
pcl_close_writer(fz_context *ctx, fz_document_writer *wri_)
1504
0
{
1505
0
  fz_pcl_writer *wri = (fz_pcl_writer*)wri_;
1506
0
  fz_close_output(ctx, wri->out);
1507
0
}
1508
1509
static void
1510
pcl_drop_writer(fz_context *ctx, fz_document_writer *wri_)
1511
0
{
1512
0
  fz_pcl_writer *wri = (fz_pcl_writer*)wri_;
1513
0
  fz_drop_pixmap(ctx, wri->pixmap);
1514
0
  fz_drop_output(ctx, wri->out);
1515
0
}
1516
1517
fz_document_writer *
1518
fz_new_pcl_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
1519
0
{
1520
0
  fz_pcl_writer *wri = NULL;
1521
0
  const char *val;
1522
1523
0
  fz_var(wri);
1524
1525
0
  fz_try(ctx)
1526
0
  {
1527
0
    wri = fz_new_derived_document_writer(ctx, fz_pcl_writer, pcl_begin_page, pcl_end_page, pcl_close_writer, pcl_drop_writer);
1528
0
    fz_parse_draw_options(ctx, &wri->draw, options);
1529
0
    fz_parse_pcl_options(ctx, &wri->pcl, options);
1530
0
    if (fz_has_option(ctx, options, "colorspace", &val))
1531
0
      if (fz_option_eq(val, "mono"))
1532
0
        wri->mono = 1;
1533
0
    wri->out = out;
1534
0
  }
1535
0
  fz_catch(ctx)
1536
0
  {
1537
0
    fz_drop_output(ctx, out);
1538
0
    fz_free(ctx, wri);
1539
0
    fz_rethrow(ctx);
1540
0
  }
1541
1542
0
  return (fz_document_writer*)wri;
1543
0
}
1544
1545
fz_document_writer *
1546
fz_new_pcl_writer(fz_context *ctx, const char *path, const char *options)
1547
0
{
1548
0
  fz_output *out = fz_new_output_with_path(ctx, path ? path : "out.pcl", 0);
1549
0
  return fz_new_pcl_writer_with_output(ctx, out, options);
1550
0
}