Coverage Report

Created: 2026-09-14 07:34

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
36.9k
#define MIN_SKIP_LINES 7
42
43
/* We round up the LINE_SIZE to a multiple of a ulong for faster scanning. */
44
12.8k
#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.20k
{
59
3.20k
    int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
60
3.20k
    int line_size_words = (line_size + W - 1) / W;
61
3.20k
    size_t storage_size_words = line_size_words * 8;  /* data, out_row, out_row_alt, prev_row */
62
3.20k
    word *storage;
63
3.20k
    word
64
3.20k
        *data_words,
65
3.20k
        *out_row_words,
66
3.20k
        *out_row_alt_words,
67
3.20k
        *prev_row_words;
68
11.0M
#define data ((byte *)data_words)
69
7.89M
#define out_row ((byte *)out_row_words)
70
4.28M
#define out_row_alt ((byte *)out_row_alt_words)
71
4.07M
#define prev_row ((byte *)prev_row_words)
72
3.20k
    byte *out_data;
73
3.20k
    int x_dpi = (int)pdev->x_pixels_per_inch;
74
3.20k
    int y_dpi = (int)pdev->y_pixels_per_inch;
75
3.20k
    int y_dots_per_pixel = dots_per_inch / y_dpi;
76
3.20k
    int num_rows = dev_print_scan_lines(pdev);
77
78
3.20k
    int out_count;
79
3.20k
    int compression = -1;
80
3.20k
    static const char *const from2to3 = "\033*b3M";
81
3.20k
    static const char *const from3to2 = "\033*b2M";
82
3.20k
    int penalty_from2to3 = strlen(from2to3);
83
3.20k
    int penalty_from3to2 = strlen(from3to2);
84
3.20k
    int paper_size = gdev_pcl_paper_size((gx_device *) pdev);
85
3.20k
    int page_orientation = gdev_pcl_page_orientation((gx_device *) pdev);
86
3.20k
    int code = 0;
87
3.20k
    bool dup = pdev->Duplex;
88
3.20k
    bool dupset = pdev->Duplex_set >= 0;
89
90
3.20k
    if (num_copies != 1 && !(features & PCL_CAN_PRINT_COPIES))
91
0
        return gx_default_print_page_copies(pdev, prn_stream, num_copies);
92
3.20k
    storage =
93
3.20k
        (ulong *)gs_alloc_byte_array(pdev->memory, storage_size_words, W,
94
3.20k
                                     "hpjet_print_page");
95
3.20k
    if (storage == 0)    /* can't allocate working area */
96
0
        return_error(gs_error_VMerror);
97
3.20k
    data_words = storage;
98
3.20k
    out_row_words = data_words + (line_size_words * 2);
99
3.20k
    out_row_alt_words = out_row_words + (line_size_words * 2);
100
3.20k
    prev_row_words = out_row_alt_words + (line_size_words * 2);
101
    /* Clear temp storage */
102
3.20k
    memset(data, 0, storage_size_words * W);
103
104
    /* Initialize printer. */
105
3.20k
    if (pdev->PageCount == 0) {
106
1.07k
        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.07k
        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.07k
        gp_fprintf(prn_stream, "\033&l%dO", page_orientation);
114
1.07k
        if (features & PCL_CAN_SET_PAPER_SIZE) {
115
1.07k
            gp_fprintf(prn_stream, "\033&l%dA", paper_size);
116
1.07k
        }
117
        /* If printer can duplex, set duplex mode appropriately. */
118
1.07k
        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.07k
    }
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.20k
    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.20k
    } else {
154
3.20k
        gp_fprintf(prn_stream, "\033&l%dO", page_orientation);
155
3.20k
        if (features & PCL_CAN_SET_PAPER_SIZE){
156
3.20k
            gp_fprintf(prn_stream, "\033&l%dA", paper_size);
157
3.20k
        }
158
3.20k
        gp_fputs("\033&l0l0E", prn_stream);
159
3.20k
        gp_fputs(odd_page_init, prn_stream);
160
3.20k
    }
161
162
3.20k
    gp_fprintf(prn_stream, "\033&l%dX", num_copies);  /* # of copies */
163
164
    /* End raster graphics, position cursor at top. */
165
3.20k
    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.20k
    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.20k
    gp_fprintf(prn_stream, "\033*t%dR", x_dpi);
176
177
    /* Send each scan line in turn */
