1 | #include "mupdf/fitz.h" |
2 | |
3 | #include <string.h> |
4 | #include <limits.h> |
5 | |
6 | enum |
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 | |
19 | enum |
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 | |
30 | struct 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 | |
40 | static 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 | |
49 | static inline int iswhite(int a) |
50 | { |
51 | switch (a) { |
52 | case ' ': case '\t': |
53 | return 1; |
54 | } |
55 | return 0; |
56 | } |
57 | |
58 | static inline int iseol(int a) |
59 | { |
60 | switch (a) { |
61 | case '\r': case '\n': |
62 | return 1; |
63 | } |
64 | return 0; |
65 | } |
66 | |
67 | static 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 | |
78 | static const unsigned char * |
79 | pnm_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 | |
123 | static const unsigned char * |
124 | pnm_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 | |
136 | static const unsigned char * |
137 | pnm_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 | |
154 | static const unsigned char * |
155 | pnm_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 | |
189 | static const unsigned char * |
190 | pnm_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 | |
222 | static int |
223 | map_color(fz_context *ctx, int color, int inmax, int outmax) |
224 | { |
225 | float f = (float) color / inmax; |
226 | return f * outmax; |
227 | } |
228 | |
229 | static fz_pixmap * |
230 | 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) |
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 | |
339 | static fz_pixmap * |
340 | 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) |
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 | |
449 | static const unsigned char * |
450 | (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 | |
482 | static fz_pixmap * |
483 | pam_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 | |
686 | static fz_pixmap * |
687 | pnm_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 | |
747 | fz_pixmap * |
748 | fz_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 | |
754 | void |
755 | 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) |
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 | |
766 | fz_pixmap * |
767 | fz_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 | |
773 | int |
774 | fz_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 | |