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