diff mupdf-source/thirdparty/jbig2dec/jbig2.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/jbig2dec/jbig2.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,566 @@
+/* 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);
+}