/src/mupdf/source/fitz/load-pnm.c
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (C) 2004-2024 Artifex Software, Inc. |
2 | | // |
3 | | // This file is part of MuPDF. |
4 | | // |
5 | | // MuPDF is free software: you can redistribute it and/or modify it under the |
6 | | // terms of the GNU Affero General Public License as published by the Free |
7 | | // Software Foundation, either version 3 of the License, or (at your option) |
8 | | // any later version. |
9 | | // |
10 | | // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY |
11 | | // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | | // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
13 | | // details. |
14 | | // |
15 | | // You should have received a copy of the GNU Affero General Public License |
16 | | // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> |
17 | | // |
18 | | // Alternative licensing terms are available from the licensor. |
19 | | // For commercial licensing, see <https://www.artifex.com/> or contact |
20 | | // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
21 | | // CA 94129, USA, for further information. |
22 | | |
23 | | #include "mupdf/fitz.h" |
24 | | |
25 | | #include "pixmap-imp.h" |
26 | | |
27 | | #include <string.h> |
28 | | #include <limits.h> |
29 | | |
30 | | enum |
31 | | { |
32 | | PAM_UNKNOWN = 0, |
33 | | PAM_BW, |
34 | | PAM_BWA, |
35 | | PAM_GRAY, |
36 | | PAM_GRAYA, |
37 | | PAM_RGB, |
38 | | PAM_RGBA, |
39 | | PAM_CMYK, |
40 | | PAM_CMYKA, |
41 | | }; |
42 | | |
43 | | enum |
44 | | { |
45 | | TOKEN_UNKNOWN = 0, |
46 | | TOKEN_WIDTH, |
47 | | TOKEN_HEIGHT, |
48 | | TOKEN_DEPTH, |
49 | | TOKEN_MAXVAL, |
50 | | TOKEN_TUPLTYPE, |
51 | | TOKEN_ENDHDR, |
52 | | }; |
53 | | |
54 | | enum |
55 | | { |
56 | | ENDIAN_UNKNOWN = 0, |
57 | | ENDIAN_LITTLE, |
58 | | ENDIAN_BIG, |
59 | | }; |
60 | | |
61 | | struct info |
62 | | { |
63 | | int subimages; |
64 | | fz_colorspace *cs; |
65 | | int width, height; |
66 | | int maxval, bitdepth; |
67 | | int depth, alpha; |
68 | | int tupletype; |
69 | | int endian; |
70 | | float scale; |
71 | | }; |
72 | | |
73 | | static inline int iswhiteeol(int a) |
74 | 629 | { |
75 | 629 | switch (a) { |
76 | 338 | case ' ': case '\t': case '\r': case '\n': |
77 | 338 | return 1; |
78 | 629 | } |
79 | 291 | return 0; |
80 | 629 | } |
81 | | |
82 | | static inline int iswhite(int a) |
83 | 4 | { |
84 | 4 | switch (a) { |
85 | 0 | case ' ': case '\t': |
86 | 0 | return 1; |
87 | 4 | } |
88 | 4 | return 0; |
89 | 4 | } |
90 | | |
91 | | static inline int bitdepth_from_maxval(int maxval) |
92 | 4 | { |
93 | 4 | int depth = 0; |
94 | 8 | while (maxval) |
95 | 4 | { |
96 | 4 | maxval >>= 1; |
97 | 4 | depth++; |
98 | 4 | } |
99 | 4 | return depth; |
100 | 4 | } |
101 | | |
102 | | static const unsigned char * |
103 | | pnm_read_signature(fz_context *ctx, const unsigned char *p, const unsigned char *e, char *signature) |
104 | 24 | { |
105 | 24 | if (e - p < 2) |
106 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse magic number in pnm image"); |
107 | 24 | if (p[0] != 'P' || ((p[1] < '1' || p[1] > '7') && p[1] != 'F' && p[1] != 'f')) |
108 | 3 | fz_throw(ctx, FZ_ERROR_FORMAT, "expected signature in pnm image"); |
109 | | |
110 | 21 | signature[0] = *p++; |
111 | 21 | signature[1] = *p++; |
112 | 21 | return p; |
113 | 24 | } |
114 | | |
115 | | static const unsigned char * |
116 | | pnm_read_until_eol(fz_context *ctx, const unsigned char *p, const unsigned char *e, int acceptCR) |
117 | 7 | { |
118 | 7 | if (e - p < 1) |
119 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse line in pnm image"); |
120 | | |
121 | 892 | while (p < e && ((acceptCR && *p != '\r' && *p != '\n') || (!acceptCR && *p != '\n'))) |
122 | 885 | p++; |
123 | | |
124 | 7 | return p; |
125 | 7 | } |
126 | | |
127 | | static const unsigned char * |
128 | | pnm_read_eol(fz_context *ctx, const unsigned char *p, const unsigned char *e, int acceptCR) |
129 | 3 | { |
130 | 3 | if (e - p < 1) |
131 | 2 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse end of line in pnm image"); |
132 | 1 | if ((acceptCR && *p != '\r' && *p != '\n') || (!acceptCR && *p != '\n')) |
133 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "expected end of line in pnm image"); |
134 | | |
135 | | /* CR, CRLF or LF depending on acceptCR. */ |
136 | 1 | if (acceptCR && *p == '\r') |
137 | 0 | p++; |
138 | 1 | if (p < e && *p == '\n') |
139 | 1 | p++; |
140 | | |
141 | 1 | return p; |
142 | 1 | } |
143 | | |
144 | | static const unsigned char * |
145 | | pnm_read_whites(fz_context *ctx, const unsigned char *p, const unsigned char *e, int required) |
146 | 4 | { |
147 | 4 | if (required && e - p < 1) |
148 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse whitespaces in pnm image"); |
149 | 4 | if (required && !iswhite(*p)) |
150 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "expected whitespaces in pnm image"); |
151 | | |
152 | 4 | while (p < e && iswhite(*p)) |
153 | 0 | p++; |
154 | | |
155 | 4 | return p; |
156 | 4 | } |
157 | | |
158 | | static const unsigned char * |
159 | | pnm_read_white_or_eol(fz_context *ctx, const unsigned char *p, const unsigned char *e) |
160 | 3 | { |
161 | 3 | if (e - p < 1) |
162 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse whitespace/eol in pnm image"); |
163 | 3 | if (!iswhiteeol(*p)) |
164 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "expected whitespace/eol in pnm image"); |
165 | | |
166 | 3 | return ++p; |
167 | 3 | } |
168 | | |
169 | | static const unsigned char * |
170 | | pnm_read_whites_and_eols(fz_context *ctx, const unsigned char *p, const unsigned char *e, int required) |
171 | 76 | { |
172 | 76 | if (required && e - p < 1) |
173 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse whitespaces/eols in pnm image"); |
174 | 76 | if (required && !iswhiteeol(*p)) |
175 | 5 | fz_throw(ctx, FZ_ERROR_FORMAT, "expected whitespaces/eols in pnm image"); |
176 | | |
177 | 364 | while (p < e && iswhiteeol(*p)) |
178 | 293 | p++; |
179 | | |
180 | 71 | return p; |
181 | 76 | } |
182 | | |
183 | | static const unsigned char * |
184 | | pnm_read_comment(fz_context *ctx, const unsigned char *p, const unsigned char *e, int acceptCR) |
185 | 3 | { |
186 | 3 | if (e - p < 1) |
187 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse line in pnm image"); |
188 | | |
189 | 3 | if (*p != '#') |
190 | 0 | return p; |
191 | | |
192 | 3 | return pnm_read_until_eol(ctx, p, e, acceptCR); |
193 | 3 | } |
194 | | |
195 | | static const unsigned char * |
196 | | pnm_read_comments(fz_context *ctx, const unsigned char *p, const unsigned char *e, int acceptCR) |
197 | 16 | { |
198 | 16 | if (e - p < 1) |
199 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse comment in pnm image"); |
200 | | |
201 | 17 | while (p < e && *p == '#') |
202 | 1 | { |
203 | 1 | p = pnm_read_comment(ctx, p, e, acceptCR); |
204 | 1 | p = pnm_read_eol(ctx, p, e, acceptCR); |
205 | 1 | } |
206 | | |
207 | 16 | return p; |
208 | 16 | } |
209 | | |
210 | | static const unsigned char * |
211 | | pnm_read_digit(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *number) |
212 | 0 | { |
213 | 0 | if (e - p < 1) |
214 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse digit in pnm image"); |
215 | 0 | if (*p < '0' || *p > '1') |
216 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "expected digit in pnm image"); |
217 | | |
218 | 0 | if (number) |
219 | 0 | *number = *p - '0'; |
220 | 0 | p++; |
221 | |
|
222 | 0 | return p; |
223 | 0 | } |
224 | | |
225 | | static const unsigned char * |
226 | | pnm_read_int(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *number) |
227 | 30 | { |
228 | 30 | if (e - p < 1) |
229 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse integer in pnm image"); |
230 | 30 | if (*p < '0' || *p > '9') |
231 | 5 | fz_throw(ctx, FZ_ERROR_FORMAT, "expected integer in pnm image"); |
232 | | |
233 | 148 | while (p < e && *p >= '0' && *p <= '9') |
234 | 123 | { |
235 | 123 | if (number) |
236 | 123 | *number = *number * 10 + *p - '0'; |
237 | 123 | p++; |
238 | 123 | } |
239 | | |
240 | 25 | return p; |
241 | 30 | } |
242 | | |
243 | | static const unsigned char * |
244 | | pnm_read_real(fz_context *ctx, const unsigned char *p, const unsigned char *e, float *number) |
245 | 3 | { |
246 | 3 | const unsigned char *orig = p; |
247 | 3 | char *buf, *end; |
248 | 3 | size_t len; |
249 | | |
250 | 3 | if (e - p < 1) |
251 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse real in pnm image"); |
252 | | |
253 | 3 | if (*p != '+' && *p != '-' && (*p < '0' || *p > '9')) |
254 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "expected numeric field in pnm image"); |
255 | | |
256 | 7 | while (p < e && (*p == '+' || *p == '-' || *p == '.' || (*p >= '0' && *p <= '9'))) |
257 | 4 | p++; |
258 | | |
259 | 3 | len = p - orig + 1; |
260 | 3 | end = buf = fz_malloc(ctx, len); |
261 | | |
262 | 6 | fz_try(ctx) |
263 | 6 | { |
264 | 3 | memcpy(buf, orig, len - 1); |
265 | 3 | buf[len - 1] = '\0'; |
266 | 3 | *number = fz_strtof(buf, &end); |
267 | 3 | p = orig + (end - buf); |
268 | 3 | } |
269 | 6 | fz_always(ctx) |
270 | 3 | fz_free(ctx, buf); |
271 | 3 | fz_catch(ctx) |
272 | 0 | fz_rethrow(ctx); |
273 | | |
274 | 3 | return p; |
275 | 3 | } |
276 | | |
277 | | static const unsigned char * |
278 | | pnm_read_tupletype(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *tupletype) |
279 | 0 | { |
280 | 0 | const struct { int len; char *str; int type; } tupletypes[] = |
281 | 0 | { |
282 | 0 | {13, "BLACKANDWHITE", PAM_BW}, |
283 | 0 | {19, "BLACKANDWHITE_ALPHA", PAM_BWA}, |
284 | 0 | {9, "GRAYSCALE", PAM_GRAY}, |
285 | 0 | {15, "GRAYSCALE_ALPHA", PAM_GRAYA}, |
286 | 0 | {3, "RGB", PAM_RGB}, |
287 | 0 | {9, "RGB_ALPHA", PAM_RGBA}, |
288 | 0 | {4, "CMYK", PAM_CMYK}, |
289 | 0 | {10, "CMYK_ALPHA", PAM_CMYKA}, |
290 | 0 | }; |
291 | 0 | const unsigned char *s; |
292 | 0 | int i, len; |
293 | |
|
294 | 0 | if (e - p < 1) |
295 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse tuple type in pnm image"); |
296 | | |
297 | 0 | s = p; |
298 | 0 | while (p < e && !iswhiteeol(*p)) |
299 | 0 | p++; |
300 | 0 | len = p - s; |
301 | |
|
302 | 0 | for (i = 0; i < (int)nelem(tupletypes); i++) |
303 | 0 | if (len == tupletypes[i].len && !strncmp((char *) s, tupletypes[i].str, len)) |
304 | 0 | { |
305 | 0 | *tupletype = tupletypes[i].type; |
306 | 0 | return p; |
307 | 0 | } |
308 | | |
309 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "unknown tuple type in pnm image"); |
310 | 0 | } |
311 | | |
312 | | static const unsigned char * |
313 | | pnm_read_token(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *token) |
314 | 2 | { |
315 | 2 | const struct { int len; char *str; int type; } tokens[] = |
316 | 2 | { |
317 | 2 | {5, "WIDTH", TOKEN_WIDTH}, |
318 | 2 | {6, "HEIGHT", TOKEN_HEIGHT}, |
319 | 2 | {5, "DEPTH", TOKEN_DEPTH}, |
320 | 2 | {6, "MAXVAL", TOKEN_MAXVAL}, |
321 | 2 | {8, "TUPLTYPE", TOKEN_TUPLTYPE}, |
322 | 2 | {6, "ENDHDR", TOKEN_ENDHDR}, |
323 | 2 | }; |
324 | 2 | const unsigned char *s; |
325 | 2 | int i, len; |
326 | | |
327 | 2 | if (e - p < 1) |
328 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse header token in pnm image"); |
329 | | |
330 | 2 | s = p; |
331 | 217 | while (p < e && !iswhiteeol(*p)) |
332 | 215 | p++; |
333 | 2 | len = p - s; |
334 | | |
335 | 14 | for (i = 0; i < (int)nelem(tokens); i++) |
336 | 12 | if (len == tokens[i].len && !strncmp((char *) s, tokens[i].str, len)) |
337 | 0 | { |
338 | 0 | *token = tokens[i].type; |
339 | 0 | return p; |
340 | 0 | } |
341 | | |
342 | 2 | fz_throw(ctx, FZ_ERROR_FORMAT, "unknown header token in pnm image"); |
343 | 2 | } |
344 | | |
345 | | static int |
346 | | map_color(fz_context *ctx, int color, int inmax, int outmax) |
347 | 0 | { |
348 | 0 | float f = (float) color / inmax; |
349 | 0 | return f * outmax; |
350 | 0 | } |
351 | | |
352 | | static fz_pixmap * |
353 | | pnm_ascii_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int bitmap, const unsigned char **out) |
354 | 4 | { |
355 | 4 | fz_pixmap *img = NULL; |
356 | | |
357 | 4 | pnm->width = 0; |
358 | 4 | p = pnm_read_comments(ctx, p, e, 1); |
359 | 4 | p = pnm_read_int(ctx, p, e, &pnm->width); |
360 | 4 | p = pnm_read_whites_and_eols(ctx, p, e, 1); |
361 | | |
362 | 4 | if (bitmap) |
363 | 1 | { |
364 | 1 | pnm->height = 0; |
365 | 1 | p = pnm_read_int(ctx, p, e, &pnm->height); |
366 | 1 | p = pnm_read_whites_and_eols(ctx, p, e, 1); |
367 | | |
368 | 1 | pnm->maxval = 1; |
369 | 1 | } |
370 | 3 | else |
371 | 3 | { |
372 | 3 | pnm->height = 0; |
373 | 3 | p = pnm_read_comments(ctx, p, e, 1); |
374 | 3 | p = pnm_read_int(ctx, p, e, &pnm->height); |
375 | 3 | p = pnm_read_whites_and_eols(ctx, p, e, 1); |
376 | | |
377 | 3 | pnm->maxval = 0; |
378 | 3 | p = pnm_read_comments(ctx, p, e, 1); |
379 | 3 | p = pnm_read_int(ctx, p, e, &pnm->maxval); |
380 | 3 | p = pnm_read_white_or_eol(ctx, p, e); |
381 | 3 | } |
382 | | |
383 | 4 | if (pnm->maxval <= 0 || pnm->maxval >= 65536) |
384 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "maximum sample value of out range in pnm image: %d", pnm->maxval); |
385 | | |
386 | 4 | pnm->bitdepth = bitdepth_from_maxval(pnm->maxval); |
387 | | |
388 | 4 | if (pnm->height <= 0) |
389 | 1 | fz_throw(ctx, FZ_ERROR_FORMAT, "image height must be > 0"); |
390 | 3 | if (pnm->width <= 0) |
391 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "image width must be > 0"); |
392 | 3 | if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1)) |
393 | 0 | fz_throw(ctx, FZ_ERROR_LIMIT, "image too large"); |
394 | | |
395 | 3 | if (onlymeta) |
396 | 0 | { |
397 | 0 | int x, y, k; |
398 | 0 | int w, h, n; |
399 | |
|
400 | 0 | w = pnm->width; |
401 | 0 | h = pnm->height; |
402 | 0 | n = fz_colorspace_n(ctx, pnm->cs); |
403 | |
|
404 | 0 | if (bitmap) |
405 | 0 | { |
406 | 0 | for (y = 0; y < h; y++) |
407 | 0 | for (x = 0; x < w; x++) |
408 | 0 | { |
409 | 0 | p = pnm_read_whites_and_eols(ctx, p, e, 0); |
410 | 0 | p = pnm_read_digit(ctx, p, e, NULL); |
411 | 0 | p = pnm_read_whites_and_eols(ctx, p, e, 0); |
412 | 0 | } |
413 | 0 | } |
414 | 0 | else |
415 | 0 | { |
416 | 0 | for (y = 0; y < h; y++) |
417 | 0 | for (x = 0; x < w; x++) |
418 | 0 | for (k = 0; k < n; k++) |
419 | 0 | { |
420 | 0 | p = pnm_read_whites_and_eols(ctx, p, e, 0); |
421 | 0 | p = pnm_read_int(ctx, p, e, NULL); |
422 | 0 | p = pnm_read_whites_and_eols(ctx, p, e, 0); |
423 | 0 | } |
424 | 0 | } |
425 | 0 | } |
426 | 3 | else |
427 | 3 | { |
428 | 3 | unsigned char *dp; |
429 | 3 | int x, y, k; |
430 | 3 | int w, h, n; |
431 | | |
432 | 3 | img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0); |
433 | 3 | dp = img->samples; |
434 | | |
435 | 3 | w = img->w; |
436 | 3 | h = img->h; |
437 | 3 | n = img->n; |
438 | | |
439 | 3 | if (bitmap) |
440 | 0 | { |
441 | 0 | for (y = 0; y < h; y++) |
442 | 0 | { |
443 | 0 | for (x = 0; x < w; x++) |
444 | 0 | { |
445 | 0 | int v = 0; |
446 | 0 | p = pnm_read_whites_and_eols(ctx, p, e, 0); |
447 | 0 | p = pnm_read_digit(ctx, p, e, &v); |
448 | 0 | p = pnm_read_whites_and_eols(ctx, p, e, 0); |
449 | 0 | *dp++ = v ? 0x00 : 0xff; |
450 | 0 | } |
451 | 0 | } |
452 | 0 | } |
453 | 3 | else |
454 | 3 | { |
455 | 3 | for (y = 0; y < h; y++) |
456 | 0 | for (x = 0; x < w; x++) |
457 | 0 | for (k = 0; k < n; k++) |
458 | 0 | { |
459 | 0 | int v = 0; |
460 | 0 | p = pnm_read_whites_and_eols(ctx, p, e, 0); |
461 | 0 | p = pnm_read_int(ctx, p, e, &v); |
462 | 0 | p = pnm_read_whites_and_eols(ctx, p, e, 0); |
463 | 0 | v = fz_clampi(v, 0, pnm->maxval); |
464 | 0 | *dp++ = map_color(ctx, v, pnm->maxval, 255); |
465 | 0 | } |
466 | 3 | } |
467 | 3 | } |
468 | | |
469 | 3 | if (out) |
470 | 0 | *out = p; |
471 | | |
472 | 3 | return img; |
473 | 3 | } |
474 | | |
475 | | static fz_pixmap * |
476 | | pnm_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int bitmap, const unsigned char **out) |
477 | 10 | { |
478 | 10 | fz_pixmap *img = NULL; |
479 | 10 | size_t span; |
480 | 10 | int n; |
481 | | |
482 | 10 | n = fz_colorspace_n(ctx, pnm->cs); |
483 | 10 | assert(n >= 1 && n <= 3); |
484 | | |
485 | 10 | pnm->width = 0; |
486 | 10 | p = pnm_read_comments(ctx, p, e, 1); |
487 | 10 | p = pnm_read_int(ctx, p, e, &pnm->width); |
488 | 10 | p = pnm_read_whites_and_eols(ctx, p, e, 1); |
489 | | |
490 | 10 | if (bitmap) |
491 | 8 | { |
492 | 8 | pnm->height = 0; |
493 | 8 | p = pnm_read_int(ctx, p, e, &pnm->height); |
494 | 8 | p = pnm_read_whites_and_eols(ctx, p, e, 1); |
495 | | |
496 | 8 | pnm->maxval = 1; |
497 | 8 | } |
498 | 2 | else |
499 | 2 | { |
500 | 2 | pnm->height = 0; |
501 | 2 | p = pnm_read_comments(ctx, p, e, 1); |
502 | 2 | p = pnm_read_int(ctx, p, e, &pnm->height); |
503 | 2 | p = pnm_read_whites_and_eols(ctx, p, e, 1); |
504 | | |
505 | 2 | pnm->maxval = 0; |
506 | 2 | p = pnm_read_comments(ctx, p, e, 1); |
507 | 2 | p = pnm_read_int(ctx, p, e, &pnm->maxval); |
508 | 2 | p = pnm_read_white_or_eol(ctx, p, e); |
509 | 2 | } |
510 | | |
511 | 10 | if (pnm->maxval <= 0 || pnm->maxval >= 65536) |
512 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "maximum sample value of out range in pnm image: %d", pnm->maxval); |
513 | | |
514 | 10 | pnm->bitdepth = bitdepth_from_maxval(pnm->maxval); |
515 | | |
516 | 10 | if (pnm->height <= 0) |
517 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "image height must be > 0"); |
518 | 10 | if (pnm->width <= 0) |
519 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "image width must be > 0"); |
520 | 10 | if (pnm->bitdepth == 1) |
521 | 3 | { |
522 | | /* Overly sensitive test, but we can live with it. */ |
523 | 3 | if ((size_t)pnm->width > SIZE_MAX / (unsigned int)n) |
524 | 0 | fz_throw(ctx, FZ_ERROR_LIMIT, "image row too large"); |
525 | 3 | span = ((size_t)n * pnm->width + 7)/8; |
526 | 3 | } |
527 | 7 | else |
528 | 7 | { |
529 | 7 | size_t bytes_per_sample = (pnm->bitdepth-1)/8 + 1; |
530 | 7 | span = (size_t)n * bytes_per_sample; |
531 | 7 | if ((size_t)pnm->width > SIZE_MAX / span) |
532 | 0 | fz_throw(ctx, FZ_ERROR_LIMIT, "image row too large"); |
533 | 7 | span = (size_t)pnm->width * span; |
534 | 7 | } |
535 | 10 | if ((size_t)pnm->height > SIZE_MAX / span) |
536 | 0 | fz_throw(ctx, FZ_ERROR_LIMIT, "image too large"); |
537 | 10 | if (e - p < 0 || ((size_t)(e - p)) < span * (size_t)pnm->height) |
538 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "insufficient data"); |
539 | | |
540 | 10 | if (onlymeta) |
541 | 3 | { |
542 | 3 | p += span * (size_t)pnm->height; |
543 | 3 | } |
544 | 7 | else |
545 | 7 | { |
546 | 7 | unsigned char *dp; |
547 | 7 | int x, y, k; |
548 | 7 | int w, h, n; |
549 | | |
550 | 7 | img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0); |
551 | 7 | dp = img->samples; |
552 | | |
553 | 7 | w = img->w; |
554 | 7 | h = img->h; |
555 | 7 | n = img->n; |
556 | | |
557 | 7 | if (pnm->maxval == 255) |
558 | 0 | { |
559 | 0 | memcpy(dp, p, (size_t)w * h * n); |
560 | 0 | p += n * w * h; |
561 | 0 | } |
562 | 7 | else if (bitmap) |
563 | 0 | { |
564 | 0 | for (y = 0; y < h; y++) |
565 | 0 | { |
566 | 0 | for (x = 0; x < w; x++) |
567 | 0 | { |
568 | 0 | *dp++ = (*p & (1 << (7 - (x & 0x7)))) ? 0x00 : 0xff; |
569 | 0 | if ((x & 0x7) == 7) |
570 | 0 | p++; |
571 | 0 | } |
572 | 0 | if (w & 0x7) |
573 | 0 | p++; |
574 | 0 | } |
575 | 0 | } |
576 | 7 | else if (pnm->maxval < 255) |
577 | 0 | { |
578 | 0 | for (y = 0; y < h; y++) |
579 | 0 | for (x = 0; x < w; x++) |
580 | 0 | for (k = 0; k < n; k++) |
581 | 0 | *dp++ = map_color(ctx, *p++, pnm->maxval, 255); |
582 | 0 | } |
583 | 7 | else |
584 | 7 | { |
585 | 7 | for (y = 0; y < h; y++) |
586 | 0 | for (x = 0; x < w; x++) |
587 | 0 | for (k = 0; k < n; k++) |
588 | 0 | { |
589 | 0 | *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255); |
590 | 0 | p += 2; |
591 | 0 | } |
592 | 7 | } |
593 | 7 | } |
594 | | |
595 | 10 | if (out) |
596 | 3 | *out = p; |
597 | | |
598 | 10 | return img; |
599 | 10 | } |
600 | | |
601 | | static const unsigned char * |
602 | | pam_binary_read_header(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e) |
603 | 3 | { |
604 | 3 | int token = TOKEN_UNKNOWN; |
605 | 3 | const unsigned char *eol; |
606 | 3 | int seen[TOKEN_ENDHDR] = { 0 }; |
607 | | |
608 | 3 | pnm->width = 0; |
609 | 3 | pnm->height = 0; |
610 | 3 | pnm->depth = 0; |
611 | 3 | pnm->maxval = 0; |
612 | 3 | pnm->tupletype = 0; |
613 | | |
614 | 7 | while (p < e && token != TOKEN_ENDHDR) |
615 | 4 | { |
616 | 4 | eol = pnm_read_until_eol(ctx, p, e, 0); |
617 | | |
618 | 4 | p = pnm_read_whites(ctx, p, eol, 0); |
619 | | |
620 | 4 | if (p < eol && *p != '#') |
621 | 2 | { |
622 | 2 | p = pnm_read_token(ctx, p, eol, &token); |
623 | | |
624 | 2 | if (seen[token - 1]) |
625 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "token occurs multiple times in pnm image"); |
626 | 2 | seen[token - 1] = 1; |
627 | | |
628 | 2 | if (token != TOKEN_ENDHDR) |
629 | 0 | { |
630 | 0 | p = pnm_read_whites(ctx, p, eol, 1); |
631 | 0 | switch (token) |
632 | 0 | { |
633 | 0 | case TOKEN_WIDTH: pnm->width = 0; p = pnm_read_int(ctx, p, eol, &pnm->width); break; |
634 | 0 | case TOKEN_HEIGHT: pnm->height = 0; p = pnm_read_int(ctx, p, eol, &pnm->height); break; |
635 | 0 | case TOKEN_DEPTH: pnm->depth = 0; p = pnm_read_int(ctx, p, eol, &pnm->depth); break; |
636 | 0 | case TOKEN_MAXVAL: pnm->maxval = 0; p = pnm_read_int(ctx, p, eol, &pnm->maxval); break; |
637 | 0 | case TOKEN_TUPLTYPE: pnm->tupletype = 0; p = pnm_read_tupletype(ctx, p, eol, &pnm->tupletype); break; |
638 | 0 | } |
639 | 0 | } |
640 | | |
641 | 2 | p = pnm_read_whites(ctx, p, eol, 0); |
642 | 2 | } |
643 | | |
644 | 4 | if (p < eol && *p == '#') |
645 | 2 | p = pnm_read_comment(ctx, p, eol, 0); |
646 | | |
647 | 4 | p = pnm_read_eol(ctx, p, e, 0); |
648 | 4 | } |
649 | | |
650 | 3 | return p; |
651 | 3 | } |
652 | | |
653 | | static fz_pixmap * |
654 | | pam_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, const unsigned char **out) |
655 | 3 | { |
656 | 3 | fz_pixmap *img = NULL; |
657 | 3 | int bitmap = 0; |
658 | 3 | int minval = 1; |
659 | 3 | int maxval = 65535; |
660 | | |
661 | 3 | fz_var(img); |
662 | | |
663 | 3 | p = pam_binary_read_header(ctx, pnm, p, e); |
664 | | |
665 | 3 | if (pnm->tupletype == PAM_UNKNOWN) |
666 | 0 | switch (pnm->depth) |
667 | 0 | { |
668 | 0 | case 1: pnm->tupletype = pnm->maxval == 1 ? PAM_BW : PAM_GRAY; break; |
669 | 0 | case 2: pnm->tupletype = pnm->maxval == 1 ? PAM_BWA : PAM_GRAYA; break; |
670 | 0 | case 3: pnm->tupletype = PAM_RGB; break; |
671 | 0 | case 4: pnm->tupletype = PAM_CMYK; break; |
672 | 0 | case 5: pnm->tupletype = PAM_CMYKA; break; |
673 | 0 | default: |
674 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot guess tuple type based on depth in pnm image"); |
675 | 0 | } |
676 | | |
677 | 3 | if (pnm->tupletype == PAM_BW && pnm->maxval > 1) |
678 | 0 | pnm->tupletype = PAM_GRAY; |
679 | 3 | else if (pnm->tupletype == PAM_GRAY && pnm->maxval == 1) |
680 | 0 | pnm->tupletype = PAM_BW; |
681 | 3 | else if (pnm->tupletype == PAM_BWA && pnm->maxval > 1) |
682 | 0 | pnm->tupletype = PAM_GRAYA; |
683 | 3 | else if (pnm->tupletype == PAM_GRAYA && pnm->maxval == 1) |
684 | 0 | pnm->tupletype = PAM_BWA; |
685 | | |
686 | 3 | switch (pnm->tupletype) |
687 | 3 | { |
688 | 0 | case PAM_BWA: |
689 | 0 | pnm->alpha = 1; |
690 | | /* fallthrough */ |
691 | 0 | case PAM_BW: |
692 | 0 | pnm->cs = fz_device_gray(ctx); |
693 | 0 | maxval = 1; |
694 | 0 | bitmap = 1; |
695 | 0 | break; |
696 | 0 | case PAM_GRAYA: |
697 | 0 | pnm->alpha = 1; |
698 | | /* fallthrough */ |
699 | 0 | case PAM_GRAY: |
700 | 0 | pnm->cs = fz_device_gray(ctx); |
701 | 0 | minval = 2; |
702 | 0 | break; |
703 | 0 | case PAM_RGBA: |
704 | 0 | pnm->alpha = 1; |
705 | | /* fallthrough */ |
706 | 0 | case PAM_RGB: |
707 | 0 | pnm->cs = fz_device_rgb(ctx); |
708 | 0 | break; |
709 | 0 | case PAM_CMYKA: |
710 | 0 | pnm->alpha = 1; |
711 | | /* fallthrough */ |
712 | 0 | case PAM_CMYK: |
713 | 0 | pnm->cs = fz_device_cmyk(ctx); |
714 | 0 | break; |
715 | 0 | default: |
716 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "unsupported tuple type"); |
717 | 3 | } |
718 | | |
719 | 0 | if (pnm->depth != fz_colorspace_n(ctx, pnm->cs) + pnm->alpha) |
720 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "depth out of tuple type range"); |
721 | 0 | if (pnm->maxval < minval || pnm->maxval > maxval) |
722 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "maxval out of range"); |
723 | | |
724 | 0 | pnm->bitdepth = bitdepth_from_maxval(pnm->maxval); |
725 | |
|
726 | 0 | if (pnm->height <= 0) |
727 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "image height must be > 0"); |
728 | 0 | if (pnm->width <= 0) |
729 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "image width must be > 0"); |
730 | 0 | if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1)) |
731 | 0 | fz_throw(ctx, FZ_ERROR_LIMIT, "image too large"); |
732 | | |
733 | 0 | if (onlymeta) |
734 | 0 | { |
735 | 0 | int packed; |
736 | 0 | int w, h, n; |
737 | 0 | size_t size; |
738 | |
|
739 | 0 | w = pnm->width; |
740 | 0 | h = pnm->height; |
741 | 0 | n = fz_colorspace_n(ctx, pnm->cs) + pnm->alpha; |
742 | | |
743 | | /* some encoders incorrectly pack bits into bytes and invert the image */ |
744 | 0 | packed = 0; |
745 | 0 | size = (size_t)w * h * n; |
746 | 0 | if (pnm->maxval == 1) |
747 | 0 | { |
748 | 0 | const unsigned char *e_packed = p + size / 8; |
749 | 0 | if (e_packed < e - 1 && e_packed[0] == 'P' && e_packed[1] >= '0' && e_packed[1] <= '7') |
750 | 0 | e = e_packed; |
751 | 0 | if (e < p || (size_t)(e - p) < size) |
752 | 0 | packed = 1; |
753 | 0 | } |
754 | 0 | if (packed && (e < p || (size_t)(e - p) < size / 8)) |
755 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "truncated packed image"); |
756 | 0 | if (!packed && (e < p || (size_t)(e - p) < size * (pnm->maxval < 256 ? 1 : 2))) |
757 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "truncated image"); |
758 | | |
759 | 0 | if (pnm->maxval == 255) |
760 | 0 | p += size; |
761 | 0 | else if (bitmap && packed) |
762 | 0 | p += ((w + 7) / 8) * h; |
763 | 0 | else if (bitmap) |
764 | 0 | p += size; |
765 | 0 | else if (pnm->maxval < 255) |
766 | 0 | p += size; |
767 | 0 | else |
768 | 0 | p += 2 * size; |
769 | 0 | } |
770 | 0 | else |
771 | 0 | { |
772 | 0 | unsigned char *dp; |
773 | 0 | int x, y, k, packed; |
774 | 0 | int w, h, n; |
775 | 0 | size_t size; |
776 | |
|
777 | 0 | img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, pnm->alpha); |
778 | 0 | fz_try(ctx) |
779 | 0 | { |
780 | 0 | dp = img->samples; |
781 | |
|
782 | 0 | w = img->w; |
783 | 0 | h = img->h; |
784 | 0 | n = img->n; |
785 | | |
786 | | /* some encoders incorrectly pack bits into bytes and invert the image */ |
787 | 0 | size = (size_t)w * h * n; |
788 | 0 | packed = 0; |
789 | 0 | if (pnm->maxval == 1) |
790 | 0 | { |
791 | 0 | const unsigned char *e_packed = p + size / 8; |
792 | 0 | if (e_packed < e - 1 && e_packed[0] == 'P' && e_packed[1] >= '0' && e_packed[1] <= '7') |
793 | 0 | e = e_packed; |
794 | 0 | if (e < p || (size_t)(e - p) < size) |
795 | 0 | packed = 1; |
796 | 0 | } |
797 | 0 | if (packed && (e < p || (size_t)(e - p) < size / 8)) |
798 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "truncated packed image"); |
799 | 0 | if (!packed && (e < p || (size_t)(e - p) < size * (pnm->maxval < 256 ? 1 : 2))) |
800 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "truncated image"); |
801 | | |
802 | 0 | if (pnm->maxval == 255) |
803 | 0 | memcpy(dp, p, size); |
804 | 0 | else if (bitmap && packed) |
805 | 0 | { |
806 | 0 | for (y = 0; y < h; y++) |
807 | 0 | for (x = 0; x < w; x++) |
808 | 0 | { |
809 | 0 | for (k = 0; k < n; k++) |
810 | 0 | { |
811 | 0 | *dp++ = (*p & (1 << (7 - (x & 0x7)))) ? 0x00 : 0xff; |
812 | 0 | if ((x & 0x7) == 7) |
813 | 0 | p++; |
814 | 0 | } |
815 | 0 | if (w & 0x7) |
816 | 0 | p++; |
817 | 0 | } |
818 | 0 | } |
819 | 0 | else if (bitmap) |
820 | 0 | { |
821 | 0 | for (y = 0; y < h; y++) |
822 | 0 | for (x = 0; x < w; x++) |
823 | 0 | for (k = 0; k < n; k++) |
824 | 0 | *dp++ = *p++ ? 0xff : 0x00; |
825 | 0 | } |
826 | 0 | else if (pnm->maxval < 255) |
827 | 0 | { |
828 | 0 | for (y = 0; y < h; y++) |
829 | 0 | for (x = 0; x < w; x++) |
830 | 0 | for (k = 0; k < n; k++) |
831 | 0 | *dp++ = map_color(ctx, *p++, pnm->maxval, 255); |
832 | 0 | } |
833 | 0 | else |
834 | 0 | { |
835 | 0 | for (y = 0; y < h; y++) |
836 | 0 | for (x = 0; x < w; x++) |
837 | 0 | for (k = 0; k < n; k++) |
838 | 0 | { |
839 | 0 | *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255); |
840 | 0 | p += 2; |
841 | 0 | } |
842 | 0 | } |
843 | |
|
844 | 0 | if (pnm->alpha) |
845 | 0 | fz_premultiply_pixmap(ctx, img); |
846 | 0 | } |
847 | 0 | fz_catch(ctx) |
848 | 0 | { |
849 | 0 | fz_drop_pixmap(ctx, img); |
850 | 0 | fz_rethrow(ctx); |
851 | 0 | } |
852 | 0 | } |
853 | | |
854 | 0 | if (out) |
855 | 0 | *out = p; |
856 | |
|
857 | 0 | return img; |
858 | 0 | } |
859 | | |
860 | | static const unsigned char * |
861 | | pfm_binary_read_header(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e) |
862 | 3 | { |
863 | 3 | pnm->width = 0; |
864 | 3 | p = pnm_read_int(ctx, p, e, &pnm->width); |
865 | 3 | p = pnm_read_whites_and_eols(ctx, p, e,1); |
866 | | |
867 | 3 | pnm->height = 0; |
868 | 3 | p = pnm_read_int(ctx, p, e, &pnm->height); |
869 | 3 | p = pnm_read_whites_and_eols(ctx, p, e,1); |
870 | | |
871 | 3 | p = pnm_read_real(ctx, p, e, &pnm->scale); |
872 | | |
873 | 3 | p = pnm_read_white_or_eol(ctx, p, e); |
874 | | |
875 | 3 | if (pnm->scale >= 0) |
876 | 3 | pnm->endian = ENDIAN_BIG; |
877 | 0 | else |
878 | 0 | { |
879 | 0 | pnm->endian = ENDIAN_LITTLE; |
880 | 0 | pnm->scale = -pnm->scale; |
881 | 0 | } |
882 | | |
883 | 3 | return p; |
884 | 3 | } |
885 | | |
886 | | static fz_pixmap * |
887 | | pfm_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int rgb, const unsigned char **out) |
888 | 3 | { |
889 | 3 | fz_pixmap *pix = NULL; |
890 | | |
891 | 3 | fz_var(pix); |
892 | | |
893 | 3 | p = pfm_binary_read_header(ctx, pnm, p, e); |
894 | 3 | pnm->cs = rgb ? fz_device_rgb(ctx) : fz_device_gray(ctx); |
895 | | |
896 | 3 | if (pnm->height <= 0) |
897 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "image height must be > 0"); |
898 | 3 | if (pnm->width <= 0) |
899 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "image width must be > 0"); |
900 | 3 | if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1)) |
901 | 0 | fz_throw(ctx, FZ_ERROR_LIMIT, "image too large"); |
902 | | |
903 | 3 | if (onlymeta) |
904 | 3 | { |
905 | 3 | size_t w = pnm->width; |
906 | 3 | size_t h = pnm->height; |
907 | 3 | int n = fz_colorspace_n(ctx, pnm->cs); |
908 | 3 | size_t size = w * h * n * sizeof(float); |
909 | | |
910 | 3 | if (e < p || (size_t)(e - p) < size) |
911 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "truncated image"); |
912 | | |
913 | 3 | p += size; |
914 | 3 | } |
915 | 0 | else |
916 | 0 | { |
917 | 0 | float *samples = NULL; |
918 | 0 | float *sample; |
919 | 0 | int w = pnm->width; |
920 | 0 | int h = pnm->height; |
921 | 0 | int n = fz_colorspace_n(ctx, pnm->cs); |
922 | 0 | size_t size = (size_t) w * h * n * sizeof(float); |
923 | 0 | int x, y, k; |
924 | |
|
925 | 0 | if (e < p || (size_t)(e - p) < size) |
926 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "truncated image"); |
927 | | |
928 | 0 | sample = samples = fz_malloc(ctx, size); |
929 | 0 | fz_try(ctx) |
930 | 0 | { |
931 | 0 | for (y = 0; y < h; y++) |
932 | 0 | for (x = 0; x < w; x++) |
933 | 0 | for (k = 0; k < n; k++) |
934 | 0 | { |
935 | 0 | uint32_t u; |
936 | 0 | float f; |
937 | |
|
938 | 0 | if (pnm->endian == ENDIAN_LITTLE) |
939 | 0 | u = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); |
940 | 0 | else |
941 | 0 | u = p[3] | (p[2] << 8) | (p[1] << 16) | (p[0] << 24); |
942 | 0 | memcpy(&f, &u, sizeof(float)); |
943 | |
|
944 | 0 | *sample++ = f / pnm->scale; |
945 | 0 | p += sizeof(float); |
946 | 0 | } |
947 | |
|
948 | 0 | pix = fz_new_pixmap_from_float_data(ctx, pnm->cs, w, h, samples); |
949 | 0 | } |
950 | 0 | fz_always(ctx) |
951 | 0 | fz_free(ctx, samples); |
952 | 0 | fz_catch(ctx) |
953 | 0 | fz_rethrow(ctx); |
954 | 0 | } |
955 | | |
956 | 3 | if (out) |
957 | 3 | *out = p; |
958 | | |
959 | 3 | return pix; |
960 | 3 | } |
961 | | |
962 | | static fz_pixmap * |
963 | | pnm_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, size_t total, int onlymeta, int subimage) |
964 | 18 | { |
965 | 18 | const unsigned char *e = p + total; |
966 | 18 | char signature[3] = { 0 }; |
967 | 18 | fz_pixmap *pix = NULL; |
968 | | |
969 | 38 | while (p < e && ((!onlymeta && subimage >= 0) || onlymeta)) |
970 | 24 | { |
971 | 24 | int subonlymeta = onlymeta || (subimage > 0); |
972 | | |
973 | 24 | p = pnm_read_whites_and_eols(ctx, p, e, 0); |
974 | 24 | p = pnm_read_signature(ctx, p, e, signature); |
975 | 24 | p = pnm_read_whites_and_eols(ctx, p, e, 1); |
976 | | |
977 | 24 | if (!strcmp(signature, "P1")) |
978 | 1 | { |
979 | 1 | pnm->cs = fz_device_gray(ctx); |
980 | 1 | pix = pnm_ascii_read_image(ctx, pnm, p, e, subonlymeta, 1, &p); |
981 | 1 | } |
982 | 23 | else if (!strcmp(signature, "P2")) |
983 | 3 | { |
984 | 3 | pnm->cs = fz_device_gray(ctx); |
985 | 3 | pix = pnm_ascii_read_image(ctx, pnm, p, e, subonlymeta, 0, &p); |
986 | 3 | } |
987 | 20 | else if (!strcmp(signature, "P3")) |
988 | 0 | { |
989 | 0 | pnm->cs = fz_device_rgb(ctx); |
990 | 0 | pix = pnm_ascii_read_image(ctx, pnm, p, e, subonlymeta, 0, &p); |
991 | 0 | } |
992 | 20 | else if (!strcmp(signature, "P4")) |
993 | 8 | { |
994 | 8 | pnm->cs = fz_device_gray(ctx); |
995 | 8 | pix = pnm_binary_read_image(ctx, pnm, p, e, subonlymeta, 1, &p); |
996 | 8 | } |
997 | 12 | else if (!strcmp(signature, "P5")) |
998 | 0 | { |
999 | 0 | pnm->cs = fz_device_gray(ctx); |
1000 | 0 | pix = pnm_binary_read_image(ctx, pnm, p, e, subonlymeta, 0, &p); |
1001 | 0 | } |
1002 | 12 | else if (!strcmp(signature, "P6")) |
1003 | 2 | { |
1004 | 2 | pnm->cs = fz_device_rgb(ctx); |
1005 | 2 | pix = pnm_binary_read_image(ctx, pnm, p, e, subonlymeta, 0, &p); |
1006 | 2 | } |
1007 | 10 | else if (!strcmp(signature, "P7")) |
1008 | 3 | pix = pam_binary_read_image(ctx, pnm, p, e, subonlymeta, &p); |
1009 | 7 | else if (!strcmp(signature, "Pf")) |
1010 | 1 | pix = pfm_binary_read_image(ctx, pnm, p, e, subonlymeta, 0, &p); |
1011 | 6 | else if (!strcmp(signature, "PF")) |
1012 | 2 | pix = pfm_binary_read_image(ctx, pnm, p, e, subonlymeta, 1, &p); |
1013 | 4 | else |
1014 | 4 | fz_throw(ctx, FZ_ERROR_FORMAT, "unsupported portable anymap signature (0x%02x, 0x%02x)", signature[0], signature[1]); |
1015 | | |
1016 | 20 | p = pnm_read_whites_and_eols(ctx, p, e, 0); |
1017 | | |
1018 | 20 | if (onlymeta) |
1019 | 6 | pnm->subimages++; |
1020 | 20 | if (subimage >= 0) |
1021 | 0 | subimage--; |
1022 | 20 | } |
1023 | | |
1024 | 14 | if (p >= e && subimage >= 0) |
1025 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "subimage count out of range"); |
1026 | | |
1027 | 14 | return pix; |
1028 | 14 | } |
1029 | | |
1030 | | fz_pixmap * |
1031 | | fz_load_pnm(fz_context *ctx, const unsigned char *p, size_t total) |
1032 | 0 | { |
1033 | 0 | struct info pnm = { 0 }; |
1034 | 0 | return pnm_read_image(ctx, &pnm, p, total, 0, 0); |
1035 | 0 | } |
1036 | | |
1037 | | void |
1038 | | fz_load_pnm_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep) |
1039 | 0 | { |
1040 | 0 | struct info pnm = { 0 }; |
1041 | 0 | (void) pnm_read_image(ctx, &pnm, p, total, 1, 0); |
1042 | 0 | *cspacep = fz_keep_colorspace(ctx, pnm.cs); /* pnm.cs is a borrowed device colorspace */ |
1043 | 0 | *wp = pnm.width; |
1044 | 0 | *hp = pnm.height; |
1045 | 0 | *xresp = 72; |
1046 | 0 | *yresp = 72; |
1047 | 0 | } |
1048 | | |
1049 | | fz_pixmap * |
1050 | | fz_load_pnm_subimage(fz_context *ctx, const unsigned char *p, size_t total, int subimage) |
1051 | 0 | { |
1052 | 0 | struct info pnm = { 0 }; |
1053 | 0 | return pnm_read_image(ctx, &pnm, p, total, 0, subimage); |
1054 | 0 | } |
1055 | | |
1056 | | int |
1057 | | fz_load_pnm_subimage_count(fz_context *ctx, const unsigned char *p, size_t total) |
1058 | 18 | { |
1059 | 18 | struct info pnm = { 0 }; |
1060 | 18 | (void) pnm_read_image(ctx, &pnm, p, total, 1, -1); |
1061 | 18 | return pnm.subimages; |
1062 | 18 | } |