Coverage Report

Created: 2026-07-24 07:44

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