WriteMethod ExifParser::encode( Blob& blob, const byte* pData, uint32_t size, ByteOrder byteOrder, const ExifData& exifData ) { ExifData ed = exifData; // Delete IFD0 tags that are "not recorded" in compressed images // Reference: Exif 2.2 specs, 4.6.8 Tag Support Levels, section A static const char* filteredIfd0Tags[] = { "Exif.Image.PhotometricInterpretation", "Exif.Image.StripOffsets", "Exif.Image.RowsPerStrip", "Exif.Image.StripByteCounts", "Exif.Image.JPEGInterchangeFormat", "Exif.Image.JPEGInterchangeFormatLength", "Exif.Image.SubIFDs" }; for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfd0Tags); ++i) { ExifData::iterator pos = ed.findKey(ExifKey(filteredIfd0Tags[i])); if (pos != ed.end()) { #ifdef DEBUG std::cerr << "Warning: Exif tag " << pos->key() << " not encoded\n"; #endif ed.erase(pos); } } // Delete IFDs which do not occur in JPEGs static const IfdId filteredIfds[] = { subImage1Id, subImage2Id, subImage3Id, subImage4Id, subImage5Id, subImage6Id, subImage7Id, subImage8Id, subImage9Id, subThumb1Id, panaRawId, ifd2Id, ifd3Id }; for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfds); ++i) { #ifdef DEBUG std::cerr << "Warning: Exif IFD " << filteredIfds[i] << " not encoded\n"; #endif eraseIfd(ed, filteredIfds[i]); } // IPTC and XMP are stored elsewhere, not in the Exif APP1 segment. IptcData emptyIptc; XmpData emptyXmp; // Encode and check if the result fits into a JPEG Exif APP1 segment MemIo mio1; std::auto_ptr<TiffHeaderBase> header(new TiffHeader(byteOrder, 0x00000008, false)); WriteMethod wm = TiffParserWorker::encode(mio1, pData, size, ed, emptyIptc, emptyXmp, Tag::root, TiffMapping::findEncoder, header.get(), 0); if (mio1.size() <= 65527) { append(blob, mio1.mmap(), mio1.size()); return wm; } // If it doesn't fit, remove additional tags // Delete preview tags if the preview is larger than 32kB. // Todo: Enhance preview classes to be able to write and delete previews and use that instead. // Table must be sorted by preview, the first tag in each group is the size static const PreviewTags filteredPvTags[] = { { pttLen, "Exif.Minolta.ThumbnailLength" }, { pttTag, "Exif.Minolta.ThumbnailOffset" }, { pttLen, "Exif.Minolta.Thumbnail" }, { pttLen, "Exif.NikonPreview.JPEGInterchangeFormatLength" }, { pttIfd, "NikonPreview" }, { pttLen, "Exif.Olympus.ThumbnailLength" }, { pttTag, "Exif.Olympus.ThumbnailOffset" }, { pttLen, "Exif.Olympus.ThumbnailImage" }, { pttLen, "Exif.Olympus.Thumbnail" }, { pttLen, "Exif.Olympus2.ThumbnailLength" }, { pttTag, "Exif.Olympus2.ThumbnailOffset" }, { pttLen, "Exif.Olympus2.ThumbnailImage" }, { pttLen, "Exif.Olympus2.Thumbnail" }, { pttLen, "Exif.OlympusCs.PreviewImageLength" }, { pttTag, "Exif.OlympusCs.PreviewImageStart" }, { pttTag, "Exif.OlympusCs.PreviewImageValid" }, { pttLen, "Exif.Pentax.PreviewLength" }, { pttTag, "Exif.Pentax.PreviewOffset" }, { pttTag, "Exif.Pentax.PreviewResolution" }, { pttLen, "Exif.PentaxDng.PreviewLength" }, { pttTag, "Exif.PentaxDng.PreviewOffset" }, { pttTag, "Exif.PentaxDng.PreviewResolution" }, { pttLen, "Exif.SamsungPreview.JPEGInterchangeFormatLength" }, { pttIfd, "SamsungPreview" }, { pttLen, "Exif.Thumbnail.StripByteCounts" }, { pttIfd, "Thumbnail" }, { pttLen, "Exif.Thumbnail.JPEGInterchangeFormatLength" }, { pttIfd, "Thumbnail" } }; bool delTags = false; ExifData::iterator pos; for (unsigned int i = 0; i < EXV_COUNTOF(filteredPvTags); ++i) { switch (filteredPvTags[i].ptt_) { case pttLen: delTags = false; pos = ed.findKey(ExifKey(filteredPvTags[i].key_)); if (pos != ed.end() && sumToLong(*pos) > 32768) { delTags = true; #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Exif tag " << pos->key() << " not encoded\n"; #endif ed.erase(pos); } break; case pttTag: if (delTags) { pos = ed.findKey(ExifKey(filteredPvTags[i].key_)); if (pos != ed.end()) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Exif tag " << pos->key() << " not encoded\n"; #endif ed.erase(pos); } } break; case pttIfd: if (delTags) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Exif IFD " << filteredPvTags[i].key_ << " not encoded\n"; #endif eraseIfd(ed, Internal::groupId(filteredPvTags[i].key_)); } break; } } // Delete unknown tags larger than 4kB and known tags larger than 40kB. for (ExifData::iterator pos = ed.begin(); pos != ed.end(); ) { if ( (pos->size() > 4096 && pos->tagName().substr(0, 2) == "0x") || pos->size() > 40960) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Exif tag " << pos->key() << " not encoded\n"; #endif pos = ed.erase(pos); } else { ++pos; } } // Encode the remaining Exif tags again, don't care if it fits this time MemIo mio2; wm = TiffParserWorker::encode(mio2, pData, size, ed, emptyIptc, emptyXmp, Tag::root, TiffMapping::findEncoder, header.get(), 0); append(blob, mio2.mmap(), mio2.size()); #ifdef DEBUG if (wm == wmIntrusive) { std::cerr << "SIZE OF EXIF DATA IS " << std::dec << mio2.size() << " BYTES\n"; } else { std::cerr << "SIZE DOESN'T MATTER, NON-INTRUSIVE WRITING USED\n"; } #endif return wm; } // ExifParser::encode
void Rw2Image::readMetadata() { #ifdef DEBUG std::cerr << "Reading RW2 file " << io_->path() << "\n"; #endif if (io_->open() != 0) { throw Error(9, io_->path(), strError()); } IoCloser closer(*io_); // Ensure that this is the correct image type if (!isRw2Type(*io_, false)) { if (io_->error() || io_->eof()) throw Error(14); throw Error(3, "RW2"); } clearMetadata(); std::ofstream devnull; printStructure(devnull, kpsRecursive, 0); ByteOrder bo = Rw2Parser::decode(exifData_, iptcData_, xmpData_, io_->mmap(), io_->size()); setByteOrder(bo); // A lot more metadata is hidden in the embedded preview image // Todo: This should go into the Rw2Parser, but for that it needs the Image PreviewManager loader(*this); PreviewPropertiesList list = loader.getPreviewProperties(); // Todo: What if there are more preview images? if (list.size() > 1) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "RW2 image contains more than one preview. None used.\n"; #endif } if (list.size() != 1) return; ExifData exifData; PreviewImage preview = loader.getPreviewImage(*list.begin()); Image::AutoPtr image = ImageFactory::open(preview.pData(), preview.size()); if (image.get() == 0) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Failed to open RW2 preview image.\n"; #endif return; } image->readMetadata(); ExifData& prevData = image->exifData(); if (!prevData.empty()) { // Filter duplicate tags for (ExifData::const_iterator pos = exifData_.begin(); pos != exifData_.end(); ++pos) { if (pos->ifdId() == panaRawId) continue; ExifData::iterator dup = prevData.findKey(ExifKey(pos->key())); if (dup != prevData.end()) { #ifdef DEBUG std::cerr << "Filtering duplicate tag " << pos->key() << " (values '" << pos->value() << "' and '" << dup->value() << "')\n"; #endif prevData.erase(dup); } } } // Remove tags not applicable for raw images static const char* filteredTags[] = { "Exif.Photo.ComponentsConfiguration", "Exif.Photo.CompressedBitsPerPixel", "Exif.Panasonic.ColorEffect", "Exif.Panasonic.Contrast", "Exif.Panasonic.NoiseReduction", "Exif.Panasonic.ColorMode", "Exif.Panasonic.OpticalZoomMode", "Exif.Panasonic.Contrast", "Exif.Panasonic.Saturation", "Exif.Panasonic.Sharpness", "Exif.Panasonic.FilmMode", "Exif.Panasonic.SceneMode", "Exif.Panasonic.WBRedLevel", "Exif.Panasonic.WBGreenLevel", "Exif.Panasonic.WBBlueLevel", "Exif.Photo.ColorSpace", "Exif.Photo.PixelXDimension", "Exif.Photo.PixelYDimension", "Exif.Photo.SceneType", "Exif.Photo.CustomRendered", "Exif.Photo.DigitalZoomRatio", "Exif.Photo.SceneCaptureType", "Exif.Photo.GainControl", "Exif.Photo.Contrast", "Exif.Photo.Saturation", "Exif.Photo.Sharpness", "Exif.Image.PrintImageMatching", "Exif.Image.YCbCrPositioning" }; for (unsigned int i = 0; i < EXV_COUNTOF(filteredTags); ++i) { ExifData::iterator pos = prevData.findKey(ExifKey(filteredTags[i])); if (pos != prevData.end()) { #ifdef DEBUG std::cerr << "Exif tag " << pos->key() << " removed\n"; #endif prevData.erase(pos); } } // Add the remaining tags for (ExifData::const_iterator pos = prevData.begin(); pos != prevData.end(); ++pos) { exifData_.add(*pos); } } // Rw2Image::readMetadata