PDFLIB_API int PDFLIB_CALL PDF_open_image( PDF *p, const char *type, const char *source, const char *data, long length, int width, int height, int components, int bpc, const char *params) { static const char fn[] = "PDF_open_image"; pdf_image *image; int im; PDF_TRACE(("%s\t(pdf[%p], \"%s\", \"%s\", source[%p], ", fn, (void *) p, type, source, (void *) data)); PDF_TRACE(("%ld, %d, %d, %d, %d, \"%s\");", length, width, height, components, bpc, params)); if (PDF_SANITY_CHECK_FAILED(p)) return -1; PDF_CHECK_SCOPE(p, fn, pdf_state_document | pdf_state_page); if (type == NULL || *type == '\0') pdf_error(p, PDF_ValueError, "No image type in PDF_open_image"); if (source == NULL || *source == '\0') pdf_error(p, PDF_ValueError, "No image source in PDF_open_image"); if (!strcmp(type, "raw") && data == NULL) pdf_error(p, PDF_ValueError, "Bad raw image pointer in PDF_open_image"); if (strcmp(type, "ccitt") && strcmp(type, "raw") && params != NULL && *params != '\0') pdf_error(p, PDF_NonfatalError, "Unnecessary CCITT parameter in PDF_open_image"); for (im = 0; im < p->images_capacity; im++) if (!p->images[im].in_use) /* found free slot */ break; if (im == p->images_capacity) pdf_grow_images(p); image = &p->images[im]; if (!strcmp(type, "jpeg")) { image->compression = dct; image->use_raw = pdf_true; } else if (!strcmp(type, "ccitt")) { image->compression = ccitt; image->use_raw = pdf_true; if (length < 0L) { image->info.ccitt.BitReverse = pdf_true; length = -length; } if (params != NULL && *params != '\0') image->params = pdf_strdup(p, params); else image->params = NULL; } else if (!strcmp(type, "raw")) { image->compression = none; } else pdf_error(p, PDF_ValueError, "Unknown image type '%s' in PDF_open_image", type); switch (components) { case 1: if (params && !strcmp(params, "mask")) { if (strcmp(type, "raw") || bpc != 1) pdf_error(p, PDF_ValueError, "Unsuitable image mask in PDF_open_image"); image->colorspace = ImageMask; } else image->colorspace = DeviceGray; break; case 3: image->colorspace = DeviceRGB; break; case 4: image->colorspace = DeviceCMYK; break; default: pdf_error(p, PDF_ValueError, "Bogus number of components (%d) in PDF_open_image", components); } image->width = (float) width; image->height = (float) height; image->bpc = bpc; image->components = components; image->in_use = pdf_true; /* mark slot as used */ if (!strcmp(source, "memory")) { /* in-memory image data */ if (image->compression == none && length != (long) (height * ((width * components * bpc + 7) / 8))) pdf_error(p, PDF_ValueError, "Bogus image data length '%ld' in PDF_open_image", length); image->src.init = pdf_noop; image->src.fill = pdf_data_source_buf_fill; image->src.terminate = pdf_noop; image->src.buffer_start = (unsigned char *) data; image->src.buffer_length= (size_t) length; image->src.bytes_available = 0; image->src.next_byte = NULL; } else if (!strcmp(source, "memory32")) { /* 32 bit aligned in-memory image data */ FILL_ALIGN fa; if (image->compression == none && length != (long) (height * ((width * components * bpc + 7) / 8))) pdf_error(p, PDF_ValueError, "Bogus image data length '%ld' in PDF_open_image", length); image->src.buffer_start = (unsigned char *) data; image->src.buffer_length= (size_t) length; image->src.init = pdf_noop; image->src.fill = pdf_data_source_buf_fill_aligned; image->src.terminate = pdf_noop; fa.cur_scanline = 0; fa.num_scanlines = (size_t) height; fa.scanline_widthbytes = (size_t) ((width * components * bpc + 7) / 8); /* dword align */ fa.scanline_widthbytes_aligned = (fa.scanline_widthbytes + 3) & ~0x3; image->src.private_data = (void *) &fa; image->src.bytes_available = 0; image->src.next_byte = NULL; } else if (!strcmp(source, "fileref")) { /* file reference */ if (p->compatibility == PDF_1_2) pdf_error(p, PDF_RuntimeError, "External image file references are not supported in PDF 1.2"); image->reference = pdf_ref_file; image->filename = pdf_strdup(p, data); } else if (!strcmp(source, "url")) { /* url reference */ if (p->compatibility == PDF_1_2) pdf_error(p, PDF_RuntimeError, "External image URLs are not supported in PDF 1.2"); image->reference = pdf_ref_url; image->filename = pdf_strdup(p, data); } else /* error */ pdf_error(p, PDF_ValueError, "Bogus image data source '%s' in PDF_open_image", source); pdf_put_image(p, im, pdf_true); PDF_TRACE((" [%d]\n", im)); return im; }
PDFLIB_API int PDFLIB_CALL PDF_open_CCITT(PDF *p, const char *filename, int width, int height, int BitReverse, int K, int BlackIs1) { pdf_image *image; int im; char scratch[30]; if (PDF_SANITY_CHECK_FAILED(p)) return -1; for (im = 0; im < p->images_capacity; im++) if (!p->images[im].in_use) /* found free slot */ break; if (im == p->images_capacity) pdf_grow_images(p); image = &p->images[im]; if ((image->fp = fopen(filename, READMODE)) == NULL) { if (p->debug['i']) { pdf_error(p, PDF_NonfatalError, "Couldn't open CCITT file '%s'", filename); } return -1; } /* Grab the image parameters and pack them into image struct */ image->filename = pdf_strdup(p, filename); /* CCITT specific information */ image->width = width; image->height = height; image->info.ccitt.BitReverse = BitReverse; if (BlackIs1 == 0 && K == 0) /* default values */ image->params = NULL; else { scratch[0] = '\0'; if (K != 0) { sprintf(scratch, "/K %d", K); } if (BlackIs1 == 1) strcat(scratch, "/BlackIs1 true"); image->params = pdf_strdup(p, scratch); } /* The following are fixed for CCITT images */ image->compression = ccitt; image->colorspace = DeviceGray; image->components = 1; image->bpc = 1; image->src.init = pdf_data_source_CCITT_init; image->src.fill = pdf_data_source_CCITT_fill; image->src.terminate = pdf_data_source_CCITT_terminate; image->src.private_data = (void *) image; image->in_use = pdf_true; /* mark slot as used */ pdf_put_image(p, im, pdf_true); fclose(image->fp); return im; }
/* 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; }