Coverage Report

Created: 2025-12-19 06:58

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