Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/devices/gdevfax.c
Line
Count
Source (jump to first uncovered line)
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
9.61k
{
33
9.61k
    gdev_prn_initialize_device_procs_mono_bg(dev);
34
35
9.61k
    set_dev_proc(dev, open_device, gdev_prn_open);
36
9.61k
    set_dev_proc(dev, get_params, gdev_fax_get_params);
37
9.61k
    set_dev_proc(dev, put_params, gdev_fax_put_params);
38
9.61k
}
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
251k
{
66
251k
    gx_device_fax *const fdev = (gx_device_fax *)dev;
67
251k
    int code = gdev_prn_get_params(dev, plist);
68
251k
    int ecode = code;
69
70
251k
    if ((code = param_write_int(plist, "AdjustWidth", &fdev->AdjustWidth)) < 0)
71
0
        ecode = code;
72
251k
    if ((code = param_write_int(plist, "MinFeatureSize", &fdev->MinFeatureSize)) < 0)
73
0
        ecode = code;
74
251k
    if ((code = param_write_int(plist, "FillOrder", &fdev->FillOrder)) < 0)
75
0
        ecode = code;
76
251k
     if ((code = param_write_bool(plist, "BlackIs1", &fdev->BlackIs1)) < 0)
77
0
        ecode = code;
78
79
251k
    return ecode;
80
251k
}
81
int
82
gdev_fax_put_params(gx_device * dev, gs_param_list * plist)
83
73.5k
{
84
73.5k
    gx_device_fax *const fdev = (gx_device_fax *)dev;
85
73.5k
    int ecode = 0;
86
73.5k
    int code;
87
73.5k
    int fill_order = fdev->FillOrder;
88
73.5k
    bool blackis1 = fdev->BlackIs1;
89
73.5k
    int aw = fdev->AdjustWidth;
90
73.5k
    int mfs = fdev->MinFeatureSize;
91
73.5k
    const char *param_name;
92
93
73.5k
    switch (code = param_read_int(plist, (param_name = "AdjustWidth"), &aw)) {
94
6.78k
        case 0:
95
6.78k
            if (aw >= 0)
96
6.78k
                break;
97
0
            code = gs_error_rangecheck;
98
0
        default:
99
0
            ecode = code;
100
0
            param_signal_error(plist, param_name, ecode);
101
66.8k
        case 1:
102
66.8k
            break;
103
73.5k
    }
104
73.5k
    switch (code = param_read_int(plist, (param_name = "FillOrder"), &fill_order)) {
105
6.78k
        case 0:
106
6.78k
            if (fill_order == 1 || fill_order == 2)
107
6.78k
                break;
108
0
            code = gs_error_rangecheck;
109
0
        default:
110
0
            ecode = code;
111
0
            param_signal_error(plist, param_name, ecode);
112
66.8k
        case 1:
113
66.8k
            break;
114
73.5k
    }
115
73.5k
    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
6.78k
        case 0:
120
73.5k
        case 1:
121
73.5k
            break;
122
73.5k
    }
123
73.5k
    switch (code = param_read_int(plist, (param_name = "MinFeatureSize"), &mfs)) {
124
6.78k
        case 0:
125
6.78k
            if (mfs >= 0 && mfs <= 4)
126
6.78k
                break;
127
0
            code = gs_error_rangecheck;
128
0
        default:
129
0
            ecode = code;
130
0
            param_signal_error(plist, param_name, ecode);
131
66.8k
        case 1:
132
66.8k
            break;
133
73.5k
    }
134
135
73.5k
    if (ecode < 0)
136
0
        return ecode;
137
73.5k
    code = gdev_prn_put_params(dev, plist);
138
73.5k
    if (code < 0)
139
28
        return code;
140
141
73.5k
    fdev->AdjustWidth = aw;
142
73.5k
    fdev->MinFeatureSize = mfs;
143
73.5k
    fdev->FillOrder = fill_order;
144
145
73.5k
    return code;
