Mercurial > hgrepos > Python2 > PyMuPDF
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/fitz/load-psd.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,508 @@ +// Copyright (C) 2004-2024 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" + +#include "pixmap-imp.h" + +#include <limits.h> +#include <string.h> + +struct info +{ + unsigned int width, height, n; + int xres, yres; + fz_colorspace *cs; +}; + +typedef struct +{ + fz_context *ctx; + const unsigned char *p; + size_t total; + int packbits; + int packbits_n; + int packbits_rep; +} source_t; + +static int +get8(source_t *source) +{ + if (source->total < 1) + fz_throw(source->ctx, FZ_ERROR_FORMAT, "Truncated PSD"); + source->total--; + + return *source->p++; +} + +static int +get16be(source_t *source) +{ + int v; + + if (source->total < 2) + { + source->total = 0; + fz_throw(source->ctx, FZ_ERROR_FORMAT, "Truncated PSD"); + } + + source->total -= 2; + + v = *source->p++; + v = (v<<8) | *source->p++; + + return v; +} + +static int +get32be(source_t *source) +{ + int v; + + if (source->total < 4) + { + source->total = 0; + fz_throw(source->ctx, FZ_ERROR_FORMAT, "Truncated PSD"); + } + + source->total -= 4; + + v = *source->p++; + v = (v<<8) | *source->p++; + v = (v<<8) | *source->p++; + v = (v<<8) | *source->p++; + + return v; +} + +static uint32_t +getu32be(source_t *source) +{ + return (uint32_t)get32be(source); +} + +static int +unpack8(source_t *source) +{ + int i; + + if (source->packbits == 0) + return get8(source); + + i = source->packbits_n; + if (i == 128) + { + do + { + i = source->packbits_n = get8(source); + } + while (i == 128); + if (i > 128) + source->packbits_rep = get8(source); + } + if (i < 128) + { + /* Literal n+1 */ + i--; + if (i < 0) + i = 128; + source->packbits_n = i; + return get8(source); + } + else + { + i++; + if (i == 257) + i = 128; + source->packbits_n = i; + return source->packbits_rep; + } +} + +static char *getString(source_t *source) +{ + size_t len = get8(source); + size_t odd = !(len & 1); + char *s; + + if (source->total < len + odd) + { + source->total = 0; + fz_throw(source->ctx, FZ_ERROR_FORMAT, "Truncated string in PSD"); + } + + s = fz_malloc(source->ctx, len+1); + memcpy(s, source->p, len); + s[len] = 0; + + source->p += len + odd; + source->total -= len + odd; + + return s; +} + +static fz_pixmap * +psd_read_image(fz_context *ctx, struct info *info, const unsigned char *p, size_t total, int only_metadata) +{ + int v, bpc, c, n; + source_t source; + size_t ir_len, data_len; + fz_separations *seps = NULL; + fz_pixmap *image = NULL; + size_t m; + unsigned char *q; + int alpha = 0; + + source.ctx = ctx; + source.p = p; + source.total = total; + source.packbits = 0; + + memset(info, 0, sizeof(*info)); + + fz_var(image); + fz_var(seps); + + fz_try(ctx) + { + info->xres = 96; + info->yres = 96; + + v = get32be(&source); + /* Read signature */ + if (v != 0x38425053 /* 8BPS */) + fz_throw(ctx, FZ_ERROR_FORMAT, "not a psd image (wrong signature)"); + + /* Version */ + v = get16be(&source); + if (v != 1) + fz_throw(ctx, FZ_ERROR_FORMAT, "Bad PSD version"); + + (void)get16be(&source); + (void)get32be(&source); + + info->n = n = get16be(&source); + info->height = getu32be(&source); + info->width = getu32be(&source); + bpc = get16be(&source); + if (bpc != 8 && bpc != 16) + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Only 8 or 16 bpc PSD files supported!"); + + c = get16be(&source); + if (c == 4) /* CMYK (+ Spots?) */ + { + if (n != 4) + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "CMYK PSD with %d chans not supported!", n); + info->cs = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); + } + else if (c == 3) /* RGB */ + { + if (n == 4) + alpha = 1; + else if (n != 3) + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "RGB PSD with %d chans not supported!", n); + info->cs = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); + } + else if (c == 1) /* Greyscale */ + { + if (n != 1) + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Greyscale PSD with %d chans not supported!", n); + info->cs = fz_keep_colorspace(ctx, fz_device_gray(ctx)); + } + else + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Unsupported PSD colorspace (%d)!", c); + + v = get32be(&source); + if (v != 0) + fz_throw(ctx, FZ_ERROR_FORMAT, "Unexpected color data in PSD!"); + + /* Now read image resources... */ + ir_len = getu32be(&source); + while (ir_len >= 12) + { + size_t start = source.p - p; + + v = get32be(&source); + if (v != 0x3842494d) /* 8BIM */ + fz_throw(ctx, FZ_ERROR_FORMAT, "Failed to find expected 8BIM in PSD"); + v = get16be(&source); + + fz_free(ctx, getString(&source)); + + data_len = getu32be(&source); + ir_len -= (source.p - p) - start; + switch (v) + { + case 0x3ef: /* Spot */ + { + int spots = 0; + int alpha_found = 0; + + while (data_len > 0) + { + int C, M, Y, K; + char text[32]; + + v = get16be(&source); + if (v == 0 && alpha_found == 0) + alpha_found = 1, alpha = 1; + else if (v != 2) + fz_throw(ctx, FZ_ERROR_FORMAT, "Non CMYK spot found in PSD"); + + C = 0xff - (get16be(&source)>>8); + M = 0xff - (get16be(&source)>>8); + Y = 0xff - (get16be(&source)>>8); + K = 0xff - (get16be(&source)>>8); + (void)get16be(&source); /* opacity */ + (void)get8(&source); /* kind */ + (void)get8(&source); /* padding */ + if (v == 2) + { + uint32_t cmyk = C | (M<<8) | (Y<<16) | (K<<24); + int R = fz_clampi(255-C-K, 0, 255); + int G = fz_clampi(255-M-K, 0, 255); + int B = fz_clampi(255-Y-K, 0, 255); + uint32_t rgba = R | (G<<8) | (B<<16); + if (seps == NULL) + seps = fz_new_separations(ctx, 1); + snprintf(text, sizeof(text), "s%d", spots); + /* Use the old entry-point until we fix the new one */ + fz_add_separation_equivalents(ctx, seps, rgba, cmyk, text); + spots++; + } + data_len -= 14; + ir_len -= 14; + } + } + } + /* Skip any unread data */ + if (data_len & 1) + data_len++; + ir_len -= data_len; + while (data_len--) + get8(&source); + } + if (fz_count_separations(ctx, seps) + info->cs->n + 1 == n && alpha == 0) + alpha = 1; + if (fz_count_separations(ctx, seps) + info->cs->n + alpha != n) + fz_throw(ctx, FZ_ERROR_FORMAT, "PSD contains mismatching spot/alpha data"); + + /* Skip over the Layer data. */ + v = get32be(&source); + if (v != 0) + { + if (source.total < (size_t)v) + fz_throw(ctx, FZ_ERROR_FORMAT, "Truncated PSD"); + source.total -= v; + source.p += v; + } + if (source.total == 0) + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Unflattened PSD not supported"); + + v = get16be(&source); + switch (v) + { + case 0: + /* No compression */ + break; + case 1: + /* Packbits */ + source.packbits = 1; + source.packbits_n = 128; + + /* Skip over rows * channels * byte counts. */ + m = ((size_t)info->height) * info->n * 2; + if (m > source.total) + fz_throw(ctx, FZ_ERROR_FORMAT, "Truncated RLE PSD"); + source.total -= m; + source.p += m; + break; + case 2: /* Deflate */ + case 3: /* Deflate with prediction */ + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Deflate PSD not supported"); + default: + fz_throw(ctx, FZ_ERROR_FORMAT, "Unexpected compression (%d) found in PSD", v); + } + + if (only_metadata) + break; + + m = ((size_t)info->width) * info->height; + image = fz_new_pixmap(ctx, info->cs, info->width, info->height, seps, alpha); + q = image->samples; + if (bpc == 8) + { + if (n == 1) + { + while (m--) + { + *q++ = 255 - unpack8(&source); + } + } + else if (n - alpha == 3) + { + int N = n; + + while (N--) + { + size_t M = m; + while (M--) + { + *q = unpack8(&source); + q += n; + } + q -= m*n - 1; + } + } + else + { + int N = n - alpha; + + /* CMYK is inverted */ + while (N--) + { + size_t M = m; + while (M--) + { + *q = 255 - unpack8(&source); + q += n; + } + q -= m*n - 1; + } + + /* But alpha is not */ + if (alpha) + { + size_t M = m; + while (M--) + { + *q = unpack8(&source); + q += n; + } + q -= m*n - 1; + } + } + } + else + { + if (n == 1) + { + while (m--) + { + *q++ = 255 - unpack8(&source); + (void)unpack8(&source); + } + } + else if (n - alpha == 3) + { + int N = n; + + while (N--) + { + size_t M = m; + + while (M--) + { + *q = unpack8(&source); + (void)unpack8(&source); + q += n; + } + q -= m*n - 1; + } + } + else + { + int N = n - alpha; + + /* CMYK is inverted */ + while (N--) + { + size_t M = m; + + while (M--) + { + *q = 255 - unpack8(&source); + (void)unpack8(&source); + q += n; + } + q -= m*n - 1; + } + + /* But alpha is not */ + if (alpha) + { + size_t M = m; + + while (M--) + { + *q = unpack8(&source); + (void)unpack8(&source); + q += n; + } + q -= m*n - 1; + } + } + } + + if (alpha) + fz_premultiply_pixmap(ctx, image); + } + fz_always(ctx) + { + fz_drop_separations(ctx, seps); + } + fz_catch(ctx) + { + fz_drop_pixmap(ctx, image); + fz_drop_colorspace(ctx, info->cs); + fz_rethrow(ctx); + } + + return image; +} + +fz_pixmap * +fz_load_psd(fz_context *ctx, const unsigned char *p, size_t total) +{ + fz_pixmap *image = NULL; + struct info psd; + + image = psd_read_image(ctx, &psd, p, total, 0); + + fz_drop_colorspace(ctx, psd.cs); + + return image; +} + +void +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) +{ + struct info psd; + + psd_read_image(ctx, &psd, p, total, 1); + + *cspacep = psd.cs; + *wp = psd.width; + *hp = psd.height; + *xresp = psd.xres; + *yresp = psd.xres; +}
