Coverage Report

Created: 2026-02-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/devices/gdevfax.c
Line
Count
Source
1
/* Copyright (C) 2001-2023 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
/* Fax devices */
17
#include "gdevprn.h"
18
#include "strimpl.h"
19
#include "scfx.h"
20
#include "gdevfax.h"
21
#include "minftrsz.h"
22
23
/* The device descriptors */
24
static dev_proc_print_page(faxg3_print_page);
25
static dev_proc_print_page(faxg32d_print_page);
26
static dev_proc_print_page(faxg4_print_page);
27
28
/* Define procedures that adjust the paper size. */
29
/* Since the print_page doesn't alter the device, this device can print in the background */
30
static void
31
fax_initialize_device_procs(gx_device *dev)
32
10.7k
{
33
10.7k
    gdev_prn_initialize_device_procs_mono_bg(dev);
34
35
10.7k
    set_dev_proc(dev, open_device, gdev_prn_open);
36
10.7k
    set_dev_proc(dev, get_params, gdev_fax_get_params);
37
10.7k
    set_dev_proc(dev, put_params, gdev_fax_put_params);
38
10.7k
}
39
40
#define FAX_DEVICE(dname, print_page)\
41
{\
42
    FAX_DEVICE_BODY(gx_device_fax, fax_initialize_device_procs, dname, print_page)\
43
}
44
45
const gx_device_fax gs_faxg3_device =
46
    FAX_DEVICE("faxg3", faxg3_print_page);
47
48
const gx_device_fax gs_faxg32d_device =
49
    FAX_DEVICE("faxg32d", faxg32d_print_page);
50
51
const gx_device_fax gs_faxg4_device =
52
    FAX_DEVICE("faxg4", faxg4_print_page);
53
54
/* Open the device. */
55
/* This is no longer needed: we retain it for client backward compatibility. */
56
int
57
gdev_fax_open(gx_device * dev)
58
0
{
59
0
    return gdev_prn_open(dev);
60
0
}
61
62
/* Get/put the fax parameters: AdjustWidth and MinFeatureSize */
63
int
64
gdev_fax_get_params(gx_device * dev, gs_param_list * plist)
65
273k
{
66
273k
    gx_device_fax *const fdev = (gx_device_fax *)dev;
67
273k
    int code = gdev_prn_get_params(dev, plist);
68
273k
    int ecode = code;
69
70
273k
    if ((code = param_write_int(plist, "AdjustWidth", &fdev->AdjustWidth)) < 0)
71
0
        ecode = code;
72
273k
    if ((code = param_write_int(plist, "MinFeatureSize", &fdev->MinFeatureSize)) < 0)
73
0
        ecode = code;
74
273k
    if ((code = param_write_int(plist, "FillOrder", &fdev->FillOrder)) < 0)
75
0
        ecode = code;
76
273k
     if ((code = param_write_bool(plist, "BlackIs1", &fdev->BlackIs1)) < 0)
77
0
        ecode = code;
78
79
273k
    return ecode;
80
273k
}
81
int
82
gdev_fax_put_params(gx_device * dev, gs_param_list * plist)
83
80.0k
{
84
80.0k
    gx_device_fax *const fdev = (gx_device_fax *)dev;
85
80.0k
    int ecode = 0;
86
80.0k
    int code;
87
80.0k
    int fill_order = fdev->FillOrder;
88
80.0k
    bool blackis1 = fdev->BlackIs1;
89
80.0k
    int aw = fdev->AdjustWidth;
90
80.0k
    int mfs = fdev->MinFeatureSize;
91
80.0k
    const char *param_name;
92
93
80.0k
    switch (code = param_read_int(plist, (param_name = "AdjustWidth"), &aw)) {
94
8.12k
        case 0:
95
8.12k
            if (aw >= 0)
96
8.12k
                break;
97
0
            code = gs_error_rangecheck;
98
0
        default:
99
0
            ecode = code;
100
0
            param_signal_error(plist, param_name, ecode);
101
71.9k
        case 1:
102
71.9k
            break;
103
80.0k
    }
104
80.0k
    switch (code = param_read_int(plist, (param_name = "FillOrder"), &fill_order)) {
105
8.12k
        case 0:
106
8.12k
            if (fill_order == 1 || fill_order == 2)
107
8.12k
                break;
108
0
            code = gs_error_rangecheck;
109
0
        default:
110
0
            ecode = code;
111
0
            param_signal_error(plist, param_name, ecode);
112
71.9k
        case 1:
113
71.9k
            break;
114
80.0k
    }
115
80.0k
    switch (code = param_read_bool(plist, (param_name = "BlackIs1"), &blackis1)) {
116
0
        default:
117
0
            ecode = code;
118
0
            param_signal_error(plist, param_name, ecode);
119
8.12k
        case 0:
120
80.0k
        case 1:
121
80.0k
            break;
122
80.0k
    }
123
80.0k
    switch (code = param_read_int(plist, (param_name = "MinFeatureSize"), &mfs)) {
124
8.12k
        case 0:
125
8.12k
            if (mfs >= 0 && mfs <= 4)
126
8.12k
                break;
127
0
            code = gs_error_rangecheck;
128
0
        default:
129
0
            ecode = code;
130
0
            param_signal_error(plist, param_name, ecode);
131
71.9k
        case 1:
132
71.9k
            break;
133
80.0k
    }
134
135
80.0k
    if (ecode < 0)
136
0
        return ecode;
137
80.0k
    code = gdev_prn_put_params(dev, plist);
138
80.0k
    if (code < 0)
139
28
        return code;
140
141
80.0k
    fdev->AdjustWidth = aw;
142
80.0k
    fdev->MinFeatureSize = mfs;
143
80.0k
    fdev->FillOrder = fill_order;
144
145
80.0k
    return code;
146
80.0k
}
147
148
/* Initialize the stream state with a set of default parameters. */
149
/* These select the same defaults as the CCITTFaxEncode filter, */
150
/* except we set BlackIs1 = true. */
151
static void
152
gdev_fax_init_state_adjust(stream_CFE_state *ss,
153
                           const gx_device_fax *fdev,
154
                           int adjust_width)