146
73.5k
}
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
7.34k
{
156
7.34k
    s_CFE_template.set_defaults((stream_state *)ss);
157
7.34k
    ss->Columns = fdev->width;
158
7.34k
    ss->Rows = fdev->height;
159
7.34k
    ss->BlackIs1 = fdev->BlackIs1;
160
7.34k
    ss->FirstBitLowOrder = fdev->FillOrder == 2;
161
7.34k
    ss->Columns = fax_adjusted_width(ss->Columns, adjust_width);
162
7.34k
}
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
7.34k
{
172
7.34k
    gdev_fax_init_state_adjust(ss, fdev, fdev->AdjustWidth);
173
7.34k
}
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
7.34k
{
184
7.34k
    gs_memory_t *mem = pdev->memory;
185
7.34k
    int code;
186
7.34k
    stream_cursor_read r;
187
7.34k
    stream_cursor_write w;
188
7.34k
    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
7.34k
    int col_size = (width * pdev->color_info.depth + 7) >> 3;
195
7.34k
    int max_size = max(in_size, col_size);
196
7.34k
    int lnum = 0;
197
7.34k
    int row_in = row_first;
198
7.34k
    byte *in;
199
7.34k
    byte *out;
200
7.34k
    void *min_feature_data = NULL;
201
    /* If the file is 'nul', don't even do the writes. */
202
7.34k
    bool nul = !strcmp(pdev->fname, "nul");
203
7.34k
    int lnum_in = row_in;
204
7.34k
    int min_feature_size = ((gx_device_fax *const)pdev)->MinFeatureSize;
205
206
    /* Initialize the common part of the encoder state. */
207
7.34k
    ss->templat = temp;
208
7.34k
    ss->memory = mem;
209
    /* Now initialize the encoder. */
210
7.34k
    code = temp->init(ss);
211
7.34k
    if (code < 0)
212
0
        return_error(gs_error_limitcheck);      /* bogus, but as good as any */
213
214
    /* Allocate the buffers. */
215
7.34k
    in = gs_alloc_bytes(mem, temp->min_in_size + max_size + 1,
216
7.34k
                        "gdev_stream_print_page(in)");
217
7.34k
#define OUT_SIZE 1000
218
7.34k
    out = gs_alloc_bytes(mem, OUT_SIZE, "gdev_stream_print_page(out)");
219
7.34k
    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
7.34k
    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
7.34k
    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
7.34k
    r.ptr = r.limit = in - 1;
239
7.34k
    w.ptr = out - 1;
240
7.34k
    w.limit = w.ptr + OUT_SIZE;
241
7.34k
#undef OUT_SIZE
242
243
    /* Process the image. */
244
15.4M
    for (lnum = row_in; ;) {
245
15.4M
        int status;
246
247
15.4M
        if_debug7m('w', mem,
248
15.4M
                   "[w]lnum=%d r="PRI_INTPTR","PRI_INTPTR","PRI_INTPTR" w="PRI_INTPTR","PRI_INTPTR","PRI_INTPTR"\n", lnum,
249
15.4M
                   (intptr_t)in, (intptr_t)r.ptr, (intptr_t)r.limit,
250
15.4M
                   (intptr_t)out, (intptr_t)w.ptr, (intptr_t)w.limit);
251
15.4M
        status = temp->process(ss, &r, &w, lnum == row_end);
252
15.4M
        if_debug7m('w', mem,
253
15.4M
                   "...%d, r="PRI_INTPTR","PRI_INTPTR","PRI_INTPTR" w="PRI_INTPTR","PRI_INTPTR","PRI_INTPTR"\n", status,
254
15.4M
                   (intptr_t)in, (intptr_t)r.ptr, (intptr_t)r.limit,
255
15.4M
                   (intptr_t)out, (intptr_t)w.ptr, (intptr_t)w.limit);
256
15.4M
        switch (status) {
257
15.1M
            case 0:             /* need more input data */
258
15.1M
                if (lnum == row_end)
259
7.34k
                    goto ok;
260
15.1M
                {
261
15.1M
                    uint left = r.limit - r.ptr;
262
15.1M
                    int filtered_count = in_size;
263
264
15.1M
                    memcpy(in, r.ptr + 1, left);
265
15.1M
                    do {
266
15.1M
                        if (lnum_in < row_end)
267
15.1M
                        {
268
15.1M
                            code = gdev_prn_copy_scan_lines(pdev, lnum_in++, in + left, in_size);
269
15.1M
                            if (code < 0) {
270
1
                                code = gs_note_error(code);
271
1
                                goto done;
272
1
                            }
273
15.1M
                        }
274
15.1M
                        if (min_feature_size > 1)
275
0
                            filtered_count =
276
0
                                min_feature_size_process(in+left, min_feature_data);
277
15.1M
                    } while (filtered_count == 0);
278
                    /* Note: we use col_size here, not in_size. */
279
15.1M
                    lnum++;
280
15.1M
                    if (col_size > in_size) {
281
7.58M
                        memset(in + left + in_size, 0, col_size - in_size);
282
7.58M
                    }
283
15.1M
                    r.limit = in + left + col_size - 1;
284
15.1M
                    r.ptr = in - 1;
285
15.1M
                }
286
0
                break;
287
258k
            case 1:             /* need to write output */
288
258k
                if (!nul)
289
258k
                    gp_fwrite(out, 1, w.ptr + 1 - out, prn_stream);
290
258k
                w.ptr = out - 1;
291
258k
                break;
292
15.4M
        }
293
15.4M
    }
294
295
7.34k
  ok:
296
    /* Write out any remaining output. */
297
7.34k
    if (!nul)
298
7.34k
        gp_fwrite(out, 1, w.ptr + 1 - out, prn_stream);
299
300
7.34k
  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
7.34k
    if ((min_feature_size > 1) /* && (lnum == pdev->height) */)
308
0
        min_feature_size_dnit(min_feature_data);
309
7.34k
    gs_free_object(mem, out, "gdev_stream_print_page(out)");
310
7.34k
    gs_free_object(mem, in, "gdev_stream_print_page(in)");
311
7.34k
    if (temp->release)
312
7.34k
        temp->release(ss);
313
7.34k
    return code;
314
7.34k
}
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
7.34k
{
321
7.34k
    return gdev_fax_print_strip(pdev, prn_stream, &s_CFE_template,
322
7.34k
                                (stream_state *)ss, ss->Columns,
323
7.34k
                                0, pdev->height);
324
7.34k
}
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
7.34k
{
330
7.34k
    stream_CFE_state state;
331
332
7.34k
    gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
333
7.34k
    state.EndOfLine = true;
334
7.34k
    state.EndOfBlock = false;
335
7.34k
    return gdev_fax_print_page(pdev, prn_stream, &state);
336
7.34k
}
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
}