Coverage Report

Created: 2025-04-22 06:20

/src/libspectre/ghostscript/devices/gdevepsn.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2020 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
13
   CA 94945, U.S.A., +1(415)492-9861, for further information.
14
*/
15
16
17
/*
18
 * Epson (and similar) dot-matrix printer driver for Ghostscript.
19
 *
20
 * Four devices are defined here: 'epson', 'eps9mid', 'eps9high', and 'ibmpro'.
21
 * The 'epson' device is the generic device, for 9-pin and 24-pin printers.
22
 * 'eps9high' is a special mode for 9-pin printers where scan lines are
23
 * interleaved in multiple passes to produce high vertical resolution at
24
 * the expense of several passes of the print head.  'eps9mid' is a special
25
 * mode for 9 pin printers too, scan lines are interleaved but with next
26
 * vertical line. 'ibmpro' is for the IBM ProPrinter, which has slightly
27
 * (but only slightly) different control codes.
28
 *
29
 * Thanks to:
30
 *  David Wexelblat (dwex@mtgzfs3.att.com) for the 'eps9high' code;
31
 *  Guenther Thomsen (thomsen@cs.tu-berlin.de) for the 'eps9mid' code;
32
 *  James W. Birdsall (jwbirdsa@picarefy.picarefy.com) for the
33
 *    'ibmpro' modifications;
34
 *  Russell J. Lang (rjl@aladdin.com) for the 180x60 and 240x180 dpi
35
 *    enhancements.
36
 */
37
#include "gdevprn.h"
38
39
/*
40
 * Define whether the printer is archaic -- so old that it doesn't
41
 * support settable tabs, pitch, or left margin.  (This should be a
42
 * run-time property....)  Note: the IBM ProPrinter is archaic.
43
 */
44
/*#define ARCHAIC 1*/
45
46
/*
47
 * Define whether the printer is a Panasonic 9-pin printer,
48
 * which sometimes doesn't recognize a horizontal tab command
49
 * when a line contains a lot of graphics commands,
50
 * requiring a "backspace, space" sequence before a tab.
51
 */
52
/*#define TAB_HICCUP 1*/
53
54
/*
55
 * Define the minimum distance for which it's worth converting white space
56
 * into a tab.  This can be specified in pixels (to save transmission time),
57
 * in tenths of an inch (for printers where tabs provoke actual head motion),
58
 * or both.  The distance must meet BOTH criteria for the driver to tab,
59
 * so an irrelevant criterion should be set to 0 rather than infinite.
60
 */
61
#define MIN_TAB_PIXELS 10
62
0
#define MIN_TAB_10THS 15
63
64
/*
65
 * Valid values for X_DPI:
66
 *
67
 *    For 9-pin printers: 60, 120, 240
68
 *    For 24-pin printers: 60, 120, 180, 240, 360
69
 *
70
 * The value specified at compile time is the default value used if the
71
 * user does not specify a resolution at runtime.
72
 */
73
#ifndef X_DPI
74
#  define X_DPI 240
75
#endif
76
77
/*
78
 * For Y_DPI, a given printer will support a base resolution of 60 or 72;
79
 * check the printer manual.  The Y_DPI value must be a multiple of this
80
 * base resolution.  Valid values for Y_DPI:
81
 *
82
 *    For 9-pin printers: 1*base_res
83
 *    For 24-pin printers: 1*base_res, 3*base_res
84
 *
85
 * The value specified at compile time is the default value used if the
86
 * user does not specify a resolution at runtime.
87
 */
88
89
#ifndef Y_BASERES
90
#  define Y_BASERES 72
91
#endif
92
#ifndef Y_DPI
93
#  define Y_DPI (1*Y_BASERES)
94
#endif
95
96
/* The device descriptors */
97
static dev_proc_print_page(epson_print_page);
98
static dev_proc_print_page(eps9mid_print_page);
99
static dev_proc_print_page(eps9high_print_page);
100
static dev_proc_print_page(ibmpro_print_page);
101
102
/* Standard Epson device */
103
const gx_device_printer far_data gs_epson_device =
104
  prn_device(prn_bg_procs, "epson", /* The print_page proc is compatible with allowing bg printing */
105
        DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
106
        X_DPI, Y_DPI,
107
        0.25, 0.02, 0.25, 0.4,      /* margins */
108
        1, epson_print_page);
