1#include "mupdf/fitz.h"
2
3#include <string.h>
4#include <limits.h>
5
6enum
7{
8 PAM_UNKNOWN = 0,
9 PAM_BW,
10 PAM_BWA,
11 PAM_GRAY,
12 PAM_GRAYA,
13 PAM_RGB,
14 PAM_RGBA,
15 PAM_CMYK,
16 PAM_CMYKA,
17};
18
19enum
20{
21 TOKEN_UNKNOWN = 0,
22 TOKEN_WIDTH,
23 TOKEN_HEIGHT,
24 TOKEN_DEPTH,
25 TOKEN_MAXVAL,
26 TOKEN_TUPLTYPE,
27 TOKEN_ENDHDR,
28};
29
30struct info
31{
32 int subimages;
33 fz_colorspace *cs;
34 int width, height;
35 int maxval, bitdepth;
36 int depth, alpha;
37 int tupletype;
38};
39
40static inline int iswhiteeol(int a)
41{
42 switch (a) {
43 case ' ': case '\t': case '\r': case '\n':
44 return 1;
45 }
46 return 0;
47}
48
49static inline int iswhite(int a)
50{
51 switch (a) {
52 case ' ': case '\t':
53 return 1;
54 }
55 return 0;
56}
57
58static inline int iseol(int a)
59{
60 switch (a) {
61 case '\r': case '\n':
62 return 1;
63 }
64 return 0;
65}
66
67static inline int bitdepth_from_maxval(int maxval)
68{
69 int depth = 0;
70 while (maxval)
71 {
72 maxval >>= 1;
73 depth++;
74 }
75 return depth;
76}
77
78static const unsigned char *
79pnm_read_white(fz_context *ctx, const unsigned char *p, const unsigned char *e, int single_line)
80{
81 if (e - p < 1)
82 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot parse whitespace in pnm image");
83
84 if (single_line)
85 {
86 if (!iswhiteeol(*p) && *p != '#')
87 fz_throw(ctx, FZ_ERROR_GENERIC, "expected whitespace/comment in pnm image");
88 while (p < e && iswhite(*p))
89 p++;
90
91 if (p < e && *p == '#')
92 while (p < e && !iseol(*p))
93 p++;
94 if (p < e && iseol(*p))
95 p++;
96 }
97 else
98 {
99 if (!iswhiteeol(*p) && *p != '#')
100 fz_throw(ctx, FZ_ERROR_GENERIC, "expected whitespace in pnm image");
101 while (p < e && iswhiteeol(*p))
102 p++;
103
104 while (p < e && *p == '#')
105 {
106 while (p < e && !iseol(*p))
107 p++;
108
109 if (p < e && iseol(*p))
110 p++;
111
112 while (p < e && iswhiteeol(*p))
113 p++;
114
115 if (p < e && iseol(*p))
116 p++;
117 }
118 }
119
120 return p;
121}
122
123static const unsigned char *
124pnm_read_signature(fz_context *ctx, const unsigned char *p, const unsigned char *e, char *signature)
125{
126 if (e - p < 2)
127 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot parse magic number in pnm image");
128 if (p[0] != 'P' || p[1] < '1' || p[1] > '7')
129 fz_throw(ctx, FZ_ERROR_GENERIC, "expected signature in pnm image");
130
131 signature[0] = *p++;
132 signature[1] = *p++;
133 return p;
134}
135
136static const unsigned char *
137pnm_read_number(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *number)
138{
139 if (e - p < 1)
140 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot parse number in pnm image");
141 if (*p < '0' || *p > '9')
142 fz_throw(ctx, FZ_ERROR_GENERIC, "expected numeric field in pnm image");
143
144 while (p < e && *p >= '0' && *p <= '9')
145 {
146 if (number)
147 *number = *number * 10 + *p - '0';
148 p++;
149 }
150
151 return p;
152}
153
154static const unsigned char *
155pnm_read_tupletype(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *tupletype)
156{
157 const struct { int len; char *str; int type; } tupletypes[] =
158 {
159 {13, "BLACKANDWHITE", PAM_BW},
160 {19, "BLACKANDWHITE_ALPHA", PAM_BWA},
161 {9, "GRAYSCALE", PAM_GRAY},
162 {15, "GRAYSCALE_ALPHA", PAM_GRAYA},
163 {3, "RGB", PAM_RGB},
164 {9, "RGB_ALPHA", PAM_RGBA},
165 {4, "CMYK", PAM_CMYK},
166 {10, "CMYK_ALPHA", PAM_CMYKA},
167 };
168 const unsigned char *s;
169 int i, len;
170
171 if (e - p < 1)
172 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot parse tuple type in pnm image");
173
174 s = p;
175 while (!iswhiteeol(*p))
176 p++;
177 len = p - s;
178
179 for (i = 0; i < nelem(tupletypes); i++)
180 if (len == tupletypes[i].len && !strncmp((char *) s, tupletypes[i].str, len))
181 {
182 *tupletype = tupletypes[i].type;
183 return p;
184 }
185
186 fz_throw(ctx, FZ_ERROR_GENERIC, "unknown tuple type in pnm image");
187}
188
189static const unsigned char *
190pnm_read_token(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *token)
191{
192 const struct { int len; char *str; int type; } tokens[] =
193 {
194 {5, "WIDTH", TOKEN_WIDTH},
195 {6, "HEIGHT", TOKEN_HEIGHT},
196 {5, "DEPTH", TOKEN_DEPTH},
197 {6, "MAXVAL", TOKEN_MAXVAL},
198 {8, "TUPLTYPE", TOKEN_TUPLTYPE},
199 {6, "ENDHDR", TOKEN_ENDHDR},
200 };
201 const unsigned char *s;
202 int i, len;
203
204 if (e - p < 1)
205 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot parse header token in pnm image");
206
207 s = p;
208 while (!iswhiteeol(*p))
209 p++;
210 len = p - s;
211
212 for (i = 0; i < nelem(tokens); i++)
213 if (len == tokens[i].len && !strncmp((char *) s, tokens[i].str, len))
214 {
215 *token = tokens[i].type;
216 return p;
217 }
218
219 fz_throw(ctx, FZ_ERROR_GENERIC, "unknown header token in pnm image");
220}
221
222static int
223map_color(fz_context *ctx, int color, int inmax, int outmax)
224{
225 float f = (float) color / inmax;
226 return f * outmax;
227}
228
229static fz_pixmap *
230pnm_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)
231{
232 fz_pixmap *img = NULL;
233
234 p = pnm_read_number(ctx, p, e, &pnm->width);
235 p = pnm_read_white(ctx, p, e, 0);
236
237 if (bitmap)
238 {
239 p = pnm_read_number(ctx, p, e, &pnm->height);
240 p = pnm_read_white(ctx, p, e, 1);
241 pnm->maxval = 1;
242 }
243 else
244 {
245 p = pnm_read_number(ctx, p, e, &pnm->height);
246 p = pnm_read_white(ctx, p, e, 0);
247 p = pnm_read_number(ctx, p, e, &pnm->maxval);
248 p = pnm_read_white(ctx, p, e, 0);
249 }
250
251 if (pnm->maxval <= 0 || pnm->maxval >= 65536)
252 fz_throw(ctx, FZ_ERROR_GENERIC, "maximum sample value of out range in pnm image: %d", pnm->maxval);
253
254 pnm->bitdepth = bitdepth_from_maxval(pnm->maxval);
255
256 if (pnm->height <= 0)
257 fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0");
258 if (pnm->width <= 0)
259 fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0");
260 if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1))
261 fz_throw(ctx, FZ_ERROR_GENERIC, "image too large");
262
263 if (onlymeta)
264 {
265 int x, y, k;
266 int w, h, n;
267
268 w = pnm->width;
269 h = pnm->height;
270 n = fz_colorspace_n(ctx, pnm->cs);
271
272 if (bitmap)
273 {
274 for (y = 0; y < h; y++)
275 for (x = -1; x < w; x++)
276 {
277 p = pnm_read_number(ctx, p, e, NULL);
278 p = pnm_read_white(ctx, p, e, 0);
279 }
280 }
281 else
282 {
283 for (y = 0; y < h; y++)
284 for (x = 0; x < w; x++)
285 for (k = 0; k < n; k++)
286 {
287 p = pnm_read_number(ctx, p, e, NULL);
288 p = pnm_read_white(ctx, p, e, 0);
289 }
290 }
291 }
292 else
293 {
294 unsigned char *dp;
295 int x, y, k;
296 int w, h, n;
297
298 img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0);
299 dp = img->samples;
300
301 w = img->w;
302 h = img->h;
303 n = img->n;
304
305 if (bitmap)
306 {
307 for (y = 0; y < h; y++)
308 {
309 for (x = 0; x < w; x++)
310 {
311 int v = 0;
312 p = pnm_read_number(ctx, p, e, &v);
313 p = pnm_read_white(ctx, p, e, 0);
314 *dp++ = v ? 0x00 : 0xff;
315 }
316 }
317 }
318 else
319 {
320 for (y = 0; y < h; y++)
321 for (x = 0; x < w; x++)
322 for (k = 0; k < n; k++)
323 {
324 int v = 0;
325 p = pnm_read_number(ctx, p, e, &v);
326 p = pnm_read_white(ctx, p, e, 0);
327 v = fz_clampi(v, 0, pnm->maxval);
328 *dp++ = map_color(ctx, v, pnm->maxval, 255);
329 }
330 }
331 }
332
333 if (out)
334 *out = p;
335
336 return img;
337}
338
339static fz_pixmap *
340pnm_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)
341{
342 fz_pixmap *img = NULL;
343
344 pnm->width = 0;
345 p = pnm_read_number(ctx, p, e, &pnm->width);
346 p = pnm_read_white(ctx, p, e, 0);
347
348 if (bitmap)
349 {
350 pnm->height = 0;
351 p = pnm_read_number(ctx, p, e, &pnm->height);
352 p = pnm_read_white(ctx, p, e, 1);
353 pnm->maxval = 1;
354 }
355 else
356 {
357 pnm->height = 0;
358 p = pnm_read_number(ctx, p, e, &pnm->height);
359 p = pnm_read_white(ctx, p, e, 0);
360 pnm->maxval = 0;
361 p = pnm_read_number(ctx, p, e, &pnm->maxval);
362 p = pnm_read_white(ctx, p, e, 1);
363 }
364
365 if (pnm->maxval <= 0 || pnm->maxval >= 65536)
366 fz_throw(ctx, FZ_ERROR_GENERIC, "maximum sample value of out range in pnm image: %d", pnm->maxval);
367
368 pnm->bitdepth = bitdepth_from_maxval(pnm->maxval);
369
370 if (pnm->height <= 0)
371 fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0");
372 if (pnm->width <= 0)
373 fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0");
374 if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1))
375 fz_throw(ctx, FZ_ERROR_GENERIC, "image too large");
376
377 if (onlymeta)
378 {
379 int w = pnm->width;
380 int h = pnm->height;
381 int n = fz_colorspace_n(ctx, pnm->cs);
382
383 if (pnm->maxval == 255)
384 p += n * w * h;
385 else if (bitmap)
386 p += ((w + 7) / 8) * h;
387 else if (pnm->maxval < 255)
388 p += n * w * h;
389 else
390 p += 2 * n * w * h;
391 }
392 else
393 {
394 unsigned char *dp;
395 int x, y, k;
396 int w, h, n;
397
398 img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0);
399 dp = img->samples;
400
401 w = img->w;
402 h = img->h;
403 n = img->n;
404
405 if (pnm->maxval == 255)
406 {
407 memcpy(dp, p, w * h * n);
408 p += n * w * h;
409 }
410 else if (bitmap)
411 {
412 for (y = 0; y < h; y++)
413 {
414 for (x = 0; x < w; x++)
415 {
416 *dp++ = (*p & (1 << (7 - (x & 0x7)))) ? 0x00 : 0xff;
417 if ((x & 0x7) == 7)
418 p++;
419 }
420 if (w & 0x7)
421 p++;
422 }
423 }
424 else if (pnm->maxval < 255)
425 {
426 for (y = 0; y < h; y++)
427 for (x = 0; x < w; x++)
428 for (k = 0; k < n; k++)
429 *dp++ = map_color(ctx, *p++, pnm->maxval, 255);
430 }
431 else
432 {
433 for (y = 0; y < h; y++)
434 for (x = 0; x < w; x++)
435 for (k = 0; k < n; k++)
436 {
437 *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255);
438 p += 2;
439 }
440 }
441 }
442
443 if (out)
444 *out = p;
445
446 return img;
447}
448
449static const unsigned char *
450pam_binary_read_header(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e)
451{
452 int token = TOKEN_UNKNOWN;
453
454 pnm->width = 0;
455 pnm->height = 0;
456 pnm->depth = 0;
457 pnm->maxval = 0;
458 pnm->tupletype = 0;
459
460 while (p < e && token != TOKEN_ENDHDR)
461 {
462 p = pnm_read_token(ctx, p, e, &token);
463 p = pnm_read_white(ctx, p, e, 0);
464 switch (token)
465 {
466 case TOKEN_WIDTH: p = pnm_read_number(ctx, p, e, &pnm->width); break;
467 case TOKEN_HEIGHT: p = pnm_read_number(ctx, p, e, &pnm->height); break;
468 case TOKEN_DEPTH: p = pnm_read_number(ctx, p, e, &pnm->depth); break;
469 case TOKEN_MAXVAL: p = pnm_read_number(ctx, p, e, &pnm->maxval); break;
470 case TOKEN_TUPLTYPE: p = pnm_read_tupletype(ctx, p, e, &pnm->tupletype); break;
471 case TOKEN_ENDHDR: break;
472 default: fz_throw(ctx, FZ_ERROR_GENERIC, "unknown header token in pnm image");
473 }
474
475 if (token != TOKEN_ENDHDR)
476 p = pnm_read_white(ctx, p, e, 0);
477 }
478
479 return p;
480}
481
482static fz_pixmap *
483pam_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, const unsigned char **out)
484{
485 fz_pixmap *img = NULL;
486 int bitmap = 0;
487 int minval = 1;
488 int maxval = 65535;
489
490 fz_var(img);
491
492 p = pam_binary_read_header(ctx, pnm, p, e);
493
494 if (pnm->tupletype == PAM_UNKNOWN)
495 switch (pnm->depth)
496 {
497 case 1: pnm->tupletype = pnm->maxval == 1 ? PAM_BW : PAM_GRAY; break;
498 case 2: pnm->tupletype = pnm->maxval == 1 ? PAM_BWA : PAM_GRAYA; break;
499 case 3: pnm->tupletype = PAM_RGB; break;
500 case 4: pnm->tupletype = PAM_CMYK; break;
501 case 5: pnm->tupletype = PAM_CMYKA; break;
502 default:
503 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot guess tuple type based on depth in pnm image");
504 }
505
506 if (pnm->tupletype == PAM_BW && pnm->maxval > 1)
507 pnm->tupletype = PAM_GRAY;
508 else if (pnm->tupletype == PAM_GRAY && pnm->maxval == 1)
509 pnm->tupletype = PAM_BW;
510 else if (pnm->tupletype == PAM_BWA && pnm->maxval > 1)
511 pnm->tupletype = PAM_GRAYA;
512 else if (pnm->tupletype == PAM_GRAYA && pnm->maxval == 1)
513 pnm->tupletype = PAM_BWA;
514
515 switch (pnm->tupletype)
516 {
517 case PAM_BWA:
518 pnm->alpha = 1;
519 /* fallthrough */
520 case PAM_BW:
521 pnm->cs = fz_device_gray(ctx);
522 maxval = 1;
523 bitmap = 1;
524 break;
525 case PAM_GRAYA:
526 pnm->alpha = 1;
527 /* fallthrough */
528 case PAM_GRAY:
529 pnm->cs = fz_device_gray(ctx);
530 minval = 2;
531 break;
532 case PAM_RGBA:
533 pnm->alpha = 1;
534 /* fallthrough */
535 case PAM_RGB:
536 pnm->cs = fz_device_rgb(ctx);
537 break;
538 case PAM_CMYKA:
539 pnm->alpha = 1;
540 /* fallthrough */
541 case PAM_CMYK:
542 pnm->cs = fz_device_cmyk(ctx);
543 break;
544 default:
545 fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported tuple type");
546 }
547
548 if (pnm->depth != fz_colorspace_n(ctx, pnm->cs) + pnm->alpha)
549 fz_throw(ctx, FZ_ERROR_GENERIC, "depth out of tuple type range");
550 if (pnm->maxval < minval || pnm->maxval > maxval)
551 fz_throw(ctx, FZ_ERROR_GENERIC, "maxval out of range");
552
553 pnm->bitdepth = bitdepth_from_maxval(pnm->maxval);
554
555 if (pnm->height <= 0)
556 fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0");
557 if (pnm->width <= 0)
558 fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0");
559 if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1))
560 fz_throw(ctx, FZ_ERROR_GENERIC, "image too large");
561
562 if (onlymeta)
563 {
564 int packed;
565 int w, h, n;
566
567 w = pnm->width;
568 h = pnm->height;
569 n = fz_colorspace_n(ctx, pnm->cs) + pnm->alpha;
570
571 /* some encoders incorrectly pack bits into bytes and invert the image */
572 packed = 0;
573 if (pnm->maxval == 1)
574 {
575 const unsigned char *e_packed = p + w * h * n / 8;
576 if (e_packed < e - 1 && e_packed[0] == 'P' && e_packed[1] >= '0' && e_packed[1] <= '7')
577 e = e_packed;
578 if (e - p < w * h * n)
579 packed = 1;
580 }
581 if (packed && e - p < w * h * n / 8)
582 fz_throw(ctx, FZ_ERROR_GENERIC, "truncated packed image");
583 if (!packed && e - p < w * h * n * (pnm->maxval < 256 ? 1 : 2))
584 fz_throw(ctx, FZ_ERROR_GENERIC, "truncated image");
585
586 if (pnm->maxval == 255)
587 p += n * w * h;
588 else if (bitmap && packed)
589 p += ((w + 7) / 8) * h;
590 else if (bitmap)
591 p += n * w * h;
592 else if (pnm->maxval < 255)
593 p += n * w * h;
594 else
595 p += 2 * n * w * h;
596 }
597
598 if (!onlymeta)
599 {
600 unsigned char *dp;
601 int x, y, k, packed;
602 int w, h, n;
603
604 img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, pnm->alpha);
605 fz_try(ctx)
606 {
607 dp = img->samples;
608
609 w = img->w;
610 h = img->h;
611 n = img->n;
612
613 /* some encoders incorrectly pack bits into bytes and invert the image */
614 packed = 0;
615 if (pnm->maxval == 1)
616 {
617 const unsigned char *e_packed = p + w * h * n / 8;
618 if (e_packed < e - 1 && e_packed[0] == 'P' && e_packed[1] >= '0' && e_packed[1] <= '7')
619 e = e_packed;
620 if (e - p < w * h * n)
621 packed = 1;
622 }
623 if (packed && e - p < w * h * n / 8)
624 fz_throw(ctx, FZ_ERROR_GENERIC, "truncated packed image");
625 if (!packed && e - p < w * h * n * (pnm->maxval < 256 ? 1 : 2))
626 fz_throw(ctx, FZ_ERROR_GENERIC, "truncated image");
627
628 if (pnm->maxval == 255)
629 memcpy(dp, p, w * h * n);
630 else if (bitmap && packed)
631 {
632 for (y = 0; y < h; y++)
633 for (x = 0; x < w; x++)
634 {
635 for (k = 0; k < n; k++)
636 {
637 *dp++ = (*p & (1 << (7 - (x & 0x7)))) ? 0x00 : 0xff;
638 if ((x & 0x7) == 7)
639 p++;
640 }
641 if (w & 0x7)
642 p++;
643 }
644 }
645 else if (bitmap)
646 {
647 for (y = 0; y < h; y++)
648 for (x = 0; x < w; x++)
649 for (k = 0; k < n; k++)
650 *dp++ = *p++ ? 0xff : 0x00;
651 }
652 else if (pnm->maxval < 255)
653 {
654 for (y = 0; y < h; y++)
655 for (x = 0; x < w; x++)
656 for (k = 0; k < n; k++)
657 *dp++ = map_color(ctx, *p++, pnm->maxval, 255);
658 }
659 else
660 {
661 for (y = 0; y < h; y++)
662 for (x = 0; x < w; x++)
663 for (k = 0; k < n; k++)
664 {
665 *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255);
666 p += 2;
667 }
668 }
669
670 if (pnm->alpha)
671 fz_premultiply_pixmap(ctx, img);
672 }
673 fz_catch(ctx)
674 {
675 fz_drop_pixmap(ctx, img);
676 fz_rethrow(ctx);
677 }
678 }
679
680 if (out)
681 *out = p;
682
683 return img;
684}
685
686static fz_pixmap *
687pnm_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, size_t total, int onlymeta, int subimage)
688{
689 const unsigned char *e = p + total;
690 char signature[3] = { 0 };
691 fz_pixmap *pix = NULL;
692
693 while (p < e && ((!onlymeta && subimage >= 0) || onlymeta))
694 {
695 int subonlymeta = onlymeta || (subimage > 0);
696
697 p = pnm_read_signature(ctx, p, e, signature);
698 p = pnm_read_white(ctx, p, e, 0);
699
700 if (!strcmp(signature, "P1"))
701 {
702 pnm->cs = fz_device_gray(ctx);
703 pix = pnm_ascii_read_image(ctx, pnm, p, e, subonlymeta, 1, &p);
704 }
705 else if (!strcmp(signature, "P2"))
706 {
707 pnm->cs = fz_device_gray(ctx);
708 pix = pnm_ascii_read_image(ctx, pnm, p, e, subonlymeta, 0, &p);
709 }
710 else if (!strcmp(signature, "P3"))
711 {
712 pnm->cs = fz_device_rgb(ctx);
713 pix = pnm_ascii_read_image(ctx, pnm, p, e, subonlymeta, 0, &p);
714 }
715 else if (!strcmp(signature, "P4"))
716 {
717 pnm->cs = fz_device_gray(ctx);
718 pix = pnm_binary_read_image(ctx, pnm, p, e, subonlymeta, 1, &p);
719 }
720 else if (!strcmp(signature, "P5"))
721 {
722 pnm->cs = fz_device_gray(ctx);
723 pix = pnm_binary_read_image(ctx, pnm, p, e, subonlymeta, 0, &p);
724 }
725 else if (!strcmp(signature, "P6"))
726 {
727 pnm->cs = fz_device_rgb(ctx);
728 pix = pnm_binary_read_image(ctx, pnm, p, e, subonlymeta, 0, &p);
729 }
730 else if (!strcmp(signature, "P7"))
731 pix = pam_binary_read_image(ctx, pnm, p, e, subonlymeta, &p);
732 else
733 fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported portable anymap signature (0x%02x, 0x%02x)", signature[0], signature[1]);
734
735 if (onlymeta)
736 pnm->subimages++;
737 if (subimage >= 0)
738 subimage--;
739 }
740
741 if (p >= e && subimage >= 0)
742 fz_throw(ctx, FZ_ERROR_GENERIC, "subimage count out of range");
743
744 return pix;
745}
746
747fz_pixmap *
748fz_load_pnm(fz_context *ctx, const unsigned char *p, size_t total)
749{
750 struct info pnm = { 0 };
751 return pnm_read_image(ctx, &pnm, p, total, 0, 0);
752}
753
754void
755fz_load_pnm_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
756{
757 struct info pnm = { 0 };
758 (void) pnm_read_image(ctx, &pnm, p, total, 1, 0);
759 *cspacep = fz_keep_colorspace(ctx, pnm.cs); /* pnm.cs is a borrowed device colorspace */
760 *wp = pnm.width;
761 *hp = pnm.height;
762 *xresp = 72;
763 *yresp = 72;
764}
765
766fz_pixmap *
767fz_load_pnm_subimage(fz_context *ctx, const unsigned char *p, size_t total, int subimage)
768{
769 struct info pnm = { 0 };
770 return pnm_read_image(ctx, &pnm, p, total, 0, subimage);
771}
772
773int
774fz_load_pnm_subimage_count(fz_context *ctx, const unsigned char *p, size_t total)
775{
776 struct info pnm = { 0 };
777 (void) pnm_read_image(ctx, &pnm, p, total, 1, -1);
778 return pnm.subimages;
779}
780