#include "libtexpdf.h"
#define JPEG_DEBUG_STR "JPEG"
#define JPEG_DEBUG 3
#ifdef HAVE_LIBJPEG
#include <jpeglib.h>
#endif
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
{
unsigned short version;
unsigned char units;
unsigned short Xdensity;
unsigned short Ydensity;
unsigned char Xthumbnail;
unsigned char Ythumbnail;
unsigned char *thumbnail;
};
struct JPEG_APPn_ICC
{
unsigned char seq_id;
unsigned char num_chunks;
unsigned char *chunk;
unsigned short length;
};
struct JPEG_APPn_Adobe
{
unsigned short version;
unsigned short flag0;
unsigned short flag1;
unsigned char transform;
};
struct JPEG_APPn_XMP
{
unsigned char *packet;
unsigned short length;
};
struct JPEG_ext
{
JPEG_marker marker;
JPEG_APPn_sig app_sig;
void *app_data;
};
#define MAX_COUNT 1024
struct JPEG_info
{
unsigned short height;
unsigned short width;
unsigned char bits_per_component;
unsigned char num_components;
double xdpi;
double ydpi;
int flags;
int num_appn, max_appn;
struct JPEG_ext *appn;
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
texpdf_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 (!texpdf_check_for_jpeg(fp)) {
WARN("%s: Not a JPEG file?", JPEG_DEBUG_STR);
rewind(fp);
return -1;
}
texpdf_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;
}
stream = texpdf_new_stream (0);
stream_dict = texpdf_stream_dict(stream);
texpdf_add_dict(stream_dict,
texpdf_new_name("Filter"), texpdf_new_name("DCTDecode"));
if (texpdf_get_version() >= 4) {
if (j_info.flags & HAVE_APPn_XMP) {
pdf_obj *XMP_stream;
XMP_stream = JPEG_get_XMP(&j_info);
texpdf_add_dict(stream_dict,
texpdf_new_name("Metadata"), texpdf_ref_obj(XMP_stream));
texpdf_release_obj(XMP_stream);
}
}
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,
pdf_stream_dataptr(icc_stream),
pdf_stream_length (icc_stream));
if (cspc_id < 0)
colorspace = NULL;
else {
colorspace = texpdf_get_colorspace_reference(cspc_id);
intent = iccp_get_rendering_intent(pdf_stream_dataptr(icc_stream),
pdf_stream_length (icc_stream));
if (intent)
texpdf_add_dict(stream_dict, texpdf_new_name("Intent"), intent);
}
}
texpdf_release_obj(icc_stream);
}
}
if (!colorspace) {
switch (colortype) {
case PDF_COLORSPACE_TYPE_GRAY:
colorspace = texpdf_new_name("DeviceGray");
break;
case PDF_COLORSPACE_TYPE_RGB:
colorspace = texpdf_new_name("DeviceRGB");
break;
case PDF_COLORSPACE_TYPE_CMYK:
colorspace = texpdf_new_name("DeviceCMYK");
break;
}
}
texpdf_add_dict(stream_dict, texpdf_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 = texpdf_new_array();
for (i = 0; i < j_info.num_components; i++) {
texpdf_add_array(decode, texpdf_new_number(1.0));
texpdf_add_array(decode, texpdf_new_number(0.0));
}
texpdf_add_dict(stream_dict, texpdf_new_name("Decode"), decode);
}
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);
texpdf_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 (compat_mode) {
*xdensity = *ydensity = 72.0 / 100.0;
return;
}
*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 = texpdf_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;
} 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);
texpdf_release_obj(icc_stream);
icc_stream = NULL;
break;
}
texpdf_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;
XMP_stream = texpdf_new_stream(STREAM_COMPRESS);
stream_dict = texpdf_stream_dict(XMP_stream);
texpdf_add_dict(stream_dict,
texpdf_new_name("Type"), texpdf_new_name("Metadata"));
texpdf_add_dict(stream_dict,
texpdf_new_name("Subtype"), texpdf_new_name("XML"));
for (i = 0; i < j_info->num_appn; i++) {
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;
texpdf_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 long
read_exif_bytes (unsigned char **pp, int n, int endian)
{
long 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
static unsigned short
read_APP1_Exif (struct JPEG_info *j_info, FILE *fp, unsigned short length)
{
unsigned char *buffer = NEW(length, unsigned char);
unsigned char *p, *endptr;
unsigned char *tiff_header;
int endian;
int num_fields;
long value = 0, offset;
double xres = 72.0, yres = 72.0;
double res_unit = 1.0;
fread(buffer, length, 1, fp);
if (length < 10)
goto err;
p = buffer; endptr = buffer + length;
while ((p < endptr) && (*p == 0))
p++;
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 = read_exif_bytes(&p, 4, endian);
p = tiff_header + offset;
num_fields = read_exif_bytes(&p, 2, endian);
while (num_fields-- > 0 && p < endptr) {
int tag, type;
long count;
unsigned long 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);
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 long) read_exif_bytes(&vp, 4, endian);
den = (unsigned long) 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 long) read_exif_bytes(&vp, 4, endian);
den = (unsigned long) 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 ;
else if (value == 3)
res_unit = 2.54;
break;
default:
p += 4;
break;
}
}
if (num_fields > 0) {
WARN("%s: Truncated Exif data...", JPEG_DEBUG_STR);
goto err;
}
if (j_info->xdpi < 0.1 && j_info->ydpi < 0.1) {
j_info->xdpi = xres * res_unit;
j_info->ydpi = yres * res_unit;
} else {
if (j_info->xdpi != xres * res_unit ||
j_info->ydpi != yres * res_unit) {
WARN("%s: Inconsistent resolution may have " \
"specified in Exif and JFIF: %gx%g - %gx%g", JPEG_DEBUG_STR,
xres * res_unit, yres * res_unit, j_info->xdpi, j_info->ydpi);
}
}
err:
RELEASE(buffer);
return length;
}
static unsigned short
read_APP0_JFIF (struct JPEG_info *j_info, FILE *fp)
{
struct JPEG_APPn_JFIF *app_data;
unsigned short 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:
j_info->xdpi = app_data->Xdensity * 2.54;
j_info->ydpi = app_data->Ydensity * 2.54;
break;
default:
j_info->xdpi = 72.0;
j_info->ydpi = 72.0;
break;
}
return (9 + thumb_data_len);
}
static unsigned short
read_APP0_JFXX (FILE *fp, unsigned short length)
{
get_unsigned_byte(fp);
seek_relative(fp, length - 1);
return length;
}
static unsigned short
read_APP1_XMP (struct JPEG_info *j_info, FILE *fp, unsigned short 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 unsigned short
read_APP2_ICC (struct JPEG_info *j_info, FILE *fp, unsigned short length)
{
struct JPEG_APPn_ICC *app_data;
app_data = NEW(1, struct JPEG_APPn_ICC);
app_data->seq_id = get_unsigned_byte(fp);
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;
long 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) { \
size_t nb_read = fread(work_buffer, sizeof(char), MIN((l), WORK_BUFFER_SIZE), (f)); \
if (nb_read > 0) \
texpdf_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;
texpdf_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;
texpdf_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;
texpdf_add_stream(stream, work_buffer, 4);
COPY_CHUNK(fp, stream, length);
}
}
}
count++;
}
while ((length = fread(work_buffer,
sizeof(char), WORK_BUFFER_SIZE, fp)) > 0) {
texpdf_add_stream(stream, work_buffer, length);
}
return (found_SOFn ? 0 : -1);
}
#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)) {
long 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)) {
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);
}
}
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)) {
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);
}
}
}
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);
SET_SKIP(j_info, count);
}
}
seek_relative(fp, length);
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)) {
j_info->flags |= HAVE_APPn_ADOBE;
length -= read_APP14_Adobe(j_info, fp);
} 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
texpdf_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;
}