109
110
/* Mid-res (interleaved, 1 pass per line) 9-pin device */
111
const gx_device_printer far_data gs_eps9mid_device =
112
  prn_device(prn_bg_procs, "eps9mid", /* The print_page proc is compatible with allowing bg printing */
113
        DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
114
        X_DPI, 3*Y_BASERES,
115
        0.2, 0.0, 0, 0.0,     /* margins */
116
        1, eps9mid_print_page);
117
118
/* High-res (interleaved) 9-pin device */
119
const gx_device_printer far_data gs_eps9high_device =
120
  prn_device(prn_bg_procs, "eps9high",  /* The print_page proc is compatible with allowing bg printing */
121
        DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
122
        X_DPI, 3*Y_BASERES,
123
        0.2, 0.0, 0.0, 0.0,     /* margins */
124
        1, eps9high_print_page);
125
126
/* IBM ProPrinter device */
127
const gx_device_printer far_data gs_ibmpro_device =
128
  prn_device(prn_bg_procs, "ibmpro",  /* The print_page proc is compatible with allowing bg printing */
129
        DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
130
        X_DPI, Y_DPI,
131
        0.2, 0.0, 0.0, 0.0,     /* margins */
132
        1, ibmpro_print_page);
133
134
/* ------ Driver procedures ------ */
135
136
/* Forward references */
137
static void eps_output_run(byte *, int, int, char, gp_file *, int);
138
139
/* Send the page to the printer. */
140
0
#define DD 0x40        /* double density flag */
141
static int
142
eps_print_page(gx_device_printer *pdev, gp_file *prn_stream, int y_9pin_high,
143
  const char *init_string, int init_length, const char *end_string,
144
  int archaic, int tab_hiccup)
145
0
{
146
0
        static const char graphics_modes_9[5] =
147
0
        {
148
0
        -1, 0 /*60*/, 1 /*120*/, 7 /*180*/, DD+3 /*240*/
149
0
        };
150
151
0
        static const char graphics_modes_24[7] =
152
0
        {
153
0
        -1, 32 /*60*/, 33 /*120*/, 39 /*180*/,
154
0
        DD+35 /*240*/, -1, DD+40 /*360*/
155
0
        };
156
157
0
        int y_24pin = (y_9pin_high ? 0 : pdev->y_pixels_per_inch > 72);
158
0
        int in_y_mult = ((y_24pin | y_9pin_high) ? 3 : 1);
159
0
        int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
160
        /* Note that in_size is a multiple of 8. */
161
0
        int in_size = line_size * (8 * in_y_mult);
162
0
        byte *buf1 = NULL;
163
0
        byte *buf2 = NULL;
164
0
        byte *in;
165
0
        byte *out;
166
0
        int out_y_mult = (y_24pin ? 3 : 1);
167
0
        int x_dpi = (int)pdev->x_pixels_per_inch;
168
0
        int y_passes = (y_9pin_high ? 3 : 1);
169
0
        int dots_per_space = x_dpi / 10;  /* pica space = 1/10" */
170
0
        int bytes_per_space = dots_per_space * out_y_mult;
171
0
        int tab_min_pixels = x_dpi * MIN_TAB_10THS / 10;
172
0
        int skip = 0, lnum = 0, pass, ypass;
173
0
        int code = 0;
174
        
175
0
        char start_graphics;
176
0
        int first_pass;
177
0
        int last_pass;
178
179
0
        if (y_24pin) {
180
0
            if (x_dpi / 60 >= sizeof(graphics_modes_24) / sizeof(graphics_modes_24[0])) {
181
0
                return_error(gs_error_rangecheck);
182
0
            }
183
0
            start_graphics = graphics_modes_24[x_dpi / 60];
184
0
        }
185
0
        else {
186
0
            if (x_dpi / 60 >= sizeof(graphics_modes_9) / sizeof(graphics_modes_9[0])) {
187
0
                return_error(gs_error_rangecheck);
188
0
            }
189
0
            start_graphics = graphics_modes_9[x_dpi / 60];
190
0
        }
191
0
        first_pass = (start_graphics & DD ? 1 : 0);
192
0
        last_pass = first_pass * (y_9pin_high == 2 ? 1 : 2);
193
194
0
        if (bytes_per_space == 0) {
195
            /* This avoids divide by zero later on, bug 701843. */
196
0
            return_error(gs_error_rangecheck);
197
0
        }
198
        
199
0
        buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "eps_print_page(buf1)");
200
0
        buf2 = (byte *)gs_malloc(pdev->memory, in_size, 1, "eps_print_page(buf2)");
201
0
        in = buf1;
