1 | /* Copyright (C) 2001-2019 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 | jbig2dec |
18 | */ |
19 | |
20 | #ifdef HAVE_CONFIG_H |
21 | #include "config.h" |
22 | #endif |
23 | #include "os_types.h" |
24 | |
25 | #include <stdlib.h> |
26 | |
27 | #ifdef OUTPUT_PBM |
28 | #include <stdio.h> |
29 | #endif |
30 | |
31 | #include "jbig2.h" |
32 | #include "jbig2_priv.h" |
33 | #include "jbig2_image.h" |
34 | #include "jbig2_page.h" |
35 | #include "jbig2_segment.h" |
36 | |
37 | /* dump the page struct info */ |
38 | static void |
39 | dump_page_info(Jbig2Ctx *ctx, Jbig2Segment *segment, Jbig2Page *page) |
40 | { |
41 | if (page->x_resolution == 0) { |
42 | jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "page %d image is %dx%d (unknown res)" , page->number, page->width, page->height); |
43 | } else if (page->x_resolution == page->y_resolution) { |
44 | jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "page %d image is %dx%d (%d ppm)" , page->number, page->width, page->height, page->x_resolution); |
45 | } else { |
46 | jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, |
47 | "page %d image is %dx%d (%dx%d ppm)" , page->number, page->width, page->height, page->x_resolution, page->y_resolution); |
48 | } |
49 | if (page->striped) { |
50 | jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "\tmaximum stripe size: %d" , page->stripe_size); |
51 | } |
52 | } |
53 | |
54 | /** |
55 | * jbig2_page_info: parse page info segment |
56 | * |
57 | * Parse the page info segment data and fill out a corresponding |
58 | * Jbig2Page struct and ready it for subsequent rendered data, |
59 | * including allocating an image buffer for the page (or the first stripe) |
60 | **/ |
61 | int |
62 | jbig2_page_info(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) |
63 | { |
64 | Jbig2Page *page, *pages; |
65 | |
66 | /* a new page info segment implies the previous page is finished */ |
67 | page = &(ctx->pages[ctx->current_page]); |
68 | if (page->number != 0 && (page->state == JBIG2_PAGE_NEW || page->state == JBIG2_PAGE_FREE)) { |
69 | page->state = JBIG2_PAGE_COMPLETE; |
70 | jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unexpected page info segment, marking previous page finished" ); |
71 | } |
72 | |
73 | /* find a free page */ |
74 | { |
75 | int index, j; |
76 | |
77 | index = ctx->current_page; |
78 | while (ctx->pages[index].state != JBIG2_PAGE_FREE) { |
79 | index++; |
80 | if (index >= ctx->max_page_index) { |
81 | /* grow the list */ |
82 | pages = jbig2_renew(ctx, ctx->pages, Jbig2Page, (ctx->max_page_index <<= 2)); |
83 | if (pages == NULL) { |
84 | return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to reallocate pages" ); |
85 | } |
86 | ctx->pages = pages; |
87 | for (j = index; j < ctx->max_page_index; j++) { |
88 | ctx->pages[j].state = JBIG2_PAGE_FREE; |
89 | ctx->pages[j].number = 0; |
90 | ctx->pages[j].image = NULL; |
91 | } |
92 | } |
93 | } |
94 | page = &(ctx->pages[index]); |
95 | ctx->current_page = index; |
96 | page->state = JBIG2_PAGE_NEW; |
97 | page->number = segment->page_association; |
98 | } |
99 | |
100 | /* FIXME: would be nice if we tried to work around this */ |
101 | if (segment->data_length < 19) { |
102 | return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short" ); |
103 | } |
104 | |
105 | /* 7.4.8.x */ |
106 | page->width = jbig2_get_uint32(segment_data); |
107 | page->height = jbig2_get_uint32(segment_data + 4); |
108 | |
109 | page->x_resolution = jbig2_get_uint32(segment_data + 8); |
110 | page->y_resolution = jbig2_get_uint32(segment_data + 12); |
111 | page->flags = segment_data[16]; |
112 | /* Check for T.88 amendment 3 */ |
113 | if (page->flags & 0x80) |
114 | return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "page segment indicates use of color segments (NYI)" ); |
115 | |
116 | /* 7.4.8.6 */ |
117 | { |
118 | int16_t striping = jbig2_get_int16(segment_data + 17); |
119 | |
120 | if (striping & 0x8000) { |
121 | page->striped = TRUE; |
122 | page->stripe_size = striping & 0x7FFF; |
123 | } else { |
124 | page->striped = FALSE; |
125 | page->stripe_size = 0; /* would page->height be better? */ |
126 | } |
127 | } |
128 | if (page->height == 0xFFFFFFFF && page->striped == FALSE) { |
129 | jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "height is unspecified but page is not marked as striped, assuming striped with maximum strip size" ); |
130 | page->striped = TRUE; |
131 | page->stripe_size = 0x7FFF; |
132 | } |
133 | page->end_row = 0; |
134 | |
135 | if (segment->data_length > 19) { |
136 | jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "extra data in segment" ); |
137 | } |
138 | |
139 | dump_page_info(ctx, segment, page); |
140 | |
141 | /* allocate an appropriate page image buffer */ |
142 | /* 7.4.8.2 */ |
143 | if (page->height == 0xFFFFFFFF) { |
144 | page->image = jbig2_image_new(ctx, page->width, page->stripe_size); |
145 | } else { |
146 | page->image = jbig2_image_new(ctx, page->width, page->height); |
147 | } |
148 | if (page->image == NULL) { |
149 | return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate buffer for page image" ); |
150 | } else { |
151 | /* 8.2 (3) fill the page with the default pixel value */ |
152 | jbig2_image_clear(ctx, page->image, (page->flags & 4)); |
153 | jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, |
154 | "allocated %dx%d page image (%d bytes)" , page->image->width, page->image->height, page->image->stride * page->image->height); |
155 | } |
156 | |
157 | return 0; |
158 | } |
159 | |
160 | /** |
161 | * jbig2_end_of_stripe: parse and implement an end of stripe segment |
162 | **/ |
163 | int |
164 | jbig2_end_of_stripe(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) |
165 | { |
166 | Jbig2Page *page = &ctx->pages[ctx->current_page]; |
167 | uint32_t end_row; |
168 | |
169 | if (segment->data_length < 4) |
170 | return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short" ); |
171 | end_row = jbig2_get_uint32(segment_data); |
172 | if (end_row < page->end_row) { |
173 | jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, |
174 | "end of stripe segment with non-positive end row advance (new end row %d vs current end row %d)" , end_row, page->end_row); |
175 | } else { |
176 | jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of stripe: advancing end row from %u to %u" , page->end_row, end_row); |
177 | } |
178 | |
179 | page->end_row = end_row; |
180 | |
181 | return 0; |
182 | } |
183 | |
184 | /** |
185 | * jbig2_complete_page: complete a page image |
186 | * |
187 | * called upon seeing an 'end of page' segment, this routine |
188 | * marks a page as completed so it can be returned. |
189 | * compositing will have already happened in the previous |
190 | * segment handlers. |
191 | **/ |
192 | int |
193 | jbig2_complete_page(Jbig2Ctx *ctx) |
194 | { |
195 | int code; |
196 | |
197 | /* check for unfinished segments */ |
198 | if (ctx->segment_index != ctx->n_segments) { |
199 | Jbig2Segment *segment = ctx->segments[ctx->segment_index]; |
200 | |
201 | /* Some versions of Xerox Workcentre generate PDF files |
202 | with the segment data length field of the last segment |
203 | set to -1. Try to cope with this here. */ |
204 | if ((segment->data_length & 0xffffffff) == 0xffffffff) { |
205 | jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "file has an invalid segment data length; trying to decode using the available data" ); |
206 | segment->data_length = ctx->buf_wr_ix - ctx->buf_rd_ix; |
207 | code = jbig2_parse_segment(ctx, segment, ctx->buf + ctx->buf_rd_ix); |
208 | ctx->buf_rd_ix += segment->data_length; |
209 | ctx->segment_index++; |
210 | if (code < 0) { |
211 | return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to parse segment" ); |
212 | } |
213 | } |
214 | } |
215 | |
216 | /* ensure image exists before marking page as complete */ |
217 | if (ctx->pages[ctx->current_page].image == NULL) { |
218 | return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "page has no image, cannot be completed" ); |
219 | } |
220 | |
221 | ctx->pages[ctx->current_page].state = JBIG2_PAGE_COMPLETE; |
222 | return 0; |
223 | } |
224 | |
225 | /** |
226 | * jbig2_end_of_page: parse and implement an end of page segment |
227 | **/ |
228 | int |
229 | jbig2_end_of_page(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) |
230 | { |
231 | uint32_t page_number = ctx->pages[ctx->current_page].number; |
232 | int code; |
233 | |
234 | if (segment->page_association != page_number) { |
235 | jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, |
236 | "end of page marker for page %d doesn't match current page number %d" , segment->page_association, page_number); |
237 | } |
238 | |
239 | jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of page %d" , page_number); |
240 | |
241 | code = jbig2_complete_page(ctx); |
242 | if (code < 0) |
243 | return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to complete page" ); |
244 | |
245 | #ifdef OUTPUT_PBM |
246 | code = jbig2_image_write_pbm(ctx->pages[ctx->current_page].image, stdout); |
247 | if (code < 0) |
248 | return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to write page image" ); |
249 | #endif |
250 | |
251 | return 0; |
252 | } |
253 | |
254 | /** |
255 | * jbig2_add_page_result: composite a decoding result onto a page |
256 | * |
257 | * this is called to add the results of segment decode (when it |
258 | * is an image) to a page image buffer |
259 | **/ |
260 | int |
261 | jbig2_page_add_result(Jbig2Ctx *ctx, Jbig2Page *page, Jbig2Image *image, uint32_t x, uint32_t y, Jbig2ComposeOp op) |
262 | { |
263 | int code; |
264 | |
265 | /* ensure image exists first */ |
266 | if (page->image == NULL) |
267 | return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "page info possibly missing, no image defined" ); |
268 | |
269 | /* grow the page to accommodate a new stripe if necessary */ |
270 | if (page->striped && page->height == 0xFFFFFFFF) { |
271 | uint32_t new_height = y + image->height; |
272 | if (page->image->height < new_height) { |
273 | Jbig2Image *resized_image = NULL; |
274 | |
275 | jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "growing page buffer to %u rows to accommodate new stripe" , new_height); |
276 | resized_image = jbig2_image_resize(ctx, page->image, page->image->width, new_height, page->flags & 4); |
277 | if (resized_image == NULL) { |
278 | return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "unable to resize image to accommodate new stripe" ); |
279 | } |
280 | page->image = resized_image; |
281 | } |
282 | } |
283 | |
284 | code = jbig2_image_compose(ctx, page->image, image, x, y, op); |
285 | if (code < 0) |
286 | return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to compose image with page" ); |
287 | |
288 | return 0; |
289 | } |
290 | |
291 | /** |
292 | * jbig2_get_page: return the next available page image buffer |
293 | * |
294 | * the client can call this at any time to check if any pages |
295 | * have been decoded. If so, it returns the first available |
296 | * one. The client should then call jbig2_release_page() when |
297 | * it no longer needs to refer to the image buffer. |
298 | * |
299 | * since this is a public routine for the library clients, we |
300 | * return an image structure pointer, even though the function |
301 | * name refers to a page; the page structure is private. |
302 | **/ |
303 | Jbig2Image * |
304 | jbig2_page_out(Jbig2Ctx *ctx) |
305 | { |
306 | int index; |
307 | |
308 | /* search for a completed page */ |
309 | for (index = 0; index < ctx->max_page_index; index++) { |
310 | if (ctx->pages[index].state == JBIG2_PAGE_COMPLETE) { |
311 | Jbig2Image *img = ctx->pages[index].image; |
312 | uint32_t page_number = ctx->pages[index].number; |
313 | |
314 | if (img == NULL) { |
315 | jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "page %d returned with no associated image" , page_number); |
316 | continue; |
317 | } |
318 | |
319 | ctx->pages[index].state = JBIG2_PAGE_RETURNED; |
320 | jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "page %d returned to the client" , page_number); |
321 | return jbig2_image_reference(ctx, img); |
322 | } |
323 | } |
324 | |
325 | /* no pages available */ |
326 | return NULL; |
327 | } |
328 | |
329 | /** |
330 | * jbig2_release_page: tell the library a page can be freed |
331 | **/ |
332 | void |
333 | jbig2_release_page(Jbig2Ctx *ctx, Jbig2Image *image) |
334 | { |
335 | int index; |
336 | |
337 | if (image == NULL) |
338 | return; |
339 | |
340 | /* find the matching page struct and mark it released */ |
341 | for (index = 0; index < ctx->max_page_index; index++) { |
342 | if (ctx->pages[index].image == image) { |
343 | jbig2_image_release(ctx, image); |
344 | ctx->pages[index].state = JBIG2_PAGE_RELEASED; |
345 | jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "page %d released by the client" , ctx->pages[index].number); |
346 | return; |
347 | } |
348 | } |
349 | |
350 | /* no matching pages */ |
351 | jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to release unknown page" ); |
352 | } |
353 | |