/* $Header: /home/cvsroot/dvipdfmx/src/jpegimage.c,v 1.9 2007/05/18 05:19:01 chofchof Exp $ This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. Copyright (C) 2002 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. */ #if HAVE_CONFIG_H #include "config.h" #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 "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 } JPEG_APPn_sig; struct JPEG_APPn_JFIF /* APP0 */ { unsigned short version; unsigned char units; /* 0: only aspect ratio * 1: dots per inch * 2: dots per cm */ unsigned short Xdensity; unsigned short Ydensity; unsigned char Xthumbnail; unsigned char Ythumbnail; unsigned char *thumbnail; /* Thumbnail data. */ }; struct JPEG_APPn_ICC /* APP2 */ { unsigned char seq_id; unsigned char num_chunks; unsigned char *chunk; /* Length of ICC profile data in this chunk. */ unsigned short length; }; struct JPEG_APPn_Adobe /* APP14 */ { unsigned short version; unsigned short flag0; unsigned short flag1; unsigned char transform; /* color transform code */ }; 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; /* 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) 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, int flags); /* flags unused yet */ 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_iccp (struct JPEG_info *j_info); 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")); colorspace = NULL; if (j_info.flags & HAVE_APPn_ICC) { pdf_obj *icc_stream, *intent; icc_stream = JPEG_get_iccp(&j_info); if (!icc_stream) colorspace = NULL; else { int cspc_id; 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, 0); 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; #define IS_JFIF(j) ((j).flags & HAVE_APPn_JFIF) if (IS_JFIF(j_info)) { int i; for (i = 0; i < j_info.num_appn; i++) { if (j_info.appn[i].marker == JM_APP0 && j_info.appn[i].app_sig == JS_APPn_JFIF) break; } if (i < j_info.num_appn) { struct JPEG_APPn_JFIF *app_data = (struct JPEG_APPn_JFIF *)j_info.appn[i].app_data; switch (app_data->units) { case 1: /* pixels per inch */ info.xdensity = 72.0 / app_data->Xdensity; info.ydensity = 72.0 / app_data->Ydensity; break; case 2: /* pixels per centimeter */ info.xdensity = 72.0 / 2.54 / app_data->Xdensity; info.ydensity = 72.0 / 2.54 / app_data->Ydensity; break; default: break; } } } else { /* resolution data from EXIF is handled here */ info.xdensity = 72.0 / j_info.xdpi; info.ydensity = 72.0 / j_info.ydpi; } pdf_ximage_set_image(ximage, &info, stream); JPEG_info_clear(&j_info); return 0; } 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; j_info->ydpi = 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); } } 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 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; } } return -1; } 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, unsigned short length) { 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; } static unsigned long read_exif_bytes(unsigned char **p, int n, int b) { unsigned long rval = 0; unsigned char *pp = *p; if (b) { switch (n) { case 4: rval += *pp++; rval <<= 8; rval += *pp++; rval <<= 8; case 2: rval += *pp++; rval <<= 8; rval += *pp; break; } } else { pp += n; switch (n) { case 4: rval += *--pp; rval <<= 8; rval += *--pp; rval <<= 8; case 2: rval += *--pp; rval <<= 8; rval += *--pp; break; } } *p += n; return rval; } static unsigned short read_APP1_Exif (struct JPEG_info *j_info, FILE *fp, unsigned short 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, *rp; unsigned char *tiff_header; char bigendian; int i; double rational_value; int num_fields, tag, type, count, value, num, den; double xres = 72.0; double yres = 72.0; double res_unit = 1.0; fread(buffer, length, 1, fp); p = buffer; while ((p < buffer + length) && (*p == 0)) ++p; tiff_header = p; if ((*p == 'M') && (*(p+1) == 'M')) bigendian = 1; else if ((*p == 'I') && (*(p+1) == 'I')) bigendian = 0; else goto err; p += 2; i = read_exif_bytes(&p, 2, bigendian); if (i != 42) goto err; i = read_exif_bytes(&p, 4, bigendian); p = tiff_header + i; num_fields = read_exif_bytes(&p, 2, bigendian); while (num_fields-- > 0) { tag = read_exif_bytes(&p, 2, bigendian); type = read_exif_bytes(&p, 2, bigendian); count = read_exif_bytes(&p, 4, bigendian); switch (type) { case 1: /* byte */ value = *p++; p += 3; break; case 3: /* short */ value = read_exif_bytes(&p, 2, bigendian); p += 2; break; case 4: /* long */ case 9: /* slong */ value = read_exif_bytes(&p, 4, bigendian); break; case 5: /* rational */ case 10: /* srational */ value = read_exif_bytes(&p, 4, bigendian); rp = tiff_header + value; num = read_exif_bytes(&rp, 4, bigendian); den = read_exif_bytes(&rp, 4, bigendian); break; case 7: /* undefined */ value = *p++; p += 3; break; case 2: /* ascii */ default: p += 4; break; } switch (tag) { case 282: /* x res */ if (den != 0) xres = num / den; break; case 283: /* y res */ if (den != 0) yres = num / den; break; case 296: /* res unit */ switch (value) { case 2: res_unit = 1.0; break; case 3: res_unit = 2.54; break; } } } j_info->xdpi = xres * res_unit; j_info->ydpi = yres * res_unit; err: RELEASE(buffer); return length; } static unsigned short read_APP0_JFIF (struct JPEG_info *j_info, FILE *fp, unsigned short length) { 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: /* 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 * app_data->Ydensity / app_data->Xdensity; break; } return (9 + thumb_data_len); } static unsigned short read_APP0_JFXX (struct JPEG_info *j_info, FILE *fp, unsigned short length) { unsigned char extension_code; extension_code = 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 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); /* 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, int flags) /* flags unused yet */ { JPEG_marker marker; long length, nb_read; int found_SOFn, count; rewind(fp); count = 0; found_SOFn = 0; while (!found_SOFn && count < MAX_COUNT && (marker = JPEG_get_marker(fp)) >= 0) { 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); count++; continue; } 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); while (length > 0) { nb_read = fread(work_buffer, sizeof(char), MIN(length, WORK_BUFFER_SIZE), fp); if (nb_read > 0) pdf_add_stream(stream, work_buffer, nb_read); length -= nb_read; } found_SOFn = 1; break; default: if (j_info->skipbits[count / 8] & (1 << (7 - (count % 8)))) { /* skip */ while (length > 0) { nb_read = fread(work_buffer, sizeof(char), MIN(length, WORK_BUFFER_SIZE), fp); length -= nb_read; } } 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); while (length > 0) { nb_read = fread(work_buffer, sizeof(char), MIN(length, WORK_BUFFER_SIZE), fp); if (nb_read > 0) pdf_add_stream(stream, work_buffer, nb_read); length -= nb_read; } } } 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); } static int JPEG_scan_file (struct JPEG_info *j_info, FILE *fp) { JPEG_marker marker; unsigned short length; int found_SOFn, count; char app_sig[128]; rewind(fp); count = 0; found_SOFn = 0; while (!found_SOFn && (marker = JPEG_get_marker(fp)) >= 0) { if (marker == JM_SOI || (marker >= JM_RST0 && marker <= JM_RST7)) { count++; continue; } 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, length); } else if (!memcmp(app_sig, "JFXX", 5)) { length -= read_APP0_JFXX(j_info, 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)) { length -= read_APP1_Exif(j_info, fp, length); } } 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); if (count < MAX_COUNT) { j_info->skipbits[count / 8] |= (1 << (7 - (count % 8))); } } } 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, length); } else { if (count < MAX_COUNT) { j_info->skipbits[count/8] |= (1 << (7 - (count % 8))); } } } seek_relative(fp, length); break; default: seek_relative(fp, length); if (marker >= JM_APP0 && marker <= JM_APP15) { if (count < MAX_COUNT) { j_info->skipbits[count / 8] |= (1 << (7 - (count % 8))); } } break; } count++; } return (found_SOFn ? 0 : -1); }