155
9.03k
{
156
9.03k
    s_CFE_template.set_defaults((stream_state *)ss);
157
9.03k
    ss->Columns = fdev->width;
158
9.03k
    ss->Rows = fdev->height;
159
9.03k
    ss->BlackIs1 = fdev->BlackIs1;
160
9.03k
    ss->FirstBitLowOrder = fdev->FillOrder == 2;
161
9.03k
    ss->Columns = fax_adjusted_width(ss->Columns, adjust_width);
162
9.03k
}
163
164
void
165
gdev_fax_init_state(stream_CFE_state *ss, const gx_device_fax *fdev)
166
0
{
167
0
    gdev_fax_init_state_adjust(ss, fdev, 1);
168
0
}
169
void
170
gdev_fax_init_fax_state(stream_CFE_state *ss, const gx_device_fax *fdev)
171
9.03k
{
172
9.03k
    gdev_fax_init_state_adjust(ss, fdev, fdev->AdjustWidth);
173
9.03k
}
174
175
/*
176
 * Print one strip with fax compression.  Fax devices call this once per
177
 * page; TIFF devices call this once per strip.
178
 */
179
int
180
gdev_fax_print_strip(gx_device_printer * pdev, gp_file * prn_stream,
181
                     const stream_template * temp, stream_state * ss,
182
                     int width, int row_first, int row_end /* last + 1 */)
