comparison 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
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
27 /*!
28 * \file jp2kheader.c
29 * <pre>
30 *
31 * Read header
32 * l_int32 readHeaderJp2k()
33 * l_int32 freadHeaderJp2k()
34 * l_int32 readHeaderMemJp2k()
35 * l_int32 fgetJp2kResolution()
36 * l_int32 readResolutionMemJp2k()
37 *
38 * Note: these function read image metadata from a jp2k file, without
39 * using any jp2k libraries.
40 *
41 * To read and write jp2k data, using the OpenJPEG library
42 * (http://www.openjpeg.org), see jpegio.c.
43 * </pre>
44 */
45
46 #ifdef HAVE_CONFIG_H
47 #include <config_auto.h>
48 #endif /* HAVE_CONFIG_H */
49
50 #include <string.h>
51 #include <math.h>
52 #include "allheaders.h"
53
54 #ifndef NO_CONSOLE_IO
55 #define DEBUG_CODEC 0
56 #endif /* ~NO_CONSOLE_IO */
57
58 /* --------------------------------------------*/
59 #if USE_JP2KHEADER /* defined in environ.h */
60 /* --------------------------------------------*/
61
62 /* a sanity check on the size read from file */
63 static const l_int32 MAX_JP2K_WIDTH = 100000;
64 static const l_int32 MAX_JP2K_HEIGHT = 100000;
65
66 /*--------------------------------------------------------------------*
67 * Stream interface *
68 *--------------------------------------------------------------------*/
69 /*!
70 * \brief readHeaderJp2k()
71 *
72 * \param[in] filename
73 * \param[out] pw [optional]
74 * \param[out] ph [optional]
75 * \param[out] pbps [optional] bits/sample
76 * \param[out] pspp [optional] samples/pixel
77 * \param[out] pcodec [optional] L_JP2_CODEC or L_J2K_CODEC
78 * \return 0 if OK, 1 on error
79 */
80 l_ok
81 readHeaderJp2k(const char *filename,
82 l_int32 *pw,
83 l_int32 *ph,
84 l_int32 *pbps,
85 l_int32 *pspp,
86 l_int32 *pcodec)
87 {
88 l_int32 ret;
89 FILE *fp;
90
91 if (!filename)
92 return ERROR_INT("filename not defined", __func__, 1);
93
94 if ((fp = fopenReadStream(filename)) == NULL)
95 return ERROR_INT_1("image file not found", filename, __func__, 1);
96 ret = freadHeaderJp2k(fp, pw, ph, pbps, pspp, pcodec);
97 fclose(fp);
98 return ret;
99 }
100
101
102 /*!
103 * \brief freadHeaderJp2k()
104 *
105 * \param[in] fp file stream opened for read
106 * \param[out] pw [optional]
107 * \param[out] ph [optional]
108 * \param[out] pbps [optional] bits/sample
109 * \param[out] pspp [optional] samples/pixel
110 * \param[out] pcodec [optional] L_JP2_CODEC or L_J2K_CODEC
111 * \return 0 if OK, 1 on error
112 */
113 l_ok
114 freadHeaderJp2k(FILE *fp,
115 l_int32 *pw,
116 l_int32 *ph,
117 l_int32 *pbps,
118 l_int32 *pspp,
119 l_int32 *pcodec)
120 {
121 l_uint8 buf[120]; /* usually just need the first 80 bytes */
122 l_int32 nread, ret;
123
124 if (!fp)
125 return ERROR_INT("fp not defined", __func__, 1);
126
127 rewind(fp);
128 nread = fread(buf, 1, sizeof(buf), fp);
129 if (nread != sizeof(buf))
130 return ERROR_INT("read failure", __func__, 1);
131
132 ret = readHeaderMemJp2k(buf, sizeof(buf), pw, ph, pbps, pspp, pcodec);
133 rewind(fp);
134 return ret;
135 }
136
137
138 /*!
139 * \brief readHeaderMemJp2k()
140 *
141 * \param[in] data
142 * \param[in] size at least 80
143 * \param[out] pw [optional]
144 * \param[out] ph [optional]
145 * \param[out] pbps [optional] bits/sample
146 * \param[out] pspp [optional] samples/pixel
147 * \param[out] pcodec [optional] L_JP2_CODEC or L_J2K_CODEC
148 * \return 0 if OK, 1 on error
149 *
150 * <pre>
151 * Notes:
152 * (1) The ISO/IEC reference for jpeg2000 is
153 * http://www.jpeg.org/public/15444-1annexi.pdf
154 * and the file format syntax begins at page 127.
155 * (2) With a image file codec (L_JP2_CODEC), the Image Header Box
156 * begins with 'ihdr' = 0x69686472 in big-endian order. This
157 * typically, but not always, starts on byte 44, with the
158 * big-endian data fields beginning at byte 48:
159 * h: 4 bytes
160 * w: 4 bytes
161 * spp: 2 bytes
162 * bps: 1 byte (contains bps - 1)
163 * (3) With a codestream codec (L_J2K_CODEC), the first 4 bytes are
164 * 0xff4fff51. The fields for w and h appear to start on byte 8,
165 * and the fields for spp and bps appear to start on byte 40.
166 * </pre>
167 */
168 l_ok
169 readHeaderMemJp2k(const l_uint8 *data,
170 size_t size,
171 l_int32 *pw,
172 l_int32 *ph,
173 l_int32 *pbps,
174 l_int32 *pspp,
175 l_int32 *pcodec)
176 {
177 l_int32 format, val, w, h, bps, spp, loc, found, index, codec;
178 l_uint8 ihdr[4] = {0x69, 0x68, 0x64, 0x72}; /* 'ihdr' */
179
180 if (pw) *pw = 0;
181 if (ph) *ph = 0;
182 if (pbps) *pbps = 0;
183 if (pspp) *pspp = 0;
184 if (pcodec) *pcodec = 0;
185 if (!data)
186 return ERROR_INT("data not defined", __func__, 1);
187 if (size < 120)
188 return ERROR_INT("size < 80", __func__, 1);
189 findFileFormatBuffer(data, &format);
190 if (format != IFF_JP2)
191 return ERROR_INT("not jp2 file", __func__, 1);
192
193 /* Find beginning of the image metadata */
194 if (!memcmp(data, "\xff\x4f\xff\x51", 4)) { /* codestream */
195 index = 8;
196 codec = L_J2K_CODEC;
197 } else { /* file data with image header box 'ihdr' */
198 arrayFindSequence(data, size, ihdr, 4, &loc, &found);
199 if (!found)
200 return ERROR_INT("image parameters not found", __func__, 1);
201 index = loc + 4;
202 codec = L_JP2_CODEC;
203 #if DEBUG_CODEC
204 if (loc != 44)
205 L_INFO("Beginning of ihdr is at byte %d\n", __func__, loc);
206 #endif /* DEBUG_CODEC */
207 }
208 if (pcodec) *pcodec = codec;
209
210 if (codec == L_JP2_CODEC) {
211 if (size < index + 4 * 3)
212 return ERROR_INT("header size is too small", __func__, 1);
213 val = *(l_uint32 *)(data + index);
214 h = convertOnLittleEnd32(val);
215 val = *(l_uint32 *)(data + index + 4);
216 w = convertOnLittleEnd32(val);
217 val = *(l_uint16 *)(data + index + 8);
218 spp = convertOnLittleEnd16(val);
219 bps = *(data + index + 10) + 1;
220 } else { /* codec == L_J2K_CODEC */
221 if (size < index + 4 * 9)
222 return ERROR_INT("header size is too small", __func__, 1);
223 val = *(l_uint32 *)(data + index);
224 w = convertOnLittleEnd32(val);
225 val = *(l_uint32 *)(data + index + 4);
226 h = convertOnLittleEnd32(val);
227 val = *(l_uint16 *)(data + index + 32);
228 spp = convertOnLittleEnd16(val);
229 bps = *(data + index + 34) + 1;
230 }
231 #if DEBUG_CODEC
232 lept_stderr("h = %d, w = %d, codec: %s, spp = %d, bps = %d\n", h, w,
233 (codec == L_JP2_CODEC ? "jp2" : "j2k"), spp, bps);
234 #endif /* DEBUG_CODEC */
235
236 if (w < 1 || h < 1)
237 return ERROR_INT("w and h must both be > 0", __func__, 1);
238 if (w > MAX_JP2K_WIDTH || h > MAX_JP2K_HEIGHT)
239 return ERROR_INT("unrealistically large sizes", __func__, 1);
240 if (spp != 1 && spp != 3 && spp != 4)
241 return ERROR_INT("spp must be in 1, 3 or 4", __func__, 1);
242 if (bps != 8 && bps != 16)
243 return ERROR_INT("bps must be 8 or 16", __func__, 1);
244 if (pw) *pw = w;
245 if (ph) *ph = h;
246 if (pspp) *pspp = spp;
247 if (pbps) *pbps = bps;
248 return 0;
249 }
250
251
252 /*
253 * \brief fgetJp2kResolution()
254 *
255 * \param[in] fp file stream opened for read
256 * \param[oui] pxres in ppi
257 * \param[oui] pyres in ppi
258 * \return 0 if found, 1 if not found or on error
259 *
260 * <pre>
261 * Notes:
262 * (1) If the capture resolution field is not set, this is not an error;
263 * the returned resolution values are 0 (designating 'unknown').
264 * (2) Side-effect: this rewinds the stream.
265 * (3) The capture resolution box is optional in the jp2 spec, and
266 * it is usually not written.
267 * (4) The big-endian data fields that follow the 4 bytes of 'resc' are:
268 * ynum: 2 bytes
269 * ydenom: 2 bytes
270 * xnum: 2 bytes
271 * xdenom: 2 bytes
272 * yexp: 1 byte
273 * xexp: 1 byte
274 * </pre>
275 */
276 l_int32
277 fgetJp2kResolution(FILE *fp,
278 l_int32 *pxres,
279 l_int32 *pyres)
280 {
281 l_uint8 *data;
282 size_t nbytes;
283 l_ok ok;
284
285 if (!fp)
286 return ERROR_INT("stream not opened", __func__, 1);
287
288 rewind(fp);
289 data = l_binaryReadStream(fp, &nbytes);
290 rewind(fp);
291
292 ok = readResolutionMemJp2k(data, nbytes, pxres, pyres);
293
294 LEPT_FREE(data);
295 return ok;
296 }
297
298
299 /*!
300 * \brief readResolutionMemJp2k()
301 *
302 * \param[in] data const; jp2k-encoded
303 * \param[in] nbytes of data
304 * \param[out] pxres [optional]
305 * \param[out] pyres [optional]
306 * \return 0 if OK, 1 on error
307 */
308 l_ok
309 readResolutionMemJp2k(const l_uint8 *data,
310 size_t nbytes,
311 l_int32 *pxres,
312 l_int32 *pyres)
313 {
314 l_uint8 xexp, yexp;
315 l_uint16 xnum, ynum, xdenom, ydenom; /* these jp2k fields are 2-byte */
316 l_int32 loc, found;
317 l_uint8 resc[4] = {0x72, 0x65, 0x73, 0x63}; /* 'resc' */
318 l_float64 xres, yres, maxres;
319
320 if (pxres) *pxres = 0;
321 if (pyres) *pyres = 0;
322 if (!pxres || !pyres)
323 return ERROR_INT("&xres and &yres not both defined", __func__, 1);
324
325 /* Search for the start of the first capture resolution box: 'resc' */
326 arrayFindSequence(data, nbytes, resc, 4, &loc, &found);
327 if (!found) {
328 L_WARNING("image resolution not found\n", __func__);
329 return 1;
330 }
331 if (nbytes < 80 || loc >= nbytes - 13) {
332 L_WARNING("image resolution found without enough space\n", __func__);
333 return 1;
334 }
335
336 /* Extract the fields and calculate the resolution in pixels/meter.
337 * See section 1.5.3.7.1 of JPEG 2000 ISO/IEC 15444-1 spec. */
338 ynum = data[loc + 5] << 8 | data[loc + 4];
339 ynum = convertOnLittleEnd16(ynum);
340 ydenom = data[loc + 7] << 8 | data[loc + 6];
341 ydenom = convertOnLittleEnd16(ydenom);
342 xnum = data[loc + 9] << 8 | data[loc + 8];
343 xnum = convertOnLittleEnd16(xnum);
344 xdenom = data[loc + 11] << 8 | data[loc + 10];
345 xdenom = convertOnLittleEnd16(xdenom);
346 if (ydenom == 0 || xdenom == 0) {
347 L_WARNING("bad data: ydenom or xdenom is 0\n", __func__);
348 return 1;
349 }
350 yexp = data[loc + 12];
351 xexp = data[loc + 13];
352 yres = ((l_float64)ynum / (l_float64)ydenom) * pow(10.0, (l_float64)yexp);
353 xres = ((l_float64)xnum / (l_float64)xdenom) * pow(10.0, (l_float64)xexp);
354
355 /* Convert from pixels/meter to ppi */
356 yres *= (300.0 / 11811.0);
357 xres *= (300.0 / 11811.0);
358
359 /* Sanity check for bad data */
360 maxres = 100000.0; /* ppi */
361 if (xres > maxres || yres > maxres) {
362 L_WARNING("ridiculously large resolution\n", __func__);
363 } else {
364 *pyres = (l_int32)(yres + 0.5);
365 *pxres = (l_int32)(xres + 0.5);
366 }
367
368 return 0;
369 }
370
371 /* --------------------------------------------*/
372 #endif /* USE_JP2KHEADER */