diff --git a/src/exif.cpp b/src/exif.cpp index 9d9e9078..33ba6658 100644 --- a/src/exif.cpp +++ b/src/exif.cpp @@ -45,6 +45,8 @@ EXIV2_RCSID("@(#) $Id$") #include "error.hpp" #include "basicio.hpp" #include "tiffimage.hpp" +#include "tiffimage_int.hpp" +#include "tiffcomposite_int.hpp" // for Tag::root // + standard includes #include @@ -57,18 +59,6 @@ EXIV2_RCSID("@(#) $Id$") // ***************************************************************************** namespace { - //! Unary predicate that matches an Exifdatum with a given IfdId. - class FindExifdatum { - public: - //! Constructor, initializes the object with the IfdId to look for. - FindExifdatum(Exiv2::IfdId ifdId) : ifdId_(ifdId) {} - //! Returns true if IFD id matches. - bool operator()(const Exiv2::Exifdatum& md) const { return ifdId_ == md.ifdId(); } - - private: - Exiv2::IfdId ifdId_; - - }; // class FindExifdatum /*! @brief Exif %Thumbnail image. This abstract base class provides the @@ -166,6 +156,8 @@ namespace { // class member definitions namespace Exiv2 { + using namespace Internal; + /*! @brief Set the value of \em exifDatum to \em value. If the object already has a value, it is replaced. Otherwise a new ValueType\ value @@ -508,17 +500,20 @@ namespace Exiv2 { } // IPTC and XMP are stored elsewhere, not in the Exif APP1 segment. - const IptcData iptcData; - const XmpData xmpData; + const IptcData emptyIptc; + const XmpData emptyXmp; // Encode and check if the result fits into a JPEG Exif APP1 segment - WriteMethod wm = TiffParser::encode(blob, - pData, - size, - byteOrder, - ed, - iptcData, - xmpData); + std::auto_ptr header(new TiffHeader(byteOrder)); + WriteMethod wm = TiffParserWorker::encode(blob, + pData, + size, + ed, + emptyIptc, + emptyXmp, + Tag::root, + TiffMapping::findEncoder, + header.get()); if (blob.size() <= 65527) return wm; // If it doesn't fit, remove additional tags @@ -604,13 +599,16 @@ namespace Exiv2 { } // Encode the remaining Exif tags again, don't care if it fits this time - wm = TiffParser::encode(blob, - pData, - size, - byteOrder, - ed, - iptcData, - xmpData); + wm = TiffParserWorker::encode(blob, + pData, + size, + ed, + emptyIptc, + emptyXmp, + Tag::root, + TiffMapping::findEncoder, + header.get()); + #ifdef DEBUG if (wm == wmIntrusive) { std::cerr << "SIZE OF EXIF DATA IS " << std::dec << blob.size() << " BYTES\n"; @@ -712,7 +710,10 @@ namespace { void eraseIfd(Exiv2::ExifData& ed, Exiv2::IfdId ifdId) { - ed.erase(std::remove_if(ed.begin(), ed.end(), FindExifdatum(ifdId)), ed.end()); + ed.erase(std::remove_if(ed.begin(), + ed.end(), + Exiv2::Internal::FindExifdatum(ifdId)), + ed.end()); } //! @endcond } diff --git a/src/exif.hpp b/src/exif.hpp index 7443e5af..73527ce6 100644 --- a/src/exif.hpp +++ b/src/exif.hpp @@ -518,15 +518,24 @@ namespace Exiv2 { /*! @brief Stateless parser class for Exif data. Images use this class to - decode and encode binary Exif data. See class TiffParser for details. + decode and encode binary Exif data. + + @note Encode is lossy and is not the inverse of decode. */ class EXIV2API ExifParser { public: /*! @brief Decode metadata from a buffer \em pData of length \em size with binary Exif data to the provided metadata container. - Return byte order in which the data is encoded. - See TiffParser::decode(). + + The buffer must start with a TIFF header. Return byte order + in which the data is encoded. + + @param exifData Exif metadata container. + @param pData Pointer to the data buffer. Must point to data in + binary Exif format; no checks are performed. + @param size Length of the data buffer + @return Byte order in which the data is encoded. */ static ByteOrder decode( ExifData& exifData, @@ -534,8 +543,42 @@ namespace Exiv2 { uint32_t size ); /*! - @brief Encode metadata from the provided metadata to Exif format. - See TiffParser::encode(). + @brief Encode Exif metadata from the provided metadata to binary Exif + format. + + The original binary Exif data in the memory block \em pData, \em size + is parsed and updated in-place if possible ("non-intrusive" + writing). If that is not possible (e.g., if new tags were added), the + entire Exif structure is re-written to the \em blob ("intrusive" + writing). The return value indicates which write method was used. If + it is \c wmNonIntrusive, the original memory \em pData, \em size + contains the result and \em blob is empty. If the return value is + \c wmIntrusive, a new Exif structure was created and returned in + \em blob. The memory block \em pData, \em size may be partly updated in + this case and should not be used anymore. + + Encode is a lossy operation. It attempts to fit the Exif data into a + binary block suitable as the payload of a JPEG APP1 Exif segment, + which can be at most 65527 bytes large. Encode omits IFD0 tags that + are "not recorded" in compressed images according to the Exif 2.2 + specification. It also doesn't write tags in groups which do not occur + in JPEG images. If the resulting binary block is larger than allowed, + it further deletes specific large preview tags and unknown tags. The + operation succeeds even if the end result is still larger than the + allowed size. Application should therefore always check the size of + the \em blob. + + @param blob Container for the binary Exif data if "intrusive" + writing is necessary. Empty otherwise. + @param pData Pointer to the binary Exif data buffer. Must + point to data in Exif format; no checks are + performed. Will be modified if "non-intrusive" + writing is possible. + @param size Length of the data buffer. + @param byteOrder Byte order to use. + @param exifData Exif metadata container. + + @return Write method used. */ static WriteMethod encode( Blob& blob, @@ -546,10 +589,9 @@ namespace Exiv2 { ); /*! @brief Encode metadata from the provided metadata to Exif format. - See TiffParser::encode(). - Encode Exif metadata from the \em ExifData container to binary format - in the \em blob encoded in \em byteOrder. + Encode Exif metadata from the \em ExifData container to binary Exif + format in the \em blob, encoded in \em byteOrder. This simpler encode method uses "intrusive" writing, i.e., it builds the binary representation of the metadata from scratch. It does not @@ -560,6 +602,11 @@ namespace Exiv2 { This is just an inline wrapper for ExifParser::encode(blob, 0, 0, byteOrder, exifData). + + @param blob Container for the binary Exif data if "intrusive" + writing is necessary. Empty otherwise. + @param byteOrder Byte order to use. + @param exifData Exif metadata container. */ static void encode( Blob& blob, diff --git a/src/tiffimage.cpp b/src/tiffimage.cpp index 45c40462..d95a084c 100644 --- a/src/tiffimage.cpp +++ b/src/tiffimage.cpp @@ -208,11 +208,28 @@ namespace Exiv2 { const XmpData& xmpData ) { + // Copy to be able to modify the Exif data + ExifData ed = exifData; + + // Delete IFDs which do not occur in TIFF images + static const IfdId filteredIfds[] = { + panaRawIfdId + }; + for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfds); ++i) { +#ifdef DEBUG + std::cerr << "Warning: Exif IFD " << filteredIfds[i] << " not encoded\n"; +#endif + ed.erase(std::remove_if(ed.begin(), + ed.end(), + FindExifdatum(filteredIfds[i])), + ed.end()); + } + std::auto_ptr header(new TiffHeader(byteOrder)); return TiffParserWorker::encode(blob, pData, size, - exifData, + ed, iptcData, xmpData, Tag::root, diff --git a/src/tiffimage_int.hpp b/src/tiffimage_int.hpp index ba67de87..e8c76c1e 100644 --- a/src/tiffimage_int.hpp +++ b/src/tiffimage_int.hpp @@ -338,6 +338,19 @@ namespace Exiv2 { }; // class TiffMapping + //! Unary predicate that matches an Exifdatum with a given IfdId. + class FindExifdatum { + public: + //! Constructor, initializes the object with the IfdId to look for. + FindExifdatum(Exiv2::IfdId ifdId) : ifdId_(ifdId) {} + //! Returns true if IFD id matches. + bool operator()(const Exiv2::Exifdatum& md) const { return ifdId_ == md.ifdId(); } + + private: + Exiv2::IfdId ifdId_; + + }; // class FindExifdatum + }} // namespace Internal, Exiv2 #endif // #ifndef TIFFIMAGE_INT_HPP_ diff --git a/src/tiffvisitor.cpp b/src/tiffvisitor.cpp index 12a63f14..51b69e60 100644 --- a/src/tiffvisitor.cpp +++ b/src/tiffvisitor.cpp @@ -56,10 +56,10 @@ EXIV2_RCSID("@(#) $Id$") // ***************************************************************************** namespace { //! Unary predicate that matches an Exifdatum with a given group and index. - class FindExifdatum { + class FindExifdatum2 { public: //! Constructor, initializes the object with the group and index to look for. - FindExifdatum(uint16_t group, int idx) + FindExifdatum2(uint16_t group, int idx) : groupName_(Exiv2::Internal::tiffGroupName(group)), idx_(idx) {} //! Returns true if group and index match. bool operator()(const Exiv2::Exifdatum& md) const @@ -71,7 +71,7 @@ namespace { const char* groupName_; int idx_; - }; // class FindExifdatum + }; // class FindExifdatum2 } // ***************************************************************************** @@ -640,7 +640,7 @@ namespace Exiv2 { // Try to find exact match (in case of duplicate tags) ExifData::iterator pos2 = std::find_if(exifData_.begin(), exifData_.end(), - FindExifdatum(object->group(), object->idx())); + FindExifdatum2(object->group(), object->idx())); if (pos2 != exifData_.end() && pos2->key() == key.key()) { ed = &(*pos2); pos = pos2; // make sure we delete the correct tag below diff --git a/src/tiffvisitor_int.hpp b/src/tiffvisitor_int.hpp index 3652c0a4..6fbd6f23 100644 --- a/src/tiffvisitor_int.hpp +++ b/src/tiffvisitor_int.hpp @@ -481,8 +481,8 @@ namespace Exiv2 { private: // DATA ExifData exifData_; //!< Copy of the Exif data to encode - IptcData iptcData_; //!< Copy of the IPTC data to encode - XmpData xmpData_; //!< Copy of the XMP data to encode + const IptcData& iptcData_; //!< IPTC data to encode, just a reference + const XmpData& xmpData_; //!< XMP data to encode, just a reference bool del_; //!< Indicates if Exif data entries should be deleted after encoding TiffComponent* pRoot_; //!< Root element of the composite TiffComponent* pSourceTree_; //!< Parsed source tree for reference