183
9.03k
{
184
9.03k
    gs_memory_t *mem = pdev->memory;
185
9.03k
    int code;
186
9.03k
    stream_cursor_read r;
187
9.03k
    stream_cursor_write w;
188
9.03k
    int in_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
189
    /*
190
     * Because of the width adjustment for fax systems, width may
191
     * be different from (either greater than or less than) pdev->width.
192
     * Allocate a large enough buffer to account for this.
193
     */
194
9.03k
    int col_size = (width * pdev->color_info.depth + 7) >> 3;
195
9.03k
    int max_size = max(in_size, col_size);
196
9.03k
    int lnum = 0;
197
9.03k
    int row_in = row_first;
198
9.03k
    byte *in;
199
9.03k
    byte *out;
200
9.03k
    void *min_feature_data = NULL;
201
    /* If the file is 'nul', don't even do the writes. */
202
9.03k
    bool nul = !strcmp(pdev->fname, "nul");
203
9.03k
    int lnum_in = row_in;
204
9.03k
    int min_feature_size = ((gx_device_fax *const)pdev)->MinFeatureSize;
205
206
    /* Initialize the common part of the encoder state. */
207
9.03k
    ss->templat = temp;
208
9.03k
    ss->memory = mem;
209
    /* Now initialize the encoder. */
210
9.03k
    code = temp->init(ss);
211
9.03k
    if (code < 0)
212
0
        return_error(gs_error_limitcheck);      /* bogus, but as good as any */
213
214
    /* Allocate the buffers. */
215
9.03k
    in = gs_alloc_bytes(mem, temp->min_in_size + max_size + 1,
216
9.03k
                        "gdev_stream_print_page(in)");
217
9.03k
#define OUT_SIZE 1000
218
9.03k
    out = gs_alloc_bytes(mem, OUT_SIZE, "gdev_stream_print_page(out)");
219
9.03k
    if (in == 0 || out == 0) {
220
0
        code = gs_note_error(gs_error_VMerror);
221
0
        goto done;
222
0
    }
223
    /* Init the min_feature_size expansion for entire image (not strip) */
224
    /* This is only called from gdev_fax_print_page() and row_first is *always* 0
225
     * By removing this check, we can make it synonymous with the use of
226
     * min_feature_data below, so we don't need to check min_feature_data there.
227
     */
228
9.03k
    if ((min_feature_size > 1) /* && (row_first == 0) */) {
229
0
        code = min_feature_size_init(mem, min_feature_size,
230
0
                                     width, pdev->height, &min_feature_data);
231
0
        if (code < 0)
232
0
            goto done;
233
0
    }
234
9.03k
    if (min_feature_size > 1)
235
0
        row_in = max(0, row_first-min_feature_size);    /* need some data before this row */
236
237
    /* Set up the processing loop. */
238
9.03k
    r.ptr = r.limit = in - 1;
239
9.03k
    w.ptr = out - 1;
240
9.03k
    w.limit = w.ptr + OUT_SIZE;
241
9.03k
#undef OUT_SIZE
242
243
    /* Process the image. */
244
17.9M
    for (lnum = row_in; ;) {
245
17.9M
        int status;
246
247
17.9M
        if_debug7m('w', mem,
248
17.9M
                   "[w]lnum=%d r="PRI_INTPTR","PRI_INTPTR","PRI_INTPTR" w="PRI_INTPTR","PRI_INTPTR","PRI_INTPTR"\n", lnum,
249
17.9M
                   (intptr_t)in, (intptr_t)r.ptr, (intptr_t)r.limit,
250
17.9M
                   (intptr_t)out, (intptr_t)w.ptr, (intptr_t)w.limit);
251
17.9M
        status = temp->process(ss, &r, &w, lnum == row_end);
252
17.9M
        if_debug7m('w', mem,
253
17.9M
                   "...%d, r="PRI_INTPTR","PRI_INTPTR","PRI_INTPTR" w="PRI_INTPTR","PRI_INTPTR","PRI_INTPTR"\n", status,
254
17.9M
                   (intptr_t)in, (intptr_t)r.ptr, (intptr_t)r.limit,
255
17.9M
                   (intptr_t)out, (intptr_t)w.ptr, (intptr_t)w.limit);
256
17.9M
        switch (status) {
257
17.7M
            case 0:             /* need more input data */
258
17.7M
                if (lnum == row_end)
259
9.03k
                    goto ok;
260
17.6M
                {
261
17.6M
                    uint left = r.limit - r.ptr;
262
17.6M
                    int filtered_count = in_size;
263
264
17.6M
                    memcpy(in, r.ptr + 1, left);
265
17.6M
                    do {
266
17.6M
                        if (lnum_in < row_end)
267
17.6M
                        {
268
17.6M
                            code = gdev_prn_copy_scan_lines(pdev, lnum_in++, in + left, in_size);
269
17.6M
                            if (code < 0) {
270
2
                                code = gs_note_error(code);
271
2
                                goto done;
272
2
                            }
273
17.6M
                        }
274
17.6M
                        if (min_feature_size > 1)
275
0
                            filtered_count =
276
0
                                min_feature_size_process(in+left, min_feature_data);
277
17.6M
                    } while (filtered_count == 0);
278
                    /* Note: we use col_size here, not in_size. */
279
17.6M
                    lnum++;
280
17.6M
                    if (col_size > in_size) {
281
8.86M
                        memset(in + left + in_size, 0, col_size - in_size);
282
8.86M
                    }
283
17.6M
                    r.limit = in + left + col_size - 1;
284
17.6M
                    r.ptr = in - 1;
285
17.6M
                }
286
0
                break;
287
255k
            case 1:             /* need to write output */
288
255k
                if (!nul)
289
255k
                    gp_fwrite(out, 1, w.ptr + 1 - out, prn_stream);
290
255k
                w.ptr = out - 1;
291
255k
                break;
292
17.9M
        }
293
17.9M
    }
294
295
9.03k
  ok:
296
    /* Write out any remaining output. */
297
9.03k
    if (!nul)
298
9.03k
        gp_fwrite(out, 1, w.ptr + 1 - out, prn_stream);
299
300
9.03k
  done:
301
    /* We only get one strip, we need to free min_feature_data without
302
     * any further checks or we will leak memory as we will allocate a
303
     * new one next time round. In truth it should only be possible to
304
     * get here with lnum != pdev-.Height in the case of an error, in
305
     * which case we still want to free the buffer!
306
     */
307
9.03k
    if ((min_feature_size > 1) /* && (lnum == pdev->height) */)
308
0
        min_feature_size_dnit(min_feature_data);
309
9.03k
    gs_free_object(mem, out, "gdev_stream_print_page(out)");
310
9.03k
    gs_free_object(mem, in, "gdev_stream_print_page(in)");
311
9.03k
    if (temp->release)
312
9.03k
        temp->release(ss);
313
9.03k
    return code;
314
9.03k
}
315
316
/* Print a fax page.  Other fax drivers use this. */
317
int
318
gdev_fax_print_page(gx_device_printer * pdev, gp_file * prn_stream,
319
                    stream_CFE_state * ss)
