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 }