/src/ghostpdl/base/sjbig2.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 | | |
17 | | /* jbig2decode filter implementation -- hooks in libjbig2dec */ |
18 | | |
19 | | #include "stdint_.h" |
20 | | #include "memory_.h" |
21 | | #include "stdio_.h" /* sprintf() for debug output */ |
22 | | |
23 | | #include "gserrors.h" |
24 | | #include "gdebug.h" |
25 | | #include "strimpl.h" |
26 | | #include "sjbig2.h" |
27 | | #include <limits.h> /* UINT_MAX */ |
28 | | |
29 | | /* stream implementation */ |
30 | | |
31 | | /* The /JBIG2Decode filter is a fairly memory intensive one to begin with, |
32 | | particularly in the initial jbig2dec library implementation. Furthermore, |
33 | | as a PDF 1.4 feature, we can assume a fairly large (host-level) machine. |
34 | | We therefore dispense with the normal Ghostscript memory discipline and |
35 | | let the library allocate all its resources on the heap. The pointers to |
36 | | these are not enumerated and so will not be garbage collected. We rely |
37 | | on our release() proc being called to deallocate state. |
38 | | */ |
39 | | |
40 | | private_st_jbig2decode_state(); /* creates a gc object for our state, defined in sjbig2.h */ |
41 | | |
42 | | /* error callback for jbig2 decoder */ |
43 | | static void |
44 | | s_jbig2decode_error(void *callback_data, const char *msg, Jbig2Severity severity, |
45 | | uint32_t seg_idx) |
46 | 10.3k | { |
47 | 10.3k | s_jbig2_callback_data_t *error_data = (s_jbig2_callback_data_t *)callback_data; |
48 | 10.3k | const char *type; |
49 | 10.3k | char segment[22]; |
50 | | |
51 | 10.3k | switch (severity) { |
52 | 5.65k | case JBIG2_SEVERITY_DEBUG: |
53 | 5.65k | type = "DEBUG"; break;; |
54 | 3.45k | case JBIG2_SEVERITY_INFO: |
55 | 3.45k | type = "info"; break;; |
56 | 1.03k | case JBIG2_SEVERITY_WARNING: |
57 | 1.03k | type = "WARNING"; break;; |
58 | 252 | case JBIG2_SEVERITY_FATAL: |
59 | 252 | type = "FATAL ERROR decoding image:"; |
60 | | /* pass the fatal error upstream if possible */ |
61 | 252 | if (error_data != NULL) error_data->error = gs_error_ioerror; |
62 | 252 | break;; |
63 | 0 | default: type = "unknown message:"; break;; |
64 | 10.3k | } |
65 | 10.3k | if (seg_idx == JBIG2_UNKNOWN_SEGMENT_NUMBER) segment[0] = '\0'; |
66 | 8.79k | else gs_snprintf(segment, sizeof(segment), "(segment 0x%02x)", seg_idx); |
67 | | |
68 | 10.3k | if (error_data) |
69 | 10.3k | { |
70 | 10.3k | char *message; |
71 | 10.3k | int len; |
72 | | |
73 | 10.3k | len = snprintf(NULL, 0, "jbig2dec %s %s %s", type, msg, segment); |
74 | 10.3k | if (len < 0) |
75 | 0 | return; |
76 | | |
77 | 10.3k | message = (char *)gs_alloc_bytes(error_data->memory, len + 1, "sjbig2decode_error(message)"); |
78 | 10.3k | if (message == NULL) |
79 | 0 | return; |
80 | | |
81 | 10.3k | len = snprintf(message, len + 1, "jbig2dec %s %s %s", type, msg, segment); |
82 | 10.3k | if (len < 0) |
83 | 0 | { |
84 | 0 | gs_free_object(error_data->memory, message, "s_jbig2decode_error(message)"); |
85 | 0 | return; |
86 | 0 | } |
87 | | |
88 | 10.3k | if (error_data->last_message != NULL && strcmp(message, error_data->last_message)) { |
89 | 9.48k | if (error_data->repeats > 1) |
90 | 26 | { |
91 | 26 | if (error_data->severity == JBIG2_SEVERITY_FATAL || error_data->severity == JBIG2_SEVERITY_WARNING) { |
92 | 23 | dmlprintf1(error_data->memory, "jbig2dec last message repeated %ld times\n", error_data->repeats); |
93 | 23 | } else { |
94 | 3 | if_debug1m('w', error_data->memory, "[w] jbig2dec last message repeated %ld times\n", error_data->repeats); |
95 | 3 | } |
96 | 26 | } |
97 | 9.48k | gs_free_object(error_data->memory, error_data->last_message, "s_jbig2decode_error(last_message)"); |
98 | 9.48k | if (severity == JBIG2_SEVERITY_FATAL || severity == JBIG2_SEVERITY_WARNING) { |
99 | 1.17k | dmlprintf1(error_data->memory, "%s\n", message); |
100 | 8.31k | } else { |
101 | 8.31k | if_debug1m('w', error_data->memory, "[w] %s\n", message); |
102 | 8.31k | } |
103 | 9.48k | error_data->last_message = message; |
104 | 9.48k | error_data->severity = severity; |
105 | 9.48k | error_data->type = type; |
106 | 9.48k | error_data->repeats = 0; |
107 | 9.48k | } |
108 | 905 | else if (error_data->last_message != NULL) { |
109 | 114 | error_data->repeats++; |
110 | 114 | if (error_data->repeats % 1000000 == 0) |
111 | 0 | { |
112 | 0 | if (error_data->severity == JBIG2_SEVERITY_FATAL || error_data->severity == JBIG2_SEVERITY_WARNING) { |
113 | 0 | dmlprintf1(error_data->memory, "jbig2dec last message repeated %ld times so far\n", error_data->repeats); |
114 | 0 | } else { |
115 | 0 | if_debug1m('w', error_data->memory, "[w] jbig2dec last message repeated %ld times so far\n", error_data->repeats); |
116 | 0 | } |
117 | 0 | } |
118 | 114 | gs_free_object(error_data->memory, message, "s_jbig2decode_error(message)"); |
119 | 114 | } |
120 | 791 | else if (error_data->last_message == NULL) { |
121 | 791 | if (severity == JBIG2_SEVERITY_FATAL || severity == JBIG2_SEVERITY_WARNING) { |
122 | 12 | dmlprintf1(error_data->memory, "%s\n", message); |
123 | 779 | } else { |
124 | 779 | if_debug1m('w', error_data->memory, "[w] %s\n", message); |
125 | 779 | } |
126 | 791 | error_data->last_message = message; |
127 | 791 | error_data->severity = severity; |
128 | 791 | error_data->type = type; |
129 | 791 | error_data->repeats = 0; |
130 | 791 | } |
131 | 10.3k | } |
132 | 0 | else |
133 | 0 | { |
134 | | /* |
135 | | FIXME s_jbig2_callback_data_t should be updated so that jbig2_ctx_new is not called |
136 | | with a NULL argument (see jbig2.h) and we never reach here with a NULL state |
137 | | */ |
138 | 0 | if (severity == JBIG2_SEVERITY_FATAL) { |
139 | 0 | dlprintf3("jbig2dec %s %s %s\n", type, msg, segment); |
140 | 0 | } else { |
141 | 0 | if_debug3('w', "[w] jbig2dec %s %s %s\n", type, msg, segment); |
142 | 0 | } |
143 | 0 | } |
144 | 10.3k | } |
145 | | |
146 | | static void |
147 | | s_jbig2decode_flush_errors(void *callback_data) |
148 | 798 | { |
149 | 798 | s_jbig2_callback_data_t *error_data = (s_jbig2_callback_data_t *)callback_data; |
150 | | |
151 | 798 | if (error_data == NULL) |
152 | 0 | return; |
153 | | |
154 | 798 | if (error_data->last_message != NULL) { |
155 | 791 | if (error_data->repeats > 1) |
156 | 0 | { |
157 | 0 | if (error_data->severity == JBIG2_SEVERITY_FATAL || error_data->severity == JBIG2_SEVERITY_WARNING) { |
158 | 0 | dmlprintf1(error_data->memory, "jbig2dec last message repeated %ld times\n", error_data->repeats); |
159 | 0 | } else { |
160 | 0 | if_debug1m('w', error_data->memory, "[w] jbig2dec last message repeated %ld times\n", error_data->repeats); |
161 | 0 | } |
162 | 0 | } |
163 | 791 | gs_free_object(error_data->memory, error_data->last_message, "s_jbig2decode_error(last_message)"); |
164 | 791 | error_data->last_message = NULL; |
165 | 791 | error_data->repeats = 0; |
166 | 791 | } |
167 | 798 | } |
168 | | |
169 | | /* invert the bits in a buffer */ |
170 | | /* jbig2 and postscript have different senses of what pixel |
171 | | value is black, so we must invert the image */ |
172 | | static void |
173 | | s_jbig2decode_invert_buffer(unsigned char *buf, size_t length) |
174 | 245k | { |
175 | 245k | size_t i; |
176 | | |
177 | 502M | for (i = 0; i < length; i++) |
178 | 502M | *buf++ ^= 0xFF; |
179 | 245k | } |
180 | | |
181 | | typedef struct { |
182 | | Jbig2Allocator allocator; |
183 | | gs_memory_t *mem; |
184 | | } s_jbig2decode_allocator_t; |
185 | | |
186 | | static void *s_jbig2decode_alloc(Jbig2Allocator *_allocator, size_t size) |
187 | 4.02M | { |
188 | 4.02M | s_jbig2decode_allocator_t *allocator = (s_jbig2decode_allocator_t *) _allocator; |
189 | 4.02M | if (size > UINT_MAX) |
190 | 2 | return NULL; |
191 | 4.02M | return gs_alloc_bytes(allocator->mem, size, "s_jbig2decode_alloc"); |
192 | 4.02M | } |
193 | | |
194 | | static void s_jbig2decode_free(Jbig2Allocator *_allocator, void *p) |
195 | 4.02M | { |
196 | 4.02M | s_jbig2decode_allocator_t *allocator = (s_jbig2decode_allocator_t *) _allocator; |
197 | 4.02M | gs_free_object(allocator->mem, p, "s_jbig2decode_free"); |
198 | 4.02M | } |
199 | | |
200 | | static void *s_jbig2decode_realloc(Jbig2Allocator *_allocator, void *p, size_t size) |
201 | 0 | { |
202 | 0 | s_jbig2decode_allocator_t *allocator = (s_jbig2decode_allocator_t *) _allocator; |
203 | 0 | if (size > UINT_MAX) |
204 | 0 | return NULL; |
205 | 0 | return gs_resize_object(allocator->mem, p, size, "s_jbig2decode_realloc"); |
206 | 0 | } |
207 | | |
208 | | /* parse a globals stream packed into a gs_bytestring for us by the postscript |
209 | | layer and stuff the resulting context into a pointer for use in later decoding */ |
210 | | int |
211 | | s_jbig2decode_make_global_data(gs_memory_t *mem, byte *data, uint length, void **result) |
212 | 0 | { |
213 | 0 | Jbig2Ctx *ctx = NULL; |
214 | 0 | int code; |
215 | 0 | s_jbig2decode_allocator_t *allocator; |
216 | | |
217 | | /* the cvision encoder likes to include empty global streams */ |
218 | 0 | if (length == 0) { |
219 | 0 | if_debug0('w', "[w] ignoring zero-length jbig2 global stream.\n"); |
220 | 0 | *result = NULL; |
221 | 0 | return 0; |
222 | 0 | } |
223 | | |
224 | 0 | allocator = (s_jbig2decode_allocator_t *) gs_alloc_bytes(mem, |
225 | 0 | sizeof (s_jbig2decode_allocator_t), "s_jbig2_make_global_data"); |
226 | 0 | if (allocator == NULL) { |
227 | 0 | *result = NULL; |
228 | 0 | return_error(gs_error_VMerror); |
229 | 0 | } |
230 | | |
231 | 0 | allocator->allocator.alloc = s_jbig2decode_alloc; |
232 | 0 | allocator->allocator.free = s_jbig2decode_free; |
233 | 0 | allocator->allocator.realloc = s_jbig2decode_realloc; |
234 | 0 | allocator->mem = mem; |
235 | | |
236 | | /* allocate a context with which to parse our global segments */ |
237 | 0 | ctx = jbig2_ctx_new((Jbig2Allocator *) allocator, JBIG2_OPTIONS_EMBEDDED, |
238 | 0 | NULL, s_jbig2decode_error, NULL); |
239 | 0 | if (ctx == NULL) { |
240 | 0 | gs_free_object(mem, allocator, "s_jbig2_make_global_data"); |
241 | 0 | return_error(gs_error_VMerror); |
242 | 0 | } |
243 | | |
244 | | /* parse the global bitstream */ |
245 | 0 | code = jbig2_data_in(ctx, data, length); |
246 | 0 | if (code) { |
247 | | /* error parsing the global stream */ |
248 | 0 | allocator = (s_jbig2decode_allocator_t *) jbig2_ctx_free(ctx); |
249 | 0 | gs_free_object(allocator->mem, allocator, "s_jbig2_make_global_data"); |
250 | 0 | *result = NULL; |
251 | 0 | return_error(gs_error_ioerror); |
252 | 0 | } |
253 | | |
254 | | /* canonize and store our global state */ |
255 | 0 | *result = jbig2_make_global_ctx(ctx); |
256 | |
|
257 | 0 | return 0; /* todo: check for allocation failure */ |
258 | 0 | } |
259 | | |
260 | | /* release a global ctx pointer */ |
261 | | void |
262 | | s_jbig2decode_free_global_data(void *data) |
263 | 0 | { |
264 | 0 | Jbig2GlobalCtx *global_ctx = (Jbig2GlobalCtx*)data; |
265 | 0 | s_jbig2decode_allocator_t *allocator; |
266 | |
|
267 | 0 | allocator = (s_jbig2decode_allocator_t *) jbig2_global_ctx_free(global_ctx); |
268 | |
|
269 | 0 | gs_free_object(allocator->mem, allocator, "s_jbig2decode_free_global_data"); |
270 | 0 | } |
271 | | |
272 | | /* store a global ctx pointer in our state structure. |
273 | | * If "gd" is NULL, then this library must free the global context. |
274 | | * If not-NULL, then it will be memory managed by caller, for example, |
275 | | * garbage collected in the case of the PS interpreter. |
276 | | * Currently gpdf will use NULL, and the PDF implemented in the gs interpreter would use |
277 | | * the garbage collection. |
278 | | */ |
279 | | int |
280 | | s_jbig2decode_set_global_data(stream_state *ss, s_jbig2_global_data_t *gd, void *global_ctx) |
281 | 798 | { |
282 | 798 | stream_jbig2decode_state *state = (stream_jbig2decode_state*)ss; |
283 | 798 | state->global_struct = gd; |
284 | 798 | state->global_ctx = global_ctx; |
285 | 798 | return 0; |
286 | 798 | } |
287 | | |
288 | | /* initialize the steam. |
289 | | this involves allocating the context structures, and |
290 | | initializing the global context from the /JBIG2Globals object reference |
291 | | */ |
292 | | static int |
293 | | s_jbig2decode_init(stream_state * ss) |
294 | 798 | { |
295 | 798 | stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss; |
296 | 798 | Jbig2GlobalCtx *global_ctx = state->global_ctx; /* may be NULL */ |
297 | 798 | int code = 0; |
298 | 798 | s_jbig2decode_allocator_t *allocator = NULL; |
299 | | |
300 | 798 | state->callback_data = (s_jbig2_callback_data_t *)gs_alloc_bytes( |
301 | 798 | ss->memory->non_gc_memory, |
302 | 798 | sizeof(s_jbig2_callback_data_t), |
303 | 798 | "s_jbig2decode_init(callback_data)"); |
304 | 798 | if (state->callback_data) { |
305 | 798 | state->callback_data->memory = ss->memory->non_gc_memory; |
306 | 798 | state->callback_data->error = 0; |
307 | 798 | state->callback_data->last_message = NULL; |
308 | 798 | state->callback_data->repeats = 0; |
309 | | |
310 | 798 | allocator = (s_jbig2decode_allocator_t *) gs_alloc_bytes(ss->memory->non_gc_memory, sizeof (s_jbig2decode_allocator_t), "s_jbig2decode_init(allocator)"); |
311 | 798 | if (allocator == NULL) { |
312 | 0 | s_jbig2decode_error(state->callback_data, "failed to allocate custom jbig2dec allocator", JBIG2_SEVERITY_FATAL, -1); |
313 | 0 | } |
314 | 798 | else { |
315 | 798 | allocator->allocator.alloc = s_jbig2decode_alloc; |
316 | 798 | allocator->allocator.free = s_jbig2decode_free; |
317 | 798 | allocator->allocator.realloc = s_jbig2decode_realloc; |
318 | 798 | allocator->mem = ss->memory->non_gc_memory; |
319 | | |
320 | | /* initialize the decoder with the parsed global context if any */ |
321 | 798 | state->decode_ctx = jbig2_ctx_new((Jbig2Allocator *) allocator, JBIG2_OPTIONS_EMBEDDED, |
322 | 798 | global_ctx, s_jbig2decode_error, state->callback_data); |
323 | | |
324 | 798 | if (state->decode_ctx == NULL) { |
325 | 0 | gs_free_object(allocator->mem, allocator, "s_jbig2decode_release"); |
326 | 0 | } |
327 | | |
328 | 798 | } |
329 | | |
330 | 798 | code = state->callback_data->error; |
331 | 798 | } |
332 | 0 | else { |
333 | 0 | code = gs_error_VMerror; |
334 | 0 | } |
335 | 798 | state->image = 0; |
336 | | |
337 | | |
338 | 798 | return_error (code); |
339 | 798 | } |
340 | | |
341 | | /* process a section of the input and return any decoded data. |
342 | | see strimpl.h for return codes. |
343 | | */ |
344 | | static int |
345 | | s_jbig2decode_process(stream_state * ss, stream_cursor_read * pr, |
346 | | stream_cursor_write * pw, bool last) |
347 | 251k | { |
348 | 251k | stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss; |
349 | 251k | Jbig2Image *image = state->image; |
350 | 251k | size_t in_size = pr->limit - pr->ptr; |
351 | 251k | size_t out_size = pw->limit - pw->ptr; |
352 | 251k | int status = 0; |
353 | | |
354 | | /* there will only be a single page image, |
355 | | so pass all data in before looking for any output. |
356 | | note that the gs stream library expects offset-by-one |
357 | | indexing of the buffers, while jbig2dec uses normal 0 indexes */ |
358 | 251k | if (in_size > 0) { |
359 | | /* pass all available input to the decoder */ |
360 | 5.97k | jbig2_data_in(state->decode_ctx, pr->ptr + 1, in_size); |
361 | 5.97k | pr->ptr += in_size; |
362 | | /* simulate end-of-page segment */ |
363 | 5.97k | if (last == 1) { |
364 | 779 | jbig2_complete_page(state->decode_ctx); |
365 | 779 | } |
366 | | /* handle fatal decoding errors reported through our callback */ |
367 | 5.97k | if (state->callback_data->error) return state->callback_data->error; |
368 | 245k | } else { |
369 | | /* Ran out of input, try and terminate cleanly */ |
370 | 245k | if (last == 1) { |
371 | 245k | jbig2_complete_page(state->decode_ctx); |
372 | 245k | } |
373 | 245k | } |
374 | 251k | if (out_size > 0) { |
375 | 251k | if (image == NULL) { |
376 | | /* see if a page image in available */ |
377 | 6.30k | image = jbig2_page_out(state->decode_ctx); |
378 | 6.30k | if (image != NULL) { |
379 | 546 | state->image = image; |
380 | 546 | state->offset = 0; |
381 | 546 | } |
382 | 6.30k | } |
383 | 251k | if (image != NULL) { |
384 | | /* copy data out of the decoded image, if any */ |
385 | 245k | size_t image_size = (size_t)image->height*image->stride; |
386 | 245k | size_t usable = min(image_size - state->offset, out_size); |
387 | 245k | memcpy(pw->ptr + 1, image->data + state->offset, usable); |
388 | 245k | s_jbig2decode_invert_buffer(pw->ptr + 1, usable); |
389 | 245k | state->offset += usable; |
390 | 245k | pw->ptr += usable; |
391 | 245k | status = (state->offset < image_size) ? 1 : 0; |
392 | 245k | } |
393 | 251k | } |
394 | | |
395 | 251k | return status; |
396 | 251k | } |
397 | | |
398 | | /* stream release. |
399 | | free all our decoder state. |
400 | | */ |
401 | | static void |
402 | | s_jbig2decode_release(stream_state *ss) |
403 | 1.59k | { |
404 | 1.59k | stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss; |
405 | | |
406 | 1.59k | if (state->decode_ctx) { |
407 | 798 | s_jbig2decode_allocator_t *allocator = NULL; |
408 | | |
409 | 798 | if (state->image) jbig2_release_page(state->decode_ctx, state->image); |
410 | 798 | state->image = NULL; |
411 | 798 | s_jbig2decode_flush_errors(state->callback_data); |
412 | 798 | allocator = (s_jbig2decode_allocator_t *) jbig2_ctx_free(state->decode_ctx); |
413 | 798 | state->decode_ctx = NULL; |
414 | | |
415 | 798 | gs_free_object(allocator->mem, allocator, "s_jbig2decode_release"); |
416 | 798 | } |
417 | 1.59k | if (state->callback_data) { |
418 | 798 | gs_memory_t *mem = state->callback_data->memory; |
419 | 798 | gs_free_object(state->callback_data->memory, state->callback_data->last_message, "s_jbig2decode_release(message)"); |
420 | 798 | gs_free_object(mem, state->callback_data, "s_jbig2decode_release(callback_data)"); |
421 | 798 | state->callback_data = NULL; |
422 | 798 | } |
423 | 1.59k | if (state->global_struct != NULL) { |
424 | | /* the interpreter calls jbig2decode_free_global_data() separately */ |
425 | 1.59k | } else { |
426 | | /* We are responsible for freeing global context */ |
427 | 1.59k | if (state->global_ctx) { |
428 | 0 | s_jbig2decode_free_global_data(state->global_ctx); |
429 | 0 | state->global_ctx = NULL; |
430 | 0 | } |
431 | 1.59k | } |
432 | 1.59k | } |
433 | | |
434 | | void |
435 | | s_jbig2decode_finalize(const gs_memory_t *cmem, void *vptr) |
436 | 798 | { |
437 | 798 | (void)cmem; |
438 | | |
439 | 798 | s_jbig2decode_release((stream_state *)vptr); |
440 | 798 | } |
441 | | |
442 | | /* set stream defaults. |
443 | | this hook exists to avoid confusing the gc with bogus |
444 | | pointers. we use it similarly just to NULL all the pointers. |
445 | | (could just be done in _init?) |
446 | | */ |
447 | | static void |
448 | | s_jbig2decode_set_defaults(stream_state *ss) |
449 | 798 | { |
450 | 798 | stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss; |
451 | | |
452 | | /* state->global_ctx is not owned by us */ |
453 | 798 | state->global_struct = NULL; |
454 | 798 | state->global_ctx = NULL; |
455 | 798 | state->decode_ctx = NULL; |
456 | 798 | state->image = NULL; |
457 | 798 | state->offset = 0; |
458 | 798 | state->callback_data = NULL; |
459 | 798 | } |
460 | | |
461 | | /* stream template */ |
462 | | const stream_template s_jbig2decode_template = { |
463 | | &st_jbig2decode_state, |
464 | | s_jbig2decode_init, |
465 | | s_jbig2decode_process, |
466 | | 1, 1, /* min in and out buffer sizes we can handle --should be ~32k,64k for efficiency? */ |
467 | | s_jbig2decode_release, |
468 | | s_jbig2decode_set_defaults |
469 | | }; |