Coverage Report

Created: 2025-04-22 06:20

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