178
3.20k
    {
179
3.20k
        int lnum;
180
3.20k
        int num_blank_lines = 0;
181
3.20k
        word rmask = ~(word) 0 << (-pdev->width & (W * 8 - 1));
182
183
        /* Transfer raster graphics. */
184
6.94M
        for (lnum = 0; lnum < num_rows; lnum++) {
185
6.94M
            register word *end_data =
186
6.94M
            data_words + line_size_words;
187
188
6.94M
            code = gdev_prn_copy_scan_lines(pdev, lnum,
189
6.94M
                                            (byte *) data, line_size);
190
6.94M
            if (code < 0)
191
0
                break;
192
            /* Mask off 1-bits beyond the line width. */
193
6.94M
            end_data[-1] &= rmask;
194
            /* Remove trailing 0s. */
195
108M
            while (end_data > data_words && end_data[-1] == 0)
196
101M
                end_data--;
197
6.94M
            if (end_data == data_words) { /* Blank line */
198
2.88M
                num_blank_lines++;
199
2.88M
                continue;
200
2.88M
            }
201
            /* We've reached a non-blank line. */
202
            /* Put out a spacing command if necessary. */
203
4.05M
            if (num_blank_lines == lnum) {
204
                /* We're at the top of a page. */
205
3.09k
                if (features & PCL_ANY_SPACING) {
206
3.09k
                    if (num_blank_lines > 0)
207
3.09k
                        gp_fprintf(prn_stream, "\033*p+%dY",
208
3.09k
                                num_blank_lines * y_dots_per_pixel);
209
                    /* Start raster graphics. */
210
3.09k
                    gp_fputs("\033*r1A", prn_stream);
211
3.09k
                } 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.09k
            }
229
            /* Skip blank lines if any */
230
4.05M
            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.4k
                if ((num_blank_lines < MIN_SKIP_LINES && compression != 3) ||
242
17.8k
                    !(features & PCL_ANY_SPACING)
243
18.4k
                    ) {
244
633
                    bool mode_3ns =
245
633
                        (features & PCL_MODE_3_COMPRESSION) &&
246
633
                        !(features & PCL_ANY_SPACING);
247
248
633
                    if (mode_3ns && compression != 2) {
249
                        /* Switch to mode 2 */
250
0
                        gp_fputs(from3to2, prn_stream);
251
0
                        compression = 2;
252
0
                    }
253
633
                    if (features & PCL_MODE_3_COMPRESSION) {
254
                        /* Must clear the seed row. */
255
633
                        gp_fputs("\033*b1Y", prn_stream);
256
633
                        num_blank_lines--;
257
633
                    }
258
633
                    if (mode_3ns) {
259
0
                        for (; num_blank_lines; num_blank_lines--)
260
0
                            gp_fputs("\033*b0W", prn_stream);
261
633
                    } else {
262
661
                        for (; num_blank_lines; num_blank_lines--)
263
28
                            gp_fputs("\033*bW", prn_stream);
264
633
                    }
265
17.8k
                } else if (features & PCL3_SPACING) {
266
0
                    gp_fprintf(prn_stream, "\033*p+%dY",
267
0
                               num_blank_lines * y_dots_per_pixel);
268
17.8k
                } else {
269
17.8k
                    gp_fprintf(prn_stream, "\033*b%dY",
270
17.8k
                               num_blank_lines);
271
17.8k
                }
272
                /* Clear the seed row (only matters for */
273
                /* mode 3 compression). */
274
18.4k
                memset(prev_row, 0, line_size);
275
18.4k
            }
276
4.05M
            num_blank_lines = 0;
277
278
            /* Choose the best compression mode */
279
            /* for this particular line. */
280
4.05M
            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.05M
                int count3 = gdev_pcl_mode3compress(line_size, data,
285
4.05M
                                                    prev_row, out_row);
286
4.05M
                int count2 = gdev_pcl_mode2compress(data_words, end_data,
287
4.05M
                                                    out_row_alt);
288
4.05M
                int penalty3 =
289
4.05M
                    (compression == 3 ? 0 : penalty_from2to3);
290
4.05M
                int penalty2 =
291
4.05M
                    (compression == 2 ? 0 : penalty_from3to2);
292
293
4.05M
                if (count3 + penalty3 < count2 + penalty2) {
294
3.83M
                    if (compression != 3)
295
108k
                        gp_fputs(from2to3, prn_stream);
296
3.83M
                    compression = 3;
297
3.83M
                    out_data = out_row;
298
3.83M
                    out_count = count3;
299
3.83M
                } else {
300
221k
                    if (compression != 2)
301
105k
                        gp_fputs(from3to2, prn_stream);
302
221k
                    compression = 2;
303
221k
                    out_data = out_row_alt;
304
221k
                    out_count = count2;
305
221k
                }
306
4.05M
            } 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.05M
            gp_fprintf(prn_stream, "\033*b%dW", out_count);
317
4.05M
            gp_fwrite(out_data, sizeof(byte), out_count,
318
4.05M
                      prn_stream);
319
4.05M
        }
320
3.20k
    }
321
322
    /* end raster graphics and eject page */
323
3.20k
    gp_fputs("\033*rB\f", prn_stream);
324
325
    /* free temporary storage */
326
3.20k
    gs_free_object(pdev->memory, storage, "hpjet_print_page");
327
328
3.20k
    return code;
329
3.20k
}