202
0
        out = buf2;
203
        
204
205
        /* Check allocations */
206
0
        if ( buf1 == 0 || buf2 == 0 ) {
207
0
            code = gs_error_VMerror;
208
0
            goto xit;
209
0
        }
210
211
        /* Initialize the printer and reset the margins. */
212
0
        gp_fwrite(init_string, 1, init_length, prn_stream);
213
0
        if ( init_string[init_length - 1] == 'Q' )
214
0
                gp_fputc((int)(pdev->width / pdev->x_pixels_per_inch * 10) + 2,
215
0
                      prn_stream);
216
217
        /* Calculate the minimum tab distance. */
218
0
        if ( tab_min_pixels < max(MIN_TAB_PIXELS, 3) )
219
0
                tab_min_pixels = max(MIN_TAB_PIXELS, 3);
220
0
        tab_min_pixels -= tab_min_pixels % 3; /* simplify life */
221
222
        /* Print lines of graphics */
223
0
        while ( lnum < pdev->height )
224
0
        {
225
0
                byte *in_data;
226
0
                byte *inp;
227
0
                byte *in_end;
228
0
                byte *out_end = NULL;
229
0
                byte *out_blk;
230
0
                register byte *outp;
231
0
                int lcnt;
232
233
                /* Copy 1 scan line and test for all zero. */
234
0
                code = gdev_prn_get_bits(pdev, lnum, in, &in_data);
235
0
                if (code < 0)
236
0
                    goto xit;
237
0
                if ( in_data[0] == 0 &&
238
0
                     !memcmp((char *)in_data, (char *)in_data + 1, line_size - 1)
239
0
                   )
240
0
                {
241
0
                        lnum++;
242
0
                        skip += 3 / in_y_mult;
243
0
                        continue;
244
0
                }
245
246
                /* Vertical tab to the appropriate position. */
247
0
                while ( skip > 255 )
248
0
                {
249
0
                        gp_fputs("\033J\377", prn_stream);
250
0
                        skip -= 255;
251
0
                }
252
0
                if ( skip )
253
0
                {
254
0
                        gp_fprintf(prn_stream, "\033J%c", skip);
255
0
                }
256
257
                /* Copy the the scan lines. */
258
0
                code = lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
259
0
                if (code < 0)
260
0
                    goto xit;
261
0
                if ( lcnt < 8 * in_y_mult )
262
0
                { /* Pad with lines of zeros. */
263
0
                        memset(in + lcnt * line_size, 0,
264
0
                               in_size - lcnt * line_size);
265
0
                }
266
267
0
                if ( y_9pin_high == 2 )
268
0
                { /* Force printing of every dot in one pass */
269
                        /* by reducing vertical resolution */
270
                        /* (ORing with the next line of data). */
271
                        /* This is necessary because some Epson compatibles */
272
                        /* can't print neighboring dots. */
273
0
                        int i;
274
0
                        for ( i = 0; i < line_size * in_y_mult; ++i )
275
0
                                in[i] |= in[i + line_size];
276
0
                }
277
278
0
                if ( y_9pin_high )
279
0
                { /* Shuffle the scan lines */
280
0
                        byte *p;
281
0
                        int i;
282
0
                        static const char index[] =
283
0
                        {  0,  8, 16,  1,  9, 17,
284
0
                           2, 10, 18,  3, 11, 19,
285
0
                           4, 12, 20,  5, 13, 21,
286
0
                           6, 14, 22,  7, 15, 23
287
0
                        };
288
289
0
                        for ( i = 0; i < 24; i++ )
290
0
                        {
291
0
                                memcpy(out+(index[i]*line_size),
292
0
                                       in+(i*line_size), line_size);
293
0
                        }
294
0
                        p = in;
295
0
                        in = out;
296
0
                        out = p;
297
0
                }
298
299
0
        for ( ypass = 0; ypass < y_passes; ypass++ )
300
0
        {
301
0
            for ( pass = first_pass; pass <= last_pass; pass++ )
302
0
            {
303
                /* We have to 'transpose' blocks of 8 pixels x 8 lines, */
304
                /* because that's how the printer wants the data. */
305
                /* If we are in a 24-pin mode, we have to transpose */
306
                /* groups of 3 lines at a time. */
307
308
0
                if ( pass == first_pass )
309
0
                {
310
0
                    out_end = out;
311
0
                    inp = in;
312
0
                    in_end = inp + line_size;
313
314
0
                    if ( y_24pin )
315
0
                    {
316
0
                        for ( ; inp < in_end; inp++, out_end += 24 )
317
0
                        {
318
0
                            gdev_prn_transpose_8x8(inp, line_size, out_end, 3);
319
0
                            gdev_prn_transpose_8x8(inp + line_size * 8,
320
0
                                                   line_size, out_end + 1, 3);
321
0
                            gdev_prn_transpose_8x8(inp + line_size * 16,
322
0
                                                   line_size, out_end + 2, 3);
323
0
                        }
324
                        /* Remove trailing 0s. */
325
0
                        while ( out_end > out && out_end[-1] == 0 &&
326
0
                                out_end[-2] == 0 && out_end[-3] == 0)
327
0
                        {
328
0
                             out_end -= 3;
329
0
                        }
330
0
                    }
331
0
                    else
332
0
                    {
333
0
                        for ( ; inp < in_end; inp++, out_end += 8 )
334
0
                        {
335
0
                            gdev_prn_transpose_8x8(inp + (ypass * 8*line_size),
336
0
                                                   line_size, out_end, 1);
337
0
                        }
338
                        /* Remove trailing 0s. */
339
0
                        while ( out_end > out && out_end[-1] == 0 )
340
0
                        {
341
0
                            out_end--;
342
0
                        }
343
0
                    }
344
0
                }
345
346
0
                for ( out_blk = outp = out; outp < out_end; )
347
0
                {
348
                    /* Skip a run of leading 0s.  At least */
349
                    /* tab_min_pixels are needed to make tabbing */
350
                    /* worth it.  We do everything by 3's to */
351
                    /* avoid having to make different cases */
352
                    /* for 9- and 24-pin. */
353
0
                   if ( !archaic &&
354
0
                        *outp == 0 && out_end - outp >= tab_min_pixels &&
355
0
                        (outp[1] | outp[2]) == 0 &&
356
0
                        !memcmp((char *)outp, (char *)outp + 3,
357
0
                                tab_min_pixels - 3)
358
0
                      )
359
0
                    {
360
0
                        byte *zp = outp;
361
0
                        int tpos;
362
0
                        byte *newp;
363
364
0
                        outp += tab_min_pixels;
365
0
                        while ( outp + 3 <= out_end &&
366
0
                                *outp == 0 &&
367
0
                                outp[1] == 0 && outp[2] == 0 )
368
0
                        {
369
0
                            outp += 3;
370
0
                        }
371
0
                        tpos = (outp - out) / bytes_per_space;
372
0
                        newp = out + tpos * bytes_per_space;
373
0
                        if ( newp > zp + 10 )
374
0
                        {
375
                            /* Output preceding bit data.*/
376
0
                            if ( zp > out_blk )
377
0
                            {
378
                                /* only false at beginning of line */
379
0
                                eps_output_run(out_blk, (int)(zp - out_blk),
380
0
                                               out_y_mult, start_graphics,
381
0
                                               prn_stream,
382
0
                                               (y_9pin_high == 2 ?
383
0
                                                (1 + ypass) & 1 : pass));
384
0
                            }
385
                            /* Tab over to the appropriate position. */
386
0
                            if ( tab_hiccup )
387
0
                              gp_fputs("\010 ", prn_stream); /* bksp, space */
388
                            /* The following statement is broken up */
389
                            /* to work around a bug in emx/gcc. */
390
0
                            gp_fprintf(prn_stream, "\033D%c", tpos);
391
0
                            gp_fputc(0, prn_stream);
392
0
                            gp_fputc('\t', prn_stream);
393
0
                            out_blk = outp = newp;
394
0
                        }
395
0
                    }
396
0
                    else
397
0
                    {
398
0
                        outp += out_y_mult;
399
0
                    }
400
0
                }
401
0
                if ( outp > out_blk )
402
0
                {
403
0
                    eps_output_run(out_blk, (int)(outp - out_blk),
404
0
                                   out_y_mult, start_graphics,
405
0
                                   prn_stream,
406
0
                                   (y_9pin_high == 2 ? (1 + ypass) & 1 : pass));
407
0
                }
408
409
0
                gp_fputc('\r', prn_stream);
410
0
            }
411
0
            if ( ypass < y_passes - 1 )
412
0
                gp_fputs("\033J\001", prn_stream);
413
0
        }
414
0
        skip = 24 - y_passes + 1;   /* no skip on last Y pass */
415
0
        lnum += 8 * in_y_mult;
416
0
        }
