xref: /plan9/sys/src/cmd/gs/src/gdevfax.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gdevfax.c,v 1.4 2002/02/21 22:24:51 giles Exp $ */
18 /* Fax devices */
19 #include "gdevprn.h"
20 #include "strimpl.h"
21 #include "scfx.h"
22 #include "gdevfax.h"
23 
24 /* The device descriptors */
25 private dev_proc_print_page(faxg3_print_page);
26 private dev_proc_print_page(faxg32d_print_page);
27 private dev_proc_print_page(faxg4_print_page);
28 
29 /* Define procedures that adjust the paper size. */
30 const gx_device_procs gdev_fax_std_procs =
31     prn_params_procs(gdev_prn_open, gdev_prn_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 
40 const gx_device_fax gs_faxg3_device =
41     FAX_DEVICE("faxg3", faxg3_print_page);
42 
43 const gx_device_fax gs_faxg32d_device =
44     FAX_DEVICE("faxg32d", faxg32d_print_page);
45 
46 const gx_device_fax gs_faxg4_device =
47     FAX_DEVICE("faxg4", faxg4_print_page);
48 
49 /* Open the device. */
50 /* This is no longer needed: we retain it for client backward compatibility. */
51 int
gdev_fax_open(gx_device * dev)52 gdev_fax_open(gx_device * dev)
53 {
54     return gdev_prn_open(dev);
55 }
56 
57 /* Get/put the AdjustWidth parameter. */
58 int
gdev_fax_get_params(gx_device * dev,gs_param_list * plist)59 gdev_fax_get_params(gx_device * dev, gs_param_list * plist)
60 {
61     gx_device_fax *const fdev = (gx_device_fax *)dev;
62     int code = gdev_prn_get_params(dev, plist);
63     int ecode = code;
64 
65     if ((code = param_write_int(plist, "AdjustWidth", &fdev->AdjustWidth)) < 0)
66         ecode = code;
67     return ecode;
68 }
69 int
gdev_fax_put_params(gx_device * dev,gs_param_list * plist)70 gdev_fax_put_params(gx_device * dev, gs_param_list * plist)
71 {
72     gx_device_fax *const fdev = (gx_device_fax *)dev;
73     int ecode = 0;
74     int code;
75     int aw = fdev->AdjustWidth;
76     const char *param_name;
77 
78     switch (code = param_read_int(plist, (param_name = "AdjustWidth"), &aw)) {
79         case 0:
80 	    if (aw >= 0 && aw <= 1)
81 		break;
82 	    code = gs_error_rangecheck;
83 	default:
84 	    ecode = code;
85 	    param_signal_error(plist, param_name, ecode);
86 	case 1:
87 	    break;
88     }
89 
90     if (ecode < 0)
91 	return ecode;
92     code = gdev_prn_put_params(dev, plist);
93     if (code < 0)
94 	return code;
95 
96     fdev->AdjustWidth = aw;
97     return code;
98 }
99 
100 /* Initialize the stream state with a set of default parameters. */
101 /* These select the same defaults as the CCITTFaxEncode filter, */
102 /* except we set BlackIs1 = true. */
103 private void
gdev_fax_init_state_adjust(stream_CFE_state * ss,const gx_device_fax * fdev,int adjust_width)104 gdev_fax_init_state_adjust(stream_CFE_state *ss,
105 			   const gx_device_fax *fdev,
106 			   int adjust_width)
107 {
108     s_CFE_template.set_defaults((stream_state *)ss);
109     ss->Columns = fdev->width;
110     ss->Rows = fdev->height;
111     ss->BlackIs1 = true;
112     if (adjust_width > 0) {
113 	/* Adjust the page width to a legal value for fax systems. */
114 	if (ss->Columns >= 1680 && ss->Columns <= 1736) {
115 	    /* Adjust width for A4 paper. */
116 	    ss->Columns = 1728;
117 	} else if (ss->Columns >= 2000 && ss->Columns <= 2056) {
118 	    /* Adjust width for B4 paper. */
119 	    ss->Columns = 2048;
120 	}
121     }
122 }
123 void
gdev_fax_init_state(stream_CFE_state * ss,const gx_device_fax * fdev)124 gdev_fax_init_state(stream_CFE_state *ss, const gx_device_fax *fdev)
125 {
126     gdev_fax_init_state_adjust(ss, fdev, 1);
127 }
128 void
gdev_fax_init_fax_state(stream_CFE_state * ss,const gx_device_fax * fdev)129 gdev_fax_init_fax_state(stream_CFE_state *ss, const gx_device_fax *fdev)
130 {
131     gdev_fax_init_state_adjust(ss, fdev, fdev->AdjustWidth);
132 }
133 
134 /*
135  * Print one strip with fax compression.  Fax devices call this once per
136  * page; TIFF devices call this once per strip.
137  */
138 int
gdev_fax_print_strip(gx_device_printer * pdev,FILE * prn_stream,const stream_template * temp,stream_state * ss,int width,int row_first,int row_end)139 gdev_fax_print_strip(gx_device_printer * pdev, FILE * prn_stream,
140 		     const stream_template * temp, stream_state * ss,
141 		     int width, int row_first, int row_end /* last + 1 */)
142 {
143     gs_memory_t *mem = pdev->memory;
144     int code;
145     stream_cursor_read r;
146     stream_cursor_write w;
147     int in_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
148     /*
149      * Because of the width adjustment for fax systems, width may
150      * be different from (either greater than or less than) pdev->width.
151      * Allocate a large enough buffer to account for this.
152      */
153     int col_size = (width * pdev->color_info.depth + 7) >> 3;
154     int max_size = max(in_size, col_size);
155     int lnum;
156     byte *in;
157     byte *out;
158     /* If the file is 'nul', don't even do the writes. */
159     bool nul = !strcmp(pdev->fname, "nul");
160 
161     /* Initialize the common part of the encoder state. */
162     ss->template = temp;
163     ss->memory = mem;
164     /* Now initialize the encoder. */
165     code = temp->init(ss);
166     if (code < 0)
167 	return_error(gs_error_limitcheck);	/* bogus, but as good as any */
168 
169     /* Allocate the buffers. */
170     in = gs_alloc_bytes(mem, temp->min_in_size + max_size + 1,
171 			"gdev_stream_print_page(in)");
172 #define OUT_SIZE 1000
173     out = gs_alloc_bytes(mem, OUT_SIZE, "gdev_stream_print_page(out)");
174     if (in == 0 || out == 0) {
175 	code = gs_note_error(gs_error_VMerror);
176 	goto done;
177     }
178     /* Set up the processing loop. */
179     lnum = 0;
180     r.ptr = r.limit = in - 1;
181     w.ptr = out - 1;
182     w.limit = w.ptr + OUT_SIZE;
183 #undef OUT_SIZE
184 
185     /* Process the image. */
186     for (lnum = row_first; ;) {
187 	int status;
188 
189 	if_debug7('w', "[w]lnum=%d r=0x%lx,0x%lx,0x%lx w=0x%lx,0x%lx,0x%lx\n", lnum,
190 		  (ulong)in, (ulong)r.ptr, (ulong)r.limit,
191 		  (ulong)out, (ulong)w.ptr, (ulong)w.limit);
192 	status = temp->process(ss, &r, &w, lnum == row_end);
193 	if_debug7('w', "...%d, r=0x%lx,0x%lx,0x%lx w=0x%lx,0x%lx,0x%lx\n", status,
194 		  (ulong)in, (ulong)r.ptr, (ulong)r.limit,
195 		  (ulong)out, (ulong)w.ptr, (ulong)w.limit);
196 	switch (status) {
197 	    case 0:		/* need more input data */
198 		if (lnum == row_end)
199 		    goto ok;
200 		{
201 		    uint left = r.limit - r.ptr;
202 
203 		    memcpy(in, r.ptr + 1, left);
204 		    gdev_prn_copy_scan_lines(pdev, lnum++, in + left, in_size);
205 		    /* Note: we use col_size here, not in_size. */
206 		    if (col_size > in_size) {
207 			memset(in + left + in_size, 0, col_size - in_size);
208 		    }
209 		    r.limit = in + left + col_size - 1;
210 		    r.ptr = in - 1;
211 		}
212 		break;
213 	    case 1:		/* need to write output */
214 		if (!nul)
215 		    fwrite(out, 1, w.ptr + 1 - out, prn_stream);
216 		w.ptr = out - 1;
217 		break;
218 	}
219     }
220 
221   ok:
222     /* Write out any remaining output. */
223     if (!nul)
224 	fwrite(out, 1, w.ptr + 1 - out, prn_stream);
225 
226   done:
227     gs_free_object(mem, out, "gdev_stream_print_page(out)");
228     gs_free_object(mem, in, "gdev_stream_print_page(in)");
229     if (temp->release)
230 	temp->release(ss);
231     return code;
232 }
233 
234 /* Print a fax page.  Other fax drivers use this. */
235 int
gdev_fax_print_page(gx_device_printer * pdev,FILE * prn_stream,stream_CFE_state * ss)236 gdev_fax_print_page(gx_device_printer * pdev, FILE * prn_stream,
237 		    stream_CFE_state * ss)
238 {
239     return gdev_fax_print_strip(pdev, prn_stream, &s_CFE_template,
240 				(stream_state *)ss, ss->Columns,
241 				0, pdev->height);
242 }
243 
244 /* Print a 1-D Group 3 page. */
245 private int
faxg3_print_page(gx_device_printer * pdev,FILE * prn_stream)246 faxg3_print_page(gx_device_printer * pdev, FILE * prn_stream)
247 {
248     stream_CFE_state state;
249 
250     gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
251     state.EndOfLine = true;
252     state.EndOfBlock = false;
253     return gdev_fax_print_page(pdev, prn_stream, &state);
254 }
255 
256 /* Print a 2-D Group 3 page. */
257 private int
faxg32d_print_page(gx_device_printer * pdev,FILE * prn_stream)258 faxg32d_print_page(gx_device_printer * pdev, FILE * prn_stream)
259 {
260     stream_CFE_state state;
261 
262     gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
263     state.K = (pdev->y_pixels_per_inch < 100 ? 2 : 4);
264     state.EndOfLine = true;
265     state.EndOfBlock = false;
266     return gdev_fax_print_page(pdev, prn_stream, &state);
267 }
268 
269 /* Print a Group 4 page. */
270 private int
faxg4_print_page(gx_device_printer * pdev,FILE * prn_stream)271 faxg4_print_page(gx_device_printer * pdev, FILE * prn_stream)
272 {
273     stream_CFE_state state;
274 
275     gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
276     state.K = -1;
277     state.EndOfBlock = false;
278     return gdev_fax_print_page(pdev, prn_stream, &state);
279 }
280