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 */
38static void
39dump_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 **/
61int
62jbig2_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 **/
163int
164jbig2_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 **/
192int
193jbig2_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 **/
228int
229jbig2_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 **/
260int
261jbig2_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 **/
303Jbig2Image *
304jbig2_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 **/
332void
333jbig2_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