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