diff --git a/src/exif.cpp b/src/exif.cpp index 924c227a..55fc87fc 100644 --- a/src/exif.cpp +++ b/src/exif.cpp @@ -20,14 +20,14 @@ */ /* File: exif.cpp - Version: $Name: $ $Revision: 1.22 $ + Version: $Name: $ $Revision: 1.23 $ 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.22 $ $RCSfile: exif.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.23 $ $RCSfile: exif.cpp,v $") // ***************************************************************************** // included header files @@ -60,6 +60,7 @@ namespace { entry of type unsigned long with one component is created. */ void setOffsetTag(Exif::Ifd& ifd, + int idx, Exif::uint16 tag, Exif::uint32 offset, Exif::ByteOrder byteOrder); @@ -71,8 +72,8 @@ namespace { namespace Exif { Metadatum::Metadatum(const Entry& e, ByteOrder byteOrder) - : tag_(e.tag()), ifdId_(e.ifdId()), makerNote_(e.makerNote()), - value_(0), key_(makeKey(e)) + : tag_(e.tag()), ifdId_(e.ifdId()), idx_(e.idx()), + makerNote_(e.makerNote()), value_(0), key_(makeKey(e)) { value_ = Value::create(TypeId(e.type())); value_->read(e.data(), e.count() * e.typeSize(), byteOrder); @@ -81,7 +82,7 @@ namespace Exif { Metadatum::Metadatum(const std::string& key, const Value* value, MakerNote* makerNote) - : makerNote_(makerNote), value_(0), key_(key) + : idx_(0), makerNote_(makerNote), value_(0), key_(key) { if (value) value_ = value->clone(); std::pair p = decomposeKey(key, makerNote); @@ -98,8 +99,8 @@ namespace Exif { } Metadatum::Metadatum(const Metadatum& rhs) - : tag_(rhs.tag_), ifdId_(rhs.ifdId_), makerNote_(rhs.makerNote_), - value_(0), key_(rhs.key_) + : tag_(rhs.tag_), ifdId_(rhs.ifdId_), idx_(rhs.idx_), + makerNote_(rhs.makerNote_), value_(0), key_(rhs.key_) { if (rhs.value_ != 0) value_ = rhs.value_->clone(); // deep copy } @@ -109,6 +110,7 @@ namespace Exif { if (this == &rhs) return *this; tag_ = rhs.tag_; ifdId_ = rhs.ifdId_; + idx_ = rhs.idx_; makerNote_ = rhs.makerNote_; delete value_; value_ = 0; @@ -498,18 +500,6 @@ namespace Exif { // Find and read ExifIFD sub-IFD of IFD0 rc = ifd0_.readSubIfd(exifIfd_, data_, byteOrder(), 0x8769); if (rc) return rc; - // Find and read Interoperability IFD in ExifIFD - rc = exifIfd_.readSubIfd(iopIfd_, data_, byteOrder(), 0xa005); - if (rc) return rc; - // Find and read GPSInfo sub-IFD in IFD0 - rc = ifd0_.readSubIfd(gpsIfd_, data_, byteOrder(), 0x8825); - if (rc) return rc; - // Read IFD1 - if (ifd0_.next()) { - rc = ifd1_.read(data_ + ifd0_.next(), byteOrder(), ifd0_.next()); - if (rc) return rc; - } - // Find MakerNote in ExifIFD, create a MakerNote class Ifd::iterator pos = exifIfd_.findTag(0x927c); Ifd::iterator make = ifd0_.findTag(0x010f); @@ -535,7 +525,17 @@ namespace Exif { if (makerNote_) { exifIfd_.erase(pos); } - + // Find and read Interoperability IFD in ExifIFD + rc = exifIfd_.readSubIfd(iopIfd_, data_, byteOrder(), 0xa005); + if (rc) return rc; + // Find and read GPSInfo sub-IFD in IFD0 + rc = ifd0_.readSubIfd(gpsIfd_, data_, byteOrder(), 0x8825); + if (rc) return rc; + // Read IFD1 + if (ifd0_.next()) { + rc = ifd1_.read(data_ + ifd0_.next(), byteOrder(), ifd0_.next()); + if (rc) return rc; + } // Find and delete ExifIFD sub-IFD of IFD1 pos = ifd1_.findTag(0x8769); if (pos != ifd1_.end()) { @@ -586,7 +586,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 (updateIfds()) { + if (updateEntries()) { //ahu Todo: remove debugging output std::cout << "->>>>>> using non-intrusive writing <<<<<<-\n"; @@ -618,11 +618,24 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n"; long exifIfdOffset = ifd0Offset + ifd0.size() + ifd0.dataSize(); Ifd exifIfd(exifIfd, exifIfdOffset); addToIfd(exifIfd, begin(), end(), byteOrder()); + if (makerNote_) { + // Create a placeholder MakerNote entry of the correct size and + // add it to the Exif IFD + Entry e; + e.setIfdId(makerIfd); + e.setTag(0x927c); + long size = makerNote_->size(); + char* buf = new char[size]; + memset(buf, 0x0, size); + e.setValue(undefined, size, buf, size); + exifIfd.add(e); + delete[] buf; + } // Set the offset to the Exif IFD in IFD0 - ifd0.erase(0x8769); + int idx = ifd0.erase(0x8769); if (exifIfd.size() > 0) { - setOffsetTag(ifd0, 0x8769, exifIfdOffset, byteOrder()); + setOffsetTag(ifd0, idx, 0x8769, exifIfdOffset, byteOrder()); } // Build Interoperability IFD from metadata @@ -631,9 +644,9 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n"; addToIfd(iopIfd, begin(), end(), byteOrder()); // Set the offset to the Interoperability IFD in Exif IFD - exifIfd.erase(0xa005); + idx = exifIfd.erase(0xa005); if (iopIfd.size() > 0) { - setOffsetTag(exifIfd, 0xa005, iopIfdOffset, byteOrder()); + setOffsetTag(exifIfd, idx, 0xa005, iopIfdOffset, byteOrder()); } // Build GPSInfo IFD from metadata @@ -642,9 +655,9 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n"; addToIfd(gpsIfd, begin(), end(), byteOrder()); // Set the offset to the GPSInfo IFD in IFD0 - ifd0.erase(0x8825); + idx = ifd0.erase(0x8825); if (gpsIfd.size() > 0) { - setOffsetTag(ifd0, 0x8825, gpsIfdOffset, byteOrder()); + setOffsetTag(ifd0, idx, 0x8825, gpsIfdOffset, byteOrder()); } // Update Exif data from thumbnail, build IFD1 from updated metadata @@ -660,11 +673,18 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n"; ifd0.setNext(ifd1Offset); } - // Copy all IFDs and the thumbnail image to the data buffer + // Copy all IFDs, the MakerNote data and the thumbnail to the data buffer ifd0.sortByTag(); ifd0.copy(buf + ifd0Offset, byteOrder(), ifd0Offset); exifIfd.sortByTag(); exifIfd.copy(buf + exifIfdOffset, byteOrder(), exifIfdOffset); + if (makerNote_) { + // Copy the MakerNote over the placeholder data + Entries::iterator mn = exifIfd.findTag(0x927c); + makerNote_->copy(buf + exifIfdOffset + mn->offset(), + byteOrder(), + exifIfdOffset + mn->offset()); + } iopIfd.sortByTag(); iopIfd.copy(buf + iopIfdOffset, byteOrder(), iopIfdOffset); gpsIfd.sortByTag(); @@ -737,6 +757,18 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n"; FindMetadatumByKey(key)); } + ExifData::const_iterator ExifData::findIfdIdIdx(IfdId ifdId, int idx) const + { + return std::find_if(metadata_.begin(), metadata_.end(), + FindMetadatumByIfdIdIdx(ifdId, idx)); + } + + ExifData::iterator ExifData::findIfdIdIdx(IfdId ifdId, int idx) + { + return std::find_if(metadata_.begin(), metadata_.end(), + FindMetadatumByIfdIdIdx(ifdId, idx)); + } + void ExifData::sortByKey() { std::sort(metadata_.begin(), metadata_.end(), cmpMetadataByKey); @@ -752,30 +784,30 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n"; metadata_.erase(pos); } - bool ExifData::updateIfds() + bool ExifData::updateEntries() { if (!this->compatible()) return false; bool compatible = true; - compatible |= updateIfd(ifd0_); - compatible |= updateIfd(exifIfd_); - compatible |= updateIfd(iopIfd_); - compatible |= updateIfd(gpsIfd_); - compatible |= updateIfd(ifd1_); + compatible |= updateRange(ifd0_.begin(), ifd0_.end()); + compatible |= updateRange(exifIfd_.begin(), exifIfd_.end()); + if (makerNote_) { + compatible |= updateRange(makerNote_->begin(), makerNote_->end()); + } + compatible |= updateRange(iopIfd_.begin(), iopIfd_.end()); + compatible |= updateRange(gpsIfd_.begin(), gpsIfd_.end()); + compatible |= updateRange(ifd1_.begin(), ifd1_.end()); return compatible; - } // ExifData::updateIfds + } // ExifData::updateEntries - bool ExifData::updateIfd(Ifd& ifd) + bool ExifData::updateRange(const Entries::iterator& begin, + const Entries::iterator& end) { - if (ifd.alloc()) throw Error("Invariant violated in ExifData::updateIfd"); - bool compatible = true; - Ifd::iterator end = ifd.end(); - for (Ifd::iterator entry = ifd.begin(); entry != end; ++entry) { + for (Entries::iterator entry = begin; entry != end; ++entry) { // find the corresponding metadatum - std::string key = makeKey(*entry); - const_iterator md = findKey(key); + const_iterator md = findIfdIdIdx(entry->ifdId(), entry->idx()); if (md == this->end()) { // corresponding metadatum was deleted: this is not (yet) a // supported non-intrusive write operation. @@ -796,31 +828,54 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n"; } } return compatible; - } // ExifData::updateIfd + } // ExifData::updateRange bool ExifData::compatible() const { bool compatible = true; - const_iterator end = this->end(); - for (const_iterator md = begin(); md != end; ++md) { - // Check if the metadatum is compatible with the - // corresponding IFD entry - const Ifd* ifd = getIfd(md->ifdId()); - if (!ifd) { - compatible = false; - break; - } - Ifd::const_iterator entry = ifd->findTag(md->tag()); - if (entry == ifd->end()) { + // For each metadatum, check if it is compatible with the corresponding + // IFD or MakerNote entry + for (const_iterator md = begin(); md != this->end(); ++md) { + std::pair rc; + rc = findEntry(md->ifdId(), md->idx()); + // Make sure that we have an entry + if (!rc.first) { compatible = false; break; } - if (md->size() > entry->size()) { + // Make sure that the size of the metadatum fits the available size + // of the entry + if (md->size() > rc.second->size()) { compatible = false; break; } } return compatible; + } // ExifData::compatible + + std::pair + ExifData::findEntry(IfdId ifdId, int idx) const + { + Entries::const_iterator entry; + std::pair rc(false, entry); + + if (ifdId == makerIfd && makerNote_) { + entry = makerNote_->findIdx(idx); + if (entry != makerNote_->end()) { + rc.first = true; + rc.second = entry; + } + return rc; + } + const Ifd* ifd = getIfd(ifdId); + if (ifdId != makerIfd && ifd) { + entry = ifd->findIdx(idx); + if (entry != ifd->end()) { + rc.first = true; + rc.second = entry; + } + } + return rc; } const Ifd* ExifData::getIfd(IfdId ifdId) const @@ -872,6 +927,7 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n"; Entry e(ifd.alloc()); e.setIfdId(metadatum.ifdId()); + e.setIdx(metadatum.idx()); e.setTag(metadatum.tag()); e.setOffset(0); // will be calculated when the IFD is written char* buf = new char[metadatum.size()]; @@ -924,6 +980,7 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n"; namespace { void setOffsetTag(Exif::Ifd& ifd, + int idx, Exif::uint16 tag, Exif::uint32 offset, Exif::ByteOrder byteOrder) @@ -932,6 +989,7 @@ namespace { if (pos == ifd.end()) { Exif::Entry e(ifd.alloc()); e.setIfdId(ifd.ifdId()); + e.setIdx(idx); e.setTag(tag); e.setOffset(0); // will be calculated when the IFD is written ifd.add(e); diff --git a/src/exif.hpp b/src/exif.hpp index 98c945db..e7f1af48 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.24 $ + @version $Name: $ $Revision: 1.25 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 09-Jan-04, ahu: created @@ -61,16 +61,14 @@ namespace Exif { */ class Metadatum { public: + //! @name Creators + //@{ /*! - @brief Constructor to build a metadatum from an IFD entry. - */ - Metadatum(const Entry& e, ByteOrder byteOrder); - /*! - @brief Constructor for new tags created by an application, which only - needs to provide a key / value pair. %Metadatum copies (clones) - the value if one is provided. Alternatively, a program can - create an 'empty' metadatum with only a key and set the value - later, using setValue(). + @brief Constructor for new tags created by an application. The + metadatum is created from a key / value pair. %Metadatum copies + (clones) the value if one is provided. Alternatively, a program + can create an 'empty' metadatum with only a key and set the + value using setValue(). @param key The key of the metadatum. @param value Pointer to a metadatum value. @@ -82,10 +80,16 @@ namespace Exif { explicit Metadatum(const std::string& key, const Value* value =0, MakerNote* makerNote =0); - //! Destructor - ~Metadatum(); + //! Constructor to build a metadatum from an IFD entry. + Metadatum(const Entry& e, ByteOrder byteOrder); //! Copy constructor Metadatum(const Metadatum& rhs); + //! Destructor + ~Metadatum(); + //@} + + //! @name Manipulators + //@{ //! Assignment operator Metadatum& operator=(const Metadatum& rhs); /*! @@ -98,6 +102,10 @@ namespace Exif { not have a value yet, then an AsciiValue is created. */ void setValue(const std::string& buf); + //@} + + //! @name Accessors + //@{ /*! @brief Write value to a character data buffer and return the number of characters (bytes) written. @@ -111,22 +119,21 @@ namespace Exif { */ long copy(char* buf, ByteOrder byteOrder) const { return value_ == 0 ? 0 : value_->copy(buf, byteOrder); } - //! @name Accessors - //@{ /*! - @brief Return a key for the tag. The key is of the form - 'ifdItem.sectionName.tagName'. + @brief Return the key of the metadatum. The key is of the form + 'ifdItem.sectionName.tagName'. Note however that the key + is not necessarily unique. */ std::string key() const { return key_; } - //! Return the related image item + //! Return the related image item (the first part of the key) const char* ifdItem() const { return ExifTags::ifdItem(ifdId_); } - //! Return the name of the section + //! Return the name of the section (the second part of the key) std::string sectionName() const; - //! Return the name of the tag + //! Return the name of the tag (which is also the third part of the key) std::string tagName() const; //! Return the tag uint16 tag() const { return tag_; } - //! Return the type id. + //! Return the type id of the value TypeId typeId() const { return value_ == 0 ? invalid : value_->typeId(); } //! Return the name of the type const char* typeName() const { return TypeInfo::typeName(typeId()); } @@ -140,6 +147,8 @@ namespace Exif { IfdId ifdId() const { return ifdId_; } //! Return the name of the IFD const char* ifdName() const { return ExifTags::ifdName(ifdId_); } + //! Return the index (unique id of this metadatum within the original IFD) + int idx() const { return idx_; } //! Return the pointer to the associated MakerNote MakerNote* makerNote() const { return makerNote_; } //! Return the value as a string. @@ -208,6 +217,7 @@ namespace Exif { private: uint16 tag_; //!< Tag value IfdId ifdId_; //!< The IFD associated with this tag + int idx_; //!< Unique id of an entry within one IFD MakerNote* makerNote_; //!< Pointer to the associated MakerNote Value* value_; //!< Pointer to the value std::string key_; //!< Key @@ -220,39 +230,26 @@ namespace Exif { */ std::ostream& operator<<(std::ostream& os, const Metadatum& md); - //! Container type to hold all metadata - typedef std::vector Metadata; - - //! Unary predicate that matches a Metadatum with a given key - class FindMetadatumByKey { - public: - //! Constructor, initializes the object with the tag to look for - FindMetadatumByKey(const std::string& key) : key_(key) {} - /*! - @brief Returns true if the key of the argument metadatum is equal - to that of the object. - */ - bool operator()(const Metadatum& metadatum) const - { return key_ == metadatum.key(); } - - private: - std::string key_; - - }; // class FindMetadatumByTag - //! %Thumbnail data Todo: add, create, rotate, delete class Thumbnail { public: + //! %Thumbnail image types + enum Type { none, jpeg, tiff }; + + //! @name Creators + //@{ //! Default constructor Thumbnail(); //! Destructor ~Thumbnail(); //! Copy constructor Thumbnail(const Thumbnail& rhs); + //@} + + //! @name Manipulators + //@{ //! Assignment operator Thumbnail& operator=(const Thumbnail& rhs); - //! %Thumbnail image types - enum Type { none, jpeg, tiff }; /*! @brief Read the thumbnail from the data buffer buf, using %Exif metadata exifData. Return 0 if successful. @@ -272,6 +269,10 @@ namespace Exif { int read(const char* buf, const ExifData& exifData, ByteOrder byteOrder =littleEndian); + //@} + + //! @name Accessors + //@{ /*! @brief Write thumbnail to file path, return 0 if successful, -1 if there is no thumbnail image to write. @@ -312,15 +313,21 @@ namespace Exif { long size() const; //! Return the type of the thumbnail Type type() const { return type_; } + //@} private: - + //! @name Manipulators + //@{ //! Read a compressed (JPEG) thumbnail image from the data buffer int readJpegImage(const char* buf, const ExifData& exifData); //! Read an uncompressed (TIFF) thumbnail image from the data buffer int readTiffImage(const char* buf, const ExifData& exifData, ByteOrder byteOrder); + //@} + + //! @name Accessors + //@{ //! Update the Exif data according to the actual JPEG thumbnail image void updateJpegImage(ExifData& exifData) const; //! Update the Exif data according to the actual TIFF thumbnail image @@ -333,15 +340,56 @@ namespace Exif { void setJpegImageOffsets(Ifd& ifd1, ByteOrder byteOrder) const; //! Update the offsets to the TIFF thumbnail image in the IFD void setTiffImageOffsets(Ifd& ifd1, ByteOrder byteOrder) const; + //@} + // DATA Type type_; // Type of thumbnail image long size_; // Size of the image data char* image_; // Thumbnail image data TiffHeader tiffHeader_; // Thumbnail TIFF Header, only for TIFF thumbs Ifd ifd_; // Thumbnail IFD, only for TIFF thumbnails - + }; // class Thumbnail + //! Container type to hold all metadata + typedef std::vector Metadata; + + //! Unary predicate that matches a Metadatum with a given key + class FindMetadatumByKey { + public: + //! Constructor, initializes the object with the tag to look for + FindMetadatumByKey(const std::string& key) : key_(key) {} + /*! + @brief Returns true if the key of the argument metadatum is equal + to that of the object. + */ + bool operator()(const Metadatum& metadatum) const + { return key_ == metadatum.key(); } + + private: + std::string key_; + + }; // class FindMetadatumByTag + + //! Unary predicate that matches a Metadatum with a given ifd id and idx + class FindMetadatumByIfdIdIdx { + public: + //! Constructor, initializes the object with the ifd id and idx to look for + FindMetadatumByIfdIdIdx(IfdId ifdId, int idx) + : ifdId_(ifdId), idx_(idx) {} + /*! + @brief Returns true if the ifd id and idx of the argument metadatum + is equal to that of the object. + */ + bool operator()(const Metadatum& metadatum) const + { return ifdId_ == metadatum.ifdId() && idx_ == metadatum.idx(); } + + private: + IfdId ifdId_; + int idx_; + + }; // class FindMetadatumByIfdIdIdx + /*! @brief A container for %Exif data. This is the top-level class of the Exiv2 library. @@ -355,15 +403,29 @@ namespace Exif { - extract and delete %Exif thumbnail (JPEG and TIFF thumbnails) */ class ExifData { - // Copying not allowed (Todo: implement me!) + //! @name Not implemented + //@{ + //! Copying not allowed (Todo: implement me!) ExifData(const ExifData& rhs); - // Assignment not allowed (Todo: implement me!) + //! Assignment not allowed (Todo: implement me!) ExifData& operator=(const ExifData& rhs); + //@} public: + //! Metadata iterator type + typedef Metadata::iterator iterator; + //! Metadata const iterator type + typedef Metadata::const_iterator const_iterator; + + //! @name Creators + //@{ //! Default constructor ExifData(); //! Destructor ~ExifData(); + //@} + + //! @name Manipulators + //@{ /*! @brief Read the %Exif data from file path. @param path Path to the file @@ -436,25 +498,12 @@ namespace Exif { multiple metadata with the same key. */ void add(const Metadatum& metadatum); - - /*! - @brief Return the approximate size of all %Exif data (TIFF header plus - metadata). The number returned may be bigger than the actual - size of the %Exif data, but it is never smaller. Only copy() - returns the exact size. - */ - long size() const; - //! Returns the byte order as specified in the TIFF header - ByteOrder byteOrder() const { return tiffHeader_.byteOrder(); } - - //! Metadata iterator type - typedef Metadata::iterator iterator; - //! Metadata const iterator type - typedef Metadata::const_iterator const_iterator; - //! Begin of the metadata - const_iterator begin() const { return metadata_.begin(); } - //! End of the metadata - const_iterator end() const { return metadata_.end(); } + //! Delete the metadatum at iterator position pos + void erase(iterator pos); + //! Sort metadata by key + void sortByKey(); + //! Sort metadata by tag + void sortByTag(); //! Begin of the metadata iterator begin() { return metadata_.begin(); } //! End of the metadata @@ -465,19 +514,57 @@ namespace Exif { which of the matching metadata is found. */ iterator findKey(const std::string& key); + /*! + @brief Find the metadatum with the given ifd id and idx, return an + iterator to it. + + This method can be used to uniquely identify a metadatum that was + created from an IFD or from the makernote (with idx greater than + 0). Metadata created by an application (not read from an IFD or a + makernote) all have their idx field set to 0, i.e., they cannot be + uniquely identified with this method. If multiple metadata with the + same key exist, it is undefined which of the matching metadata is + found. + */ + iterator findIfdIdIdx(IfdId ifdId, int idx); + //@} + + //! @name Accessors + //@{ + //! Begin of the metadata + const_iterator begin() const { return metadata_.begin(); } + //! End of the metadata + const_iterator end() const { return metadata_.end(); } /*! @brief Find a metadatum with the given key, return a const iterator to it. If multiple metadata with the same key exist, it is undefined which of the matching metadata is found. */ const_iterator findKey(const std::string& key) const; - //! Sort metadata by key - void sortByKey(); - //! Sort metadata by tag - void sortByTag(); - //! Delete the metadatum at iterator position pos - void erase(iterator pos); + /*! + @brief Find the metadatum with the given ifd id and idx, return an + iterator to it. + This method can be used to uniquely identify a metadatum that was + created from an IFD or from the makernote (with idx greater than + 0). Metadata created by an application (not read from an IFD or a + makernote) all have their idx field set to 0, i.e., they cannot be + uniquely identified with this method. If multiple metadata with the + same key exist, it is undefined which of the matching metadata is + found. + */ + const_iterator findIfdIdIdx(IfdId ifdId, int idx) const; + //! Get the number of metadata entries + long count() const { return metadata_.size(); } + /*! + @brief Return the approximate size of all %Exif data (TIFF header plus + metadata). The number returned may be bigger than the actual + size of the %Exif data, but it is never smaller. Only copy() + returns the exact size. + */ + long size() const; + //! 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 @@ -489,44 +576,68 @@ namespace Exif { Thumbnail::Type thumbnailType() const { return thumbnail_.type(); } //! Return the size of the thumbnail data long thumbnailSize() const { return thumbnail_.size(); } + //@} private: - // Return a pointer to the internal IFD identified by its IFD id - const Ifd* getIfd(IfdId ifdId) const; - /* - Check if the metadata changed and update the internal IFDs if the - changes are compatible with the existing data (non-intrusive write - support). Return true if only compatible changes were detected in the - metadata and the internal IFDs (i.e., data buffer) were updated - successfully. Return false, if non-intrusive writing is not - possible. The internal IFDs and the data buffer may or may not be - modified in this case. - */ - bool updateIfds(); - /* - Update the metadata of one IFD. Called by updateIfds() for each - of the internal IFDs. - */ - bool updateIfd(Ifd& ifd); - /* - Check if the metadata is compatible with the internal IFDs for - non-intrusive writing. Return true if compatible, false if not. + //! @name Accessors + //@{ + /*! + @brief Check if the metadata is compatible with the internal IFDs for + non-intrusive writing. Return true if compatible, false if not. - Note: This function does not detect deleted metadata as incompatible, - although the deletion of metadata is not (yet) a supported - non-intrusive write operation. + @note This function does not detect deleted metadata as incompatible, + although the deletion of metadata is not (yet) a supported + non-intrusive write operation. */ bool compatible() const; - /* - Write Exif data to a data buffer the hard way, return number of bytes - written. Rebuilds the Exif data from scratch, using the TIFF header, - metadata container and thumbnail. In particular, the internal IFDs and - the original data buffer are not used. Furthermore, this method - updates the Exif data with the metadata from the actual thumbnail - image (overriding existing metadata). + /*! + @brief Find the IFD or makernote entry corresponding to ifd id and idx. + + @return A pair of which the first part determines if a match was found + and, if true, the second contains an iterator to the entry. + */ + std::pair + findEntry(IfdId ifdId, int idx) const; + //! Return a pointer to the internal IFD identified by its IFD id + const Ifd* getIfd(IfdId ifdId) const; + //@} + + //! @name Manipulators + //@{ + /*! + @brief Check if the metadata changed and update the internal IFDs and + the MakerNote if the changes are compatible with the existing + data (non-intrusive write support). + + @return True if only compatible changes were detected in the metadata + and the internal IFDs and MakerNote (and thus the data buffer) + were updated successfully. Return false, if non-intrusive + writing is not possible. The internal IFDs and the MakerNote + (and thus the data buffer) may or may not be modified in this + case. + */ + bool updateEntries(); + /*! + @brief Update the metadata for a range of entries. Called by + updateEntries() for each of the internal IFDs and the MakerNote + (if any). + */ + bool updateRange(const Entries::iterator& begin, + const Entries::iterator& end); + /*! + @brief Write Exif data to a data buffer the hard way, return number of + bytes written. + + Rebuilds the Exif data from scratch, using the TIFF header, metadata + container and thumbnail. In particular, the internal IFDs and the + original data buffer are not used. Furthermore, this method updates + the Exif data with the metadata from the actual thumbnail image + (overriding existing metadata). */ long copyFromMetadata(char* buf); + //@} + // DATA TiffHeader tiffHeader_; Metadata metadata_; Thumbnail thumbnail_;