/src/ghostpdl/psi/zfjpx.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2021 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 | | |
17 | | |
18 | | /* This is the ps interpreter interface to the JPXDecode filter |
19 | | used for (JPEG2000) scanned image compression. PDF only specifies |
20 | | a decoder filter, and we don't currently implement anything else. */ |
21 | | |
22 | | #include "memory_.h" |
23 | | #include "ghost.h" |
24 | | #include "oper.h" |
25 | | #include "gsstruct.h" |
26 | | #include "gstypes.h" |
27 | | #include "ialloc.h" |
28 | | #include "idict.h" |
29 | | #include "store.h" |
30 | | #include "stream.h" |
31 | | #include "strimpl.h" |
32 | | #include "ifilter.h" |
33 | | #include "iname.h" |
34 | | #include "gdebug.h" |
35 | | |
36 | | #include "igstate.h" /* For igs macro */ |
37 | | #include "gxdevcli.h" /* for dev_spec_op */ |
38 | | #include "gxdevsop.h" /* For spec_op enumerated types */ |
39 | | |
40 | | #if defined(USE_OPENJPEG_JP2) |
41 | | # include "sjpx_openjpeg.h" |
42 | | #else |
43 | | # include "sjpx.h" |
44 | | #endif |
45 | | |
46 | | /* macro to test a name ref against a C string */ |
47 | 0 | # define ISTRCMP(ref, string) (memcmp((ref)->value.const_bytes, string, \ |
48 | 0 | min(strlen(string), r_size(ref)))) |
49 | | |
50 | | static int PS_JPXD_PassThrough(void *d, byte *Buffer, int Size) |
51 | 0 | { |
52 | 0 | gx_device *dev = (gx_device *)d; |
53 | |
|
54 | 0 | if (Buffer == NULL) { |
55 | 0 | if (Size == 0) |
56 | 0 | dev_proc(dev, dev_spec_op)(dev, gxdso_JPX_passthrough_end, NULL, 0); |
57 | 0 | else |
58 | 0 | dev_proc(dev, dev_spec_op)(dev, gxdso_JPX_passthrough_begin, NULL, 0); |
59 | 0 | } else { |
60 | 0 | dev_proc(dev, dev_spec_op)(dev, gxdso_JPX_passthrough_data, Buffer, Size); |
61 | 0 | } |
62 | 0 | return 0; |
63 | 0 | } |
64 | | |
65 | | /* <source> /JPXDecode <file> */ |
66 | | /* <source> <dict> /JPXDecode <file> */ |
67 | | static int |
68 | | z_jpx_decode(i_ctx_t * i_ctx_p) |
69 | 0 | { |
70 | 0 | os_ptr op = osp; |
71 | 0 | ref *sop = NULL; |
72 | 0 | ref *csname = NULL; |
73 | 0 | stream_jpxd_state state; |
74 | 0 | gx_device *dev = gs_currentdevice(igs); |
75 | | |
76 | | /* it's our responsibility to call set_defaults() */ |
77 | 0 | state.memory = imemory->non_gc_memory; |
78 | 0 | if (s_jpxd_template.set_defaults) |
79 | 0 | (*s_jpxd_template.set_defaults)((stream_state *)&state); |
80 | 0 | if (r_has_type(op, t_dictionary)) { |
81 | 0 | check_dict_read(*op); |
82 | 0 | if ( dict_find_string(op, "Alpha", &sop) > 0) { |
83 | 0 | check_type(*sop, t_boolean); |
84 | 0 | if (sop->value.boolval) |
85 | 0 | state.alpha = true; |
86 | 0 | } |
87 | 0 | if ( dict_find_string(op, "ColorSpace", &sop) > 0) { |
88 | | /* parse the value */ |
89 | 0 | if (r_is_array(sop)) { |
90 | | /* assume it's the first array element */ |
91 | 0 | csname = sop->value.refs; |
92 | 0 | } else if (r_has_type(sop,t_name)) { |
93 | | /* use the name directly */ |
94 | 0 | csname = sop; |
95 | 0 | } else { |
96 | 0 | dmprintf(imemory, "warning: JPX ColorSpace value is an unhandled type!\n"); |
97 | 0 | } |
98 | 0 | if (csname != NULL) { |
99 | 0 | ref sref; |
100 | | /* get a reference to the name's string value */ |
101 | 0 | name_string_ref(imemory, csname, &sref); |
102 | | /* request raw index values if the colorspace is /Indexed */ |
103 | 0 | if (!ISTRCMP(&sref, "Indexed")) |
104 | 0 | state.colorspace = gs_jpx_cs_indexed; |
105 | | /* tell the filter what output we want for other spaces */ |
106 | 0 | else if (!ISTRCMP(&sref, "DeviceGray")) |
107 | 0 | state.colorspace = gs_jpx_cs_gray; |
108 | 0 | else if (!ISTRCMP(&sref, "DeviceRGB")) |
109 | 0 | state.colorspace = gs_jpx_cs_rgb; |
110 | 0 | else if (!ISTRCMP(&sref, "DeviceCMYK")) |
111 | 0 | state.colorspace = gs_jpx_cs_cmyk; |
112 | 0 | else if (!ISTRCMP(&sref, "ICCBased")) { |
113 | | /* The second array element should be the profile's |
114 | | stream dict */ |
115 | 0 | ref *csdict = sop->value.refs + 1; |
116 | 0 | ref *nref; |
117 | 0 | ref altname; |
118 | 0 | if (r_is_array(sop) && (r_size(sop) > 1) && |
119 | 0 | r_has_type(csdict, t_dictionary)) { |
120 | 0 | check_dict_read(*csdict); |
121 | | /* try to look up the alternate space */ |
122 | 0 | if (dict_find_string(csdict, "Alternate", &nref) > 0) { |
123 | 0 | name_string_ref(imemory, csname, &altname); |
124 | 0 | if (!ISTRCMP(&altname, "DeviceGray")) |
125 | 0 | state.colorspace = gs_jpx_cs_gray; |
126 | 0 | else if (!ISTRCMP(&altname, "DeviceRGB")) |
127 | 0 | state.colorspace = gs_jpx_cs_rgb; |
128 | 0 | else if (!ISTRCMP(&altname, "DeviceCMYK")) |
129 | 0 | state.colorspace = gs_jpx_cs_cmyk; |
130 | 0 | } |
131 | | /* else guess based on the number of components */ |
132 | 0 | if (state.colorspace == gs_jpx_cs_unset && |
133 | 0 | dict_find_string(csdict, "N", &nref) > 0) { |
134 | 0 | if_debug1m('w', imemory, "[w] JPX image has an external %"PRIpsint |
135 | 0 | " channel colorspace\n", nref->value.intval); |
136 | 0 | if (r_type(nref) != t_integer) |
137 | 0 | return gs_note_error(gs_error_typecheck); |
138 | 0 | switch (nref->value.intval) { |
139 | 0 | case 1: state.colorspace = gs_jpx_cs_gray; |
140 | 0 | break; |
141 | 0 | case 3: state.colorspace = gs_jpx_cs_rgb; |
142 | 0 | break; |
143 | 0 | case 4: state.colorspace = gs_jpx_cs_cmyk; |
144 | 0 | break; |
145 | 0 | } |
146 | 0 | } |
147 | 0 | } |
148 | 0 | } |
149 | 0 | } else { |
150 | 0 | if_debug0m('w', imemory, "[w] Couldn't read JPX ColorSpace key!\n"); |
151 | 0 | } |
152 | 0 | } |
153 | 0 | } |
154 | | |
155 | 0 | if (dev_proc(dev, dev_spec_op)(dev, gxdso_JPX_passthrough_query, NULL, 0) > 0) { |
156 | 0 | state.StartedPassThrough = 0; |
157 | 0 | state.PassThrough = 1; |
158 | 0 | state.PassThroughfn = (PS_JPXD_PassThrough); |
159 | 0 | state.device = (void *)dev; |
160 | 0 | } |
161 | 0 | else { |
162 | 0 | state.PassThrough = 0; |
163 | 0 | state.device = (void *)NULL; |
164 | 0 | } |
165 | | |
166 | | /* we pass npop=0, since we've no arguments left to consume */ |
167 | | /* we pass 0 instead of the usual rspace(sop) which will allocate storage |
168 | | for filter state from the same memory pool as the stream it's coding. |
169 | | this causes no trouble because we maintain no pointers */ |
170 | 0 | return filter_read(i_ctx_p, 0, &s_jpxd_template, |
171 | 0 | (stream_state *) & state, 0); |
172 | 0 | } |
173 | | |
174 | | /* Match the above routine to the corresponding filter name. |
175 | | This is how our static routines get called externally. */ |
176 | | const op_def zfjpx_op_defs[] = { |
177 | | op_def_begin_filter(), |
178 | | {"2JPXDecode", z_jpx_decode}, |
179 | | op_def_end(0) |
180 | | }; |