/* analyze JPEG marker */ BOOL AnalyzeJPEG P1(imagedata *, image) { int b, c, unit; unsigned long i, length = 0; #define APP_MAX 255 unsigned char appstring[APP_MAX]; BOOL SOF_done = FALSE; /* Tommy's special trick for Macintosh JPEGs: simply skip some */ /* hundred bytes at the beginning of the file! */ do { do { /* skip if not FF */ c = getc(image->fp); } while (!feof(image->fp) && c != 0xFF); do { /* skip repeated FFs */ c = getc(image->fp); } while (c == 0xFF); /* remember start position */ if ((image->startpos = ftell(image->fp)) < 0L) { fprintf(stderr, "Error: internal error in ftell()!\n"); return FALSE; } image->startpos -= 2; /* subtract marker length */ if (c == M_SOI) { fseek(image->fp, image->startpos, SEEK_SET); break; } } while (!feof(image->fp)); if (feof(image->fp)) { fprintf(stderr, "Error: SOI marker not found!\n"); return FALSE; } if (image->startpos > 0L && !quiet) { fprintf(stderr, "Note: skipped %ld bytes ", image->startpos); fprintf(stderr, "Probably Macintosh JPEG file?\n"); } /* process JPEG markers */ while (!SOF_done && (c = next_marker(image->fp)) != M_EOI) { switch (c) { case M_ERROR: fprintf(stderr, "Error: unexpected end of JPEG file!\n"); return FALSE; /* The following are not officially supported in PostScript level 2 */ case M_SOF2: case M_SOF3: case M_SOF5: case M_SOF6: case M_SOF7: case M_SOF9: case M_SOF10: case M_SOF11: case M_SOF13: case M_SOF14: case M_SOF15: fprintf(stderr, "Warning: JPEG file uses compression method %X - proceeding anyway.\n", c); fprintf(stderr, "PostScript output does not work on all PS interpreters!\n"); /* FALLTHROUGH */ case M_SOF0: case M_SOF1: length = get_2bytes(image->fp); /* read segment length */ image->bits_per_component = getc(image->fp); image->height = get_2bytes(image->fp); image->width = get_2bytes(image->fp); image->components = getc(image->fp); SOF_done = TRUE; break; case M_APP0: /* check for JFIF marker with resolution */ length = get_2bytes(image->fp); for (i = 0; i < length-2; i++) { /* get contents of marker */ b = getc(image->fp); if (i < APP_MAX) /* store marker in appstring */ appstring[i] = b; } /* Check for JFIF application marker and read density values * per JFIF spec version 1.02. * We only check X resolution, assuming X and Y resolution are equal. * Use values only if resolution not preset by user or to be ignored. */ #define ASPECT_RATIO 0 /* JFIF unit byte: aspect ratio only */ #define DOTS_PER_INCH 1 /* JFIF unit byte: dots per inch */ #define DOTS_PER_CM 2 /* JFIF unit byte: dots per cm */ if (image->dpi == DPI_USE_FILE && length >= 14 && !strncmp((const char *)appstring, "JFIF", 4)) { unit = appstring[7]; /* resolution unit */ /* resolution value */ image->dpi = (float) ((appstring[8]<<8) + appstring[9]); if (image->dpi == 0.0) { image->dpi = DPI_USE_FILE; break; } switch (unit) { /* tell the caller we didn't find a resolution value */ case ASPECT_RATIO: image->dpi = DPI_USE_FILE; break; case DOTS_PER_INCH: break; case DOTS_PER_CM: image->dpi *= (float) 2.54; break; default: /* unknown ==> ignore */ fprintf(stderr, "Warning: JPEG file contains unknown JFIF resolution unit - ignored!\n"); image->dpi = DPI_IGNORE; break; } } break; case M_APP14: /* check for Adobe marker */ length = get_2bytes(image->fp); for (i = 0; i < length-2; i++) { /* get contents of marker */ b = getc(image->fp); if (i < APP_MAX) /* store marker in appstring */ appstring[i] = b; } /* Check for Adobe application marker. It is known (per Adobe's TN5116) * to contain the string "Adobe" at the start of the APP14 marker. */ if (length >= 12 && !strncmp((const char *) appstring, "Adobe", 5)) image->adobe = TRUE; /* set Adobe flag */ break; case M_SOI: /* ignore markers without parameters */ case M_EOI: case M_TEM: case M_RST0: case M_RST1: case M_RST2: case M_RST3: case M_RST4: case M_RST5: case M_RST6: case M_RST7: break; default: /* skip variable length markers */ length = get_2bytes(image->fp); for (length -= 2; length > 0; length--) (void) getc(image->fp); break; } } /* do some sanity checks with the parameters */ if (image->height <= 0 || image->width <= 0 || image->components <= 0) { fprintf(stderr, "Error: DNL marker not supported in PostScript Level 2!\n"); return FALSE; } /* some broken JPEG files have this but they print anyway... */ if (length != (unsigned int) (image->components * 3 + 8)) fprintf(stderr, "Warning: SOF marker has incorrect length - ignored!\n"); if (image->bits_per_component != 8) { fprintf(stderr, "Error: %d bits per color component ", image->bits_per_component); fprintf(stderr, "not supported in PostScript level 2!\n"); return FALSE; } if (image->components!=1 && image->components!=3 && image->components!=4) { fprintf(stderr, "Error: unknown color space (%d components)!\n", image->components); return FALSE; } return TRUE; }
/* open JPEG image and analyze marker */ int pdf_open_JPEG_data( PDF *p, int imageslot, const char *filename, const char *stringparam, int intparam) { static const char fn[] = "pdf_open_JPEG_data"; int b, c, unit; unsigned long i, length; #define APP_MAX 255 unsigned char appstring[APP_MAX]; pdf_byte *app13; pdf_byte *s; pdf_image *image; int mask = -1; pdf_bool adobeflag = pdf_false; pdf_bool SOF_done = pdf_false; pdf_bool colorize = pdf_false; image = &p->images[imageslot]; if ((image->fp = fopen(filename, READMODE)) == NULL) { if (p->debug['i']) pdf_error(p, PDF_NonfatalError, "Couldn't open JPEG file '%s'", filename); return -1; /* Couldn't open JPEG file */ } image->compression = dct; image->use_raw = pdf_true; image->src.init = pdf_data_source_JPEG_init; image->src.fill = pdf_data_source_JPEG_fill; image->src.terminate = pdf_data_source_JPEG_terminate; image->src.private_data = (void *) image; /* Tommy's special trick for Macintosh JPEGs: simply skip some */ /* hundred bytes at the beginning of the file! */ do { do { /* skip if not FF */ c = getc(image->fp); } while (!feof(image->fp) && c != 0xFF); if (feof(image->fp)) { fclose(image->fp); if (p->debug['i']) pdf_error(p, PDF_NonfatalError, "File problem with JPEG file '%s'", filename); return -1; } do { /* skip repeated FFs */ c = getc(image->fp); } while (c == 0xFF); /* remember start position */ if ((image->info.jpeg.startpos = ftell(image->fp)) < 0L) { fclose(image->fp); if (p->debug['i']) pdf_error(p, PDF_NonfatalError, "File problem with JPEG file '%s'", filename); return -1; } image->info.jpeg.startpos -= 2; /* subtract marker length */ if (c == M_SOI) { fseek(image->fp, image->info.jpeg.startpos, SEEK_SET); break; } } while (!feof(image->fp)); #define BOGUS_LENGTH 768 /* Heuristics: if we are that far from the start chances are * it is a TIFF file with embedded JPEG data which we cannot * handle - regard as hopeless... */ if (feof(image->fp) || image->info.jpeg.startpos > BOGUS_LENGTH) { fclose(image->fp); if (p->debug['i']) pdf_error(p, PDF_NonfatalError, "File '%s' doesn't appear to be of type JPEG", filename); return -1; } /* process JPEG markers */ while (!SOF_done && (c = pdf_next_jpeg_marker(image->fp)) != M_EOI) { switch (c) { case M_ERROR: /* The following are not supported in PDF 1.3 */ case M_SOF3: case M_SOF5: case M_SOF6: case M_SOF7: case M_SOF9: case M_SOF11: case M_SOF13: case M_SOF14: case M_SOF15: fclose(image->fp); if (p->debug['i']) pdf_error(p, PDF_NonfatalError, "JPEG compression scheme '%d' in file '%s' is not supported in PDF 1.3", (int) c, filename); return -1; /* * SOF2 and SOF10 are progressive DCT which are not * supported prior to Acrobat 4. */ case M_SOF2: case M_SOF10: if (p->compatibility == PDF_1_2) { fclose(image->fp); if (p->debug['i']) pdf_error(p, PDF_NonfatalError, "Progressive JPEG images are not supported in PDF 1.2"); return -1; } /* fallthrough */ case M_SOF0: case M_SOF1: (void) get_2bytes(image->fp); /* read segment length */ image->bpc = getc(image->fp); image->height = (float) get_2bytes(image->fp); image->width = (float) get_2bytes(image->fp); image->components = getc(image->fp); SOF_done = pdf_true; break; case M_APP0: /* check for JFIF marker with resolution */ length = get_2bytes(image->fp); for (i = 0; i < length-2; i++) { /* get contents of marker */ b = getc(image->fp); if (i < APP_MAX) /* store marker in appstring */ appstring[i] = (unsigned char) b; } /* Check for JFIF application marker and read density values * per JFIF spec version 1.02. */ #define JFIF_ASPECT_RATIO 0 /* JFIF unit byte: aspect ratio only */ #define JFIF_DOTS_PER_INCH 1 /* JFIF unit byte: dots per inch */ #define JFIF_DOTS_PER_CM 2 /* JFIF unit byte: dots per cm */ #define PDF_STRING_JFIF "\112\106\111\106" if (length >= 14 && !strncmp(PDF_STRING_JFIF, (char *) appstring, 4)) { unit = appstring[7]; /* resolution unit */ /* resolution value */ image->dpi_x = (float) ((appstring[8]<<8) + appstring[9]); image->dpi_y = (float) ((appstring[10]<<8) + appstring[11]); if (image->dpi_x <= (float) 0.0 || image->dpi_y <= (float) 0.0) { image->dpi_x = (float) 0.0; image->dpi_y = (float) 0.0; break; } switch (unit) { case JFIF_DOTS_PER_INCH: break; case JFIF_DOTS_PER_CM: image->dpi_x *= (float) 2.54; image->dpi_y *= (float) 2.54; break; case JFIF_ASPECT_RATIO: image->dpi_x *= -1; image->dpi_y *= -1; break; default: /* unknown ==> ignore */ /* */ ; } } break; #ifdef NYI /* LATER: read SPIFF marker */ case M_APP8: /* check for SPIFF marker */ break; #endif case M_APP13: /* check for Photoshop marker */ length = get_2bytes(image->fp); /* get marker contents */ length -= 2; /* account for two length bytes */ app13 = p->malloc(p, length, fn); if (fread(app13, 1, length, image->fp) != length) { fclose(image->fp); p->free(p, app13); if (p->debug['i']) { pdf_error(p, PDF_NonfatalError, "JPEG file '%s' is damaged (Photoshop marker too short)", filename); } return -1; } #define PS_HEADER_LEN 14 #define PDF_STRING_Photoshop "\120\150\157\164\157\163\150\157\160" #define PDF_STRING_8BIM "\070\102\111\115" #define RESOLUTION_INFO_ID 0x03ED /* resolution info resource block */ #define PS_FIXED_TO_FLOAT(h, l) ((float) (h) + ((float) (l)/(1<<16))) /* Not a valid Photoshop marker */ if (length < 9 || strncmp(PDF_STRING_Photoshop, (char *) app13, 9)) { p->free(p, app13); break; } /* walk all image resource blocks and look for ResolutionInfo */ for (s = app13 + PS_HEADER_LEN; s < app13 + length; /* */) { long len; unsigned int type; if (strncmp((char *) s, PDF_STRING_8BIM, 4)) break; /* out of sync */ s += 4; /* identifying string */ type = (unsigned int) ((s[0]<<8) + s[1]); s += 2; /* resource type */ s += *s + ((*s & 1) ? 1 : 2); /* resource name */ len = (((((s[0]<<8) + s[1])<<8) + s[2])<<8) + s[3]; s += 4; /* Size */ if (type == RESOLUTION_INFO_ID && len >= 16) { image->dpi_x = PS_FIXED_TO_FLOAT((s[0]<<8) + s[1], (s[2]<<8) + s[3]); image->dpi_y = PS_FIXED_TO_FLOAT((s[8]<<8) + s[9], (s[10]<<8) + s[11]); break; } s += len + ((len & 1) ? 1 : 0); /* Data */ } p->free(p, app13); break; case M_APP14: /* check for Adobe marker */ length = get_2bytes(image->fp); for (i = 0; i < length-2; i++) { /* get contents of marker */ b = getc(image->fp); if (i < APP_MAX) /* store marker in appstring */ appstring[i] = (unsigned char) b; else break; } /* * Check for Adobe application marker. It is known (per Adobe's TN5116) * to contain the string "Adobe" at the start of the APP14 marker. */ #define PDF_STRING_Adobe "\101\144\157\142\145" if (length >= 12 && !strncmp(PDF_STRING_Adobe, (char *) appstring, 5)) adobeflag = pdf_true; /* set Adobe flag */ break; case M_SOI: /* ignore markers without parameters */ case M_EOI: case M_TEM: case M_RST0: case M_RST1: case M_RST2: case M_RST3: case M_RST4: case M_RST5: case M_RST6: case M_RST7: break; default: /* skip variable length markers */ length = get_2bytes(image->fp); for (length -= 2; length > 0; length--) { if (feof(image->fp)) { fclose(image->fp); if (p->debug['i']) pdf_error(p, PDF_NonfatalError, "JPEG file '%s' is damaged", filename); return -1; } (void) getc(image->fp); } break; } } /* do some sanity checks with the parameters */ if (image->height <= 0 || image->width <= 0 || image->components <= 0) { fclose(image->fp); if (p->debug['i']) pdf_error(p, PDF_NonfatalError, "Bad image parameters in JPEG file '%s' (w=%d h=%d, colors=%d", filename, image->width, image->height, image->components); return -1; } if (image->bpc != 8) { fclose(image->fp); if (p->debug['i']) pdf_error(p, PDF_NonfatalError, "Bad number of bits per pixel (%d) in JPEG file '%s'", image->bpc, filename); return -1; } if (stringparam && *stringparam) { if (!strcmp(stringparam, "mask")) { fclose(image->fp); pdf_error(p, PDF_ValueError, "Can't handle JPEG image mask"); } else if (!strcmp(stringparam, "colorize")) { if (image->components != 1) { fclose(image->fp); pdf_error(p, PDF_ValueError, "Can't colorize JPEG image with more than 1 component"); } if (intparam >= p->colorspaces_number) { fclose(image->fp); pdf_error(p, PDF_ValueError, "Invalid color number %d for image %s", intparam, filename); } image->colorspace = (pdf_colorspace) (intparam + LastCS); colorize = pdf_true; } else if (!strcmp(stringparam, "masked")) { mask = intparam; if (mask >= 0 && (mask >= p->images_capacity || !p->images[mask].in_use || p->images[mask].strips != 1 || p->images[mask].colorspace != ImageMask)) { fclose(image->fp); pdf_error(p, PDF_ValueError, "Bad image mask (no %d) for image '%s'", mask, filename); } } else { fclose(image->fp); pdf_error(p, PDF_ValueError, "Unknown parameter %s in pdf_open_JPEG", stringparam); } } image->mask = mask; switch (image->components) { case 1: if (!colorize) image->colorspace = DeviceGray; break; case 3: image->colorspace = DeviceRGB; break; case 4: image->colorspace = DeviceCMYK; /* special handling of Photoshop-generated CMYK JPEG files */ if (adobeflag) image->invert = pdf_true; break; default: fclose(image->fp); if (p->debug['i']) pdf_error(p, PDF_NonfatalError, "Unknown number of color components (%d) in JPEG file '%s'", image->components, filename); return -1; } image->in_use = pdf_true; /* mark slot as used */ image->filename = pdf_strdup(p, filename); pdf_put_image(p, imageslot, pdf_true); fclose(image->fp); return imageslot; }