Coverage Report

Created: 2026-07-24 07:44

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