// Returns true on success. The caller must use MetadataFree() on 'metadata' in // all cases. static int ExtractMetadataFromTIFF(TIFF* const tif, Metadata* const metadata) { int i; toff_t exif_ifd_offset; for (i = 0; kTIFFMetadataMap[i].tag != 0; ++i) { MetadataPayload* const payload = (MetadataPayload*)((uint8_t*)metadata + kTIFFMetadataMap[i].storage_offset); void* tag_data; uint32 tag_data_len; if (TIFFGetField(tif, kTIFFMetadataMap[i].tag, &tag_data_len, &tag_data) && !MetadataCopy((const char*)tag_data, tag_data_len, payload)) { return 0; } } // TODO(jzern): To extract the raw EXIF directory some parsing of it would be // necessary to determine the overall size. In addition, value offsets in // individual directory entries may need to be updated as, depending on the // type, they are file based. // Exif 2.2 Section 4.6.2 Tag Structure // TIFF Revision 6.0 Part 1 Section 2 TIFF Structure #Image File Directory if (TIFFGetField(tif, TIFFTAG_EXIFIFD, &exif_ifd_offset)) { fprintf(stderr, "Warning: EXIF extraction from TIFF is unsupported.\n"); } return 1; }
// Returns true on success and false for memory errors and corrupt profiles. // The caller must use MetadataFree() on 'metadata' in all cases. static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo, Metadata* const metadata) { static const struct { int marker; const char* signature; size_t signature_length; size_t storage_offset; } kJPEGMetadataMap[] = { // Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ... { JPEG_APP1, "Exif\0", 6, METADATA_OFFSET(exif) }, // XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG // TODO(jzern) Add support for 'ExtendedXMP' { JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) }, { 0, NULL, 0, 0 }, }; jpeg_saved_marker_ptr marker; // Treat ICC profiles separately as they may be segmented and out of order. if (!StoreICCP(dinfo, &metadata->iccp)) return 0; for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) { int i; for (i = 0; kJPEGMetadataMap[i].marker != 0; ++i) { if (marker->marker == kJPEGMetadataMap[i].marker && marker->data_length > kJPEGMetadataMap[i].signature_length && !memcmp(marker->data, kJPEGMetadataMap[i].signature, kJPEGMetadataMap[i].signature_length)) { MetadataPayload* const payload = (MetadataPayload*)((uint8_t*)metadata + kJPEGMetadataMap[i].storage_offset); if (payload->bytes == NULL) { const char* marker_data = (const char*)marker->data + kJPEGMetadataMap[i].signature_length; const size_t marker_data_length = marker->data_length - kJPEGMetadataMap[i].signature_length; if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0; } else { fprintf(stderr, "Ignoring additional '%s' marker\n", kJPEGMetadataMap[i].signature); } } } } return 1; }
// Looks for metadata at both the beginning and end of the PNG file, giving // preference to the head. // Returns true on success. The caller must use MetadataFree() on 'metadata' in // all cases. static int ExtractMetadataFromPNG(png_structp png, png_infop const head_info, png_infop const end_info, Metadata* const metadata) { int p; for (p = 0; p < 2; ++p) { png_infop const info = (p == 0) ? head_info : end_info; png_textp text = NULL; const png_uint_32 num = png_get_text(png, info, &text, NULL); png_uint_32 i; // Look for EXIF / XMP metadata. for (i = 0; i < num; ++i, ++text) { int j; for (j = 0; kPNGMetadataMap[j].name != NULL; ++j) { if (!strcmp(text->key, kPNGMetadataMap[j].name)) { MetadataPayload* const payload = (MetadataPayload*)((uint8_t*)metadata + kPNGMetadataMap[j].storage_offset); png_size_t text_length; switch (text->compression) { #ifdef PNG_iTXt_SUPPORTED case PNG_ITXT_COMPRESSION_NONE: case PNG_ITXT_COMPRESSION_zTXt: text_length = text->itxt_length; break; #endif case PNG_TEXT_COMPRESSION_NONE: case PNG_TEXT_COMPRESSION_zTXt: default: text_length = text->text_length; break; } if (payload->bytes != NULL) { fprintf(stderr, "Ignoring additional '%s'\n", text->key); } else if (!kPNGMetadataMap[j].process(text->text, text_length, payload)) { fprintf(stderr, "Failed to process: '%s'\n", text->key); return 0; } break; } } } // Look for an ICC profile. { png_charp name; int comp_type; #if LOCAL_PNG_PREREQ(1,5) png_bytep profile; #else png_charp profile; #endif png_uint_32 len; if (png_get_iCCP(png, info, &name, &comp_type, &profile, &len) == PNG_INFO_iCCP) { if (!MetadataCopy((const char*)profile, len, &metadata->iccp)) return 0; } } } return 1; }