| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- // 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;
- }
|