Coverage Report

Created: 2026-10-28 08:05

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