Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/fitz/load-psd.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-2024 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 <limits.h> | |
| 28 #include <string.h> | |
| 29 | |
| 30 struct info | |
| 31 { | |
| 32 unsigned int width, height, n; | |
| 33 int xres, yres; | |
| 34 fz_colorspace *cs; | |
| 35 }; | |
| 36 | |
| 37 typedef struct | |
| 38 { | |
| 39 fz_context *ctx; | |
| 40 const unsigned char *p; | |
| 41 size_t total; | |
| 42 int packbits; | |
| 43 int packbits_n; | |
| 44 int packbits_rep; | |
| 45 } source_t; | |
| 46 | |
| 47 static int | |
| 48 get8(source_t *source) | |
| 49 { | |
| 50 if (source->total < 1) | |
| 51 fz_throw(source->ctx, FZ_ERROR_FORMAT, "Truncated PSD"); | |
| 52 source->total--; | |
| 53 | |
| 54 return *source->p++; | |
| 55 } | |
| 56 | |
| 57 static int | |
| 58 get16be(source_t *source) | |
| 59 { | |
| 60 int v; | |
| 61 | |
| 62 if (source->total < 2) | |
| 63 { | |
| 64 source->total = 0; | |
| 65 fz_throw(source->ctx, FZ_ERROR_FORMAT, "Truncated PSD"); | |
| 66 } | |
| 67 | |
| 68 source->total -= 2; | |
| 69 | |
| 70 v = *source->p++; | |
| 71 v = (v<<8) | *source->p++; | |
| 72 | |
| 73 return v; | |
| 74 } | |
| 75 | |
| 76 static int | |
| 77 get32be(source_t *source) | |
| 78 { | |
| 79 int v; | |
| 80 | |
| 81 if (source->total < 4) | |
| 82 { | |
| 83 source->total = 0; | |
| 84 fz_throw(source->ctx, FZ_ERROR_FORMAT, "Truncated PSD"); | |
| 85 } | |
| 86 | |
| 87 source->total -= 4; | |
| 88 | |
| 89 v = *source->p++; | |
| 90 v = (v<<8) | *source->p++; | |
| 91 v = (v<<8) | *source->p++; | |
| 92 v = (v<<8) | *source->p++; | |
| 93 | |
| 94 return v; | |
| 95 } | |
| 96 | |
| 97 static uint32_t | |
| 98 getu32be(source_t *source) | |
| 99 { | |
| 100 return (uint32_t)get32be(source); | |
| 101 } | |
| 102 | |
| 103 static int | |
| 104 unpack8(source_t *source) | |
| 105 { | |
| 106 int i; | |
| 107 | |
| 108 if (source->packbits == 0) | |
| 109 return get8(source); | |
| 110 | |
| 111 i = source->packbits_n; | |
| 112 if (i == 128) | |
| 113 { | |
| 114 do | |
| 115 { | |
| 116 i = source->packbits_n = get8(source); | |
| 117 } | |
| 118 while (i == 128); | |
| 119 if (i > 128) | |
| 120 source->packbits_rep = get8(source); | |
| 121 } | |
| 122 if (i < 128) | |
| 123 { | |
| 124 /* Literal n+1 */ | |
| 125 i--; | |
| 126 if (i < 0) | |
| 127 i = 128; | |
| 128 source->packbits_n = i; | |
| 129 return get8(source); | |
| 130 } | |
| 131 else | |
| 132 { | |
| 133 i++; | |
| 134 if (i == 257) | |
| 135 i = 128; | |
| 136 source->packbits_n = i; | |
| 137 return source->packbits_rep; | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 static char *getString(source_t *source) | |
| 142 { | |
| 143 size_t len = get8(source); | |
| 144 size_t odd = !(len & 1); | |
| 145 char *s; | |
| 146 | |
| 147 if (source->total < len + odd) | |
| 148 { | |
| 149 source->total = 0; | |
| 150 fz_throw(source->ctx, FZ_ERROR_FORMAT, "Truncated string in PSD"); | |
| 151 } | |
| 152 | |
| 153 s = fz_malloc(source->ctx, len+1); | |
| 154 memcpy(s, source->p, len); | |
| 155 s[len] = 0; | |
| 156 | |
| 157 source->p += len + odd; | |
| 158 source->total -= len + odd; | |
| 159 | |
| 160 return s; | |
| 161 } | |
| 162 | |
| 163 static fz_pixmap * | |
| 164 psd_read_image(fz_context *ctx, struct info *info, const unsigned char *p, size_t total, int only_metadata) | |
| 165 { | |
| 166 int v, bpc, c, n; | |
| 167 source_t source; | |
| 168 size_t ir_len, data_len; | |
| 169 fz_separations *seps = NULL; | |
| 170 fz_pixmap *image = NULL; | |
| 171 size_t m; | |
| 172 unsigned char *q; | |
| 173 int alpha = 0; | |
| 174 | |
| 175 source.ctx = ctx; | |
| 176 source.p = p; | |
| 177 source.total = total; | |
| 178 source.packbits = 0; | |
| 179 | |
| 180 memset(info, 0, sizeof(*info)); | |
| 181 | |
| 182 fz_var(image); | |
| 183 fz_var(seps); | |
| 184 | |
| 185 fz_try(ctx) | |
| 186 { | |
| 187 info->xres = 96; | |
| 188 info->yres = 96; | |
| 189 | |
| 190 v = get32be(&source); | |
| 191 /* Read signature */ | |
| 192 if (v != 0x38425053 /* 8BPS */) | |
| 193 fz_throw(ctx, FZ_ERROR_FORMAT, "not a psd image (wrong signature)"); | |
| 194 | |
| 195 /* Version */ | |
| 196 v = get16be(&source); | |
| 197 if (v != 1) | |
| 198 fz_throw(ctx, FZ_ERROR_FORMAT, "Bad PSD version"); | |
| 199 | |
| 200 (void)get16be(&source); | |
| 201 (void)get32be(&source); | |
| 202 | |
| 203 info->n = n = get16be(&source); | |
| 204 info->height = getu32be(&source); | |
| 205 info->width = getu32be(&source); | |
| 206 bpc = get16be(&source); | |
| 207 if (bpc != 8 && bpc != 16) | |
| 208 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Only 8 or 16 bpc PSD files supported!"); | |
| 209 | |
| 210 c = get16be(&source); | |
| 211 if (c == 4) /* CMYK (+ Spots?) */ | |
| 212 { | |
| 213 if (n != 4) | |
| 214 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "CMYK PSD with %d chans not supported!", n); | |
| 215 info->cs = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); | |
| 216 } | |
| 217 else if (c == 3) /* RGB */ | |
| 218 { | |
| 219 if (n == 4) | |
| 220 alpha = 1; | |
| 221 else if (n != 3) | |
| 222 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "RGB PSD with %d chans not supported!", n); | |
| 223 info->cs = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); | |
| 224 } | |
| 225 else if (c == 1) /* Greyscale */ | |
| 226 { | |
| 227 if (n != 1) | |
| 228 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Greyscale PSD with %d chans not supported!", n); | |
| 229 info->cs = fz_keep_colorspace(ctx, fz_device_gray(ctx)); | |
| 230 } | |
| 231 else | |
| 232 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Unsupported PSD colorspace (%d)!", c); | |
| 233 | |
| 234 v = get32be(&source); | |
| 235 if (v != 0) | |
| 236 fz_throw(ctx, FZ_ERROR_FORMAT, "Unexpected color data in PSD!"); | |
| 237 | |
| 238 /* Now read image resources... */ | |
| 239 ir_len = getu32be(&source); | |
| 240 while (ir_len >= 12) | |
| 241 { | |
| 242 size_t start = source.p - p; | |
| 243 | |
| 244 v = get32be(&source); | |
| 245 if (v != 0x3842494d) /* 8BIM */ | |
| 246 fz_throw(ctx, FZ_ERROR_FORMAT, "Failed to find expected 8BIM in PSD"); | |
| 247 v = get16be(&source); | |
| 248 | |
| 249 fz_free(ctx, getString(&source)); | |
| 250 | |
| 251 data_len = getu32be(&source); | |
| 252 ir_len -= (source.p - p) - start; | |
| 253 switch (v) | |
| 254 { | |
| 255 case 0x3ef: /* Spot */ | |
| 256 { | |
| 257 int spots = 0; | |
| 258 int alpha_found = 0; | |
| 259 | |
| 260 while (data_len > 0) | |
| 261 { | |
| 262 int C, M, Y, K; | |
| 263 char text[32]; | |
| 264 | |
| 265 v = get16be(&source); | |
| 266 if (v == 0 && alpha_found == 0) | |
| 267 alpha_found = 1, alpha = 1; | |
| 268 else if (v != 2) | |
| 269 fz_throw(ctx, FZ_ERROR_FORMAT, "Non CMYK spot found in PSD"); | |
| 270 | |
| 271 C = 0xff - (get16be(&source)>>8); | |
| 272 M = 0xff - (get16be(&source)>>8); | |
| 273 Y = 0xff - (get16be(&source)>>8); | |
| 274 K = 0xff - (get16be(&source)>>8); | |
| 275 (void)get16be(&source); /* opacity */ | |
| 276 (void)get8(&source); /* kind */ | |
| 277 (void)get8(&source); /* padding */ | |
| 278 if (v == 2) | |
| 279 { | |
| 280 uint32_t cmyk = C | (M<<8) | (Y<<16) | (K<<24); | |
| 281 int R = fz_clampi(255-C-K, 0, 255); | |
| 282 int G = fz_clampi(255-M-K, 0, 255); | |
| 283 int B = fz_clampi(255-Y-K, 0, 255); | |
| 284 uint32_t rgba = R | (G<<8) | (B<<16); | |
| 285 if (seps == NULL) | |
| 286 seps = fz_new_separations(ctx, 1); | |
| 287 snprintf(text, sizeof(text), "s%d", spots); | |
| 288 /* Use the old entry-point until we fix the new one */ | |
| 289 fz_add_separation_equivalents(ctx, seps, rgba, cmyk, text); | |
| 290 spots++; | |
| 291 } | |
| 292 data_len -= 14; | |
| 293 ir_len -= 14; | |
| 294 } | |
| 295 } | |
| 296 } | |
| 297 /* Skip any unread data */ | |
| 298 if (data_len & 1) | |
| 299 data_len++; | |
| 300 ir_len -= data_len; | |
| 301 while (data_len--) | |
| 302 get8(&source); | |
| 303 } | |
| 304 if (fz_count_separations(ctx, seps) + info->cs->n + 1 == n && alpha == 0) | |
| 305 alpha = 1; | |
| 306 if (fz_count_separations(ctx, seps) + info->cs->n + alpha != n) | |
| 307 fz_throw(ctx, FZ_ERROR_FORMAT, "PSD contains mismatching spot/alpha data"); | |
| 308 | |
| 309 /* Skip over the Layer data. */ | |
| 310 v = get32be(&source); | |
| 311 if (v != 0) | |
| 312 { | |
| 313 if (source.total < (size_t)v) | |
| 314 fz_throw(ctx, FZ_ERROR_FORMAT, "Truncated PSD"); | |
| 315 source.total -= v; | |
| 316 source.p += v; | |
| 317 } | |
| 318 if (source.total == 0) | |
| 319 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Unflattened PSD not supported"); | |
| 320 | |
| 321 v = get16be(&source); | |
| 322 switch (v) | |
| 323 { | |
| 324 case 0: | |
| 325 /* No compression */ | |
| 326 break; | |
| 327 case 1: | |
| 328 /* Packbits */ | |
| 329 source.packbits = 1; | |
| 330 source.packbits_n = 128; | |
| 331 | |
| 332 /* Skip over rows * channels * byte counts. */ | |
| 333 m = ((size_t)info->height) * info->n * 2; | |
| 334 if (m > source.total) | |
| 335 fz_throw(ctx, FZ_ERROR_FORMAT, "Truncated RLE PSD"); | |
| 336 source.total -= m; | |
| 337 source.p += m; | |
| 338 break; | |
| 339 case 2: /* Deflate */ | |
| 340 case 3: /* Deflate with prediction */ | |
| 341 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Deflate PSD not supported"); | |
| 342 default: | |
| 343 fz_throw(ctx, FZ_ERROR_FORMAT, "Unexpected compression (%d) found in PSD", v); | |
| 344 } | |
| 345 | |
| 346 if (only_metadata) | |
| 347 break; | |
| 348 | |
| 349 m = ((size_t)info->width) * info->height; | |
| 350 image = fz_new_pixmap(ctx, info->cs, info->width, info->height, seps, alpha); | |
| 351 q = image->samples; | |
| 352 if (bpc == 8) | |
| 353 { | |
| 354 if (n == 1) | |
| 355 { | |
| 356 while (m--) | |
| 357 { | |
| 358 *q++ = 255 - unpack8(&source); | |
| 359 } | |
| 360 } | |
| 361 else if (n - alpha == 3) | |
| 362 { | |
| 363 int N = n; | |
| 364 | |
| 365 while (N--) | |
| 366 { | |
| 367 size_t M = m; | |
| 368 while (M--) | |
| 369 { | |
| 370 *q = unpack8(&source); | |
| 371 q += n; | |
| 372 } | |
| 373 q -= m*n - 1; | |
| 374 } | |
| 375 } | |
| 376 else | |
| 377 { | |
| 378 int N = n - alpha; | |
| 379 | |
| 380 /* CMYK is inverted */ | |
| 381 while (N--) | |
| 382 { | |
| 383 size_t M = m; | |
| 384 while (M--) | |
| 385 { | |
| 386 *q = 255 - unpack8(&source); | |
| 387 q += n; | |
| 388 } | |
| 389 q -= m*n - 1; | |
| 390 } | |
| 391 | |
| 392 /* But alpha is not */ | |
| 393 if (alpha) | |
| 394 { | |
| 395 size_t M = m; | |
| 396 while (M--) | |
| 397 { | |
| 398 *q = unpack8(&source); | |
| 399 q += n; | |
| 400 } | |
| 401 q -= m*n - 1; | |
| 402 } | |
| 403 } | |
| 404 } | |
| 405 else | |
| 406 { | |
| 407 if (n == 1) | |
| 408 { | |
| 409 while (m--) | |
| 410 { | |
| 411 *q++ = 255 - unpack8(&source); | |
| 412 (void)unpack8(&source); | |
| 413 } | |
| 414 } | |
| 415 else if (n - alpha == 3) | |
| 416 { | |
| 417 int N = n; | |
| 418 | |
| 419 while (N--) | |
| 420 { | |
| 421 size_t M = m; | |
| 422 | |
| 423 while (M--) | |
| 424 { | |
| 425 *q = unpack8(&source); | |
| 426 (void)unpack8(&source); | |
| 427 q += n; | |
| 428 } | |
| 429 q -= m*n - 1; | |
| 430 } | |
| 431 } | |
| 432 else | |
| 433 { | |
| 434 int N = n - alpha; | |
| 435 | |
| 436 /* CMYK is inverted */ | |
| 437 while (N--) | |
| 438 { | |
| 439 size_t M = m; | |
| 440 | |
| 441 while (M--) | |
| 442 { | |
| 443 *q = 255 - unpack8(&source); | |
| 444 (void)unpack8(&source); | |
| 445 q += n; | |
| 446 } | |
| 447 q -= m*n - 1; | |
| 448 } | |
| 449 | |
| 450 /* But alpha is not */ | |
| 451 if (alpha) | |
| 452 { | |
| 453 size_t M = m; | |
| 454 | |
| 455 while (M--) | |
| 456 { | |
| 457 *q = unpack8(&source); | |
| 458 (void)unpack8(&source); | |
| 459 q += n; | |
| 460 } | |
| 461 q -= m*n - 1; | |
| 462 } | |
| 463 } | |
| 464 } | |
| 465 | |
| 466 if (alpha) | |
| 467 fz_premultiply_pixmap(ctx, image); | |
| 468 } | |
| 469 fz_always(ctx) | |
| 470 { | |
| 471 fz_drop_separations(ctx, seps); | |
| 472 } | |
| 473 fz_catch(ctx) | |
| 474 { | |
| 475 fz_drop_pixmap(ctx, image); | |
| 476 fz_drop_colorspace(ctx, info->cs); | |
| 477 fz_rethrow(ctx); | |
| 478 } | |
| 479 | |
| 480 return image; | |
| 481 } | |
| 482 | |
| 483 fz_pixmap * | |
| 484 fz_load_psd(fz_context *ctx, const unsigned char *p, size_t total) | |
| 485 { | |
| 486 fz_pixmap *image = NULL; | |
| 487 struct info psd; | |
| 488 | |
| 489 image = psd_read_image(ctx, &psd, p, total, 0); | |
| 490 | |
| 491 fz_drop_colorspace(ctx, psd.cs); | |
| 492 | |
| 493 return image; | |
| 494 } | |
| 495 | |
| 496 void | |
| 497 fz_load_psd_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep) | |
| 498 { | |
| 499 struct info psd; | |
| 500 | |
| 501 psd_read_image(ctx, &psd, p, total, 1); | |
| 502 | |
| 503 *cspacep = psd.cs; | |
| 504 *wp = psd.width; | |
| 505 *hp = psd.height; | |
| 506 *xresp = psd.xres; | |
| 507 *yresp = psd.xres; | |
| 508 } |
