Coverage Report

Created: 2025-06-10 07:26

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