417
418
        /* Eject the page and reinitialize the printer */
419
0
        gp_fputs(end_string, prn_stream);
420
0
        gp_fflush(prn_stream);
421
422
0
xit:
423
0
        if ( buf1 )
424
0
            gs_free(pdev->memory, (char *)buf1, in_size, 1, "eps_print_page(buf1)");
425
0
        if ( buf2 )
426
0
            gs_free(pdev->memory, (char *)buf2, in_size, 1, "eps_print_page(buf2)");
427
0
        if (code < 0)
428
0
            return_error(code);
429
0
        return code;
430
0
}
431
432
/* Output a single graphics command. */
433
/* pass=0 for all columns, 1 for even columns, 2 for odd columns. */
434
static void
435
eps_output_run(byte *data, int count, int y_mult,
436
  char start_graphics, gp_file *prn_stream, int pass)
437
0
{
438
0
        int xcount = count / y_mult;
439
440
0
        gp_fputc(033, prn_stream);
441
0
        if ( !(start_graphics & ~3) )
442
0
        {
443
0
                gp_fputc("KLYZ"[(int)start_graphics], prn_stream);
444
0
        }
445
0
        else
446
0
        {
447
0
                gp_fputc('*', prn_stream);
448
0
                gp_fputc(start_graphics & ~DD, prn_stream);
449
0
        }
450
0
        gp_fputc(xcount & 0xff, prn_stream);
451
0
        gp_fputc(xcount >> 8, prn_stream);
452
0
        if ( !pass )
453
0
        {
454
0
                gp_fwrite(data, 1, count, prn_stream);
455
0
        }
456
0
        else
457
0
        {
458
                /* Only write every other column of y_mult bytes. */
459
0
                int which = pass;
460
0
                register byte *dp = data;
461
0
                register int i, j;
462
463
0
                for ( i = 0; i < xcount; i++, which++ )
464
0
                {
465
0
                        for ( j = 0; j < y_mult; j++, dp++ )
466
0
                        {
467
0
                                gp_fputc(((which & 1) ? *dp : 0), prn_stream);
468
0
                        }
469
0
                }
470
0
        }
471
0
}
472
473
/* The print_page procedures are here, to avoid a forward reference. */
474
#ifndef ARCHAIC
475
0
#  define ARCHAIC 0
476
#endif
477
#ifndef TAB_HICCUP
478
0
#  define TAB_HICCUP 0
479
#endif
480
481
0
#define ESC 0x1b
482
static const char eps_init_string[] = {
483
#if ARCHAIC
484
        ESC, '@', 022 /*^R*/, ESC, 'Q'
485
#else
486
        ESC, '@', ESC, 'P', ESC, 'l', 0, '\r', ESC, 'Q'
487
#endif
488
};
489
490
static int
491
epson_print_page(gx_device_printer *pdev, gp_file *prn_stream)
492
0
{
493
0
        return eps_print_page(pdev, prn_stream, 0, eps_init_string,
494
0
                              sizeof(eps_init_string), "\f\033@",
495
0
                              ARCHAIC, TAB_HICCUP);
496
0
}
497
498
static int
499
eps9high_print_page(gx_device_printer *pdev, gp_file *prn_stream)
500
0
{
501
0
        return eps_print_page(pdev, prn_stream, 1, eps_init_string,
502
0
                              sizeof(eps_init_string), "\f\033@",
503
0
                              ARCHAIC, TAB_HICCUP);
504
0
}
505
506
static int
507
eps9mid_print_page(gx_device_printer *pdev, gp_file *prn_stream)
508
0
{
509
0
        return eps_print_page(pdev, prn_stream, 2, eps_init_string,
510
0
                              sizeof(eps_init_string), "\f\033@",
511
0
                              ARCHAIC, TAB_HICCUP);
512
0
}
513
514
static int
515
ibmpro_print_page(gx_device_printer *pdev, gp_file *prn_stream)
516
0
{
517
    /*
518
     * IBM Proprinter Guide to Operations, p. 4-5: "DC1: Select Printer: Sets
519
     * the printer to accept data from your computer."  Prevents printer from
520
     * interpreting first characters as literal text.
521
     */
522
0
#define DC1 0x11
523
0
        static const char ibmpro_init_string[] = {
524
0
                DC1, ESC, '3', 0x30
525
0
        };
526
0
#undef DC1
527
0
        return eps_print_page(pdev, prn_stream, 0, ibmpro_init_string,
528
0
                              sizeof(ibmpro_init_string), "\f", 1, 0);
529
0
}