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