| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- /* Copyright (C) 2001-2023 Artifex Software, Inc.
- All Rights Reserved.
- This software is provided AS-IS with no warranty, either express or
- implied.
- This software is distributed under license and may not be copied,
- modified or distributed except as expressly authorized under the terms
- of the license contained in the file LICENSE in this distribution.
- Refer to licensing information at http://www.artifex.com or contact
- Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
- CA 94129, USA, for further information.
- */
- /*
- jbig2dec
- */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include "os_types.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <string.h>
- #include <limits.h>
- #include "jbig2.h"
- #include "jbig2_priv.h"
- #include "jbig2_image.h"
- #include "jbig2_page.h"
- #include "jbig2_segment.h"
- static void *
- jbig2_default_alloc(Jbig2Allocator *allocator, size_t size)
- {
- return malloc(size);
- }
- static void
- jbig2_default_free(Jbig2Allocator *allocator, void *p)
- {
- free(p);
- }
- static void *
- jbig2_default_realloc(Jbig2Allocator *allocator, void *p, size_t size)
- {
- return realloc(p, size);
- }
- static Jbig2Allocator jbig2_default_allocator = {
- jbig2_default_alloc,
- jbig2_default_free,
- jbig2_default_realloc
- };
- void *
- jbig2_alloc(Jbig2Allocator *allocator, size_t size, size_t num)
- {
- /* Check for integer multiplication overflow when computing
- the full size of the allocation. */
- if (num > 0 && size > SIZE_MAX / num)
- return NULL;
- return allocator->alloc(allocator, size * num);
- }
- /* jbig2_free and jbig2_realloc moved to the bottom of this file */
- static void
- jbig2_default_error(void *data, const char *msg, Jbig2Severity severity, uint32_t seg_idx)
- {
- /* report only fatal errors by default */
- if (severity == JBIG2_SEVERITY_FATAL) {
- fprintf(stderr, "jbig2 decoder FATAL ERROR: %s", msg);
- if (seg_idx != JBIG2_UNKNOWN_SEGMENT_NUMBER)
- fprintf(stderr, " (segment 0x%02x)", seg_idx);
- fprintf(stderr, "\n");
- fflush(stderr);
- }
- }
- int
- jbig2_error(Jbig2Ctx *ctx, Jbig2Severity severity, uint32_t segment_number, const char *fmt, ...)
- {
- char buf[1024];
- va_list ap;
- int n;
- va_start(ap, fmt);
- n = vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- if (n < 0 || n == sizeof(buf))
- strncpy(buf, "failed to generate error string", sizeof(buf));
- ctx->error_callback(ctx->error_callback_data, buf, severity, segment_number);
- return -1;
- }
- Jbig2Ctx *
- jbig2_ctx_new_imp(Jbig2Allocator *allocator, Jbig2Options options, Jbig2GlobalCtx *global_ctx, Jbig2ErrorCallback error_callback, void *error_callback_data, int jbig2_version_major, int jbig2_version_minor)
- {
- Jbig2Ctx *result;
- if (jbig2_version_major != JBIG2_VERSION_MAJOR || jbig2_version_minor != JBIG2_VERSION_MINOR) {
- Jbig2Ctx fakectx;
- fakectx.error_callback = error_callback;
- fakectx.error_callback_data = error_callback_data;
- jbig2_error(&fakectx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "incompatible jbig2dec header (%d.%d) and library (%d.%d) versions",
- jbig2_version_major, jbig2_version_minor, JBIG2_VERSION_MAJOR, JBIG2_VERSION_MINOR);
- return NULL;
- }
- if (allocator == NULL)
- allocator = &jbig2_default_allocator;
- if (error_callback == NULL)
- error_callback = &jbig2_default_error;
- result = (Jbig2Ctx *) jbig2_alloc(allocator, sizeof(Jbig2Ctx), 1);
- if (result == NULL) {
- error_callback(error_callback_data, "failed to allocate initial context", JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER);
- return NULL;
- }
- result->allocator = allocator;
- result->options = options;
- result->global_ctx = (const Jbig2Ctx *)global_ctx;
- result->error_callback = error_callback;
- result->error_callback_data = error_callback_data;
- result->state = (options & JBIG2_OPTIONS_EMBEDDED) ? JBIG2_FILE_SEQUENTIAL_HEADER : JBIG2_FILE_HEADER;
- result->buf = NULL;
- result->n_segments = 0;
- result->n_segments_max = 16;
- result->segments = jbig2_new(result, Jbig2Segment *, result->n_segments_max);
- if (result->segments == NULL) {
- error_callback(error_callback_data, "failed to allocate initial segments", JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER);
- jbig2_free(allocator, result);
- return NULL;
- }
- result->segment_index = 0;
- result->current_page = 0;
- result->max_page_index = 4;
- result->pages = jbig2_new(result, Jbig2Page, result->max_page_index);
- if (result->pages == NULL) {
- error_callback(error_callback_data, "failed to allocated initial pages", JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER);
- jbig2_free(allocator, result->segments);
- jbig2_free(allocator, result);
- return NULL;
- }
- {
- uint32_t index;
- for (index = 0; index < result->max_page_index; index++) {
- result->pages[index].state = JBIG2_PAGE_FREE;
- result->pages[index].number = 0;
- result->pages[index].width = 0;
- result->pages[index].height = 0xffffffff;
- result->pages[index].x_resolution = 0;
- result->pages[index].y_resolution = 0;
- result->pages[index].stripe_size = 0;
- result->pages[index].striped = 0;
- result->pages[index].end_row = 0;
- result->pages[index].flags = 0;
- result->pages[index].image = NULL;
- }
- }
- return result;
- }
- #define get_uint16(bptr)\
- (((bptr)[0] << 8) | (bptr)[1])
- #define get_int16(bptr)\
- (((int)get_uint16(bptr) ^ 0x8000) - 0x8000)
- /* coverity[ -tainted_data_return ] */
- /* coverity[ -tainted_data_argument : arg-0 ] */
- int16_t
- jbig2_get_int16(const byte *bptr)
- {
- return get_int16(bptr);
- }
- /* coverity[ -tainted_data_return ] */
- /* coverity[ -tainted_data_argument : arg-0 ] */
- uint16_t
- jbig2_get_uint16(const byte *bptr)
- {
- return get_uint16(bptr);
- }
- /* coverity[ -tainted_data_return ] */
- /* coverity[ -tainted_data_argument : arg-0 ] */
- int32_t
- jbig2_get_int32(const byte *bptr)
- {
- return ((int32_t) get_int16(bptr) << 16) | get_uint16(bptr + 2);
- }
- /* coverity[ -tainted_data_return ] */
- /* coverity[ -tainted_data_argument : arg-0 ] */
- uint32_t
- jbig2_get_uint32(const byte *bptr)
- {
- return ((uint32_t) get_uint16(bptr) << 16) | get_uint16(bptr + 2);
- }
- static size_t
- jbig2_find_buffer_size(size_t desired)
- {
- const size_t initial_buf_size = 1024;
- size_t size = initial_buf_size;
- if (desired == SIZE_MAX)
- return SIZE_MAX;
- while (size < desired)
- size <<= 1;
- return size;
- }
- /**
- * jbig2_data_in: submit data for decoding
- * @ctx: The jbig2dec decoder context
- * @data: a pointer to the data buffer
- * @size: the size of the data buffer in bytes
- *
- * Copies the specified data into internal storage and attempts
- * to (continue to) parse it as part of a jbig2 data stream.
- *
- * Return code: 0 on success
- * -1 if there is a parsing error
- **/
- int
- jbig2_data_in(Jbig2Ctx *ctx, const unsigned char *data, size_t size)
- {
- if (ctx->buf == NULL) {
- size_t buf_size = jbig2_find_buffer_size(size);
- ctx->buf = jbig2_new(ctx, byte, buf_size);
- if (ctx->buf == NULL) {
- return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to allocate buffer when reading data");
- }
- ctx->buf_size = buf_size;
- ctx->buf_rd_ix = 0;
- ctx->buf_wr_ix = 0;
- } else if (size > ctx->buf_size - ctx->buf_wr_ix) {
- size_t already = ctx->buf_wr_ix - ctx->buf_rd_ix;
- if (ctx->buf_rd_ix <= (ctx->buf_size >> 1) && size <= ctx->buf_size - already) {
- memmove(ctx->buf, ctx->buf + ctx->buf_rd_ix, already);
- } else {
- byte *buf;
- size_t buf_size;
- if (already > SIZE_MAX - size) {
- return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "read data causes buffer to grow too large");
- }
- buf_size = jbig2_find_buffer_size(size + already);
- buf = jbig2_new(ctx, byte, buf_size);
- if (buf == NULL) {
- return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to allocate bigger buffer when reading data");
- }
- memcpy(buf, ctx->buf + ctx->buf_rd_ix, already);
- jbig2_free(ctx->allocator, ctx->buf);
- ctx->buf = buf;
- ctx->buf_size = buf_size;
- }
- ctx->buf_wr_ix -= ctx->buf_rd_ix;
- ctx->buf_rd_ix = 0;
- }
- memcpy(ctx->buf + ctx->buf_wr_ix, data, size);
- ctx->buf_wr_ix += size;
- /* data has now been added to buffer */
- for (;;) {
- const byte jbig2_id_string[8] = { 0x97, 0x4a, 0x42, 0x32, 0x0d, 0x0a, 0x1a, 0x0a };
- Jbig2Segment *segment;
- size_t header_size;
- int code;
- switch (ctx->state) {
- case JBIG2_FILE_HEADER:
- /* D.4.1 */
- if (ctx->buf_wr_ix - ctx->buf_rd_ix < 9)
- return 0;
- if (memcmp(ctx->buf + ctx->buf_rd_ix, jbig2_id_string, 8))
- return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "not a JBIG2 file header");
- /* D.4.2 */
- ctx->file_header_flags = ctx->buf[ctx->buf_rd_ix + 8];
- /* Check for T.88 amendment 2 */
- if (ctx->file_header_flags & 0x04)
- return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "file header indicates use of 12 adaptive template pixels (NYI)");
- /* Check for T.88 amendment 3 */
- if (ctx->file_header_flags & 0x08)
- return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "file header indicates use of colored region segments (NYI)");
- if (ctx->file_header_flags & 0xFC) {
- jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "reserved bits (2-7) of file header flags are not zero (0x%02x)", ctx->file_header_flags);
- }
- /* D.4.3 */
- if (!(ctx->file_header_flags & 2)) { /* number of pages is known */
- if (ctx->buf_wr_ix - ctx->buf_rd_ix < 13)
- return 0;
- ctx->n_pages = jbig2_get_uint32(ctx->buf + ctx->buf_rd_ix + 9);
- ctx->buf_rd_ix += 13;
- if (ctx->n_pages == 1)
- jbig2_error(ctx, JBIG2_SEVERITY_INFO, JBIG2_UNKNOWN_SEGMENT_NUMBER, "file header indicates a single page document");
- else
- jbig2_error(ctx, JBIG2_SEVERITY_INFO, JBIG2_UNKNOWN_SEGMENT_NUMBER, "file header indicates a %d page document", ctx->n_pages);
- } else { /* number of pages not known */
- ctx->n_pages = 0;
- ctx->buf_rd_ix += 9;
- }
- /* determine the file organization based on the flags - D.4.2 again */
- if (ctx->file_header_flags & 1) {
- ctx->state = JBIG2_FILE_SEQUENTIAL_HEADER;
- jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "file header indicates sequential organization");
- } else {
- ctx->state = JBIG2_FILE_RANDOM_HEADERS;
- jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "file header indicates random-access organization");
- }
- break;
- case JBIG2_FILE_SEQUENTIAL_HEADER:
- case JBIG2_FILE_RANDOM_HEADERS:
- segment = jbig2_parse_segment_header(ctx, ctx->buf + ctx->buf_rd_ix, ctx->buf_wr_ix - ctx->buf_rd_ix, &header_size);
- if (segment == NULL)
- return 0; /* need more data */
- ctx->buf_rd_ix += header_size;
- if (ctx->n_segments >= ctx->n_segments_max) {
- Jbig2Segment **segments;
- if (ctx->n_segments_max == UINT32_MAX) {
- ctx->state = JBIG2_FILE_EOF;
- return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "too many segments in jbig2 image");
- }
- else if (ctx->n_segments_max > (UINT32_MAX >> 2)) {
- ctx->n_segments_max = UINT32_MAX;
- }
- segments = jbig2_renew(ctx, ctx->segments, Jbig2Segment *, (ctx->n_segments_max <<= 2));
- if (segments == NULL) {
- ctx->state = JBIG2_FILE_EOF;
- return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate space for more segments");
- }
- ctx->segments = segments;
- }
- ctx->segments[ctx->n_segments++] = segment;
- if (ctx->state == JBIG2_FILE_RANDOM_HEADERS) {
- if ((segment->flags & 63) == 51) /* end of file */
- ctx->state = JBIG2_FILE_RANDOM_BODIES;
- } else /* JBIG2_FILE_SEQUENTIAL_HEADER */
- ctx->state = JBIG2_FILE_SEQUENTIAL_BODY;
- break;
- case JBIG2_FILE_SEQUENTIAL_BODY:
- case JBIG2_FILE_RANDOM_BODIES:
- segment = ctx->segments[ctx->segment_index];
- /* immediate generic regions may have unknown size */
- if (segment->data_length == 0xffffffff && (segment->flags & 63) == 38) {
- byte *s, *e, *p;
- int mmr;
- byte mmr_marker[2] = { 0x00, 0x00 };
- byte arith_marker[2] = { 0xff, 0xac };
- byte *desired_marker;
- s = p = ctx->buf + ctx->buf_rd_ix;
- e = ctx->buf + ctx->buf_wr_ix;
- if (e - p < 18)
- return 0; /* need more data */
- mmr = p[17] & 1;
- p += 18;
- desired_marker = mmr ? mmr_marker : arith_marker;
- /* look for two byte marker */
- if (e - p < 2)
- return 0; /* need more data */
- while (p[0] != desired_marker[0] || p[1] != desired_marker[1]) {
- p++;
- if (e - p < 2)
- return 0; /* need more data */
- }
- p += 2;
- /* the marker is followed by a four byte row count */
- if (e - p < 4)
- return 0; /* need more data */
- segment->rows = jbig2_get_uint32(p);
- p += 4;
- segment->data_length = (size_t) (p - s);
- jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "unknown length determined to be %lu", (long) segment->data_length);
- }
- else if (segment->data_length > ctx->buf_wr_ix - ctx->buf_rd_ix)
- return 0; /* need more data */
- code = jbig2_parse_segment(ctx, segment, ctx->buf + ctx->buf_rd_ix);
- ctx->buf_rd_ix += segment->data_length;
- ctx->segment_index++;
- if (ctx->state == JBIG2_FILE_RANDOM_BODIES) {
- if (ctx->segment_index == ctx->n_segments)
- ctx->state = JBIG2_FILE_EOF;
- } else { /* JBIG2_FILE_SEQUENTIAL_BODY */
- ctx->state = JBIG2_FILE_SEQUENTIAL_HEADER;
- }
- if (code < 0) {
- ctx->state = JBIG2_FILE_EOF;
- return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode; treating as end of file");
- }
- break;
- case JBIG2_FILE_EOF:
- if (ctx->buf_rd_ix == ctx->buf_wr_ix)
- return 0;
- return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "garbage beyond end of file");
- }
- }
- }
- Jbig2Allocator *
- jbig2_ctx_free(Jbig2Ctx *ctx)
- {
- Jbig2Allocator *ca;
- uint32_t i;
- if (ctx == NULL)
- return NULL;
- ca = ctx->allocator;
- jbig2_free(ca, ctx->buf);
- if (ctx->segments != NULL) {
- for (i = 0; i < ctx->n_segments; i++)
- jbig2_free_segment(ctx, ctx->segments[i]);
- jbig2_free(ca, ctx->segments);
- }
- if (ctx->pages != NULL) {
- for (i = 0; i <= ctx->current_page; i++)
- if (ctx->pages[i].image != NULL)
- jbig2_image_release(ctx, ctx->pages[i].image);
- jbig2_free(ca, ctx->pages);
- }
- jbig2_free(ca, ctx);
- return ca;
- }
- Jbig2GlobalCtx *
- jbig2_make_global_ctx(Jbig2Ctx *ctx)
- {
- return (Jbig2GlobalCtx *) ctx;
- }
- Jbig2Allocator *
- jbig2_global_ctx_free(Jbig2GlobalCtx *global_ctx)
- {
- return jbig2_ctx_free((Jbig2Ctx *) global_ctx);
- }
- /* I'm not committed to keeping the word stream interface. It's handy
- when you think you may be streaming your input, but if you're not
- (as is currently the case), it just adds complexity.
- */
- typedef struct {
- Jbig2WordStream super;
- const byte *data;
- size_t size;
- } Jbig2WordStreamBuf;
- static int
- jbig2_word_stream_buf_get_next_word(Jbig2Ctx *ctx, Jbig2WordStream *self, size_t offset, uint32_t *word)
- {
- Jbig2WordStreamBuf *z = (Jbig2WordStreamBuf *) self;
- uint32_t val = 0;
- int ret = 0;
- if (self == NULL || word == NULL) {
- return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to read next word of stream because stream or output missing");
- }
- if (offset >= z->size) {
- *word = 0;
- return 0;
- }
- if (offset < z->size) {
- val = (uint32_t) z->data[offset] << 24;
- ret++;
- }
- if (offset + 1 < z->size) {
- val |= (uint32_t) z->data[offset + 1] << 16;
- ret++;
- }
- if (offset + 2 < z->size) {
- val |= (uint32_t) z->data[offset + 2] << 8;
- ret++;
- }
- if (offset + 3 < z->size) {
- val |= z->data[offset + 3];
- ret++;
- }
- *word = val;
- return ret;
- }
- Jbig2WordStream *
- jbig2_word_stream_buf_new(Jbig2Ctx *ctx, const byte *data, size_t size)
- {
- Jbig2WordStreamBuf *result = jbig2_new(ctx, Jbig2WordStreamBuf, 1);
- if (result == NULL) {
- jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to allocate word stream");
- return NULL;
- }
- result->super.get_next_word = jbig2_word_stream_buf_get_next_word;
- result->data = data;
- result->size = size;
- return &result->super;
- }
- void
- jbig2_word_stream_buf_free(Jbig2Ctx *ctx, Jbig2WordStream *ws)
- {
- jbig2_free(ctx->allocator, ws);
- }
- /* When Memento is in use, the ->free and ->realloc calls get
- * turned into ->Memento_free and ->Memento_realloc, which is
- * obviously problematic. Undefine free and realloc here to
- * avoid this. */
- #ifdef MEMENTO
- #undef free
- #undef realloc
- #endif
- void
- jbig2_free(Jbig2Allocator *allocator, void *p)
- {
- allocator->free(allocator, p);
- }
- void *
- jbig2_realloc(Jbig2Allocator *allocator, void *p, size_t size, size_t num)
- {
- /* check for integer multiplication overflow */
- if (num > 0 && size >= SIZE_MAX / num)
- return NULL;
- return allocator->realloc(allocator, p, size * num);
- }
|