// ***************************************************************** -*- C++ -*- /* * Copyright (C) 2004 Andreas Huggel * * This program is part of the Exiv2 distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*! @file ifd.hpp @brief Encoding and decoding of IFD (%Image File Directory) data @version $Name: $ $Revision: 1.20 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 09-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component */ #ifndef IFD_HPP_ #define IFD_HPP_ // ***************************************************************************** // included header files #include "types.hpp" // + standard includes #include #include #include // ***************************************************************************** // namespace extensions namespace Exiv2 { // ***************************************************************************** // class declarations class MakerNote; // ***************************************************************************** // class definitions /*! @brief Data structure for one IFD directory entry. See the description of class Ifd for an explanation of the supported modes for memory allocation. */ class Entry { public: //! @name Creators //@{ /*! @brief Default constructor. The entry allocates memory for its data if alloc is true (the default), otherwise it remembers just the pointers into a read and writeable data buffer which it doesn't allocate or delete. */ explicit Entry(bool alloc =true); //! Destructor ~Entry(); //! Copy constructor Entry(const Entry& rhs); //@} //! @name Manipulators //@{ //! Assignment operator Entry& operator=(const Entry& rhs); //! Set the tag void setTag(uint16 tag) { tag_ = tag; } //! Set the IFD id void setIfdId(IfdId ifdId) { ifdId_ = ifdId; } //! Set the index (unique id of an entry within one IFD) void setIdx(int idx) { idx_ = idx; } //! Set the pointer to the MakerNote void setMakerNote(MakerNote* makerNote) { pMakerNote_ = makerNote; } //! Set the offset. The offset is relative to the start of the IFD. void setOffset(uint32 offset) { offset_ = offset; } /*! @brief Set the value of the entry to a single unsigned long component, i.e., set the type of the entry to unsigned long, number of components to one and the value according to the data provided. The size of the data buffer is set to at least four bytes, but is left unchanged if it can accomodate the pointer. This method can be used to set the value of a tag which contains a pointer (offset) to a location in the Exif data (like e.g., ExifTag, 0x8769 in IFD0, which contains a pointer to the Exif IFD).
This method cannot be used to set the value of a newly created %Entry in non-alloc mode. */ void setValue(uint32 data, ByteOrder byteOrder); /*! @brief Set type, count, the data buffer and its size. Copies the provided buffer when called in memory allocation mode.
In non-alloc mode, use this method to initialise the data of a newly created %Entry. In this case, only the pointer to the buffer is copied, i.e., the buffer must remain valid throughout the life of the %Entry. Subsequent calls in non-alloc mode will overwrite the data pointed to by this pointer with the data provided, i.e., the buffer provided in subsequent calls can be deleted after the call.
In either memory allocation mode, the data buffer provided must be large enough to hold count components of type. The size of the buffer will be as indicated in the size argument. I.e., it is possible to allocate (set) a data buffer larger than required to hold count components of the given type. @param type The type of the data. @param count Number of components in the buffer. @param data Pointer to the data buffer. @param size Size of the desired data buffer in bytes. @throw Error ("Value too large") if no memory allocation is allowed and the size of the data buffer is larger than the existing data buffer of the entry.
@throw Error ("Size too small") if size is not large enough to hold count components of the given type. */ void setValue(uint16 type, uint32 count, const byte* data, long size); //@} //! @name Accessors //@{ //! Return the tag uint16 tag() const { return tag_; } //! Return the type id. uint16 type() const { return type_; } //! Return the name of the type const char* typeName() const { return TypeInfo::typeName(TypeId(type_)); } //! Return the size in bytes of one element of this type long typeSize() const { return TypeInfo::typeSize(TypeId(type_)); } //! Return the IFD id IfdId ifdId() const { return ifdId_; } //! Return the index (unique id >0 of an entry within an IFD, 0 if not set) int idx() const { return idx_; } //! Return the pointer to the associated MakerNote MakerNote* makerNote() const { return pMakerNote_; } //! Return the number of components in the value uint32 count() const { return count_; } /*! @brief Return the size of the data buffer in bytes. @note There is no minimum size for the data buffer, except that it must be large enough to hold the data. */ long size() const { return size_; } //! Return the offset from the start of the IFD to the data of the entry uint32 offset() const { return offset_; } /*! @brief Return a pointer to the data area. Do not attempt to write to this pointer. */ const byte* data() const { return pData_; } /*! @brief Return a pointer to the n-th component, 0 if there is no n-th component. Do not attempt to write to this pointer. */ const byte* component(uint32 n) const; //! Get the memory allocation mode bool alloc() const { return alloc_; } //@} private: // DATA /*! True: Requires memory allocation and deallocation,
False: No memory management needed. */ bool alloc_; //! Redundant IFD id (it is also at the IFD) IfdId ifdId_; //! Unique id of an entry within an IFD (0 if not set) int idx_; //! Pointer to the associated MakerNote MakerNote* pMakerNote_; //! Tag uint16 tag_; //! Type uint16 type_; //! Number of components uint32 count_; //! Offset from the start of the IFD to the data uint32 offset_; /*! Size of the data buffer holding the value in bytes, there is no minimum size. */ long size_; //! Pointer to the data buffer byte* pData_; }; // class Entry //! Container type to hold all IFD directory entries typedef std::vector Entries; //! Unary predicate that matches an Entry with a given index class FindEntryByIdx { public: //! Constructor, initializes the object with the index to look for FindEntryByIdx(int idx) : idx_(idx) {} /*! @brief Returns true if the idx of the argument entry is equal to that of the object. */ bool operator()(const Entry& entry) const { return idx_ == entry.idx(); } private: int idx_; }; // class FindEntryByIdx //! Unary predicate that matches an Entry with a given tag class FindEntryByTag { public: //! Constructor, initializes the object with the tag to look for FindEntryByTag(uint16 tag) : tag_(tag) {} /*! @brief Returns true if the tag of the argument entry is equal to that of the object. */ bool operator()(const Entry& entry) const { return tag_ == entry.tag(); } private: uint16 tag_; }; // class FindEntryByTag /*! @brief Models an IFD (%Image File Directory) This class models an IFD as described in the TIFF 6.0 specification. An instance of class %Ifd can operate in two modes, one that allocates and deallocates the memory required to store data, and one that doesn't perform such memory management.
An external data buffer (not managed by %Ifd) is needed for an instance of %Ifd which operates in no memory management mode. The %Ifd will maintain only pointers into this buffer.
The mode without memory management is used to make "non-intrusive write support" possible. This allows writing to Exif data of an image without changing the data layout of the Exif data, to maximize chances that tag data, which the Exif reader may not understand (e.g., the Makernote) remains valid. A "non-intrusive write operation" is the modification of tag data without increasing the data size. @note Use the mode with memory management (the default) if you are unsure or if these memory management considerations are of no concern to you. @note The two different modes imply completely different copy and assignment behaviours, with the first resulting in entirely separate classes and the second mode resulting in multiple classes using one and the same data buffer. */ class Ifd { //! @name Not implemented //@{ //! Assignment not allowed (memory management mode alloc_ is const) Ifd& operator=(const Ifd& rhs); //@} public: //! @name Creators //@{ /*! @brief Constructor. Allows to set the IFD identifier. Memory management is enabled, offset is set to 0. Serves as default constructor. */ explicit Ifd(IfdId ifdId =ifdIdNotSet); /*! @brief Constructor. Allows to set the IFD identifier and the offset of the IFD from the start of TIFF header. Memory management is enabled. */ Ifd(IfdId ifdId, uint32 offset); /*! @brief Constructor. Allows to set the IFD identifier, offset of the IFD from the start of TIFF header and choose whether or not memory management is required for the Entries. */ Ifd(IfdId ifdId, uint32 offset, bool alloc); //! Copy constructor Ifd(const Ifd& rhs); //! Destructor ~Ifd(); //@} //! Entries const iterator type typedef Entries::const_iterator const_iterator; //! Entries iterator type typedef Entries::iterator iterator; //! @name Manipulators //@{ /*! @brief Read a complete IFD and its data from a data buffer @param buf Pointer to the data to decode. The buffer must start with the IFD data (unlike the readSubIfd() method). @param len Number of bytes in the data buffer @param byteOrder Applicable byte order (little or big endian). @param offset (Optional) offset of the IFD from the start of the TIFF header, if known. If not given, the offset will be guessed using the assumption that the smallest offset of all IFD directory entries points to a data buffer immediately follwing the IFD. @return 0 if successful;
6 if the data buffer is too small, e.g., if an offset points beyond the provided buffer. The IFD is cleared in this case. */ int read(const byte* buf, long len, ByteOrder byteOrder, long offset =0); /*! @brief Read a sub-IFD from the location pointed to by the directory entry with the given tag. @param dest References the destination IFD. @param buf The data buffer to read from. The buffer must contain all Exif data starting from the TIFF header (unlike the read() method). @param len Number of bytes in the data buffer @param byteOrder Applicable byte order (little or big endian). @param tag Tag to look for. @return 0 if successful;
6 if reading the sub-IFD failed (see read() above) or the location pointed to by the directory entry with the given tag is outside of the data buffer. @note It is not considered an error if the tag cannot be found in the IFD. 0 is returned and no action is taken in this case. */ int readSubIfd( Ifd& dest, const byte* buf, long len, ByteOrder byteOrder, uint16 tag ) const; /*! @brief Copy the IFD to a data array, update the offsets of the IFD and all its entries, return the number of bytes written. First the number of IFD entries is written (2 bytes), followed by all directory entries: tag (2), type (2), number of data components (4) and offset to the data or the data, if it occupies not more than four bytes (4). The directory entries are followed by the offset of the next IFD (4). All these fields are encoded according to the byte order argument. Data that doesn't fit into the offset fields follows immediately after the IFD entries. The offsets in the IFD are set to correctly point to the data fields, using the offset parameter or the offset of the IFD. @param buf Pointer to the data buffer. The user must ensure that the buffer has enough memory. Otherwise the call results in undefined behaviour. @param byteOrder Applicable byte order (little or big endian). @param offset Target offset from the start of the TIFF header of the data array. The IFD offsets will be adjusted as necessary. If not given, then it is assumed that the IFD will remain at its original position, i.e., the offset of the IFD will be used. @return Returns the number of characters written. */ long copy(byte* buf, ByteOrder byteOrder, long offset =0); /*! @brief Reset the IFD. Delete all IFD entries from the class and put the object in a state where it can accept completely new entries. */ void clear(); /*! @brief Set the offset of the next IFD. Byte order is needed to update the underlying data buffer in non-alloc mode. */ void setNext(uint32 next, ByteOrder byteOrder); /*! @brief Add the entry to the IFD. No duplicate-check is performed, i.e., it is possible to add multiple entries with the same tag. The memory allocation mode of the entry to be added must match that of the IFD and the IFD ids of the IFD and entry must match. */ void add(const Entry& entry); /*! @brief Delete the directory entry with the given tag. Return the index of the deleted entry or 0 if no entry with tag was found. */ int erase(uint16 tag); /*! @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 iterator begin() { return entries_.begin(); } //! End of the entries iterator end() { return entries_.end(); } //! Find an IFD entry by idx, return an iterator into the entries list iterator findIdx(int idx); //! Find an IFD entry by tag, return an iterator into the entries list iterator findTag(uint16 tag); //@} //! @name Accessors //@{ //! Get the memory allocation mode, see the Ifd class description for details bool alloc() const { return alloc_; } //! The first entry const_iterator begin() const { return entries_.begin(); } //! End of the entries const_iterator end() const { return entries_.end(); } //! Find an IFD entry by idx, return a const iterator into the entries list const_iterator findIdx(int idx) const; //! Find an IFD entry by tag, return a const iterator into the entries list const_iterator findTag(uint16 tag) const; //! Get the IfdId of the IFD 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 long count() const { return static_cast(entries_.size()); } //! Get the size of this IFD in bytes (IFD only, without data) long size() const; /*! @brief Return the total size of the data of this IFD in bytes, sums the size of all directory entries where size is greater than four (i.e., only data that requires memory outside the IFD directory entries is counted). */ long dataSize() const; /*! @brief Print the IFD in human readable format to the given stream; begin each line with prefix. */ void print(std::ostream& os, const std::string& prefix ="") const; //@} private: //! Helper structure to build IFD entries struct PreEntry { uint16 tag_; uint16 type_; uint32 count_; long size_; long offsetLoc_; uint32 offset_; }; //! cmpPreEntriesByOffset needs to know about PreEntry, that's all. friend bool cmpPreEntriesByOffset(const PreEntry&, const PreEntry&); //! Container for 'pre-entries' typedef std::vector PreEntries; // DATA /*! True: requires memory allocation and deallocation, False: no memory management needed. */ const bool alloc_; //! IFD entries Entries entries_; //! IFD Id IfdId ifdId_; //! Offset of the IFD from the start of TIFF header long offset_; //! 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 byte* pNext_; //! The offset of the next IFD as data value (always in sync with *pNext_) uint32 next_; }; // class Ifd // ***************************************************************************** // free functions /*! @brief Compare two IFD entries by tag. Return true if the tag of entry lhs is less than that of rhs. */ bool cmpEntriesByTag(const Entry& lhs, const Entry& rhs); /*! @brief Compare two 'pre-IFD entries' by offset, taking care of special cases where one or both of the entries don't have an offset. Return true if the offset of entry lhs is less than that of rhs, else false. By definition, entries without an offset are greater than those with an offset. */ bool cmpPreEntriesByOffset(const Ifd::PreEntry& lhs, const Ifd::PreEntry& rhs); } // namespace Exiv2 #endif // #ifndef IFD_HPP_