diff --git a/src/exif.cpp b/src/exif.cpp index c571003c..2b28adce 100644 --- a/src/exif.cpp +++ b/src/exif.cpp @@ -20,14 +20,14 @@ */ /* File: exif.cpp - Version: $Name: $ $Revision: 1.39 $ + Version: $Name: $ $Revision: 1.40 $ Author(s): Andreas Huggel (ahu) History: 26-Jan-04, ahu: created 11-Feb-04, ahu: isolated as a component */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.39 $ $RCSfile: exif.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.40 $ $RCSfile: exif.cpp,v $") // Define DEBUG_MAKERNOTE to output debug information to std::cerr #undef DEBUG_MAKERNOTE @@ -159,7 +159,7 @@ namespace Exif { } TiffThumbnail::TiffThumbnail() - : size_(0), pImage_(0), ifd_(ifd1, 0, false) + : offset_(0), size_(0), pImage_(0), ifd_(ifd1, 0, false) { } @@ -169,7 +169,8 @@ namespace Exif { } TiffThumbnail::TiffThumbnail(const TiffThumbnail& rhs) - : size_(rhs.size_), pImage_(0), ifd_(ifd1, 0, false) + : offset_(rhs.offset_), size_(rhs.size_), pImage_(0), + ifd_(ifd1, 0, false) { if (rhs.pImage_ && rhs.size_ > 0) { pImage_ = new char[rhs.size_]; @@ -190,6 +191,7 @@ namespace Exif { ifd_.read(pNewImage + tiffHeader_.offset(), tiffHeader_.byteOrder(), tiffHeader_.offset()); } + offset_ = rhs.offset_; size_ = rhs.size_; delete[] pImage_; pImage_ = pNewImage; @@ -212,10 +214,8 @@ namespace Exif { // Create IFD (without Exif and GPS tags) from metadata Ifd ifd1(ifd1); addToIfd(ifd1, exifData.begin(), exifData.end(), tiffHeader.byteOrder()); - Ifd::iterator i = ifd1.findTag(0x8769); - if (i != ifd1.end()) ifd1.erase(i); - i = ifd1.findTag(0x8825); - if (i != ifd1.end()) ifd1.erase(i); + ifd1.erase(0x8769); + ifd1.erase(0x8825); // Do not copy the IFD yet, remember the location and leave a gap long ifdOffset = len; @@ -229,19 +229,23 @@ namespace Exif { ExifData::const_iterator sizes = exifData.findKey(key); if (sizes == exifData.end()) return 2; std::ostringstream os; // for the new strip offsets + long minOffset = 0; for (long k = 0; k < offsets->count(); ++k) { long offset = offsets->toLong(k); long size = sizes->toLong(k); memcpy(img.pData_ + len, buf + offset, size); os << len << " "; len += size; - } + minOffset = offset; // just to initialize minOffset + } + for (long k = 0; k < offsets->count(); ++k) { + minOffset = std::min(minOffset, offsets->toLong(k)); + } // Update the IFD with the actual strip offsets (replace existing entry) Metadatum newOffsets(*offsets); newOffsets.setValue(os.str()); - i = ifd1.findTag(0x0111); - if (i != ifd1.end()) ifd1.erase(i); + ifd1.erase(0x0111); addToIfd(ifd1, newOffsets, tiffHeader.byteOrder()); // Finally, sort and copy the IFD @@ -252,6 +256,7 @@ namespace Exif { pImage_ = new char[len]; memcpy(pImage_, img.pData_, len); size_ = len; + offset_ = minOffset; tiffHeader_.read(pImage_); ifd_.read(pImage_ + tiffHeader_.offset(), tiffHeader_.byteOrder(), tiffHeader_.offset()); @@ -326,7 +331,12 @@ namespace Exif { return size_; } - void TiffThumbnail::setOffsets(Ifd& ifd1, ByteOrder byteOrder) const + long TiffThumbnail::offset() const + { + return offset_; + } + + void TiffThumbnail::setOffsets(Ifd& ifd1, ByteOrder byteOrder) { // Adjust the StripOffsets, assuming that the existing TIFF strips // start immediately after the thumbnail IFD @@ -336,20 +346,25 @@ namespace Exif { if (pos == ifd_.end()) throw Error("Bad thumbnail (0x0111)"); Metadatum offsets(*pos, tiffHeader_.byteOrder()); std::ostringstream os; + long minOffset = 0; for (long k = 0; k < offsets.count(); ++k) { os << offsets.toLong(k) + shift << " "; + minOffset = offsets.toLong(k) + shift; // initialize minOffset } offsets.setValue(os.str()); + for (long k = 0; k < offsets.count(); ++k) { + minOffset = std::min(minOffset, offsets.toLong(k)); + } + offset_ = minOffset; // Update the IFD with the re-calculated strip offsets // (replace existing entry) - Ifd::iterator i = ifd1.findTag(0x0111); - if (i != ifd1.end()) ifd1.erase(i); + ifd1.erase(0x0111); addToIfd(ifd1, offsets, byteOrder); } // TiffThumbnail::setOffsets JpegThumbnail::JpegThumbnail() - : size_(0), pImage_(0) + : offset_(0), size_(0), pImage_(0) { } @@ -359,7 +374,7 @@ namespace Exif { } JpegThumbnail::JpegThumbnail(const JpegThumbnail& rhs) - : size_(rhs.size_), pImage_(0) + : offset_(rhs.offset_), size_(rhs.size_), pImage_(0) { if (rhs.pImage_ && rhs.size_ > 0) { pImage_ = new char[rhs.size_]; @@ -374,6 +389,7 @@ namespace Exif { pNewImage = new char[rhs.size_]; memcpy(pNewImage, rhs.pImage_, rhs.size_); } + offset_ = rhs.offset_; size_ = rhs.size_; delete[] pImage_; pImage_ = pNewImage; @@ -396,6 +412,7 @@ namespace Exif { pImage_ = new char[size]; memcpy(pImage_, buf + offset, size); size_ = size; + offset_ = offset; return 0; } // JpegThumbnail::read @@ -429,7 +446,7 @@ namespace Exif { delete value; pos = exifData.findKey(key); } - pos->setValue("0"); + pos->setValue(toString(offset_)); key = "Thumbnail.RecordingOffset.JPEGInterchangeFormatLength"; pos = exifData.findKey(key); @@ -459,18 +476,24 @@ namespace Exif { return size_; } - void JpegThumbnail::setOffsets(Ifd& ifd1, ByteOrder byteOrder) const + long JpegThumbnail::offset() const + { + return offset_; + } + + void JpegThumbnail::setOffsets(Ifd& ifd1, ByteOrder byteOrder) { Ifd::iterator pos = ifd1.findTag(0x0201); if (pos == ifd1.end()) throw Error("Bad thumbnail (0x0201)"); - pos->setValue(ifd1.offset() + ifd1.size() + ifd1.dataSize(), byteOrder); + offset_ = ifd1.offset() + ifd1.size() + ifd1.dataSize(); + pos->setValue(offset_, byteOrder); } ExifData::ExifData() : pThumbnail_(0), pMakerNote_(0), ifd0_(ifd0, 0, false), exifIfd_(exifIfd, 0, false), iopIfd_(iopIfd, 0, false), gpsIfd_(gpsIfd, 0, false), ifd1_(ifd1, 0, false), - size_(0), pData_(0) + size_(0), pData_(0), compatible_(true) { } @@ -571,7 +594,6 @@ namespace Exif { ifd1_.erase(pos); ret = -99; } - // Copy all entries from the IFDs and the MakerNote to the metadata metadata_.clear(); add(ifd0_.begin(), ifd0_.end(), byteOrder()); @@ -582,7 +604,6 @@ namespace Exif { add(iopIfd_.begin(), iopIfd_.end(), byteOrder()); add(gpsIfd_.begin(), gpsIfd_.end(), byteOrder()); add(ifd1_.begin(), ifd1_.end(), byteOrder()); - // Read the thumbnail readThumbnail(); @@ -626,7 +647,7 @@ namespace Exif { // If we can update the internal IFDs and the underlying data buffer // from the metadata without changing the data size, then it is enough // to copy the data buffer. - if (updateEntries()) { + if (compatible_ && updateEntries()) { #ifdef DEBUG_MAKERNOTE std::cerr << "->>>>>> using non-intrusive writing <<<<<<-\n"; #endif @@ -845,28 +866,79 @@ namespace Exif { std::sort(metadata_.begin(), metadata_.end(), cmpMetadataByTag); } - void ExifData::erase(ExifData::iterator pos) + ExifData::iterator ExifData::erase(ExifData::iterator pos) { - metadata_.erase(pos); + return metadata_.erase(pos); } long ExifData::eraseThumbnail() { // Delete all Thumbnail.*.* (IFD1) metadata - for (Metadata::iterator i = begin(); i != end(); ++i) { - if (i->ifdId() == ifd1) erase(i); + Metadata::iterator i = begin(); + while (i != end()) { + if (i->ifdId() == ifd1) { + i = erase(i); + } + else { + ++i; + } + } + long delta = 0; + if (stdThumbPosition()) { + delta = size_; + if (size_ > 0 && ifd0_.next() > 0) { + // Truncate IFD1 and thumbnail data from the data buffer + size_ = ifd0_.next(); + ifd0_.setNext(0, byteOrder()); + } + delta -= size_; + } + else { + // We will have to write the hard way and re-arrange the data + compatible_ = false; + delta = ifd1_.size() + ifd1_.dataSize() + + pThumbnail_ ? pThumbnail_->size() : 0; } - // Truncate IFD1 and thumbnail data from the data buffer - long delta = size_; - if (size_ > 0) size_ = ifd0_.next(); - delta -= size_; - ifd0_.setNext(0, byteOrder()); // Delete the thumbnail itself - delete pThumbnail_; - pThumbnail_ = 0; + if (pThumbnail_) { + delete pThumbnail_; + pThumbnail_ = 0; + } return delta; } + bool ExifData::stdThumbPosition() const + { + // Todo: There is still an invalid assumption here: The data of an IFD + // can be stored in multiple non-contiguous blocks. In this case, + // dataOffset + dataSize does not point to the end of the IFD data. + // in particular, this is potentially the case for the remaining Exif + // data in the presence of a known Makernote. + bool rc = true; + if (pThumbnail_) { + long maxOffset; + maxOffset = std::max(ifd0_.offset(), ifd0_.dataOffset()); + maxOffset = std::max(maxOffset, exifIfd_.offset()); + maxOffset = std::max(maxOffset, exifIfd_.dataOffset() + + exifIfd_.dataSize()); + if (pMakerNote_) { + maxOffset = std::max(maxOffset, pMakerNote_->offset() + + pMakerNote_->size()); + } + maxOffset = std::max(maxOffset, iopIfd_.offset()); + maxOffset = std::max(maxOffset, iopIfd_.dataOffset() + + iopIfd_.dataSize()); + maxOffset = std::max(maxOffset, gpsIfd_.offset()); + maxOffset = std::max(maxOffset, gpsIfd_.dataOffset() + + gpsIfd_.dataSize()); + + if ( maxOffset > ifd1_.offset() + || maxOffset > ifd1_.dataOffset() && ifd1_.dataOffset() > 0 + || maxOffset > pThumbnail_->offset()) rc = false; + } + return rc; + } + int ExifData::readThumbnail() { delete pThumbnail_; diff --git a/src/exif.hpp b/src/exif.hpp index 47006b39..735e3bbe 100644 --- a/src/exif.hpp +++ b/src/exif.hpp @@ -21,7 +21,7 @@ /*! @file exif.hpp @brief Encoding and decoding of %Exif data - @version $Name: $ $Revision: 1.37 $ + @version $Name: $ $Revision: 1.38 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 09-Jan-04, ahu: created @@ -271,6 +271,20 @@ namespace Exif { virtual int read(const char* buf, const ExifData& exifData, ByteOrder byteOrder =littleEndian) =0; + /*! + @brief Update the internal offset and the thumbnail data offsets + in IFD1 assuming the thumbnail data follows immediately after + IFD1. + + If the type of the thumbnail image is JPEG, JPEGInterchangeFormat is + set to point directly behind the data area of IFD1. If the type is + TIFF, StripOffsets from the thumbnail image are adjusted to point to + the strips, which have to follow immediately after IFD1. Use copy() to + write the thumbnail image data. The offset of IFD1 must be set + correctly. Changing the size or data size of IFD1 invalidates the + thumbnail data offsets set by this method. + */ + virtual void setOffsets(Ifd& ifd1, ByteOrder byteOrder) =0; //@} //! @name Accessors @@ -308,18 +322,10 @@ namespace Exif { */ virtual void update(ExifData& exifData) const =0; /*! - @brief Update the thumbnail data offsets in IFD1 assuming the - thumbnail data follows immediately after IFD1. - - If the type of the thumbnail image is JPEG, JPEGInterchangeFormat is - set to point directly behind the data area of IFD1. If the type is - TIFF, StripOffsets from the thumbnail image are adjusted to point to - the strips, which have to follow immediately after IFD1. Use copy() to - write the thumbnail image data. The offset of IFD1 must be set - correctly. Changing the size of IFD1 invalidates the thumbnail data - offsets set by this method. + @brief Return the position of the thumbnail image data from the + start of the TIFF header in the original %Exif data. */ - virtual void setOffsets(Ifd& ifd1, ByteOrder byteOrder) const =0; + virtual long offset() const =0; /*! @brief Return the size of the thumbnail image (the size it would occupy when extracted from the %Exif data) @@ -364,6 +370,7 @@ namespace Exif { int read(const char* buf, const ExifData& exifData, ByteOrder byteOrder =littleEndian); + void setOffsets(Ifd& ifd1, ByteOrder byteOrder); //@} //! @name Accessors @@ -373,13 +380,15 @@ namespace Exif { const char* extension() const; long copy(char* buf) const; void update(ExifData& exifData) const; - void setOffsets(Ifd& ifd1, ByteOrder byteOrder) const; + long offset() const; long size() const; long dataSize() const; //@} private: // DATA + long offset_; // Original offset of the thumbnail data + // from the start of the TIFF header long size_; //!< Size of the image data char* pImage_; //!< Thumbnail image data TiffHeader tiffHeader_; //!< Thumbnail TIFF Header @@ -407,6 +416,7 @@ namespace Exif { int read(const char* buf, const ExifData& exifData, ByteOrder byteOrder =littleEndian); + void setOffsets(Ifd& ifd1, ByteOrder byteOrder); //@} //! @name Accessors @@ -416,13 +426,15 @@ namespace Exif { const char* extension() const; long copy(char* buf) const; void update(ExifData& exifData) const; - void setOffsets(Ifd& ifd1, ByteOrder byteOrder) const; + long offset() const; long size() const; long dataSize() const; //@} private: // DATA + long offset_; // Original offset of the thumbnail data + // from the start of the TIFF header long size_; // Size of the image data char* pImage_; // Thumbnail image data @@ -562,7 +574,7 @@ namespace Exif { buffer has enough memory. Otherwise the call results in undefined behaviour. @return Number of characters written to the buffer. - */ + */ long copy(char* buf); /*! @brief Add all (IFD) entries in the range from iterator position begin @@ -586,8 +598,13 @@ namespace Exif { multiple metadata with the same key. */ void add(const Metadatum& metadatum); - //! Delete the metadatum at iterator position pos - void erase(iterator pos); + /*! + @brief Delete the metadatum at iterator position pos, return the + position of the next metadatum. Note that iterators into + the metadata, including pos, are potentially invalidated + by this call. + */ + iterator erase(iterator pos); //! Sort metadata by key void sortByKey(); //! Sort metadata by tag @@ -617,8 +634,8 @@ namespace Exif { iterator findIfdIdIdx(IfdId ifdId, int idx); /*! @brief Delete the thumbnail from the %Exif data. Removes all related - (Thumbnail.*.*, i.e., IFD1) metadata as well. - @return The number of bytes truncated from the original %Exif data. + (%Thumbnail.*.*, i.e., IFD1) metadata as well. + @return The number of bytes erased from the original %Exif data. */ long eraseThumbnail(); //@} @@ -666,18 +683,21 @@ namespace Exif { //! Returns the byte order as specified in the TIFF header ByteOrder byteOrder() const { return tiffHeader_.byteOrder(); } /*! - @brief Write the thumbnail image to a file. The filename extension - will be set according to the image type of the thumbnail, so - the path should not include an extension. + @brief Write the thumbnail image to a file. A filename extension + is appended to path according to the image type of the + thumbnail, so the path should not include an extension. */ int writeThumbnail(const std::string& path) const { return pThumbnail_ ? pThumbnail_->write(path) : 0; } - //! Return the file extension of the thumbnail image file + /*! + @brief Return a short string describing the format of the %Exif + thumbnail ("TIFF", "JPEG"). + */ const char* thumbnailFormat() const { return pThumbnail_ ? pThumbnail_->format() : ""; } /*! - @brief Return the file extension for the format of the thumbnail - (".tif", ".jpg"). + @brief Return the file extension for the %Exif thumbnail depending + on the format (".tif", ".jpg"). */ const char* thumbnailExtension() const { return pThumbnail_ ? pThumbnail_->extension() : ""; } @@ -751,6 +771,12 @@ namespace Exif { findEntry(IfdId ifdId, int idx) const; //! Return a pointer to the internal IFD identified by its IFD id const Ifd* getIfd(IfdId ifdId) const; + /*! + @brief Check if IFD1, the IFD1 data and thumbnail data are located at + the end of the Exif data. Return true, if they are or if there + is no thumbnail at all, else return false. + */ + bool stdThumbPosition() const; //@} // DATA @@ -770,6 +796,13 @@ namespace Exif { long size_; //!< Size of the Exif raw data in bytes char* pData_; //!< Exif raw data buffer + /*! + Can be set to false to indicate that non-intrusive writing is not + possible. If it is true (the default), then the compatibility checks + will be performed to determine which writing method to use. + */ + bool compatible_; + }; // class ExifData // ***************************************************************************** diff --git a/src/ifd.cpp b/src/ifd.cpp index 89db135b..c07176bb 100644 --- a/src/ifd.cpp +++ b/src/ifd.cpp @@ -20,14 +20,14 @@ */ /* File: ifd.cpp - Version: $Name: $ $Revision: 1.15 $ + Version: $Name: $ $Revision: 1.16 $ Author(s): Andreas Huggel (ahu) History: 26-Jan-04, ahu: created 11-Feb-04, ahu: isolated as a component */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.15 $ $RCSfile: ifd.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.16 $ $RCSfile: ifd.cpp,v $") // ***************************************************************************** // included header files @@ -156,21 +156,24 @@ namespace Exif { } // Entry::component Ifd::Ifd(IfdId ifdId) - : alloc_(true), ifdId_(ifdId), offset_(0), pNext_(0), next_(0) + : alloc_(true), ifdId_(ifdId), offset_(0), dataOffset_(0), + pNext_(0), next_(0) { pNext_ = new char[4]; memset(pNext_, 0x0, 4); } Ifd::Ifd(IfdId ifdId, uint32 offset) - : alloc_(true), ifdId_(ifdId), offset_(offset), pNext_(0), next_(0) + : alloc_(true), ifdId_(ifdId), offset_(offset), dataOffset_(0), + pNext_(0), next_(0) { pNext_ = new char[4]; memset(pNext_, 0x0, 4); } Ifd::Ifd(IfdId ifdId, uint32 offset, bool alloc) - : alloc_(alloc), ifdId_(ifdId), offset_(offset), pNext_(0), next_(0) + : alloc_(alloc), ifdId_(ifdId), offset_(offset), dataOffset_(0), + pNext_(0), next_(0) { if (alloc_) { pNext_ = new char[4]; @@ -185,7 +188,8 @@ namespace Exif { Ifd::Ifd(const Ifd& rhs) : alloc_(rhs.alloc_), entries_(rhs.entries_), ifdId_(rhs.ifdId_), - offset_(rhs.offset_), pNext_(rhs.pNext_), next_(rhs.next_) + offset_(rhs.offset_), dataOffset_(rhs.dataOffset_), + pNext_(rhs.pNext_), next_(rhs.next_) { if (alloc_ && rhs.pNext_) { pNext_ = new char[4]; @@ -221,18 +225,24 @@ namespace Exif { } next_ = getULong(buf+o, byteOrder); - // Guess the offset of the IFD, if it was not given. The guess is based - // on the assumption that the smallest offset points to a data buffer - // directly following the IFD. Subsequently all offsets of IFD entries - // will need to be recalculated. - if (offset_ == 0 && preEntries.size() > 0) { + // Set the offset of the first data entry outside of the IFD. + // At the same time we guess the offset of the IFD, if it was not + // given. The guess is based on the assumption that the smallest offset + // points to a data buffer directly following the IFD. Subsequently all + // offsets of IFD entries will need to be recalculated. + if (preEntries.size() > 0) { // Find the entry with the smallest offset Ifd::PreEntries::const_iterator i = std::min_element( preEntries.begin(), preEntries.end(), cmpPreEntriesByOffset); - // Set the 'guessed' IFD offset, the test is needed for the case when - // all entries have data sizes not exceeding 4. + // Only do something if there is at least one entry with data + // outside the IFD directory itself. if (i->size_ > 4) { - offset_ = i->offset_ - size(); + if (offset_ == 0) { + // Set the 'guessed' IFD offset + offset_ = i->offset_ - size(); + } + // Set the offset of the first data entry outside of the IFD + dataOffset_ = i->offset_; } } @@ -362,6 +372,7 @@ namespace Exif { pNext_ = 0; } offset_ = 0; + dataOffset_ = 0; } // Ifd::clear void Ifd::setNext(uint32 next, ByteOrder byteOrder) @@ -390,9 +401,9 @@ namespace Exif { return idx; } - void Ifd::erase(iterator pos) + Ifd::iterator Ifd::erase(iterator pos) { - entries_.erase(pos); + return entries_.erase(pos); } long Ifd::size() const diff --git a/src/ifd.hpp b/src/ifd.hpp index c8e0dc63..01b5a4ca 100644 --- a/src/ifd.hpp +++ b/src/ifd.hpp @@ -21,7 +21,7 @@ /*! @file ifd.hpp @brief Encoding and decoding of IFD (Image File Directory) data - @version $Name: $ $Revision: 1.13 $ + @version $Name: $ $Revision: 1.14 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 09-Jan-04, ahu: created @@ -385,8 +385,13 @@ namespace Exif { of the deleted entry or 0 if no entry with tag was found. */ int erase(uint16 tag); - //! Delete the directory entry at iterator position pos - void erase(iterator pos); + /*! + @brief Delete the directory entry at iterator position pos, return the + position of the next entry. Note that iterators into the + directory, including pos, are potentially invalidated by this + call. + */ + iterator erase(iterator pos); //! Sort the IFD entries by tag void sortByTag(); //! The first entry @@ -415,6 +420,12 @@ namespace Exif { IfdId ifdId() const { return ifdId_; } //! Get the offset of the IFD from the start of the TIFF header long offset() const { return offset_; } + /*! + @brief Get the offset of the first data entry outside of the IFD, + return 0 if there is none. The data offset is determined when + the IFD is read. + */ + long dataOffset() const { return dataOffset_; } //! Get the offset to the next IFD from the start of the TIFF header uint32 next() const { return next_; } //! Get the number of directory entries in the IFD @@ -462,11 +473,13 @@ namespace Exif { Entries entries_; //! IFD Id IfdId ifdId_; - //! offset of the IFD from the start of TIFF header + //! Offset of the IFD from the start of TIFF header long offset_; - // Pointer to the offset of next IFD from the start of the TIFF header + //! Offset of the first data entry outside of the IFD directory + long dataOffset_; + //! Pointer to the offset of next IFD from the start of the TIFF header char* pNext_; - // The offset of the next IFD as data value (always in sync with *pNext_) + //! The offset of the next IFD as data value (always in sync with *pNext_) uint32 next_; }; // class Ifd diff --git a/src/makernote.cpp b/src/makernote.cpp index f9fb06b2..16a39265 100644 --- a/src/makernote.cpp +++ b/src/makernote.cpp @@ -20,13 +20,13 @@ */ /* File: makernote.cpp - Version: $Name: $ $Revision: 1.15 $ + Version: $Name: $ $Revision: 1.16 $ Author(s): Andreas Huggel (ahu) History: 18-Feb-04, ahu: created */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.15 $ $RCSfile: makernote.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.16 $ $RCSfile: makernote.cpp,v $") // Define DEBUG_MAKERNOTE to output debug information to std::cerr #undef DEBUG_MAKERNOTE @@ -155,6 +155,8 @@ namespace Exif { ByteOrder byteOrder, long offset) { + // Remember the offset + offset_ = offset; // Set byte order if none is set yet if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder; int rc = 0; @@ -182,7 +184,6 @@ namespace Exif { i->setMakerNote(this); } } - #ifdef DEBUG_MAKERNOTE hexdump(std::cerr, buf, len, offset); if (rc == 0) ifd_.print(std::cerr); @@ -193,10 +194,23 @@ namespace Exif { long IfdMakerNote::copy(char* buf, ByteOrder byteOrder, long offset) { + // Remember the new offset + offset_ = offset; // Set byte order if none is set yet if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder; - return ifd_.copy(buf, byteOrder_, offset); - } + long len = 0; + if (!prefix_.empty()) { + // Write the prefix string to the Makernote buffer + memcpy(buf, prefix_.data(), prefix_.size()); + len += prefix_.size(); + } + if (!absOffset_) { + // Use offsets relative to the start of the Makernote field + offset = 0; + } + len += ifd_.copy(buf + len, byteOrder_, offset + len); + return len; + } // IfdMakerNote::copy Entries::const_iterator IfdMakerNote::findIdx(int idx) const { @@ -205,7 +219,7 @@ namespace Exif { long IfdMakerNote::size() const { - return ifd_.size() + ifd_.dataSize(); + return prefix_.size() + ifd_.size() + ifd_.dataSize(); } MakerNoteFactory* MakerNoteFactory::pInstance_ = 0; @@ -229,7 +243,7 @@ namespace Exif { // Todo: use case insensitive make and model comparisons - // find or create a registry entry for make + // Find or create a registry entry for make ModelRegistry* modelRegistry = 0; Registry::const_iterator end1 = registry_.end(); Registry::const_iterator pos1; @@ -243,7 +257,7 @@ namespace Exif { modelRegistry = new ModelRegistry; registry_.push_back(std::make_pair(make, modelRegistry)); } - // find or create a registry entry for model + // Find or create a registry entry for model ModelRegistry::iterator end2 = modelRegistry->end(); ModelRegistry::iterator pos2; for (pos2 = modelRegistry->begin(); pos2 != end2; ++pos2) { diff --git a/src/makernote.hpp b/src/makernote.hpp index e94b6a54..8b81461a 100644 --- a/src/makernote.hpp +++ b/src/makernote.hpp @@ -22,7 +22,7 @@ @file makernote.hpp @brief Contains the %Exif %MakerNote interface, IFD %MakerNote and a MakerNote factory - @version $Name: $ $Revision: 1.14 $ + @version $Name: $ $Revision: 1.15 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 18-Feb-04, ahu: created @@ -113,7 +113,8 @@ namespace Exif { for the Entries. */ MakerNote(const MnTagInfo* pMnTagInfo =0, bool alloc =true) - : pMnTagInfo_(pMnTagInfo), alloc_(alloc), byteOrder_(invalidByteOrder) {} + : pMnTagInfo_(pMnTagInfo), alloc_(alloc), + byteOrder_(invalidByteOrder), offset_(0) {} //! Virtual destructor. virtual ~MakerNote() {} //@} @@ -160,6 +161,8 @@ namespace Exif { uint16 decomposeKey(const std::string& key) const; //! Return the byte order (little or big endian). ByteOrder byteOrder() const { return byteOrder_; } + //! Return the offset of the makernote from the start of the TIFF header + long offset() const { return offset_; } /*! @brief Return the name of a makernote tag. The default implementation looks up the makernote info tag array if one is set, else @@ -208,7 +211,7 @@ namespace Exif { virtual Entries::const_iterator end() const =0; //! Find an entry by idx, return a const iterator to the record virtual Entries::const_iterator findIdx(int idx) const =0; - //! Return the size of the makernote in bytes. + //! Return the size of the makernote in bytes virtual long size() const =0; //! Return the name of the makernote section virtual std::string sectionName(uint16 tag) const =0; @@ -219,19 +222,23 @@ namespace Exif { //@} protected: + // DATA //! Pointer to an array of makernote tag infos const MnTagInfo* pMnTagInfo_; /*! - Memory management - True: requires memory allocation and deallocation, - False: no memory management needed. + @brief Flag to control the memory management:
+ True: requires memory allocation and deallocation,
+ False: no memory management needed. */ const bool alloc_; /*! - Alternative byte order to use, invalid if the byte order of the - %Exif block can be used + @brief Alternative byte order to use, invalid if the byte order of the + %Exif block can be used */ ByteOrder byteOrder_; + //! Offset of the makernote from the start of the TIFF header + long offset_; + }; // class MakerNote /*! @@ -279,11 +286,12 @@ namespace Exif { //@} protected: - //! Prefix before the start of the IFD + // DATA + //! String prefix at the beginning of the makernote, before the IFD std::string prefix_; /*! - True: Offsets are from start of the TIFF header - False: Offsets are from start of the makernote + @brief True: Offsets are from start of the TIFF header, + False: Offsets are from start of the makernote */ bool absOffset_; //! MakerNote IFD