1#include "fitz-imp.h"
2
3#ifdef HAVE_LURATECH
4
5#include <string.h>
6
7#include <ldf_jb2.h>
8
9typedef struct fz_jbig2d_s fz_jbig2d;
10
11struct fz_jbig2_globals_s
12{
13 fz_storable storable;
14 fz_buffer *buf;
15};
16
17struct 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
31fz_jbig2_globals *
32fz_keep_jbig2_globals(fz_context *ctx, fz_jbig2_globals *globals)
33{
34 return fz_keep_storable(ctx, &globals->storable);
35}
36
37void
38fz_drop_jbig2_globals(fz_context *ctx, fz_jbig2_globals *globals)
39{
40 fz_drop_storable(ctx, &globals->storable);
41}
42
43static void
44close_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
53static void * JB2_Callback
54jbig2_alloc(unsigned long size, void *userdata)
55{
56 fz_jbig2d *state = userdata;
57 return fz_malloc(state->ctx, size);
58}
59
60static JB2_Error JB2_Callback
61jbig2_free(void *ptr, void *userdata)
62{
63 fz_jbig2d *state = userdata;
64 fz_free(state->ctx, ptr);
65 return cJB2_Error_OK;
66}
67
68static void JB2_Callback
69jbig2_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
93static JB2_Size_T JB2_Callback
94jbig2_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
117static JB2_Error JB2_Callback
118jbig2_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
136static int
137next_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
205fz_jbig2_globals *
206fz_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
216void
217fz_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
224fz_stream *
225fz_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
244typedef struct fz_jbig2d_s fz_jbig2d;
245
246struct fz_jbig2_alloc_s
247{
248 Jbig2Allocator alloc;
249 fz_context *ctx;
250};
251
252struct fz_jbig2_globals_s
253{
254 fz_storable storable;
255 Jbig2GlobalCtx *gctx;
256 struct fz_jbig2_alloc_s alloc;
257};
258
259struct 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
270fz_jbig2_globals *
271fz_keep_jbig2_globals(fz_context *ctx, fz_jbig2_globals *globals)
272{
273 return fz_keep_storable(ctx, &globals->storable);
274}
275
276void
277fz_drop_jbig2_globals(fz_context *ctx, fz_jbig2_globals *globals)
278{
279 fz_drop_storable(ctx, &globals->storable);
280}
281
282static void
283close_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
294static int
295next_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
345static void
346error_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
361static 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
367static 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
373static 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
386fz_jbig2_globals *
387fz_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
413void
414fz_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
421fz_stream *
422fz_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