1 | #include "fitz-imp.h" |
2 | |
3 | #ifdef HAVE_LURATECH |
4 | |
5 | #include <string.h> |
6 | |
7 | #include <ldf_jb2.h> |
8 | |
9 | typedef struct fz_jbig2d_s fz_jbig2d; |
10 | |
11 | struct fz_jbig2_globals_s |
12 | { |
13 | fz_storable storable; |
14 | fz_buffer *buf; |
15 | }; |
16 | |
17 | struct fz_jbig2d_s |
18 | { |
19 | fz_stream *chain; |
20 | fz_context *ctx; |
21 | fz_jbig2_globals *gctx; |
22 | JB2_Handle_Document doc; |
23 | unsigned long width; |
24 | unsigned long height; |
25 | int stride; |
26 | fz_buffer *input; |
27 | unsigned char *output; |
28 | int idx; |
29 | }; |
30 | |
31 | fz_jbig2_globals * |
32 | fz_keep_jbig2_globals(fz_context *ctx, fz_jbig2_globals *globals) |
33 | { |
34 | return fz_keep_storable(ctx, &globals->storable); |
35 | } |
36 | |
37 | void |
38 | fz_drop_jbig2_globals(fz_context *ctx, fz_jbig2_globals *globals) |
39 | { |
40 | fz_drop_storable(ctx, &globals->storable); |
41 | } |
42 | |
43 | static void |
44 | close_jbig2d(fz_context *ctx, void *state_) |
45 | { |
46 | fz_jbig2d *state = state_; |
47 | fz_free(ctx, state->output); |
48 | fz_drop_jbig2_globals(ctx, state->gctx); |
49 | fz_drop_stream(ctx, state->chain); |
50 | fz_free(ctx, state); |
51 | } |
52 | |
53 | static void * JB2_Callback |
54 | jbig2_alloc(unsigned long size, void *userdata) |
55 | { |
56 | fz_jbig2d *state = userdata; |
57 | return fz_malloc(state->ctx, size); |
58 | } |
59 | |
60 | static JB2_Error JB2_Callback |
61 | jbig2_free(void *ptr, void *userdata) |
62 | { |
63 | fz_jbig2d *state = userdata; |
64 | fz_free(state->ctx, ptr); |
65 | return cJB2_Error_OK; |
66 | } |
67 | |
68 | static void JB2_Callback |
69 | jbig2_message(const char *msg, JB2_Message_Level level, void *userdata) |
70 | { |
71 | fz_jbig2d *state = userdata; |
72 | |
73 | if (msg != NULL && msg[0] != '\0') |
74 | switch (level) |
75 | { |
76 | case cJB2_Message_Information: |
77 | #ifndef NDEBUG |
78 | fz_warn(state->ctx, "luratech jbig2 info: %s" , msg); |
79 | #endif |
80 | break; |
81 | case cJB2_Message_Warning: |
82 | fz_warn(state->ctx, "luratech jbig2 warning: %s" , msg); |
83 | break; |
84 | case cJB2_Message_Error: |
85 | fz_warn(state->ctx, "luratech jbig2 error: %s" , msg); |
86 | break; |
87 | default: |
88 | fz_warn(state->ctx, "luratech jbig2 message: %s" , msg); |
89 | break; |
90 | } |
91 | } |
92 | |
93 | static JB2_Size_T JB2_Callback |
94 | jbig2_read(unsigned char *buf, JB2_Size_T offset, JB2_Size_T size, void *userdata) |
95 | { |
96 | fz_jbig2d *state = userdata; |
97 | size_t available; |
98 | |
99 | /* globals data */ |
100 | if (state->gctx && offset < state->gctx->buf->len) |
101 | { |
102 | available = fz_minz(state->gctx->buf->len - offset, size); |
103 | memcpy(buf, state->gctx->buf->data + offset, available); |
104 | return (JB2_Size_T)available; |
105 | } |
106 | |
107 | /* image data */ |
108 | if (state->gctx) |
109 | offset -= (JB2_Size_T)state->gctx->buf->len; |
110 | if (state->input->len <= offset) |
111 | return 0; |
112 | available = fz_minz(state->input->len - offset, size); |
113 | memcpy(buf, state->input->data + offset, available); |
114 | return (JB2_Size_T)available; |
115 | } |
116 | |
117 | static JB2_Error JB2_Callback |
118 | jbig2_write(unsigned char *buf, unsigned long row, unsigned long width, unsigned long bpp, void *userdata) |
119 | { |
120 | fz_jbig2d *state = userdata; |
121 | int stride = (width + 7) >> 3; |
122 | unsigned char *dp = state->output + row * stride; |
123 | |
124 | if (row >= state->height) |
125 | { |
126 | fz_warn(state->ctx, "row %lu outside of image" , row); |
127 | return cJB2_Error_OK; |
128 | } |
129 | |
130 | while (stride--) |
131 | *(dp++) = *(buf++) ^ 0xff; |
132 | |
133 | return cJB2_Error_OK; |
134 | } |
135 | |
136 | static int |
137 | next_jbig2d(fz_context *ctx, fz_stream *stm, size_t len) |
138 | { |
139 | fz_jbig2d *state = stm->state; |
140 | JB2_Error err; |
141 | JB2_Scaling_Factor scale = {1, 1}; |
142 | JB2_Rect rect = {0, 0, 0, 0}; |
143 | |
144 | if (!state->output) |
145 | { |
146 | fz_try(ctx) |
147 | { |
148 | state->input = fz_read_all(state->ctx, state->chain, 0); |
149 | |
150 | err = JB2_Document_Start(&state->doc, |
151 | jbig2_alloc, state, |
152 | jbig2_free, state, |
153 | jbig2_read, state, |
154 | jbig2_message, state); |
155 | if (err != cJB2_Error_OK) |
156 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open image: %d" , (int) err); |
157 | |
158 | #if defined(JB2_LICENSE_NUM_1) && defined(JB2_LICENSE_NUM_2) |
159 | err = JB2_Document_Set_License(doc, JB2_LICENSE_NUM_1, JB2_LICENSE_NUM_2); |
160 | if (err != cJB2_Error_OK) |
161 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot set license: %d" , (int) err); |
162 | #endif |
163 | |
164 | err = JB2_Document_Set_Page(state->doc, 0); |
165 | if (err != cJB2_Error_OK) |
166 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot select page: %d" , (int) err); |
167 | |
168 | err = JB2_Document_Get_Property(state->doc, cJB2_Prop_Page_Width, &state->width); |
169 | if (err != cJB2_Error_OK) |
170 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get page width: %d" , (int) err); |
171 | err = JB2_Document_Get_Property(state->doc, cJB2_Prop_Page_Height, &state->height); |
172 | if (err != cJB2_Error_OK) |
173 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get page height: %d" , (int) err); |
174 | |
175 | state->stride = (state->width + 7) >> 3; |
176 | stm->pos = state->stride * state->height; |
177 | state->output = fz_malloc(state->ctx, stm->pos); |
178 | stm->rp = state->output; |
179 | stm->wp = state->output; |
180 | |
181 | err = JB2_Document_Decompress_Page(state->doc, scale, rect, jbig2_write, state); |
182 | if (err != cJB2_Error_OK) |
183 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot decode image: %d" , (int) err); |
184 | |
185 | /* update wp last. rp == wp upon errors above, causing |
186 | subsequent EOFs in comparison below */ |
187 | stm->wp += stm->pos; |
188 | } |
189 | fz_always(ctx) |
190 | { |
191 | fz_drop_buffer(ctx, state->input); |
192 | JB2_Document_End(&state->doc); |
193 | } |
194 | fz_catch(ctx) |
195 | { |
196 | fz_rethrow(ctx); |
197 | } |
198 | } |
199 | |
200 | if (stm->rp == stm->wp) |
201 | return EOF; |
202 | return *stm->rp++; |
203 | } |
204 | |
205 | fz_jbig2_globals * |
206 | fz_load_jbig2_globals(fz_context *ctx, fz_buffer *buf) |
207 | { |
208 | fz_jbig2_globals *globals = fz_malloc_struct(ctx, fz_jbig2_globals); |
209 | |
210 | FZ_INIT_STORABLE(globals, 1, fz_drop_jbig2_globals_imp); |
211 | globals->buf = fz_keep_buffer(ctx, buf); |
212 | |
213 | return globals; |
214 | } |
215 | |
216 | void |
217 | fz_drop_jbig2_globals_imp(fz_context *ctx, fz_storable *globals_) |
218 | { |
219 | fz_jbig2_globals *globals = (fz_jbig2_globals *)globals_; |
220 | fz_drop_buffer(ctx, globals->buf); |
221 | fz_free(ctx, globals); |
222 | } |
223 | |
224 | fz_stream * |
225 | fz_open_jbig2d(fz_context *ctx, fz_stream *chain, fz_jbig2_globals *globals) |
226 | { |
227 | fz_jbig2d *state = NULL; |
228 | |
229 | state = fz_malloc_struct(ctx, fz_jbig2d); |
230 | state->ctx = ctx; |
231 | state->gctx = fz_keep_jbig2_globals(ctx, globals); |
232 | state->chain = fz_keep_stream(ctx, chain); |
233 | state->idx = 0; |
234 | state->output = NULL; |
235 | state->doc = NULL; |
236 | |
237 | return fz_new_stream(ctx, state, next_jbig2d, close_jbig2d); |
238 | } |
239 | |
240 | #else /* HAVE_LURATECH */ |
241 | |
242 | #include <jbig2.h> |
243 | |
244 | typedef struct fz_jbig2d_s fz_jbig2d; |
245 | |
246 | struct fz_jbig2_alloc_s |
247 | { |
248 | Jbig2Allocator alloc; |
249 | fz_context *ctx; |
250 | }; |
251 | |
252 | struct fz_jbig2_globals_s |
253 | { |
254 | fz_storable storable; |
255 | Jbig2GlobalCtx *gctx; |
256 | struct fz_jbig2_alloc_s alloc; |
257 | }; |
258 | |
259 | struct fz_jbig2d_s |
260 | { |
261 | fz_stream *chain; |
262 | Jbig2Ctx *ctx; |
263 | struct fz_jbig2_alloc_s alloc; |
264 | fz_jbig2_globals *gctx; |
265 | Jbig2Image *page; |
266 | int idx; |
267 | unsigned char buffer[4096]; |
268 | }; |
269 | |
270 | fz_jbig2_globals * |
271 | fz_keep_jbig2_globals(fz_context *ctx, fz_jbig2_globals *globals) |
272 | { |
273 | return fz_keep_storable(ctx, &globals->storable); |
274 | } |
275 | |
276 | void |
277 | fz_drop_jbig2_globals(fz_context *ctx, fz_jbig2_globals *globals) |
278 | { |
279 | fz_drop_storable(ctx, &globals->storable); |
280 | } |
281 | |
282 | static void |
283 | close_jbig2d(fz_context *ctx, void *state_) |
284 | { |
285 | fz_jbig2d *state = state_; |
286 | if (state->page) |
287 | jbig2_release_page(state->ctx, state->page); |
288 | fz_drop_jbig2_globals(ctx, state->gctx); |
289 | jbig2_ctx_free(state->ctx); |
290 | fz_drop_stream(ctx, state->chain); |
291 | fz_free(ctx, state); |
292 | } |
293 | |
294 | static int |
295 | next_jbig2d(fz_context *ctx, fz_stream *stm, size_t len) |
296 | { |
297 | fz_jbig2d *state = stm->state; |
298 | unsigned char tmp[4096]; |
299 | unsigned char *buf = state->buffer; |
300 | unsigned char *p = buf; |
301 | unsigned char *ep; |
302 | unsigned char *s; |
303 | int x, w; |
304 | size_t n; |
305 | |
306 | if (len > sizeof(state->buffer)) |
307 | len = sizeof(state->buffer); |
308 | ep = buf + len; |
309 | |
310 | if (!state->page) |
311 | { |
312 | while (1) |
313 | { |
314 | n = fz_read(ctx, state->chain, tmp, sizeof tmp); |
315 | if (n == 0) |
316 | break; |
317 | |
318 | if (jbig2_data_in(state->ctx, tmp, n) < 0) |
319 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot decode jbig2 image" ); |
320 | } |
321 | |
322 | if (jbig2_complete_page(state->ctx) < 0) |
323 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot complete jbig2 image" ); |
324 | |
325 | state->page = jbig2_page_out(state->ctx); |
326 | if (!state->page) |
327 | fz_throw(ctx, FZ_ERROR_GENERIC, "no jbig2 image decoded" ); |
328 | } |
329 | |
330 | s = state->page->data; |
331 | w = state->page->height * state->page->stride; |
332 | x = state->idx; |
333 | while (p < ep && x < w) |
334 | *p++ = s[x++] ^ 0xff; |
335 | state->idx = x; |
336 | |
337 | stm->rp = buf; |
338 | stm->wp = p; |
339 | if (p == buf) |
340 | return EOF; |
341 | stm->pos += p - buf; |
342 | return *stm->rp++; |
343 | } |
344 | |
345 | static void |
346 | error_callback(void *data, const char *msg, Jbig2Severity severity, int32_t seg_idx) |
347 | { |
348 | fz_context *ctx = data; |
349 | if (severity == JBIG2_SEVERITY_FATAL) |
350 | fz_warn(ctx, "jbig2dec error: %s (segment %d)" , msg, seg_idx); |
351 | else if (severity == JBIG2_SEVERITY_WARNING) |
352 | fz_warn(ctx, "jbig2dec warning: %s (segment %d)" , msg, seg_idx); |
353 | #ifndef NDEBUG |
354 | else if (severity == JBIG2_SEVERITY_INFO) |
355 | fz_warn(ctx, "jbig2dec info: %s (segment %d)" , msg, seg_idx); |
356 | else if (severity == JBIG2_SEVERITY_DEBUG) |
357 | fz_warn(ctx, "jbig2dec debug: %s (segment %d)" , msg, seg_idx); |
358 | #endif |
359 | } |
360 | |
361 | static void *fz_jbig2_alloc(Jbig2Allocator *allocator, size_t size) |
362 | { |
363 | fz_context *ctx = ((struct fz_jbig2_alloc_s *) allocator)->ctx; |
364 | return fz_malloc_no_throw(ctx, size); |
365 | } |
366 | |
367 | static void fz_jbig2_free(Jbig2Allocator *allocator, void *p) |
368 | { |
369 | fz_context *ctx = ((struct fz_jbig2_alloc_s *) allocator)->ctx; |
370 | fz_free(ctx, p); |
371 | } |
372 | |
373 | static void *fz_jbig2_realloc(Jbig2Allocator *allocator, void *p, size_t size) |
374 | { |
375 | fz_context *ctx = ((struct fz_jbig2_alloc_s *) allocator)->ctx; |
376 | if (size == 0) |
377 | { |
378 | fz_free(ctx, p); |
379 | return NULL; |
380 | } |
381 | if (p == NULL) |
382 | return fz_malloc(ctx, size); |
383 | return fz_realloc_no_throw(ctx, p, size); |
384 | } |
385 | |
386 | fz_jbig2_globals * |
387 | fz_load_jbig2_globals(fz_context *ctx, fz_buffer *buf) |
388 | { |
389 | fz_jbig2_globals *globals = fz_malloc_struct(ctx, fz_jbig2_globals); |
390 | Jbig2Ctx *jctx; |
391 | |
392 | globals->alloc.ctx = ctx; |
393 | globals->alloc.alloc.alloc = fz_jbig2_alloc; |
394 | globals->alloc.alloc.free = fz_jbig2_free; |
395 | globals->alloc.alloc.realloc = fz_jbig2_realloc; |
396 | |
397 | jctx = jbig2_ctx_new((Jbig2Allocator *) &globals->alloc, JBIG2_OPTIONS_EMBEDDED, NULL, error_callback, ctx); |
398 | if (!jctx) |
399 | { |
400 | fz_free(ctx, globals); |
401 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot allocate jbig2 globals context" ); |
402 | } |
403 | |
404 | if (jbig2_data_in(jctx, buf->data, buf->len) < 0) |
405 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot decode jbig2 globals" ); |
406 | |
407 | FZ_INIT_STORABLE(globals, 1, fz_drop_jbig2_globals_imp); |
408 | globals->gctx = jbig2_make_global_ctx(jctx); |
409 | |
410 | return globals; |
411 | } |
412 | |
413 | void |
414 | fz_drop_jbig2_globals_imp(fz_context *ctx, fz_storable *globals_) |
415 | { |
416 | fz_jbig2_globals *globals = (fz_jbig2_globals *)globals_; |
417 | jbig2_global_ctx_free(globals->gctx); |
418 | fz_free(ctx, globals); |
419 | } |
420 | |
421 | fz_stream * |
422 | fz_open_jbig2d(fz_context *ctx, fz_stream *chain, fz_jbig2_globals *globals) |
423 | { |
424 | fz_jbig2d *state = NULL; |
425 | |
426 | fz_var(state); |
427 | |
428 | state = fz_malloc_struct(ctx, fz_jbig2d); |
429 | state->gctx = fz_keep_jbig2_globals(ctx, globals); |
430 | state->alloc.ctx = ctx; |
431 | state->alloc.alloc.alloc = fz_jbig2_alloc; |
432 | state->alloc.alloc.free = fz_jbig2_free; |
433 | state->alloc.alloc.realloc = fz_jbig2_realloc; |
434 | |
435 | state->ctx = jbig2_ctx_new((Jbig2Allocator *) &state->alloc, JBIG2_OPTIONS_EMBEDDED, globals ? globals->gctx : NULL, error_callback, ctx); |
436 | if (state->ctx == NULL) |
437 | { |
438 | fz_drop_jbig2_globals(ctx, state->gctx); |
439 | fz_free(ctx, state); |
440 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot allocate jbig2 context" ); |
441 | } |
442 | |
443 | state->page = NULL; |
444 | state->idx = 0; |
445 | state->chain = fz_keep_stream(ctx, chain); |
446 | |
447 | return fz_new_stream(ctx, state, next_jbig2d, close_jbig2d); |
448 | } |
449 | |
450 | #endif /* HAVE_LURATECH */ |
451 | |