320
9.03k
{
321
9.03k
    return gdev_fax_print_strip(pdev, prn_stream, &s_CFE_template,
322
9.03k
                                (stream_state *)ss, ss->Columns,
323
9.03k
                                0, pdev->height);
324
9.03k
}
325
326
/* Print a 1-D Group 3 page. */
327
static int
328
faxg3_print_page(gx_device_printer * pdev, gp_file * prn_stream)
329
9.03k
{
330
9.03k
    stream_CFE_state state;
331
332
9.03k
    gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
333
9.03k
    state.EndOfLine = true;
334
9.03k
    state.EndOfBlock = false;
335
9.03k
    return gdev_fax_print_page(pdev, prn_stream, &state);
336
9.03k
}
337
338
/* Print a 2-D Group 3 page. */
339
static int
340
faxg32d_print_page(gx_device_printer * pdev, gp_file * prn_stream)
341
0
{
342
0
    stream_CFE_state state;
343
344
0
    gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
345
0
    state.K = (pdev->y_pixels_per_inch < 100 ? 2 : 4);
346
0
    state.EndOfLine = true;
347
0
    state.EndOfBlock = false;
348
0
    return gdev_fax_print_page(pdev, prn_stream, &state);
349
0
}
350
351
/* Print a Group 4 page. */
352
static int
353
faxg4_print_page(gx_device_printer * pdev, gp_file * prn_stream)
354
0
{
355
0
    stream_CFE_state state;
356
357
0
    gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
358
0
    state.K = -1;
359
0
    state.EndOfBlock = false;
360
0
    return gdev_fax_print_page(pdev, prn_stream, &state);
361
0
}