Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
