Coverage Report

Created: 2026-04-09 07:06

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