Coverage Report

Created: 2026-08-08 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/devices/gdevdljm.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 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.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
/* Generic monochrome H-P DeskJet/LaserJet driver */
17
#include "gdevprn.h"
18
#include "gdevdljm.h"
19
20
/*
21
 * Thanks for various improvements to:
22
 *      Jim Mayer (mayer@wrc.xerox.com)
23
 *      Jan-Mark Wams (jms@cs.vu.nl)
24
 *      Frans van Hoesel (hoesel@chem.rug.nl)
25
 *      George Cameron (g.cameron@biomed.abdn.ac.uk)
26
 *      Nick Duffek (nsd@bbc.com)
27
 * Thanks for the FS-600 driver to:
28
 *  Peter Schildmann (peter.schildmann@etechnik.uni-rostock.de)
29
 * Thanks for the LJIIID duplex capability to:
30
 *      PDP (Philip) Brown (phil@3soft-uk.com)
31
 * Thanks for the OCE 9050 driver to:
32
 *      William Bader (wbader@EECS.Lehigh.Edu)
33
 * Thanks for the LJ4D duplex capability to:
34
 *  Les Johnson <les@infolabs.com>
35
 */
36
37
/* See gdevdljm.h for the definitions of the PCL_ features. */
38
39
/* The number of blank lines that make it worthwhile to reposition */
40
/* the cursor. */
41
37.8k
#define MIN_SKIP_LINES 7
42
43
/* We round up the LINE_SIZE to a multiple of a ulong for faster scanning. */
44
13.3k
#define W sizeof(word)
45
46
/* Send a page to the printer. */
47
int
48
dljet_mono_print_page(gx_device_printer * pdev, gp_file * prn_stream,
49
                      int dots_per_inch, int features, const char *page_init)
50
0
{
51
0
    return dljet_mono_print_page_copies(pdev, prn_stream, 1, dots_per_inch,
52
0
                                        features, page_init, page_init, false);
53
0
}
54
int
55
dljet_mono_print_page_copies(gx_device_printer * pdev, gp_file * prn_stream,
56
                             int num_copies, int dots_per_inch, int features,
57
                             const char *odd_page_init, const char *even_page_init, bool tumble)
