xref: /plan9/sys/src/cmd/gs/src/zfilter.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1993, 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: zfilter.c,v 1.10 2004/01/17 20:46:32 dan Exp $ */
18 /* Filter creation */
19 #include "memory_.h"
20 #include "ghost.h"
21 #include "oper.h"
22 #include "gsstruct.h"
23 #include "ialloc.h"
24 #include "idict.h"
25 #include "idparam.h"
26 #include "ilevel.h"		/* SubFileDecode is different in LL3 */
27 #include "stream.h"
28 #include "strimpl.h"
29 #include "sfilter.h"
30 #include "srlx.h"
31 #include "sstring.h"
32 #include "ifilter.h"
33 #include "files.h"		/* for filter_open, file_d'_buffer_size */
34 
35 /* <source> ASCIIHexEncode/filter <file> */
36 /* <source> <dict> ASCIIHexEncode/filter <file> */
37 private int
zAXE(i_ctx_t * i_ctx_p)38 zAXE(i_ctx_t *i_ctx_p)
39 {
40     return filter_write_simple(i_ctx_p, &s_AXE_template);
41 }
42 
43 /* <target> ASCIIHexDecode/filter <file> */
44 /* <target> <dict> ASCIIHexDecode/filter <file> */
45 private int
zAXD(i_ctx_t * i_ctx_p)46 zAXD(i_ctx_t *i_ctx_p)
47 {
48     return filter_read_simple(i_ctx_p, &s_AXD_template);
49 }
50 
51 /* <target> NullEncode/filter <file> */
52 /* <target> <dict_ignored> NullEncode/filter <file> */
53 private int
zNullE(i_ctx_t * i_ctx_p)54 zNullE(i_ctx_t *i_ctx_p)
55 {
56     return filter_write_simple(i_ctx_p, &s_NullE_template);
57 }
58 
59 /* <source> <bool> PFBDecode/filter <file> */
60 /* <source> <dict> <bool> PFBDecode/filter <file> */
61 private int
zPFBD(i_ctx_t * i_ctx_p)62 zPFBD(i_ctx_t *i_ctx_p)
63 {
64     os_ptr sop = osp;
65     stream_PFBD_state state;
66 
67     check_type(*sop, t_boolean);
68     state.binary_to_hex = sop->value.boolval;
69     return filter_read(i_ctx_p, 1, &s_PFBD_template, (stream_state *)&state, 0);
70 }
71 
72 /* <target> PSStringEncode/filter <file> */
73 /* <target> <dict> PSStringEncode/filter <file> */
74 private int
zPSSE(i_ctx_t * i_ctx_p)75 zPSSE(i_ctx_t *i_ctx_p)
76 {
77     return filter_write_simple(i_ctx_p, &s_PSSE_template);
78 }
79 
80 /* ------ RunLength filters ------ */
81 
82 /* Common setup for RLE and RLD filters. */
83 private int
rl_setup(os_ptr dop,bool * eod)84 rl_setup(os_ptr dop, bool * eod)
85 {
86     if (r_has_type(dop, t_dictionary)) {
87 	int code;
88 
89 	check_dict_read(*dop);
90 	if ((code = dict_bool_param(dop, "EndOfData", true, eod)) < 0)
91 	    return code;
92 	return 1;
93     } else {
94 	*eod = true;
95 	return 0;
96     }
97 }
98 
99 /* <target> <record_size> RunLengthEncode/filter <file> */
100 /* <target> <dict> <record_size> RunLengthEncode/filter <file> */
101 private int
zRLE(i_ctx_t * i_ctx_p)102 zRLE(i_ctx_t *i_ctx_p)
103 {
104     os_ptr op = osp;
105     stream_RLE_state state;
106     int code;
107 
108     check_op(2);
109     code = rl_setup(op - 1, &state.EndOfData);
110     if (code < 0)
111 	return code;
112     check_int_leu(*op, max_uint);
113     state.record_size = op->value.intval;
114     return filter_write(i_ctx_p, 1, &s_RLE_template, (stream_state *) & state, 0);
115 }
116 
117 /* <source> RunLengthDecode/filter <file> */
118 /* <source> <dict> RunLengthDecode/filter <file> */
119 private int
zRLD(i_ctx_t * i_ctx_p)120 zRLD(i_ctx_t *i_ctx_p)
121 {
122     os_ptr op = osp;
123     stream_RLD_state state;
124     int code = rl_setup(op, &state.EndOfData);
125 
126     if (code < 0)
127 	return code;
128     return filter_read(i_ctx_p, 0, &s_RLD_template, (stream_state *) & state, 0);
129 }
130 
131 /* <source> <EODcount> <EODstring> SubFileDecode/filter <file> */
132 /* <source> <dict> <EODcount> <EODstring> SubFileDecode/filter <file> */
133 /* <source> <dict> SubFileDecode/filter <file> *//* (LL3 only) */
134 private int
zSFD(i_ctx_t * i_ctx_p)135 zSFD(i_ctx_t *i_ctx_p)
136 {
137     os_ptr op = osp;
138     stream_SFD_state state;
139     ref *sop = op;
140     int npop;
141 
142     if (s_SFD_template.set_defaults)
143 	s_SFD_template.set_defaults((stream_state *)&state);
144     if (LL3_ENABLED && r_has_type(op, t_dictionary)) {
145 	int count;
146 	int code;
147 
148 	check_dict_read(*op);
149 	/*
150 	 * The PLRM-3rd says that EODCount is a required parameter.  However
151 	 * Adobe accepts files without this value and apparently defaults to
152 	 * zero.  Thus we are doing the same.
153 	 */
154 	if ((code = dict_int_param(op, "EODCount", 0, max_int, 0, &count)) < 0)
155 	    return code;
156 	if (dict_find_string(op, "EODString", &sop) <= 0)
157 	    return_error(e_rangecheck);
158 	state.count = count;
159 	npop = 0;
160     } else {
161 	check_type(sop[-1], t_integer);
162 	if (sop[-1].value.intval < 0)
163 	    return_error(e_rangecheck);
164 	state.count = sop[-1].value.intval;
165 	npop = 2;
166     }
167     check_read_type(*sop, t_string);
168     state.eod.data = sop->value.const_bytes;
169     state.eod.size = r_size(sop);
170     return filter_read(i_ctx_p, npop, &s_SFD_template,
171 		       (stream_state *)&state, r_space(sop));
172 }
173 
174 /* ------ Utilities ------ */
175 
176 /* Forward references */
177 private int filter_ensure_buf(stream **, uint, gs_ref_memory_t *, bool);
178 
179 /* Set up an input filter. */
180 int
filter_read(i_ctx_t * i_ctx_p,int npop,const stream_template * template,stream_state * st,uint space)181 filter_read(i_ctx_t *i_ctx_p, int npop, const stream_template * template,
182 	    stream_state * st, uint space)
183 {
184     os_ptr op = osp;
185     uint min_size = template->min_out_size + max_min_left;
186     uint save_space = ialloc_space(idmemory);
187     uint use_space = max(space, save_space);
188     os_ptr sop = op - npop;
189     stream *s;
190     stream *sstrm;
191     bool close = false;
192     int code;
193 
194     /* Skip over an optional dictionary parameter. */
195     if (r_has_type(sop, t_dictionary)) {
196 	check_dict_read(*sop);
197 	if ((code = dict_bool_param(sop, "CloseSource", false, &close)) < 0)
198 	    return code;
199 	--sop;
200     }
201     /*
202      * Check to make sure that the underlying data
203      * can function as a source for reading.
204      */
205     use_space = max(use_space, r_space(sop));
206     switch (r_type(sop)) {
207 	case t_string:
208 	    check_read(*sop);
209 	    ialloc_set_space(idmemory, use_space);
210 	    sstrm = file_alloc_stream(imemory, "filter_read(string stream)");
211 	    if (sstrm == 0) {
212 		code = gs_note_error(e_VMerror);
213 		goto out;
214 	    }
215 	    sread_string(sstrm, sop->value.bytes, r_size(sop));
216 	    sstrm->is_temp = 1;
217 	    break;
218 	case t_file:
219 	    check_read_known_file(sstrm, sop, return);
220 	    ialloc_set_space(idmemory, use_space);
221 	    goto ens;
222 	default:
223 	    check_proc(*sop);
224 	    ialloc_set_space(idmemory, use_space);
225 	    code = sread_proc(sop, &sstrm, iimemory);
226 	    if (code < 0)
227 		goto out;
228 	    sstrm->is_temp = 2;
229 	  ens:
230 	    code = filter_ensure_buf(&sstrm,
231 				     template->min_in_size +
232 				     sstrm->state->template->min_out_size,
233 				     iimemory, false);
234 	    if (code < 0)
235 		goto out;
236 	    break;
237     }
238     if (min_size < 128)
239 	min_size = file_default_buffer_size;
240     code = filter_open("r", min_size, (ref *) sop,
241 		       &s_filter_read_procs, template, st, imemory);
242     if (code < 0)
243 	goto out;
244     s = fptr(sop);
245     s->strm = sstrm;
246     s->close_strm = close;
247     pop(op - sop);
248 out:
249     ialloc_set_space(idmemory, save_space);
250     return code;
251 }
252 int
filter_read_simple(i_ctx_t * i_ctx_p,const stream_template * template)253 filter_read_simple(i_ctx_t *i_ctx_p, const stream_template * template)
254 {
255     return filter_read(i_ctx_p, 0, template, NULL, 0);
256 }
257 
258 /* Set up an output filter. */
259 int
filter_write(i_ctx_t * i_ctx_p,int npop,const stream_template * template,stream_state * st,uint space)260 filter_write(i_ctx_t *i_ctx_p, int npop, const stream_template * template,
261 	     stream_state * st, uint space)
262 {
263     os_ptr op = osp;
264     uint min_size = template->min_in_size + max_min_left;
265     uint save_space = ialloc_space(idmemory);
266     uint use_space = max(space, save_space);
267     register os_ptr sop = op - npop;
268     stream *s;
269     stream *sstrm;
270     bool close = false;
271     int code;
272 
273     /* Skip over an optional dictionary parameter. */
274     if (r_has_type(sop, t_dictionary)) {
275 	check_dict_read(*sop);
276 	if ((code = dict_bool_param(sop, "CloseTarget", false, &close)) < 0)
277 	    return code;
278 	--sop;
279     }
280     /*
281      * Check to make sure that the underlying data
282      * can function as a sink for writing.
283      */
284     use_space = max(use_space, r_space(sop));
285     switch (r_type(sop)) {
286 	case t_string:
287 	    check_write(*sop);
288 	    ialloc_set_space(idmemory, use_space);
289 	    sstrm = file_alloc_stream(imemory, "filter_write(string)");
290 	    if (sstrm == 0) {
291 		code = gs_note_error(e_VMerror);
292 		goto out;
293 	    }
294 	    swrite_string(sstrm, sop->value.bytes, r_size(sop));
295 	    sstrm->is_temp = 1;
296 	    break;
297 	case t_file:
298 	    check_write_known_file(sstrm, sop, return);
299 	    ialloc_set_space(idmemory, use_space);
300 	    goto ens;
301 	default:
302 	    check_proc(*sop);
303 	    ialloc_set_space(idmemory, use_space);
304 	    code = swrite_proc(sop, &sstrm, iimemory);
305 	    if (code < 0)
306 		goto out;
307 	    sstrm->is_temp = 2;
308 	  ens:
309 	    code = filter_ensure_buf(&sstrm,
310 				     template->min_out_size +
311 				     sstrm->state->template->min_in_size,
312 				     iimemory, true);
313 	    if (code < 0)
314 		goto out;
315 	    break;
316     }
317     if (min_size < 128)
318 	min_size = file_default_buffer_size;
319     code = filter_open("w", min_size, (ref *) sop,
320 		       &s_filter_write_procs, template, st, imemory);
321     if (code < 0)
322 	goto out;
323     s = fptr(sop);
324     s->strm = sstrm;
325     s->close_strm = close;
326     pop(op - sop);
327 out:
328     ialloc_set_space(idmemory, save_space);
329     return code;
330 }
331 int
filter_write_simple(i_ctx_t * i_ctx_p,const stream_template * template)332 filter_write_simple(i_ctx_t *i_ctx_p, const stream_template * template)
333 {
334     return filter_write(i_ctx_p, 0, template, NULL, 0);
335 }
336 
337 /* Define a byte-at-a-time NullDecode filter for intermediate buffers. */
338 /* (The standard NullDecode filter can read ahead too far.) */
339 private int
s_Null1D_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)340 s_Null1D_process(stream_state * st, stream_cursor_read * pr,
341 		 stream_cursor_write * pw, bool last)
342 {
343     if (pr->ptr >= pr->limit)
344 	return 0;
345     if (pw->ptr >= pw->limit)
346 	return 1;
347     *++(pw->ptr) = *++(pr->ptr);
348     return 1;
349 }
350 private const stream_template s_Null1D_template = {
351     &st_stream_state, NULL, s_Null1D_process, 1, 1
352 };
353 
354 /* A utility filter that returns an immediate EOF without consuming */
355 /* any data from its source. Used by PDF interpreter for unknown    */
356 /* filter types.                                                    */
357 private int
s_EOFD_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)358 s_EOFD_process(stream_state * st, stream_cursor_read * pr,
359 		 stream_cursor_write * pw, bool last)
360 {
361     return EOFC;
362 }
363 private const stream_template s_EOFD_template = {
364     &st_stream_state, NULL, s_EOFD_process, 1, 1
365 };
366 
367 /* <target> /.EOFDecode filter <file> */
368 /* <target> <dict> /.EOFDecode filter <file> */
369 private int
zEOFD(i_ctx_t * i_ctx_p)370 zEOFD(i_ctx_t *i_ctx_p)
371 {
372     return filter_read_simple(i_ctx_p, &s_EOFD_template);
373 }
374 
375 
376 /* Ensure a minimum buffer size for a filter. */
377 /* This may require creating an intermediate stream. */
378 private int
filter_ensure_buf(stream ** ps,uint min_buf_size,gs_ref_memory_t * imem,bool writing)379 filter_ensure_buf(stream ** ps, uint min_buf_size, gs_ref_memory_t *imem,
380 		  bool writing)
381 {
382     stream *s = *ps;
383     uint min_size = min_buf_size + max_min_left;
384     stream *bs;
385     ref bsop;
386     int code;
387 
388     if (s->modes == 0 /* stream is closed */  || s->bsize >= min_size)
389 	return 0;
390     /* Otherwise, allocate an intermediate stream. */
391     if (s->cbuf == 0) {
392 	/* This is a newly created procedure stream. */
393 	/* Just allocate a buffer for it. */
394 	uint len = max(min_size, 128);
395 	byte *buf = gs_alloc_bytes((gs_memory_t *)imem, len,
396 				   "filter_ensure_buf");
397 
398 	if (buf == 0)
399 	    return_error(e_VMerror);
400 	s->cbuf = buf;
401 	s->srptr = s->srlimit = s->swptr = buf - 1;
402 	s->swlimit = buf - 1 + len;
403 	s->bsize = s->cbsize = len;
404 	return 0;
405     } else {
406 	/* Allocate an intermediate stream. */
407 	if (writing)
408 	    code = filter_open("w", min_size, &bsop, &s_filter_write_procs,
409 			       &s_NullE_template, NULL, (gs_memory_t *)imem);
410 	else
411 	    code = filter_open("r", min_size, &bsop, &s_filter_read_procs,
412 			       &s_Null1D_template, NULL, (gs_memory_t *)imem);
413 	if (code < 0)
414 	    return code;
415 	bs = fptr(&bsop);
416 	bs->strm = s;
417 	bs->is_temp = 2;
418 	*ps = bs;
419 	return code;
420     }
421 }
422 
423 /* Mark a (filter) stream as temporary. */
424 /* We define this here to avoid importing stream.h into zf*.c. */
425 void
filter_mark_temp(const ref * fop,int is_temp)426 filter_mark_temp(const ref * fop, int is_temp)
427 {
428     fptr(fop)->is_temp = is_temp;
429 }
430 
431 /* Mark the source or target of a filter as temporary, and propagate */
432 /* close_strm from the temporary stream to the filter. */
433 void
filter_mark_strm_temp(const ref * fop,int is_temp)434 filter_mark_strm_temp(const ref * fop, int is_temp)
435 {
436     stream *s = fptr(fop);
437     stream *strm = s->strm;
438 
439     strm->is_temp = is_temp;
440     s->close_strm = strm->close_strm;
441 }
442 
443 /* ------ Initialization procedure ------ */
444 
445 const op_def zfilter_op_defs[] = {
446 		/* We enter PSStringEncode and SubFileDecode (only) */
447 		/* as separate operators. */
448     {"1.psstringencode", zPSSE},
449     {"2.subfiledecode", zSFD},
450     op_def_begin_filter(),
451     {"1ASCIIHexEncode", zAXE},
452     {"1ASCIIHexDecode", zAXD},
453     {"1NullEncode", zNullE},
454     {"2PFBDecode", zPFBD},
455     {"1PSStringEncode", zPSSE},
456     {"2RunLengthEncode", zRLE},
457     {"1RunLengthDecode", zRLD},
458     {"3SubFileDecode", zSFD},
459     {"1.EOFDecode", zEOFD},
460     op_def_end(0)
461 };
462