| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- // Copyright (C) 2004-2023 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 <math.h>
- #include <stdio.h>
- #include <string.h>
- #include <limits.h>
- #include <jpeglib.h>
- #ifdef SHARE_JPEG
- #define JZ_CTX_FROM_CINFO(c) (fz_context *)((c)->client_data)
- static void fz_jpg_mem_init(j_common_ptr cinfo, fz_context *ctx)
- {
- cinfo->client_data = ctx;
- }
- #define fz_jpg_mem_term(cinfo)
- #else /* SHARE_JPEG */
- typedef void * backing_store_ptr;
- #include "jmemcust.h"
- #define JZ_CTX_FROM_CINFO(c) (fz_context *)(GET_CUST_MEM_DATA(c)->priv)
- static void *
- fz_jpg_mem_alloc(j_common_ptr cinfo, size_t size)
- {
- fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo);
- return fz_malloc_no_throw(ctx, size);
- }
- static void
- fz_jpg_mem_free(j_common_ptr cinfo, void *object, size_t size)
- {
- fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo);
- fz_free(ctx, object);
- }
- static void
- fz_jpg_mem_init(j_common_ptr cinfo, fz_context *ctx)
- {
- jpeg_cust_mem_data *custmptr;
- custmptr = fz_malloc_struct(ctx, jpeg_cust_mem_data);
- if (!jpeg_cust_mem_init(custmptr, (void *) ctx, NULL, NULL, NULL,
- fz_jpg_mem_alloc, fz_jpg_mem_free,
- fz_jpg_mem_alloc, fz_jpg_mem_free, NULL))
- {
- fz_free(ctx, custmptr);
- fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot initialize custom JPEG memory handler");
- }
- cinfo->client_data = custmptr;
- }
- static void
- fz_jpg_mem_term(j_common_ptr cinfo)
- {
- if (cinfo->client_data)
- {
- fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo);
- fz_free(ctx, cinfo->client_data);
- cinfo->client_data = NULL;
- }
- }
- #endif /* SHARE_JPEG */
- static void output_message(j_common_ptr cinfo)
- {
- /* swallow message */
- }
- static void error_exit(j_common_ptr cinfo)
- {
- char msg[JMSG_LENGTH_MAX];
- fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo);
- cinfo->err->format_message(cinfo, msg);
- fz_throw(ctx, FZ_ERROR_LIBRARY, "jpeg error: %s", msg);
- }
- static void init_source(j_decompress_ptr cinfo)
- {
- /* nothing to do */
- }
- static void term_source(j_decompress_ptr cinfo)
- {
- /* nothing to do */
- }
- static boolean fill_input_buffer(j_decompress_ptr cinfo)
- {
- static unsigned char eoi[2] = { 0xFF, JPEG_EOI };
- struct jpeg_source_mgr *src = cinfo->src;
- src->next_input_byte = eoi;
- src->bytes_in_buffer = 2;
- return 1;
- }
- static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
- {
- struct jpeg_source_mgr *src = cinfo->src;
- if (num_bytes > 0)
- {
- size_t skip = (size_t)num_bytes; /* size_t may be 64bit */
- if (skip > src->bytes_in_buffer)
- skip = (size_t)src->bytes_in_buffer;
- src->next_input_byte += skip;
- src->bytes_in_buffer -= skip;
- }
- }
- static inline int read_value(const unsigned char *data, int bytes, int is_big_endian)
- {
- int value = 0;
- if (!is_big_endian)
- data += bytes;
- for (; bytes > 0; bytes--)
- value = (value << 8) | (is_big_endian ? *data++ : *--data);
- return value;
- }
- enum {
- MAX_ICC_PARTS = 256
- };
- static fz_colorspace *extract_icc_profile(fz_context *ctx, jpeg_saved_marker_ptr init_marker, int output_components, fz_colorspace *colorspace)
- {
- #if FZ_ENABLE_ICC
- const char idseq[] = { 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0'};
- jpeg_saved_marker_ptr marker = init_marker;
- fz_buffer *buf = NULL;
- fz_colorspace *icc;
- int part = 1;
- int parts = MAX_ICC_PARTS;
- const unsigned char *data;
- size_t size;
- fz_var(buf);
- if (init_marker == NULL)
- return colorspace;
- fz_try(ctx)
- {
- while (part < parts && marker != NULL)
- {
- for (marker = init_marker; marker != NULL; marker = marker->next)
- {
- if (marker->marker != JPEG_APP0 + 2)
- continue;
- if (marker->data_length < nelem(idseq) + 2)
- continue;
- if (memcmp(marker->data, idseq, nelem(idseq)))
- continue;
- if (marker->data[nelem(idseq)] != part)
- continue;
- if (parts == MAX_ICC_PARTS)
- parts = marker->data[nelem(idseq) + 1];
- else if (marker->data[nelem(idseq) + 1] != parts)
- fz_warn(ctx, "inconsistent number of icc profile chunks in jpeg");
- if (part > parts)
- {
- fz_warn(ctx, "skipping out of range icc profile chunk in jpeg");
- continue;
- }
- data = marker->data + 14;
- size = marker->data_length - 14;
- if (!buf)
- buf = fz_new_buffer_from_copied_data(ctx, data, size);
- else
- fz_append_data(ctx, buf, data, size);
- part++;
- break;
- }
- }
- if (buf)
- {
- icc = fz_new_icc_colorspace(ctx, fz_colorspace_type(ctx, colorspace), 0, NULL, buf);
- fz_drop_colorspace(ctx, colorspace);
- colorspace = icc;
- }
- }
- fz_always(ctx)
- fz_drop_buffer(ctx, buf);
- fz_catch(ctx)
- {
- fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
- fz_report_error(ctx);
- fz_warn(ctx, "ignoring embedded ICC profile in JPEG");
- }
- return colorspace;
- #else
- return colorspace;
- #endif
- }
- /* Returns true if <x> can be represented as an integer without overflow.
- *
- * We can't use comparisons such as 'return x < INT_MAX' because INT_MAX is
- * not safely convertible to float - it ends up as INT_MAX+1 so the comparison
- * doesn't do what we want.
- *
- * Instead we do a round-trip conversion and return true if this differs by
- * less than 1. This relies on high adjacent float values that differ by more
- * than 1, actually being exact integers, so the round-trip doesn't change the
- * value.
- */
- static int float_can_be_int(float x)
- {
- return fabsf(x - (float)(int) x) < 1;
- }
- static uint8_t exif_orientation_to_mupdf[9] = { 0, 1, 5, 3, 7, 6, 4, 8, 2 };
- static int extract_exif_resolution(jpeg_saved_marker_ptr marker,
- int *xres, int *yres, uint8_t *orientation)
- {
- int is_big_endian, orient;
- const unsigned char *data;
- unsigned int offset, ifd_len, res_type = 0;
- float x_res = 0, y_res = 0;
- if (!marker || marker->marker != JPEG_APP0 + 1 || marker->data_length < 14)
- return 0;
- data = (const unsigned char *)marker->data;
- if (read_value(data, 4, 1) != 0x45786966 /* Exif */ || read_value(data + 4, 2, 1) != 0x0000)
- return 0;
- if (read_value(data + 6, 4, 1) == 0x49492A00)
- is_big_endian = 0;
- else if (read_value(data + 6, 4, 1) == 0x4D4D002A)
- is_big_endian = 1;
- else
- return 0;
- offset = read_value(data + 10, 4, is_big_endian) + 6;
- if (offset < 14 || offset > marker->data_length - 2)
- return 0;
- ifd_len = read_value(data + offset, 2, is_big_endian);
- for (offset += 2; ifd_len > 0 && offset + 12 < marker->data_length; ifd_len--, offset += 12)
- {
- int tag = read_value(data + offset, 2, is_big_endian);
- int type = read_value(data + offset + 2, 2, is_big_endian);
- int count = read_value(data + offset + 4, 4, is_big_endian);
- unsigned int value_off = read_value(data + offset + 8, 4, is_big_endian) + 6;
- switch (tag)
- {
- case 0x112:
- if (type == 3 && count == 1) {
- orient = read_value(data + offset + 8, 2, is_big_endian);
- if (orient >= 1 && orient <= 8 && orientation)
- *orientation = exif_orientation_to_mupdf[orient];
- }
- break;
- case 0x11A:
- if (type == 5 && value_off > offset && value_off <= marker->data_length - 8)
- x_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian);
- break;
- case 0x11B:
- if (type == 5 && value_off > offset && value_off <= marker->data_length - 8)
- y_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian);
- break;
- case 0x128:
- if (type == 3 && count == 1)
- res_type = read_value(data + offset + 8, 2, is_big_endian);
- break;
- }
- }
- if (x_res <= 0 || !float_can_be_int(x_res) || y_res <= 0 || !float_can_be_int(y_res))
- return 0;
- if (res_type == 2)
- {
- *xres = (int)x_res;
- *yres = (int)y_res;
- }
- else if (res_type == 3)
- {
- *xres = (int)(x_res * 254 / 100);
- *yres = (int)(y_res * 254 / 100);
- }
- else
- {
- *xres = 0;
- *yres = 0;
- }
- return 1;
- }
- static int extract_app13_resolution(jpeg_saved_marker_ptr marker, int *xres, int *yres)
- {
- const unsigned char *data, *data_end;
- if (!marker || marker->marker != JPEG_APP0 + 13 || marker->data_length < 42 ||
- strcmp((const char *)marker->data, "Photoshop 3.0") != 0)
- {
- return 0;
- }
- data = (const unsigned char *)marker->data;
- data_end = data + marker->data_length;
- for (data += 14; data + 12 < data_end; ) {
- int data_size = -1;
- int tag = read_value(data + 4, 2, 1);
- int value_off = 11 + read_value(data + 6, 2, 1);
- if (value_off % 2 == 1)
- value_off++;
- if (read_value(data, 4, 1) == 0x3842494D /* 8BIM */ && value_off <= data_end - data)
- data_size = read_value(data + value_off - 4, 4, 1);
- if (data_size < 0 || data_size > data_end - data - value_off)
- return 0;
- if (tag == 0x3ED && data_size == 16)
- {
- *xres = read_value(data + value_off, 2, 1);
- *yres = read_value(data + value_off + 8, 2, 1);
- return 1;
- }
- if (data_size % 2 == 1)
- data_size++;
- data += value_off + data_size;
- }
- return 0;
- }
- static void invert_cmyk(unsigned char *p, int n)
- {
- int i;
- for (i = 0; i < n; ++i)
- p[i] = 255 - p[i];
- }
- fz_pixmap *
- fz_load_jpeg(fz_context *ctx, const unsigned char *rbuf, size_t rlen)
- {
- struct jpeg_decompress_struct cinfo;
- struct jpeg_error_mgr err;
- struct jpeg_source_mgr src;
- unsigned char *row[1], *sp, *dp;
- fz_colorspace *colorspace = NULL;
- unsigned int x;
- int k;
- size_t stride;
- fz_pixmap *image = NULL;
- fz_var(colorspace);
- fz_var(image);
- fz_var(row);
- row[0] = NULL;
- cinfo.mem = NULL;
- cinfo.global_state = 0;
- cinfo.err = jpeg_std_error(&err);
- err.output_message = output_message;
- err.error_exit = error_exit;
- cinfo.client_data = NULL;
- fz_jpg_mem_init((j_common_ptr)&cinfo, ctx);
- fz_try(ctx)
- {
- jpeg_create_decompress(&cinfo);
- cinfo.src = &src;
- src.init_source = init_source;
- src.fill_input_buffer = fill_input_buffer;
- src.skip_input_data = skip_input_data;
- src.resync_to_restart = jpeg_resync_to_restart;
- src.term_source = term_source;
- src.next_input_byte = rbuf;
- src.bytes_in_buffer = rlen;
- jpeg_save_markers(&cinfo, JPEG_APP0+1, 0xffff);
- jpeg_save_markers(&cinfo, JPEG_APP0+13, 0xffff);
- jpeg_save_markers(&cinfo, JPEG_APP0+2, 0xffff);
- jpeg_read_header(&cinfo, 1);
- jpeg_start_decompress(&cinfo);
- if (cinfo.output_components == 1)
- colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
- else if (cinfo.output_components == 3)
- colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
- else if (cinfo.output_components == 4)
- colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
- colorspace = extract_icc_profile(ctx, cinfo.marker_list, cinfo.output_components, colorspace);
- if (!colorspace)
- fz_throw(ctx, FZ_ERROR_FORMAT, "cannot determine colorspace");
- image = fz_new_pixmap(ctx, colorspace, cinfo.output_width, cinfo.output_height, NULL, 0);
- if (extract_exif_resolution(cinfo.marker_list, &image->xres, &image->yres, NULL))
- /* XPS prefers EXIF resolution to JFIF density */;
- else if (extract_app13_resolution(cinfo.marker_list, &image->xres, &image->yres))
- /* XPS prefers APP13 resolution to JFIF density */;
- else if (cinfo.density_unit == 1)
- {
- image->xres = cinfo.X_density;
- image->yres = cinfo.Y_density;
- }
- else if (cinfo.density_unit == 2)
- {
- image->xres = cinfo.X_density * 254 / 100;
- image->yres = cinfo.Y_density * 254 / 100;
- }
- if (image->xres <= 0) image->xres = 96;
- if (image->yres <= 0) image->yres = 96;
- fz_clear_pixmap(ctx, image);
- row[0] = fz_malloc(ctx, (size_t)cinfo.output_components * cinfo.output_width);
- dp = image->samples;
- stride = image->stride - image->w * (size_t)image->n;
- while (cinfo.output_scanline < cinfo.output_height)
- {
- jpeg_read_scanlines(&cinfo, row, 1);
- // Invert CMYK polarity for some CMYK images (see comment in filter-dct for details).
- if (cinfo.out_color_space == JCS_CMYK && cinfo.Adobe_transform == 2)
- invert_cmyk(row[0], image->stride);
- sp = row[0];
- for (x = 0; x < cinfo.output_width; x++)
- {
- for (k = 0; k < cinfo.output_components; k++)
- *dp++ = *sp++;
- }
- dp += stride;
- }
- }
- fz_always(ctx)
- {
- fz_drop_colorspace(ctx, colorspace);
- fz_free(ctx, row[0]);
- row[0] = NULL;
- /* We call jpeg_abort rather than the more usual
- * jpeg_finish_decompress here. This has the same effect,
- * but doesn't spew warnings if we didn't read enough data etc.
- * Annoyingly jpeg_abort can throw
- */
- fz_try(ctx)
- jpeg_abort((j_common_ptr)&cinfo);
- fz_catch(ctx)
- {
- /* Ignore any errors here */
- }
- jpeg_destroy_decompress(&cinfo);
- fz_jpg_mem_term((j_common_ptr)&cinfo);
- }
- fz_catch(ctx)
- {
- fz_drop_pixmap(ctx, image);
- fz_rethrow(ctx);
- }
- return image;
- }
- void
- fz_load_jpeg_info(fz_context *ctx, const unsigned char *rbuf, size_t rlen, int *xp, int *yp, int *xresp, int *yresp, fz_colorspace **cspacep, uint8_t *orientation)
- {
- struct jpeg_decompress_struct cinfo;
- struct jpeg_error_mgr err;
- struct jpeg_source_mgr src;
- fz_colorspace *icc = NULL;
- *cspacep = NULL;
- if (orientation)
- *orientation = 0;
- cinfo.mem = NULL;
- cinfo.global_state = 0;
- cinfo.err = jpeg_std_error(&err);
- err.error_exit = error_exit;
- cinfo.client_data = NULL;
- fz_jpg_mem_init((j_common_ptr)&cinfo, ctx);
- fz_try(ctx)
- {
- jpeg_create_decompress(&cinfo);
- cinfo.src = &src;
- src.init_source = init_source;
- src.fill_input_buffer = fill_input_buffer;
- src.skip_input_data = skip_input_data;
- src.resync_to_restart = jpeg_resync_to_restart;
- src.term_source = term_source;
- src.next_input_byte = rbuf;
- src.bytes_in_buffer = rlen;
- jpeg_save_markers(&cinfo, JPEG_APP0+1, 0xffff);
- jpeg_save_markers(&cinfo, JPEG_APP0+13, 0xffff);
- jpeg_save_markers(&cinfo, JPEG_APP0+2, 0xffff);
- jpeg_read_header(&cinfo, 1);
- *xp = cinfo.image_width;
- *yp = cinfo.image_height;
- if (cinfo.num_components == 1)
- *cspacep = fz_keep_colorspace(ctx, fz_device_gray(ctx));
- else if (cinfo.num_components == 3)
- *cspacep = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
- else if (cinfo.num_components == 4)
- *cspacep = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
- *cspacep = extract_icc_profile(ctx, cinfo.marker_list, cinfo.num_components, *cspacep);
- if (!*cspacep)
- fz_throw(ctx, FZ_ERROR_FORMAT, "cannot determine colorspace");
- if (extract_exif_resolution(cinfo.marker_list, xresp, yresp, orientation))
- /* XPS prefers EXIF resolution to JFIF density */;
- else if (extract_app13_resolution(cinfo.marker_list, xresp, yresp))
- /* XPS prefers APP13 resolution to JFIF density */;
- else if (cinfo.density_unit == 1)
- {
- *xresp = cinfo.X_density;
- *yresp = cinfo.Y_density;
- }
- else if (cinfo.density_unit == 2)
- {
- *xresp = cinfo.X_density * 254 / 100;
- *yresp = cinfo.Y_density * 254 / 100;
- }
- else
- {
- *xresp = 0;
- *yresp = 0;
- }
- if (*xresp <= 0) *xresp = 96;
- if (*yresp <= 0) *yresp = 96;
- }
- fz_always(ctx)
- {
- jpeg_destroy_decompress(&cinfo);
- fz_jpg_mem_term((j_common_ptr)&cinfo);
- }
- fz_catch(ctx)
- {
- fz_drop_colorspace(ctx, icc);
- fz_rethrow(ctx);
- }
- }
|