58
3.32k
{
59
3.32k
    int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
60
3.32k
    int line_size_words = (line_size + W - 1) / W;
61
3.32k
    size_t storage_size_words = line_size_words * 8;  /* data, out_row, out_row_alt, prev_row */
62
3.32k
    word *storage;
63
3.32k
    word
64
3.32k
        *data_words,
65
3.32k
        *out_row_words,
66
3.32k
        *out_row_alt_words,
67
3.32k
        *prev_row_words;
68
11.4M
#define data ((byte *)data_words)
69
8.21M
#define out_row ((byte *)out_row_words)
70
4.44M
#define out_row_alt ((byte *)out_row_alt_words)
71
4.24M
#define prev_row ((byte *)prev_row_words)
72
3.32k
    byte *out_data;
73
3.32k
    int x_dpi = (int)pdev->x_pixels_per_inch;
74
3.32k
    int y_dpi = (int)pdev->y_pixels_per_inch;
75
3.32k
    int y_dots_per_pixel = dots_per_inch / y_dpi;
76
3.32k
    int num_rows = dev_print_scan_lines(pdev);
77
78
3.32k
    int out_count;
79
3.32k
    int compression = -1;
80
3.32k
    static const char *const from2to3 = "\033*b3M";
81
3.32k
    static const char *const from3to2 = "\033*b2M";
82
3.32k
    int penalty_from2to3 = strlen(from2to3);
83
3.32k
    int penalty_from3to2 = strlen(from3to2);
84
3.32k
    int paper_size = gdev_pcl_paper_size((gx_device *) pdev);
85
3.32k
    int page_orientation = gdev_pcl_page_orientation((gx_device *) pdev);
86
3.32k
    int code = 0;
87
3.32k
    bool dup = pdev->Duplex;
88
3.32k
    bool dupset = pdev->Duplex_set >= 0;
89
90
3.32k
    if (num_copies != 1 && !(features & PCL_CAN_PRINT_COPIES))
91
0
        return gx_default_print_page_copies(pdev, prn_stream, num_copies);
92
3.32k
    storage =
93
3.32k
        (ulong *)gs_alloc_byte_array(pdev->memory, storage_size_words, W,
94
3.32k
                                     "hpjet_print_page");
95
3.32k
    if (storage == 0)    /* can't allocate working area */
96
0
        return_error(gs_error_VMerror);
97
3.32k
    data_words = storage;
98
3.32k
    out_row_words = data_words + (line_size_words * 2);
99
3.32k
    out_row_alt_words = out_row_words + (line_size_words * 2);
100
3.32k
    prev_row_words = out_row_alt_words + (line_size_words * 2);
101
    /* Clear temp storage */
102
3.32k
    memset(data, 0, storage_size_words * W);
103
104
    /* Initialize printer. */
105
3.32k
    if (pdev->PageCount == 0) {
106
1.10k
        if (features & HACK__IS_A_LJET4PJL) {
107
0
            gp_fputs("\033%-12345X@PJL\r\n@PJL ENTER LANGUAGE = PCL\r\n",
108
0
                  prn_stream);
109
0
        }
110
1.10k
        gp_fputs("\033E", prn_stream);  /* reset printer */
111
        /* If the printer supports it, set the paper size */
112
        /* based on the actual requested size. */
113
1.10k
        gp_fprintf(prn_stream, "\033&l%dO", page_orientation);
114
1.10k
        if (features & PCL_CAN_SET_PAPER_SIZE) {
115
1.10k
            gp_fprintf(prn_stream, "\033&l%dA", paper_size);
116
1.10k
        }
117
        /* If printer can duplex, set duplex mode appropriately. */
118
1.10k
        if (features & PCL_HAS_DUPLEX) {
119
0
            if (dupset && dup && !tumble)
120
0
                gp_fputs("\033&l1S", prn_stream);
121
0
       else if (dupset && dup && tumble)
122
0
                gp_fputs("\033&l2S", prn_stream);
123
0
            else if (dupset && !dup)
124
0
                gp_fputs("\033&l0S", prn_stream);
125
0
            else    /* default to duplex for this printer */
126
0
                gp_fputs("\033&l1S", prn_stream);
127
0
        }
128
1.10k
    }
129
    /* Put out per-page initialization. */
130
    /*
131
       Modified by karsten@sengebusch.de
132
       in duplex mode the sheet is alread in process, so there are some
133
       commands which must not be sent to the printer for the 2nd page,
134
       as this commands will cause the printer to eject the sheet with
135
       only the 1st page printed. This commands are:
136
       \033&l%dA (setting paper size)
137
       \033&l%dH (setting paper tray)
138
       in simplex mode we set this parameters for each page,
139
       in duplex mode we set this parameters for each odd page
140
    */
141
142
3.32k
    if ((features & PCL_HAS_DUPLEX) && dupset && dup) {
143
       /* We are printing duplex, so change margins as needed */
144
0
       if (( (pdev->PageCount/num_copies)%2)==0) {
145
0
          gp_fprintf(prn_stream, "\033&l%dO", page_orientation);
146
0
          if (features & PCL_CAN_SET_PAPER_SIZE) {
147
0
              gp_fprintf(prn_stream, "\033&l%dA", paper_size);
148
0
          }
149
0
          gp_fputs("\033&l0l0E", prn_stream);
150
0
          gp_fputs(odd_page_init, prn_stream);
151
0
       } else
152
0
          gp_fputs(even_page_init, prn_stream);
153
3.32k
    } else {
154
3.32k
        gp_fprintf(prn_stream, "\033&l%dO", page_orientation);
155
3.32k
        if (features & PCL_CAN_SET_PAPER_SIZE){
156
3.32k
            gp_fprintf(prn_stream, "\033&l%dA", paper_size);
157
3.32k
        }
158
3.32k
        gp_fputs("\033&l0l0E", prn_stream);
159
3.32k
        gp_fputs(odd_page_init, prn_stream);
160
3.32k
    }
161
162
3.32k
    gp_fprintf(prn_stream, "\033&l%dX", num_copies);  /* # of copies */
163
164
    /* End raster graphics, position cursor at top. */
165
3.32k
    gp_fputs("\033*rB\033*p0x0Y", prn_stream);
166
167
    /* The DeskJet and DeskJet Plus reset everything upon */
168
    /* receiving \033*rB, so we must reinitialize graphics mode. */
169
3.32k
    if (features & PCL_END_GRAPHICS_DOES_RESET) {
170
0
        gp_fputs(odd_page_init, prn_stream); /* Assume this does the right thing */
171
0
        gp_fprintf(prn_stream, "\033&l%dX", num_copies);  /* # of copies */
172
0
    }
173
174
    /* Set resolution. */
175
3.32k
    gp_fprintf(prn_stream, "\033*t%dR", x_dpi);
176
177
    /* Send each scan line in turn */
178
3.32k
    {
179
3.32k
        int lnum;
180
3.32k
        int num_blank_lines = 0;
181
3.32k
        word rmask = ~(word) 0 << (-pdev->width & (W * 8 - 1));
182
183
        /* Transfer raster graphics. */
184
7.20M
        for (lnum = 0; lnum < num_rows; lnum++) {
185
7.20M
            register word *end_data =
186
7.20M
            data_words + line_size_words;
187
188
7.20M
            code = gdev_prn_copy_scan_lines(pdev, lnum,
189
7.20M
                                            (byte *) data, line_size);
190
7.20M
            if (code < 0)
191
0
                break;
192
            /* Mask off 1-bits beyond the line width. */
193
7.20M
            end_data[-1] &= rmask;
194
            /* Remove trailing 0s. */
195
112M
            while (end_data > data_words && end_data[-1] == 0)
196
105M
                end_data--;
197
7.20M
            if (end_data == data_words) { /* Blank line */
198
2.98M
                num_blank_lines++;
199
2.98M
                continue;
200
2.98M
            }
201
            /* We've reached a non-blank line. */
202
            /* Put out a spacing command if necessary. */
203
4.22M
            if (num_blank_lines == lnum) {
204
                /* We're at the top of a page. */
205
3.21k
                if (features & PCL_ANY_SPACING) {
206
3.21k
                    if (num_blank_lines > 0)
207
3.21k
                        gp_fprintf(prn_stream, "\033*p+%dY",
208
3.21k
                                num_blank_lines * y_dots_per_pixel);
209
                    /* Start raster graphics. */
210
3.21k
                    gp_fputs("\033*r1A", prn_stream);
211
3.21k
                } else if (features & PCL_MODE_3_COMPRESSION) {
212
                    /* Start raster graphics. */
213
0
                    gp_fputs("\033*r1A", prn_stream);
214
0
#if 1       /* don't waste paper */
215
0
                    if (num_blank_lines > 0)
216
0
                        gp_fputs("\033*b0W", prn_stream);
217
0
                    num_blank_lines = 0;
218
#else
219
                    for (; num_blank_lines; num_blank_lines--)
220
                        fputs("\033*b0W", prn_stream);
221
#endif
222
0
                } else {
223
                    /* Start raster graphics. */
224
0
                    gp_fputs("\033*r1A", prn_stream);
225
0
                    for (; num_blank_lines; num_blank_lines--)
226
0
                        gp_fputs("\033*bW", prn_stream);
227
0
                }
228
3.21k
            }
229
            /* Skip blank lines if any */
230
4.21M
            else if (num_blank_lines != 0) {
231
                /*
232
                 * Moving down from current position causes head motion
233
                 * on the DeskJet, so if the number of lines is small,
234
                 * we're better off printing blanks.
235
                 */
236
                /*
237
                 * For Canon LBP4i and some others, <ESC>*b<n>Y doesn't
238
                 * properly clear the seed row if we are in compression mode
239
                 * 3.
240
                 */
241
18.9k
                if ((num_blank_lines < MIN_SKIP_LINES && compression != 3) ||
242
18.3k
                    !(features & PCL_ANY_SPACING)
243
18.9k
                    ) {
244
603
                    bool mode_3ns =
245
603
                        (features & PCL_MODE_3_COMPRESSION) &&
246
603
                        !(features & PCL_ANY_SPACING);
247
248
603
                    if (mode_3ns && compression != 2) {
249
                        /* Switch to mode 2 */
250
0
                        gp_fputs(from3to2, prn_stream);
251
0
                        compression = 2;
252
0
                    }
253
603
                    if (features & PCL_MODE_3_COMPRESSION) {
254
                        /* Must clear the seed row. */
255
603
                        gp_fputs("\033*b1Y", prn_stream);
256
603
                        num_blank_lines--;
257
603
                    }
258
603
                    if (mode_3ns) {
259
0
                        for (; num_blank_lines; num_blank_lines--)
260
0
                            gp_fputs("\033*b0W", prn_stream);
261
603
                    } else {
262
633
                        for (; num_blank_lines; num_blank_lines--)
263
30
                            gp_fputs("\033*bW", prn_stream);
264
603
                    }
265
18.3k
                } else if (features & PCL3_SPACING) {
266
0
                    gp_fprintf(prn_stream, "\033*p+%dY",
267
0
                               num_blank_lines * y_dots_per_pixel);
268
18.3k
                } else {
269
18.3k
                    gp_fprintf(prn_stream, "\033*b%dY",
270
18.3k
                               num_blank_lines);
271
18.3k
                }
272
                /* Clear the seed row (only matters for */
273
                /* mode 3 compression). */
274
18.9k
                memset(prev_row, 0, line_size);
275
18.9k
            }
276
4.22M
            num_blank_lines = 0;
277
278
            /* Choose the best compression mode */
279
            /* for this particular line. */
280
4.22M
            if (features & PCL_MODE_3_COMPRESSION) {
281
                /* Compression modes 2 and 3 are both */
282
                /* available.  Try both and see which one */
283
                /* produces the least output data. */
284
4.22M
                int count3 = gdev_pcl_mode3compress(line_size, data,
285
4.22M
                                                    prev_row, out_row);
286
4.22M
                int count2 = gdev_pcl_mode2compress(data_words, end_data,
287
4.22M
                                                    out_row_alt);
288
4.22M
                int penalty3 =
289
4.22M
                    (compression == 3 ? 0 : penalty_from2to3);
290
4.22M
                int penalty2 =
291
4.22M
                    (compression == 2 ? 0 : penalty_from3to2);
292
293
4.22M
                if (count3 + penalty3 < count2 + penalty2) {
294
3.99M
                    if (compression != 3)
295
111k
                        gp_fputs(from2to3, prn_stream);
296
3.99M
                    compression = 3;
297
3.99M
                    out_data = out_row;
298
3.99M
                    out_count = count3;
299
3.99M
                } else {
300
228k
                    if (compression != 2)
301
109k
                        gp_fputs(from3to2, prn_stream);
302
228k
                    compression = 2;
303
228k
                    out_data = out_row_alt;
304
228k
                    out_count = count2;
305
228k
                }
306
4.22M
            } else if (features & PCL_MODE_2_COMPRESSION) {
307
0
                out_data = out_row;
308
0
                out_count = gdev_pcl_mode2compress(data_words, end_data,
309
0
                                                   out_row);
310
0
            } else {
311
0
                out_data = data;
312
0
                out_count = (byte *) end_data - data;
313
0
            }
314
315
            /* Transfer the data */
316
4.22M
            gp_fprintf(prn_stream, "\033*b%dW", out_count);
317
4.22M
            gp_fwrite(out_data, sizeof(byte), out_count,
318
4.22M
                      prn_stream);
319
4.22M
        }
320
3.32k
    }
321
322
    /* end raster graphics and eject page */
323
3.32k
    gp_fputs("\033*rB\f", prn_stream);
324
325
    /* free temporary storage */
326
3.32k
    gs_free_object(pdev->memory, storage, "hpjet_print_page");
327
328
3.32k
    return code;
329
3.32k
}