/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. Copyright (C) 2002-2020 by Jin-Hwan Cho and Shunsaku Hirata, the dvipdfmx project team. Copyright (C) 1998, 1999 by Mark A. Wicks This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifdef HAVE_CONFIG_H #include #endif /* * JPEG SUPPORT * * Accroding to Libjpeg document: * * CAUTION: it appears that Adobe Photoshop writes inverted data in CMYK * JPEG files: 0 represents 100% ink coverage, rather than 0% ink as you'd * expect.... * * To wrok with this problem, we must detect whether CMYK JPEG file is * created by Photoshop. But there are no reliable way to determine this. * * According to Adobe Technical Note #5516, * "Supporting the DCT Filters in PostScript Level 2", Section 18, p.27. * * DCTDecode ignores and skips any APPE marker segment does not begin with * the `Adobe' 5-character string. * * PDF Reference Manual 4th ed., p.61-62. * * The JPEG filter implementation in Adobe Acrobat products does not * support features of the JPEG standard that are irrelevant to images. * In addition, certain choices have been made regarding reserved marker * codes and other optional features of the standard. For details, see * Adobe Technical Note #5116, Supporting the DCT Filters in PostScript * Level 2. */ #include "system.h" #include "error.h" #include "mem.h" #include "mfileio.h" #include "numbers.h" #include "dpxconf.h" #include "pdfobj.h" #include "jpegimage.h" #include "pdfcolor.h" #include "pdfximage.h" #define JPEG_DEBUG_STR "JPEG" #define JPEG_DEBUG 3 #ifdef HAVE_LIBJPEG #include #endif /* HAVE_LIBJPEG */ /* JPEG Markers */ typedef enum { JM_SOF0 = 0xc0, JM_SOF1 = 0xc1, JM_SOF2 = 0xc2, JM_SOF3 = 0xc3, JM_SOF5 = 0xc5, JM_DHT = 0xc4, JM_SOF6 = 0xc6, JM_SOF7 = 0xc7, JM_SOF9 = 0xc9, JM_SOF10 = 0xca, JM_SOF11 = 0xcb, JM_DAC = 0xcc, JM_SOF13 = 0xcd, JM_SOF14 = 0xce, JM_SOF15 = 0xcf, JM_RST0 = 0xd0, JM_RST1 = 0xd1, JM_RST2 = 0xd2, JM_RST3 = 0xd3, JM_RST4 = 0xd4, JM_RST5 = 0xd5, JM_RST6 = 0xd6, JM_RST7 = 0xd7, JM_SOI = 0xd8, JM_EOI = 0xd9, JM_SOS = 0xda, JM_DQT = 0xdb, JM_DNL = 0xdc, JM_DRI = 0xdd, JM_DHP = 0xde, JM_EXP = 0xdf, JM_APP0 = 0xe0, JM_APP1 = 0xe1, JM_APP2 = 0xe2, JM_APP14 = 0xee, JM_APP15 = 0xef, JM_COM = 0xfe } JPEG_marker; typedef enum { JS_APPn_JFIF, JS_APPn_ADOBE, JS_APPn_ICC, JS_APPn_XMP } JPEG_APPn_sig; struct JPEG_APPn_JFIF /* APP0 */ { uint16_t version; uint8_t units; /* 0: only aspect ratio * 1: dots per inch * 2: dots per cm */ uint16_t Xdensity; uint16_t Ydensity; uint8_t Xthumbnail; uint8_t Ythumbnail; unsigned char *thumbnail; /* Thumbnail data. */ }; struct JPEG_APPn_ICC /* APP2 */ { uint8_t seq_id; uint8_t num_chunks; unsigned char *chunk; /* Length of ICC profile data in this chunk. */ size_t length; }; struct JPEG_APPn_Adobe /* APP14 */ { uint16_t version; uint16_t flag0; uint16_t flag1; uint8_t transform; /* color transform code */ }; struct JPEG_APPn_XMP /* APP1 */ { unsigned char *packet; /* XMP packet */ /* Length of XMP packet data */ size_t length; }; struct JPEG_ext { JPEG_marker marker; JPEG_APPn_sig app_sig; void *app_data; }; #define MAX_COUNT 1024 struct JPEG_info { uint16_t height; uint16_t width; uint8_t bits_per_component; uint8_t num_components; double xdpi; double ydpi; /* Application specific extensions */ int flags; int num_appn, max_appn; struct JPEG_ext *appn; /* Skip chunks not necessary. */ char skipbits[MAX_COUNT / 8 + 1]; }; #define HAVE_APPn_JFIF (1 << 0) #define HAVE_APPn_ADOBE (1 << 1) #define HAVE_APPn_ICC (1 << 2) #define HAVE_APPn_Exif (1 << 3) #define HAVE_APPn_XMP (1 << 4) static int JPEG_scan_file (struct JPEG_info *j_info, FILE *fp); static int JPEG_copy_stream (struct JPEG_info *j_info, pdf_obj *stream, FILE *fp); static void JPEG_info_init (struct JPEG_info *j_info); static void JPEG_info_clear (struct JPEG_info *j_info); static pdf_obj *JPEG_get_XMP (struct JPEG_info *j_info); static pdf_obj *JPEG_get_iccp (struct JPEG_info *j_info); static void jpeg_get_density (struct JPEG_info *j_info, double *xdensity, double *ydensity); int check_for_jpeg (FILE *fp) { unsigned char jpeg_sig[2]; rewind(fp); if (fread(jpeg_sig, sizeof(unsigned char), 2, fp) != 2) return 0; else if (jpeg_sig[0] != 0xff || jpeg_sig[1] != JM_SOI) return 0; return 1; } int jpeg_include_image (pdf_ximage *ximage, FILE *fp) { pdf_obj *stream; pdf_obj *stream_dict; pdf_obj *colorspace; int colortype; ximage_info info; struct JPEG_info j_info; if (!check_for_jpeg(fp)) { WARN("%s: Not a JPEG file?", JPEG_DEBUG_STR); rewind(fp); return -1; } /* File position is 2 here... */ pdf_ximage_init_image_info(&info); JPEG_info_init(&j_info); if (JPEG_scan_file(&j_info, fp) < 0) { WARN("%s: Not a JPEG file?", JPEG_DEBUG_STR); JPEG_info_clear(&j_info); return -1; } switch (j_info.num_components) { case 1: colortype = PDF_COLORSPACE_TYPE_GRAY; break; case 3: colortype = PDF_COLORSPACE_TYPE_RGB; break; case 4: colortype = PDF_COLORSPACE_TYPE_CMYK; break; default: WARN("%s: Unknown color space (num components: %d)", JPEG_DEBUG_STR, info.num_components); JPEG_info_clear(&j_info); return -1; } /* JPEG image use DCTDecode. */ stream = pdf_new_stream (0); stream_dict = pdf_stream_dict(stream); pdf_add_dict(stream_dict, pdf_new_name("Filter"), pdf_new_name("DCTDecode")); /* XMP Metadata */ if (pdf_check_version(1, 4) >= 0) { if (j_info.flags & HAVE_APPn_XMP) { pdf_obj *XMP_stream; XMP_stream = JPEG_get_XMP(&j_info); pdf_add_dict(stream_dict, pdf_new_name("Metadata"), pdf_ref_obj(XMP_stream)); pdf_release_obj(XMP_stream); } } /* Check embedded ICC Profile */ colorspace = NULL; if (j_info.flags & HAVE_APPn_ICC) { pdf_obj *icc_stream, *intent; int cspc_id; icc_stream = JPEG_get_iccp(&j_info); if (!icc_stream) colorspace = NULL; else { if (iccp_check_colorspace(colortype, pdf_stream_dataptr(icc_stream), pdf_stream_length (icc_stream)) < 0) colorspace = NULL; else { cspc_id = iccp_load_profile(NULL, /* noname */ pdf_stream_dataptr(icc_stream), pdf_stream_length (icc_stream)); if (cspc_id < 0) colorspace = NULL; else { colorspace = pdf_get_colorspace_reference(cspc_id); intent = iccp_get_rendering_intent(pdf_stream_dataptr(icc_stream), pdf_stream_length (icc_stream)); if (intent) pdf_add_dict(stream_dict, pdf_new_name("Intent"), intent); } } pdf_release_obj(icc_stream); } } /* No ICC or invalid ICC profile. */ if (!colorspace) { switch (colortype) { case PDF_COLORSPACE_TYPE_GRAY: colorspace = pdf_new_name("DeviceGray"); break; case PDF_COLORSPACE_TYPE_RGB: colorspace = pdf_new_name("DeviceRGB"); break; case PDF_COLORSPACE_TYPE_CMYK: colorspace = pdf_new_name("DeviceCMYK"); break; } } pdf_add_dict(stream_dict, pdf_new_name("ColorSpace"), colorspace); #define IS_ADOBE_CMYK(j) (((j).flags & HAVE_APPn_ADOBE) && (j).num_components == 4) if (IS_ADOBE_CMYK(j_info)) { pdf_obj *decode; int i; WARN("Adobe CMYK JPEG: Inverted color assumed."); decode = pdf_new_array(); for (i = 0; i < j_info.num_components; i++) { pdf_add_array(decode, pdf_new_number(1.0)); pdf_add_array(decode, pdf_new_number(0.0)); } pdf_add_dict(stream_dict, pdf_new_name("Decode"), decode); } /* Copy file */ JPEG_copy_stream(&j_info, stream, fp); info.width = j_info.width; info.height = j_info.height; info.bits_per_component = j_info.bits_per_component; info.num_components = j_info.num_components; jpeg_get_density(&j_info, &info.xdensity, &info.ydensity); pdf_ximage_set_image(ximage, &info, stream); JPEG_info_clear(&j_info); return 0; } static void jpeg_get_density (struct JPEG_info *j_info, double *xdensity, double *ydensity) { if (dpx_conf.compat_mode == dpx_mode_compat_mode) { *xdensity = *ydensity = 72.0 / 100.0; return; } /* * j_info->xdpi and j_info->ydpi are determined in most cases * in JPEG_scan_file(). FIXME: However, in some kinds of JPEG files, * j_info->xdpi, and j_info->ydpi are not determined in * JPEG_scan_file(). In this case we assume * that j_info->xdpi = j_info->ydpi = 72.0. */ if (j_info->xdpi < 0.1 && j_info->ydpi < 0.1) { j_info->xdpi = j_info->ydpi = 72.0; } *xdensity = 72.0 / j_info->xdpi; *ydensity = 72.0 / j_info->ydpi; return; } static void JPEG_info_init (struct JPEG_info *j_info) { j_info->width = 0; j_info->height = 0; j_info->bits_per_component = 0; j_info->num_components = 0; j_info->xdpi = 0.0; j_info->ydpi = 0.0; j_info->flags = 0; j_info->num_appn = 0; j_info->max_appn = 0; j_info->appn = NULL; memset(j_info->skipbits, 0, MAX_COUNT / 8 + 1); } static void JPEG_release_APPn_data (JPEG_marker marker, JPEG_APPn_sig app_sig, void *app_data) { if (marker == JM_APP0 && app_sig == JS_APPn_JFIF) { struct JPEG_APPn_JFIF *data; data = (struct JPEG_APPn_JFIF *) app_data; if (data->thumbnail) RELEASE(data->thumbnail); data->thumbnail = NULL; RELEASE(data); } else if (marker == JM_APP2 && app_sig == JS_APPn_ICC) { struct JPEG_APPn_ICC *data; data = (struct JPEG_APPn_ICC *) app_data; if (data->chunk) RELEASE(data->chunk); data->chunk = NULL; RELEASE(data); } else if (marker == JM_APP14 && app_sig == JS_APPn_ADOBE) { struct JPEG_APPn_Adobe *data; data = (struct JPEG_APPn_Adobe *) app_data; RELEASE(data); } else if (marker == JM_APP1 && app_sig == JS_APPn_XMP) { struct JPEG_APPn_XMP *data; data = (struct JPEG_APPn_XMP *) app_data; if (data->packet) RELEASE(data->packet); RELEASE(data); } } static void JPEG_info_clear (struct JPEG_info *j_info) { if (j_info->num_appn > 0 && j_info->appn != NULL) { int i; for (i = 0; i < j_info->num_appn; i++) JPEG_release_APPn_data(j_info->appn[i].marker, j_info->appn[i].app_sig, j_info->appn[i].app_data); RELEASE(j_info->appn); } j_info->appn = NULL; j_info->num_appn = 0; j_info->max_appn = 0; j_info->flags = 0; } static pdf_obj * JPEG_get_iccp (struct JPEG_info *j_info) { pdf_obj *icc_stream; struct JPEG_APPn_ICC *icc; int i, prev_id = 0, num_icc_seg = -1; icc_stream = pdf_new_stream(STREAM_COMPRESS); for (i = 0; i < j_info->num_appn; i++) { if (j_info->appn[i].marker != JM_APP2 || j_info->appn[i].app_sig != JS_APPn_ICC) continue; icc = (struct JPEG_APPn_ICC *) j_info->appn[i].app_data; if (num_icc_seg < 0 && prev_id == 0) { num_icc_seg = icc->num_chunks; /* ICC chunks are sorted? */ } else if (icc->seq_id != prev_id + 1 || num_icc_seg != icc->num_chunks || icc->seq_id > icc->num_chunks) { WARN("Invalid JPEG ICC chunk: %d (p:%d, n:%d)", icc->seq_id, prev_id, icc->num_chunks); pdf_release_obj(icc_stream); icc_stream = NULL; break; } pdf_add_stream(icc_stream, icc->chunk, icc->length); prev_id = icc->seq_id; num_icc_seg = icc->num_chunks; } return icc_stream; } static pdf_obj * JPEG_get_XMP (struct JPEG_info *j_info) { pdf_obj *XMP_stream, *stream_dict; struct JPEG_APPn_XMP *XMP; int i, count = 0; /* I don't know if XMP Metadata should be compressed here.*/ XMP_stream = pdf_new_stream(STREAM_COMPRESS); stream_dict = pdf_stream_dict(XMP_stream); pdf_add_dict(stream_dict, pdf_new_name("Type"), pdf_new_name("Metadata")); pdf_add_dict(stream_dict, pdf_new_name("Subtype"), pdf_new_name("XML")); for (i = 0; i < j_info->num_appn; i++) { /* Not sure for the case of multiple segments */ if (j_info->appn[i].marker != JM_APP1 || j_info->appn[i].app_sig != JS_APPn_XMP) continue; XMP = (struct JPEG_APPn_XMP *) j_info->appn[i].app_data; pdf_add_stream(XMP_stream, XMP->packet, XMP->length); count++; } if (count > 1) WARN("%s: Multiple XMP segments found in JPEG file. (untested)", JPEG_DEBUG_STR); return XMP_stream; } static JPEG_marker JPEG_get_marker (FILE *fp) { int c; c = fgetc(fp); if (c != 255) return -1; for (;;) { c = fgetc(fp); if (c < 0) return -1; else if (c > 0 && c < 255) { return c; } } } static int add_APPn_marker (struct JPEG_info *j_info, JPEG_marker marker, int app_sig, void *app_data) { int n; if (j_info->num_appn >= j_info->max_appn) { j_info->max_appn += 16; j_info->appn = RENEW(j_info->appn, j_info->max_appn, struct JPEG_ext); } n = j_info->num_appn; j_info->appn[n].marker = marker; j_info->appn[n].app_sig = app_sig; j_info->appn[n].app_data = app_data; j_info->num_appn += 1; return n; } static unsigned short read_APP14_Adobe (struct JPEG_info *j_info, FILE *fp) { struct JPEG_APPn_Adobe *app_data; app_data = NEW(1, struct JPEG_APPn_Adobe); app_data->version = get_unsigned_pair(fp); app_data->flag0 = get_unsigned_pair(fp); app_data->flag1 = get_unsigned_pair(fp); app_data->transform = get_unsigned_byte(fp); add_APPn_marker(j_info, JM_APP14, JS_APPn_ADOBE, app_data); return 7; } #define JPEG_EXIF_BIGENDIAN 0 #define JPEG_EXIF_LITTLEENDIAN 1 static int read_exif_bytes (unsigned char **pp, int n, int endian) { int rval = 0; unsigned char *p = *pp; int i; switch (endian) { case JPEG_EXIF_BIGENDIAN: for (i = 0; i < n; i++) { rval = (rval << 8) + p[i]; } break; case JPEG_EXIF_LITTLEENDIAN: for (i = n - 1; i >= 0; i--) { rval = (rval << 8) + p[i]; } break; } *pp += n; return rval; } #define JPEG_EXIF_TYPE_BYTE 1 #define JPEG_EXIF_TYPE_ASCII 2 #define JPEG_EXIF_TYPE_SHORT 3 #define JPEG_EXIF_TYPE_LONG 4 #define JPEG_EXIF_TYPE_RATIONAL 5 #define JPEG_EXIF_TYPE_UNDEFINED 7 #define JPEG_EXIF_TYPE_SLONG 9 #define JPEG_EXIF_TYPE_SRATIONAL 10 #define JPEG_EXIF_TAG_XRESOLUTION 282 #define JPEG_EXIF_TAG_YRESOLUTION 283 #define JPEG_EXIF_TAG_RESOLUTIONUNIT 296 #define JPEG_EXIF_TAG_RESUNIT_MS 0x5110 #define JPEG_EXIF_TAG_XRES_MS 0x5111 #define JPEG_EXIF_TAG_YRES_MS 0x5112 static size_t read_APP1_Exif (struct JPEG_info *j_info, FILE *fp, size_t length) { /* this doesn't save the data, just reads the tags we need */ /* based on info from http://www.exif.org/Exif2-2.PDF */ unsigned char *buffer = NEW(length, unsigned char); unsigned char *p, *endptr; unsigned char *tiff_header; int endian; int num_fields; int value = 0, offset; double xres = 0.0, yres = 0.0; double res_unit = 1.0; unsigned int xres_ms = 0; unsigned int yres_ms = 0; double res_unit_ms = 0.0; double exifxdpi = 0.0, exifydpi = 0.0; fread(buffer, length, 1, fp); if (length < 10) goto err; p = buffer; endptr = buffer + length; while ((p < endptr) && (*p == 0)) p++; if (p + 8 >= endptr) goto err; /* TIFF header */ tiff_header = p; if ((p[0] == 'M') && (p[1] == 'M')) endian = JPEG_EXIF_BIGENDIAN; else if ((p[0] == 'I') && (p[1] == 'I')) endian = JPEG_EXIF_LITTLEENDIAN; else { WARN("%s: Invalid value in Exif TIFF header.", JPEG_DEBUG_STR); goto err; } p += 2; value = read_exif_bytes(&p, 2, endian); if (value != 42) { WARN("%s: Invalid value in Exif TIFF header.", JPEG_DEBUG_STR); goto err; } /* Offset to 0th IFD */ offset = read_exif_bytes(&p, 4, endian); p = tiff_header + offset; if (p + 2 >= endptr) goto err; num_fields = read_exif_bytes(&p, 2, endian); while (num_fields-- > 0 && p < endptr) { int tag, type; int count; unsigned int den, num; if (p + 12 > endptr) { WARN("%s: Truncated Exif data...", JPEG_DEBUG_STR); goto err; } tag = (int) read_exif_bytes(&p, 2, endian); type = (int) read_exif_bytes(&p, 2, endian); count = read_exif_bytes(&p, 4, endian); /* Exif data is redundant... */ switch (tag) { case JPEG_EXIF_TAG_XRESOLUTION: if (type != JPEG_EXIF_TYPE_RATIONAL || count != 1) { WARN("%s: Invalid data for XResolution in Exif chunk.", JPEG_DEBUG_STR); goto err; } offset = read_exif_bytes(&p, 4, endian); if (tiff_header + offset + 8 > buffer + length) { WARN("%s: Invalid offset value in Exif data.", JPEG_DEBUG_STR); goto err; } else { unsigned char *vp = tiff_header + offset; num = (unsigned int) read_exif_bytes(&vp, 4, endian); den = (unsigned int) read_exif_bytes(&vp, 4, endian); } if (den > 0) xres = (double) num / den; break; case JPEG_EXIF_TAG_YRESOLUTION: if (type != JPEG_EXIF_TYPE_RATIONAL || count != 1) { WARN("%s: Invalid data for XResolution in Exif chunk.", JPEG_DEBUG_STR); goto err; } offset = read_exif_bytes(&p, 4, endian); if (tiff_header + offset + 8 > buffer + length) { WARN("%s: Invalid offset value in Exif data.", JPEG_DEBUG_STR); goto err; } else { unsigned char *vp = tiff_header + offset; num = (unsigned int) read_exif_bytes(&vp, 4, endian); den = (unsigned int) read_exif_bytes(&vp, 4, endian); } if (den > 0) yres = (double) num / den; break; case JPEG_EXIF_TAG_RESOLUTIONUNIT: if (type != JPEG_EXIF_TYPE_SHORT || count != 1) { WARN("%s: Invalid data for ResolutionUnit in Exif chunk.", JPEG_DEBUG_STR); goto err; } value = read_exif_bytes(&p, 2, endian); p += 2; if (value == 2) res_unit = 1.0 ; /* inch */ else if (value == 3) res_unit = 2.54; /* cm */ break; case JPEG_EXIF_TAG_RESUNIT_MS: /* PixelUnit */ if (type != JPEG_EXIF_TYPE_BYTE || count != 1) { WARN("%s: Invalid data for ResolutionUnit in Exif chunk.", JPEG_DEBUG_STR); goto err; } value = read_exif_bytes(&p, 1, endian); p += 3; if (value == 1) res_unit_ms = 0.0254; /* Unit is meter */ else res_unit_ms = 0.0; break; case JPEG_EXIF_TAG_XRES_MS: /* PixelPerUnitX */ if (type != JPEG_EXIF_TYPE_LONG || count != 1) { WARN("%s: Invalid data for PixelPerUnitX in Exif chunk.", JPEG_DEBUG_STR); goto err; } value = read_exif_bytes(&p, 4, endian); xres_ms = value; break; case JPEG_EXIF_TAG_YRES_MS: /* PixelPerUnitY */ if (type != JPEG_EXIF_TYPE_LONG || count != 1) { WARN("%s: Invalid data for PixelPerUnitY in Exif chunk.", JPEG_DEBUG_STR); goto err; } value = read_exif_bytes(&p, 4, endian); yres_ms = value; break; default: /* 40901 ColorSpace and 42240 Gamma unsupported... */ p += 4; break; } } if (num_fields > 0) { WARN("%s: Truncated Exif data...", JPEG_DEBUG_STR); goto err; } /* Calculate Exif resolution, if given. */ if (xres > 0.0 && yres > 0.0) { exifxdpi = xres * res_unit; exifydpi = yres * res_unit; } else if (xres_ms > 0 && yres_ms > 0 && res_unit_ms > 0.0) { exifxdpi = xres_ms * res_unit_ms; exifydpi = yres_ms * res_unit_ms; } else { exifxdpi = 72.0 * res_unit; exifydpi = 72.0 * res_unit; } /* Do not overwrite j_info->xdpi and j_info->ydpi if they are * already determined in JFIF. */ if (j_info->xdpi < 0.1 && j_info->ydpi < 0.1) { j_info->xdpi = exifxdpi; j_info->ydpi = exifydpi; } else { double xxx1 = floor(exifxdpi+0.5); double xxx2 = floor(j_info->xdpi+0.5); double yyy1 = floor(exifydpi+0.5); double yyy2 = floor(j_info->ydpi+0.5); if (xxx1 != xxx2 || yyy1 != yyy2) { WARN("%s: Inconsistent resolution may have " \ "specified in Exif and JFIF: %gx%g - %gx%g", JPEG_DEBUG_STR, xxx1, yyy1, xxx2, yyy2); } } err: RELEASE(buffer); return length; } static size_t read_APP0_JFIF (struct JPEG_info *j_info, FILE *fp) { struct JPEG_APPn_JFIF *app_data; size_t thumb_data_len; app_data = NEW(1, struct JPEG_APPn_JFIF); app_data->version = get_unsigned_pair(fp); app_data->units = get_unsigned_byte(fp); app_data->Xdensity = get_unsigned_pair(fp); app_data->Ydensity = get_unsigned_pair(fp); app_data->Xthumbnail = get_unsigned_byte(fp); app_data->Ythumbnail = get_unsigned_byte(fp); thumb_data_len = 3 * app_data->Xthumbnail * app_data->Ythumbnail; if (thumb_data_len > 0) { app_data->thumbnail = NEW(thumb_data_len, unsigned char); fread(app_data->thumbnail, 1, thumb_data_len, fp); } else { app_data->thumbnail = NULL; } add_APPn_marker(j_info, JM_APP0, JS_APPn_JFIF, app_data); switch (app_data->units) { case 1: j_info->xdpi = app_data->Xdensity; j_info->ydpi = app_data->Ydensity; break; case 2: /* density is in pixels per cm */ j_info->xdpi = app_data->Xdensity * 2.54; j_info->ydpi = app_data->Ydensity * 2.54; break; default: /* FIXME: not sure what to do with this.... */ j_info->xdpi = 72.0; j_info->ydpi = 72.0; break; } return (9 + thumb_data_len); } static size_t read_APP0_JFXX (FILE *fp, size_t length) { get_unsigned_byte(fp); /* Extension Code: * * 0x10: Thumbnail coded using JPEG * 0x11: Thumbnail stored using 1 byte/pixel * 0x13: Thumbnail stored using 3 bytes/pixel */ seek_relative(fp, length - 1); /* Thunbnail image */ /* Ignore */ return length; } static size_t read_APP1_XMP (struct JPEG_info *j_info, FILE *fp, size_t length) { struct JPEG_APPn_XMP *app_data; app_data = NEW(1, struct JPEG_APPn_XMP); app_data->length = length; app_data->packet = NEW(app_data->length, unsigned char); fread(app_data->packet, 1, app_data->length, fp); add_APPn_marker(j_info, JM_APP1, JS_APPn_XMP, app_data); return length; } static size_t read_APP2_ICC (struct JPEG_info *j_info, FILE *fp, size_t length) { struct JPEG_APPn_ICC *app_data; app_data = NEW(1, struct JPEG_APPn_ICC); app_data->seq_id = get_unsigned_byte(fp); /* Starting at 1 */ app_data->num_chunks = get_unsigned_byte(fp); app_data->length = length - 2; app_data->chunk = NEW(app_data->length, unsigned char); fread(app_data->chunk, 1, app_data->length, fp); add_APPn_marker(j_info, JM_APP2, JS_APPn_ICC, app_data); return length; } static int JPEG_copy_stream (struct JPEG_info *j_info, pdf_obj *stream, FILE *fp) { JPEG_marker marker; int length; int found_SOFn, count; #define SKIP_CHUNK(j,c) ((j)->skipbits[(c) / 8] & (1 << (7 - (c) % 8))) #define COPY_CHUNK(f,s,l) while ((l) > 0) { \ int nb_read = fread(work_buffer, sizeof(char), MIN((l), WORK_BUFFER_SIZE), (f)); \ if (nb_read > 0) \ pdf_add_stream((s), work_buffer, nb_read); \ (l) -= nb_read; \ } rewind(fp); count = 0; found_SOFn = 0; while (!found_SOFn && count < MAX_COUNT && (marker = JPEG_get_marker(fp)) != (JPEG_marker) - 1) { if ( marker == JM_SOI || (marker >= JM_RST0 && marker <= JM_RST7)) { work_buffer[0] = (char) 0xff; work_buffer[1] = (char) marker; pdf_add_stream(stream, work_buffer, 2); } else { length = get_unsigned_pair(fp) - 2; switch (marker) { case JM_SOF0: case JM_SOF1: case JM_SOF2: case JM_SOF3: case JM_SOF5: case JM_SOF6: case JM_SOF7: case JM_SOF9: case JM_SOF10: case JM_SOF11: case JM_SOF13: case JM_SOF14: case JM_SOF15: work_buffer[0] = (char) 0xff; work_buffer[1] = (char) marker; work_buffer[2] = ((length + 2) >> 8) & 0xff; work_buffer[3] = (length + 2) & 0xff; pdf_add_stream(stream, work_buffer, 4); COPY_CHUNK(fp, stream, length); found_SOFn = 1; break; default: if (SKIP_CHUNK(j_info, count)) { seek_relative(fp, length); } else { work_buffer[0] = (char) 0xff; work_buffer[1] = (char) marker; work_buffer[2] = ((length + 2) >> 8) & 0xff; work_buffer[3] = (length + 2) & 0xff; pdf_add_stream(stream, work_buffer, 4); COPY_CHUNK(fp, stream, length); } } } count++; } while ((length = fread(work_buffer, sizeof(char), WORK_BUFFER_SIZE, fp)) > 0) { pdf_add_stream(stream, work_buffer, length); } return (found_SOFn ? 0 : -1); } /* Adobe Technical Note #5116 "Supporting the DCT Filters in PostScript Level 2" * 6. "DCTDecode Filter Summary" * ... APPn (application-specific) markers are skipped over harmlessly except * for the Adobe reserved marker described later. * */ #define SET_SKIP(j,c) if ((c) < MAX_COUNT) { \ (j)->skipbits[(c) / 8] |= (1 << (7 - ((c) % 8))); \ } static int JPEG_scan_file (struct JPEG_info *j_info, FILE *fp) { JPEG_marker marker; int found_SOFn, count; char app_sig[128]; rewind(fp); count = 0; found_SOFn = 0; while (!found_SOFn && (marker = JPEG_get_marker(fp)) != (JPEG_marker) -1) { if ( marker != JM_SOI && (marker < JM_RST0 || marker > JM_RST7)) { int length = get_unsigned_pair(fp) - 2; switch (marker) { case JM_SOF0: case JM_SOF1: case JM_SOF2: case JM_SOF3: case JM_SOF5: case JM_SOF6: case JM_SOF7: case JM_SOF9: case JM_SOF10: case JM_SOF11: case JM_SOF13: case JM_SOF14: case JM_SOF15: j_info->bits_per_component = get_unsigned_byte(fp); j_info->height = get_unsigned_pair(fp); j_info->width = get_unsigned_pair(fp); j_info->num_components = get_unsigned_byte(fp); found_SOFn = 1; break; case JM_APP0: if (length > 5) { if (fread(app_sig, sizeof(char), 5, fp) != 5) return -1; length -= 5; if (!memcmp(app_sig, "JFIF\000", 5)) { /* APP0 JFIF marker preserved */ j_info->flags |= HAVE_APPn_JFIF; length -= read_APP0_JFIF(j_info, fp); } else if (!memcmp(app_sig, "JFXX", 5)) { length -= read_APP0_JFXX(fp, length); SET_SKIP(j_info, count); } else { SET_SKIP(j_info, count); } } else { SET_SKIP(j_info, count); } seek_relative(fp, length); break; case JM_APP1: if (length > 5) { if (fread(app_sig, sizeof(char), 5, fp) != 5) return -1; length -= 5; if (!memcmp(app_sig, "Exif\000", 5)) { /* APP1 Exif marker preserved */ j_info->flags |= HAVE_APPn_Exif; length -= read_APP1_Exif(j_info, fp, length); } else if (!memcmp(app_sig, "http:", 5) && length > 24) { if (fread(app_sig, sizeof(char), 24, fp) != 24) return -1; length -= 24; if (!memcmp(app_sig, "//ns.adobe.com/xap/1.0/\000", 24)) { j_info->flags |= HAVE_APPn_XMP; length -= read_APP1_XMP(j_info, fp, length); } SET_SKIP(j_info, count); } } else { SET_SKIP(j_info, count); } seek_relative(fp, length); break; case JM_APP2: if (length >= 14) { if (fread(app_sig, sizeof(char), 12, fp) != 12) return -1; length -= 12; if (!memcmp(app_sig, "ICC_PROFILE\000", 12)) { j_info->flags |= HAVE_APPn_ICC; length -= read_APP2_ICC(j_info, fp, length); } } seek_relative(fp, length); SET_SKIP(j_info, count); break; case JM_APP14: if (length > 5) { if (fread(app_sig, sizeof(char), 5, fp) != 5) return -1; length -= 5; if (!memcmp(app_sig, "Adobe", 5)) { /* APP14 Adobe marker preserved */ j_info->flags |= HAVE_APPn_ADOBE; length -= read_APP14_Adobe(j_info, fp); } else { SET_SKIP(j_info, count); } } else { SET_SKIP(j_info, count); } seek_relative(fp, length); break; default: seek_relative(fp, length); if (marker >= JM_APP0 && marker <= JM_APP15) { SET_SKIP(j_info, count); } break; } } count++; } return (found_SOFn ? 0 : -1); } int jpeg_get_bbox (FILE *fp, int *width, int *height, double *xdensity, double *ydensity) { struct JPEG_info j_info; JPEG_info_init(&j_info); if (JPEG_scan_file(&j_info, fp) < 0) { WARN("%s: Not a JPEG file?", JPEG_DEBUG_STR); JPEG_info_clear(&j_info); return -1; } *width = j_info.width; *height = j_info.height; jpeg_get_density(&j_info, xdensity, ydensity); JPEG_info_clear(&j_info); return 0; }