Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/jp2kheader.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/leptonica/src/jp2kheader.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,372 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - Redistribution and use in source and binary forms, with or without + - modification, are permitted provided that the following conditions + - are met: + - 1. Redistributions of source code must retain the above copyright + - notice, this list of conditions and the following disclaimer. + - 2. Redistributions in binary form must reproduce the above + - copyright notice, this list of conditions and the following + - disclaimer in the documentation and/or other materials + - provided with the distribution. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY + - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *====================================================================*/ + +/*! + * \file jp2kheader.c + * <pre> + * + * Read header + * l_int32 readHeaderJp2k() + * l_int32 freadHeaderJp2k() + * l_int32 readHeaderMemJp2k() + * l_int32 fgetJp2kResolution() + * l_int32 readResolutionMemJp2k() + * + * Note: these function read image metadata from a jp2k file, without + * using any jp2k libraries. + * + * To read and write jp2k data, using the OpenJPEG library + * (http://www.openjpeg.org), see jpegio.c. + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <math.h> +#include "allheaders.h" + +#ifndef NO_CONSOLE_IO +#define DEBUG_CODEC 0 +#endif /* ~NO_CONSOLE_IO */ + +/* --------------------------------------------*/ +#if USE_JP2KHEADER /* defined in environ.h */ +/* --------------------------------------------*/ + + /* a sanity check on the size read from file */ +static const l_int32 MAX_JP2K_WIDTH = 100000; +static const l_int32 MAX_JP2K_HEIGHT = 100000; + +/*--------------------------------------------------------------------* + * Stream interface * + *--------------------------------------------------------------------*/ +/*! + * \brief readHeaderJp2k() + * + * \param[in] filename + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \param[out] pcodec [optional] L_JP2_CODEC or L_J2K_CODEC + * \return 0 if OK, 1 on error + */ +l_ok +readHeaderJp2k(const char *filename, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *pcodec) +{ +l_int32 ret; +FILE *fp; + + if (!filename) + return ERROR_INT("filename not defined", __func__, 1); + + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT_1("image file not found", filename, __func__, 1); + ret = freadHeaderJp2k(fp, pw, ph, pbps, pspp, pcodec); + fclose(fp); + return ret; +} + + +/*! + * \brief freadHeaderJp2k() + * + * \param[in] fp file stream opened for read + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \param[out] pcodec [optional] L_JP2_CODEC or L_J2K_CODEC + * \return 0 if OK, 1 on error + */ +l_ok +freadHeaderJp2k(FILE *fp, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *pcodec) +{ +l_uint8 buf[120]; /* usually just need the first 80 bytes */ +l_int32 nread, ret; + + if (!fp) + return ERROR_INT("fp not defined", __func__, 1); + + rewind(fp); + nread = fread(buf, 1, sizeof(buf), fp); + if (nread != sizeof(buf)) + return ERROR_INT("read failure", __func__, 1); + + ret = readHeaderMemJp2k(buf, sizeof(buf), pw, ph, pbps, pspp, pcodec); + rewind(fp); + return ret; +} + + +/*! + * \brief readHeaderMemJp2k() + * + * \param[in] data + * \param[in] size at least 80 + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \param[out] pcodec [optional] L_JP2_CODEC or L_J2K_CODEC + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) The ISO/IEC reference for jpeg2000 is + * http://www.jpeg.org/public/15444-1annexi.pdf + * and the file format syntax begins at page 127. + * (2) With a image file codec (L_JP2_CODEC), the Image Header Box + * begins with 'ihdr' = 0x69686472 in big-endian order. This + * typically, but not always, starts on byte 44, with the + * big-endian data fields beginning at byte 48: + * h: 4 bytes + * w: 4 bytes + * spp: 2 bytes + * bps: 1 byte (contains bps - 1) + * (3) With a codestream codec (L_J2K_CODEC), the first 4 bytes are + * 0xff4fff51. The fields for w and h appear to start on byte 8, + * and the fields for spp and bps appear to start on byte 40. + * </pre> + */ +l_ok +readHeaderMemJp2k(const l_uint8 *data, + size_t size, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *pcodec) +{ +l_int32 format, val, w, h, bps, spp, loc, found, index, codec; +l_uint8 ihdr[4] = {0x69, 0x68, 0x64, 0x72}; /* 'ihdr' */ + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (pcodec) *pcodec = 0; + if (!data) + return ERROR_INT("data not defined", __func__, 1); + if (size < 120) + return ERROR_INT("size < 80", __func__, 1); + findFileFormatBuffer(data, &format); + if (format != IFF_JP2) + return ERROR_INT("not jp2 file", __func__, 1); + + /* Find beginning of the image metadata */ + if (!memcmp(data, "\xff\x4f\xff\x51", 4)) { /* codestream */ + index = 8; + codec = L_J2K_CODEC; + } else { /* file data with image header box 'ihdr' */ + arrayFindSequence(data, size, ihdr, 4, &loc, &found); + if (!found) + return ERROR_INT("image parameters not found", __func__, 1); + index = loc + 4; + codec = L_JP2_CODEC; +#if DEBUG_CODEC + if (loc != 44) + L_INFO("Beginning of ihdr is at byte %d\n", __func__, loc); +#endif /* DEBUG_CODEC */ + } + if (pcodec) *pcodec = codec; + + if (codec == L_JP2_CODEC) { + if (size < index + 4 * 3) + return ERROR_INT("header size is too small", __func__, 1); + val = *(l_uint32 *)(data + index); + h = convertOnLittleEnd32(val); + val = *(l_uint32 *)(data + index + 4); + w = convertOnLittleEnd32(val); + val = *(l_uint16 *)(data + index + 8); + spp = convertOnLittleEnd16(val); + bps = *(data + index + 10) + 1; + } else { /* codec == L_J2K_CODEC */ + if (size < index + 4 * 9) + return ERROR_INT("header size is too small", __func__, 1); + val = *(l_uint32 *)(data + index); + w = convertOnLittleEnd32(val); + val = *(l_uint32 *)(data + index + 4); + h = convertOnLittleEnd32(val); + val = *(l_uint16 *)(data + index + 32); + spp = convertOnLittleEnd16(val); + bps = *(data + index + 34) + 1; + } +#if DEBUG_CODEC + lept_stderr("h = %d, w = %d, codec: %s, spp = %d, bps = %d\n", h, w, + (codec == L_JP2_CODEC ? "jp2" : "j2k"), spp, bps); +#endif /* DEBUG_CODEC */ + + if (w < 1 || h < 1) + return ERROR_INT("w and h must both be > 0", __func__, 1); + if (w > MAX_JP2K_WIDTH || h > MAX_JP2K_HEIGHT) + return ERROR_INT("unrealistically large sizes", __func__, 1); + if (spp != 1 && spp != 3 && spp != 4) + return ERROR_INT("spp must be in 1, 3 or 4", __func__, 1); + if (bps != 8 && bps != 16) + return ERROR_INT("bps must be 8 or 16", __func__, 1); + if (pw) *pw = w; + if (ph) *ph = h; + if (pspp) *pspp = spp; + if (pbps) *pbps = bps; + return 0; +} + + +/* + * \brief fgetJp2kResolution() + * + * \param[in] fp file stream opened for read + * \param[oui] pxres in ppi + * \param[oui] pyres in ppi + * \return 0 if found, 1 if not found or on error + * + * <pre> + * Notes: + * (1) If the capture resolution field is not set, this is not an error; + * the returned resolution values are 0 (designating 'unknown'). + * (2) Side-effect: this rewinds the stream. + * (3) The capture resolution box is optional in the jp2 spec, and + * it is usually not written. + * (4) The big-endian data fields that follow the 4 bytes of 'resc' are: + * ynum: 2 bytes + * ydenom: 2 bytes + * xnum: 2 bytes + * xdenom: 2 bytes + * yexp: 1 byte + * xexp: 1 byte + * </pre> + */ +l_int32 +fgetJp2kResolution(FILE *fp, + l_int32 *pxres, + l_int32 *pyres) +{ +l_uint8 *data; +size_t nbytes; +l_ok ok; + + if (!fp) + return ERROR_INT("stream not opened", __func__, 1); + + rewind(fp); + data = l_binaryReadStream(fp, &nbytes); + rewind(fp); + + ok = readResolutionMemJp2k(data, nbytes, pxres, pyres); + + LEPT_FREE(data); + return ok; +} + + +/*! + * \brief readResolutionMemJp2k() + * + * \param[in] data const; jp2k-encoded + * \param[in] nbytes of data + * \param[out] pxres [optional] + * \param[out] pyres [optional] + * \return 0 if OK, 1 on error + */ +l_ok +readResolutionMemJp2k(const l_uint8 *data, + size_t nbytes, + l_int32 *pxres, + l_int32 *pyres) +{ +l_uint8 xexp, yexp; +l_uint16 xnum, ynum, xdenom, ydenom; /* these jp2k fields are 2-byte */ +l_int32 loc, found; +l_uint8 resc[4] = {0x72, 0x65, 0x73, 0x63}; /* 'resc' */ +l_float64 xres, yres, maxres; + + if (pxres) *pxres = 0; + if (pyres) *pyres = 0; + if (!pxres || !pyres) + return ERROR_INT("&xres and &yres not both defined", __func__, 1); + + /* Search for the start of the first capture resolution box: 'resc' */ + arrayFindSequence(data, nbytes, resc, 4, &loc, &found); + if (!found) { + L_WARNING("image resolution not found\n", __func__); + return 1; + } + if (nbytes < 80 || loc >= nbytes - 13) { + L_WARNING("image resolution found without enough space\n", __func__); + return 1; + } + + /* Extract the fields and calculate the resolution in pixels/meter. + * See section 1.5.3.7.1 of JPEG 2000 ISO/IEC 15444-1 spec. */ + ynum = data[loc + 5] << 8 | data[loc + 4]; + ynum = convertOnLittleEnd16(ynum); + ydenom = data[loc + 7] << 8 | data[loc + 6]; + ydenom = convertOnLittleEnd16(ydenom); + xnum = data[loc + 9] << 8 | data[loc + 8]; + xnum = convertOnLittleEnd16(xnum); + xdenom = data[loc + 11] << 8 | data[loc + 10]; + xdenom = convertOnLittleEnd16(xdenom); + if (ydenom == 0 || xdenom == 0) { + L_WARNING("bad data: ydenom or xdenom is 0\n", __func__); + return 1; + } + yexp = data[loc + 12]; + xexp = data[loc + 13]; + yres = ((l_float64)ynum / (l_float64)ydenom) * pow(10.0, (l_float64)yexp); + xres = ((l_float64)xnum / (l_float64)xdenom) * pow(10.0, (l_float64)xexp); + + /* Convert from pixels/meter to ppi */ + yres *= (300.0 / 11811.0); + xres *= (300.0 / 11811.0); + + /* Sanity check for bad data */ + maxres = 100000.0; /* ppi */ + if (xres > maxres || yres > maxres) { + L_WARNING("ridiculously large resolution\n", __func__); + } else { + *pyres = (l_int32)(yres + 0.5); + *pxres = (l_int32)(xres + 0.5); + } + + return 0; +} + +/* --------------------------------------------*/ +#endif /* USE_JP2KHEADER */
