// Copyright (C) 2004-2025 Artifex Software, Inc. // // This file is part of MuPDF. // // MuPDF is free software: you can redistribute it and/or modify it under the // terms of the GNU Affero General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. // // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more // details. // // You should have received a copy of the GNU Affero General Public License // along with MuPDF. If not, see // // Alternative licensing terms are available from the licensor. // For commercial licensing, see or contact // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, // CA 94129, USA, for further information. #include "mupdf/fitz.h" #if FZ_ENABLE_BROTLI #include "brotli/decode.h" #include typedef struct { fz_stream *chain; BrotliDecoderState *dec_state; unsigned char buffer[4096]; } fz_brotli_state; void *my_brotlid_alloc(void *ctx, size_t size) { return Memento_label(fz_malloc_no_throw(ctx, size), "brotlid_alloc"); } void my_brotlid_free(void *ctx, void *ptr) { fz_free(ctx, ptr); } static int next_brotlid(fz_context *ctx, fz_stream *stm, size_t required) { fz_brotli_state *state = stm->state; fz_stream *chain = state->chain; unsigned char *outbuf = state->buffer; size_t outlen = sizeof(state->buffer); BrotliDecoderResult res; if (stm->eof) return EOF; while (outlen > 0) { size_t avail_in = fz_available(ctx, chain, 1); res = BrotliDecoderDecompressStream(state->dec_state, &avail_in, (const uint8_t **)&chain->rp, &outlen, &outbuf, NULL); chain->rp = chain->wp - avail_in; if (res == BROTLI_DECODER_RESULT_SUCCESS) { break; } else if (res == BROTLI_DECODER_RESULT_ERROR) { fz_throw(ctx, FZ_ERROR_LIBRARY, "brotli decompression error"); } } stm->rp = state->buffer; stm->wp = state->buffer + sizeof(state->buffer) - outlen; stm->pos += sizeof(state->buffer) - outlen; if (stm->rp == stm->wp) { stm->eof = 1; return EOF; } return *stm->rp++; } static void close_brotlid(fz_context *ctx, void *state_) { fz_brotli_state *state = (fz_brotli_state *)state_; BrotliDecoderDestroyInstance(state->dec_state); fz_drop_stream(ctx, state->chain); fz_free(ctx, state); } fz_stream * fz_open_brotlid(fz_context *ctx, fz_stream *chain) { fz_brotli_state *state; state = fz_malloc_struct(ctx, fz_brotli_state); state->dec_state = BrotliDecoderCreateInstance( my_brotlid_alloc, my_brotlid_free, ctx); if (state->dec_state == NULL) { fz_free(ctx, state); fz_throw(ctx, FZ_ERROR_LIBRARY, "brotli error: decoder creation failed"); } state->chain = fz_keep_stream(ctx, chain); return fz_new_stream(ctx, state, next_brotlid, close_brotlid); } #else fz_stream * fz_open_brotlid(fz_context *ctx, fz_stream *chain) { fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "brotli compression not enabled"); return NULL; } #endif