/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 | } |