diff --git a/src/Makefile b/src/Makefile index a75a0f2d..7d9a76a8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -47,11 +47,11 @@ include $(top_srcdir)/config/config.mk # Source files # Add standalone C++ header files to this list -CCHDR = exv_conf.h exv_msvc.h mn.hpp rcsid.hpp tiffvisitor_tmpl.hpp +CCHDR = exv_conf.h exv_msvc.h mn.hpp rcsid.hpp # Add library C++ source files to this list CCSRC = basicio.cpp canonmn.cpp crwimage.cpp datasets.cpp error.cpp exif.cpp \ - futils.cpp fujimn.cpp ifd.cpp image.cpp imgreg.cpp iptc.cpp \ + futils.cpp fujimn.cpp fujimn2.cpp ifd.cpp image.cpp imgreg.cpp iptc.cpp \ jpgimage.cpp makernote.cpp makernote2.cpp metadatum.cpp mnreg.cpp \ nikonmn.cpp olympusmn.cpp olympusmn2.cpp panasonicmn.cpp sigmamn.cpp \ sonymn.cpp tags.cpp tiffcomposite.cpp tiffimage.cpp tiffparser.cpp \ diff --git a/src/fujimn2.cpp b/src/fujimn2.cpp new file mode 100644 index 00000000..043a5e56 --- /dev/null +++ b/src/fujimn2.cpp @@ -0,0 +1,133 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2006 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., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/* + File: fujimn2.cpp + Version: $Rev$ + Author(s): Andreas Huggel (ahu) + History: 15-Apr-06, ahu: created + */ +// ***************************************************************************** +#include "rcsid.hpp" +EXIV2_RCSID("@(#) $Id$"); + +// Define DEBUG to output debug information to std::cerr, e.g, by calling make +// like this: make DEFS=-DDEBUG makernote2.o +//#define DEBUG + +// ***************************************************************************** +// included header files +#ifdef _MSC_VER +# include "exv_msvc.h" +#else +# include "exv_conf.h" +#endif + +#include "fujimn2.hpp" +#include "tiffcomposite.hpp" +#include "tiffparser.hpp" +#include "types.hpp" + +// + standard includes +#include +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + + const char* FujiMnHeader::signature_ = "FUJIFILM\12\0\0\0"; + const uint32_t FujiMnHeader::size_ = 12; + const ByteOrder FujiMnHeader::byteOrder_ = littleEndian; + + FujiMnHeader::FujiMnHeader() + { + read(reinterpret_cast(signature_), size_, byteOrder_); + } + + bool FujiMnHeader::read(const byte* pData, + uint32_t size, + ByteOrder /*byteOrder*/) + { + assert (pData != 0); + + if (size < size_) return false; + + header_.alloc(size_); + memcpy(header_.pData_, pData, header_.size_); + + // Read offset to the IFD relative to the start of the makernote + // from the header. Note that we ignore the byteOrder argument + start_ = getUShort(header_.pData_ + 8, byteOrder_); + + return true; + } // FujiMnHeader::read + + bool FujiMnHeader::check() const + { + if ( static_cast(header_.size_) < size_ + || 0 != memcmp(header_.pData_, signature_, 8)) { + return false; + } + return true; + } // FujiMnHeader::check + + bool TiffFujiMn::doReadHeader(const byte* pData, + uint32_t size, + ByteOrder byteOrder) + { + return header_.read(pData, size, byteOrder); + } + + bool TiffFujiMn::doCheckHeader() const + { + return header_.check(); + } + + uint32_t TiffFujiMn::doIfdOffset() const + { + return header_.ifdOffset(); + } + + TiffRwState::AutoPtr TiffFujiMn::doGetState(uint32_t mnOffset) const + { + // Byteorder: from the header (little endian) + // Offsets : relative to the start of the makernote + // Creator : standard TIFF component factory + return TiffRwState::AutoPtr( + new TiffRwState(header_.byteOrder(), + mnOffset, + TiffCreator::create)); + } + + // ************************************************************************* + // free functions + + TiffComponent* newFujiMn(uint16_t tag, + uint16_t group, + uint16_t mnGroup, + const byte* /*pData*/, + uint32_t /*size*/, + ByteOrder /*byteOrder*/) + { + return new TiffFujiMn(tag, group, mnGroup); + } + +} // namespace Exiv2 diff --git a/src/fujimn2.hpp b/src/fujimn2.hpp new file mode 100644 index 00000000..39dd3c90 --- /dev/null +++ b/src/fujimn2.hpp @@ -0,0 +1,133 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2006 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., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file fujimn2.hpp + @brief TIFF Fujifilm makernote + @version $Rev$ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @date 15-Apr-06, ahu: created + */ +#ifndef FUJIMN2_HPP_ +#define FUJIMN2_HPP_ + +// ***************************************************************************** +// included header files +#include "makernote2.hpp" +#include "tiffcomposite.hpp" +#include "types.hpp" + +// + standard includes + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class definitions + + namespace Group { + const uint16_t fujimn = 258; //!< Fujifilm makernote + } + + //! Header of a Fujifilm Makernote + class FujiMnHeader : public MnHeader { + public: + //! @name Creators + //@{ + //! Default constructor + FujiMnHeader(); + //! Virtual destructor. + virtual ~FujiMnHeader() {} + //@} + //! @name Manipulators + //@{ + virtual bool read(const byte* pData, + uint32_t size, + ByteOrder byteOrder); + //@} + //! @name Accessors + //@{ + virtual uint32_t size() const { return header_.size_; } + virtual uint32_t ifdOffset() const { return start_; } + virtual bool check() const; + //! Return the byte order for the header + ByteOrder byteOrder() const { return byteOrder_; } + //@} + + private: + DataBuf header_; //!< Data buffer for the makernote header + static const char* signature_; //!< Fujifilm makernote header signature + static const uint32_t size_; //!< Size of the signature + static const ByteOrder byteOrder_; //!< Byteorder for makernote (II) + uint32_t start_; //!< Start of the mn IFD rel. to mn start + + }; // class FujiMnHeader + + /*! + @brief Fujifilm Makernote + */ + class TiffFujiMn : public TiffIfdMakernote { + public: + //! @name Creators + //@{ + //! Default constructor + TiffFujiMn(uint16_t tag, uint16_t group, uint16_t mnGroup) + : TiffIfdMakernote(tag, group, mnGroup) {} + //! Virtual destructor + virtual ~TiffFujiMn() {} + //@} + + private: + //! @name Manipulators + //@{ + virtual bool doReadHeader(const byte* pData, + uint32_t size, + ByteOrder byteOrder); + //@} + + //! @name Accessors + //@{ + virtual bool doCheckHeader() const; + virtual uint32_t doIfdOffset() const; + virtual TiffRwState::AutoPtr doGetState(uint32_t mnOffset) const; + //@} + + private: + // DATA + FujiMnHeader header_; //!< Makernote header + + }; // TiffFujiMn + +// ***************************************************************************** +// template, inline and free functions + + //! Function to create a Fujifilm makernote + TiffComponent* newFujiMn(uint16_t tag, + uint16_t group, + uint16_t mnGroup, + const byte* pData, + uint32_t size, + ByteOrder byteOrder); + +} // namespace Exiv2 + +#endif // #ifndef FUJIMN2_HPP_ diff --git a/src/makernote2.cpp b/src/makernote2.cpp index 10fc4cee..a7c063b8 100644 --- a/src/makernote2.cpp +++ b/src/makernote2.cpp @@ -57,9 +57,11 @@ namespace Exiv2 { return make == key.make_.substr(0, make.length()); } - bool TiffIfdMakernote::readHeader(const byte* pData, uint32_t size) + bool TiffIfdMakernote::readHeader(const byte* pData, + uint32_t size, + ByteOrder byteOrder) { - return doReadHeader(pData, size); + return doReadHeader(pData, size, byteOrder); } bool TiffIfdMakernote::checkHeader() const @@ -72,6 +74,16 @@ namespace Exiv2 { return doIfdOffset(); } + TiffRwState::AutoPtr TiffIfdMakernote::getState(uint32_t mnOffset) const + { + return doGetState(mnOffset); + } + + TiffRwState::AutoPtr TiffIfdMakernote::doGetState(uint32_t mnOffset) const + { + return TiffRwState::AutoPtr(0); + } + void TiffIfdMakernote::doAddChild(TiffComponent::AutoPtr tiffComponent) { ifd_.addChild(tiffComponent); @@ -86,6 +98,7 @@ namespace Exiv2 { { visitor.visitIfdMakernote(this); ifd_.accept(visitor); + visitor.visitIfdMakernoteEnd(this); } } // namespace Exiv2 diff --git a/src/makernote2.hpp b/src/makernote2.hpp index ab9123b8..bb8de2f1 100644 --- a/src/makernote2.hpp +++ b/src/makernote2.hpp @@ -20,7 +20,7 @@ */ /*! @file makernote2.hpp - @brief + @brief Makernote base classes, factory and registry @version $Rev$ @author Andreas Huggel (ahu) ahuggel@gmx.net @@ -32,6 +32,7 @@ // ***************************************************************************** // included header files #include "tiffcomposite.hpp" +#include "tiffvisitor.hpp" #include "types.hpp" // + standard includes @@ -41,11 +42,6 @@ // namespace extensions namespace Exiv2 { -// ***************************************************************************** -// class declarations - - template class TiffReader; - // ***************************************************************************** // class definitions @@ -122,7 +118,8 @@ namespace Exiv2 { //@{ //! Read the header from a data buffer, return true if successful virtual bool read(const byte* pData, - uint32_t size) =0; + uint32_t size, + ByteOrder byteOrder) =0; //@} //! @name Accessors //@{ @@ -145,7 +142,6 @@ namespace Exiv2 { the IFD entries. */ class TiffIfdMakernote : public TiffComponent { - template friend class TiffReader; public: //! @name Creators @@ -160,7 +156,7 @@ namespace Exiv2 { //! @name Manipulators //@{ //! Read the header from a data buffer, return true if successful - bool readHeader(const byte* pData, uint32_t size); + bool readHeader(const byte* pData, uint32_t size, ByteOrder byteOrder); //@} //! @name Accessors @@ -172,6 +168,18 @@ namespace Exiv2 { the start of the Makernote. */ uint32_t ifdOffset() const; + /*! + @brief Get status information relevant for the makernote. + + State includes byte order, offset and TIFF component factory. + This method allows the TiffReader to change state, i.e., change + these parameters, to parse the Makernote and its sub components + (if any). + + @param mnOffset Offset to the makernote from the start of the + TIFF header. + */ + TiffRwState::AutoPtr getState(uint32_t mnOffset) const; //@} protected: @@ -181,7 +189,9 @@ namespace Exiv2 { virtual void doAddNext(TiffComponent::AutoPtr tiffComponent); virtual void doAccept(TiffVisitor& visitor); //! Implements readHeader(); - virtual bool doReadHeader(const byte* pData, uint32_t size) =0; + virtual bool doReadHeader(const byte* pData, + uint32_t size, + ByteOrder byteOrder) =0; //@} //! @name Accessors @@ -190,6 +200,8 @@ namespace Exiv2 { virtual bool doCheckHeader() const =0; //! Implements ifdOffset() virtual uint32_t doIfdOffset() const =0; + //! Implements getState(). The default implementation returns a 0-pointer. + virtual TiffRwState::AutoPtr doGetState(uint32_t mnOffset) const; //@} private: diff --git a/src/mnreg.cpp b/src/mnreg.cpp index 6d19d094..0e1bdb33 100644 --- a/src/mnreg.cpp +++ b/src/mnreg.cpp @@ -33,6 +33,7 @@ EXIV2_RCSID("@(#) $Id$"); // included header files #include "makernote2.hpp" #include "olympusmn2.hpp" +#include "fujimn2.hpp" // + standard includes @@ -41,11 +42,12 @@ EXIV2_RCSID("@(#) $Id$"); namespace Exiv2 { const TiffMnRegistry TiffMnCreator::registry_[] = { - { "OLYMPUS", newOlympusMn, Group::olympmn } + { "OLYMPUS", newOlympusMn, Group::olympmn }, + { "FUJIFILM", newFujiMn, Group::fujimn } }; - // The find template needs to be in the same compilation unit as the array + // The find template needs to see the array from where it is called TiffComponent* TiffMnCreator::create(uint16_t tag, uint16_t group, std::string make, @@ -64,5 +66,4 @@ namespace Exiv2 { return tc; } // TiffMnCreator::create - } // namespace Exiv2 diff --git a/src/olympusmn2.cpp b/src/olympusmn2.cpp index 11165278..92a327c7 100644 --- a/src/olympusmn2.cpp +++ b/src/olympusmn2.cpp @@ -22,7 +22,7 @@ File: olympusmn2.cpp Version: $Rev$ Author(s): Andreas Huggel (ahu) - History: 11-Apr-06, ahu: created + History: 15-Apr-06, ahu: created */ // ***************************************************************************** #include "rcsid.hpp" @@ -57,10 +57,12 @@ namespace Exiv2 { OlympusMnHeader::OlympusMnHeader() { - read(reinterpret_cast(signature_), size_); + read(reinterpret_cast(signature_), size_, invalidByteOrder); } - bool OlympusMnHeader::read(const byte* pData, uint32_t size) + bool OlympusMnHeader::read(const byte* pData, + uint32_t size, + ByteOrder /*byteOrder*/) { assert (pData != 0); @@ -80,9 +82,11 @@ namespace Exiv2 { return true; } // OlympusMnHeader::check - bool TiffOlympusMn::doReadHeader(const byte* pData, uint32_t size) + bool TiffOlympusMn::doReadHeader(const byte* pData, + uint32_t size, + ByteOrder byteOrder) { - return header_.read(pData, size); + return header_.read(pData, size, byteOrder); } bool TiffOlympusMn::doCheckHeader() const diff --git a/src/olympusmn2.hpp b/src/olympusmn2.hpp index 128df9a3..b37f0cb5 100644 --- a/src/olympusmn2.hpp +++ b/src/olympusmn2.hpp @@ -61,7 +61,8 @@ namespace Exiv2 { //! @name Manipulators //@{ virtual bool read(const byte* pData, - uint32_t size); + uint32_t size, + ByteOrder byteOrder); //@} //! @name Accessors //@{ @@ -94,7 +95,9 @@ namespace Exiv2 { private: //! @name Manipulators //@{ - virtual bool doReadHeader(const byte* pData, uint32_t size); + virtual bool doReadHeader(const byte* pData, + uint32_t size, + ByteOrder byteOrder); //@} //! @name Accessors diff --git a/src/tiffcomposite.cpp b/src/tiffcomposite.cpp index e63e1f8e..2827823d 100644 --- a/src/tiffcomposite.cpp +++ b/src/tiffcomposite.cpp @@ -125,6 +125,7 @@ namespace Exiv2 { case 4: group = "GPSInfo"; break; case 5: group = "Iop"; break; case 257: group = "Olympus"; break; + case 258: group = "Fujifilm"; break; default: group = "Unknown"; break; } return group; diff --git a/src/tiffcomposite.hpp b/src/tiffcomposite.hpp index 5d2a33a5..32bd3af9 100644 --- a/src/tiffcomposite.hpp +++ b/src/tiffcomposite.hpp @@ -48,7 +48,7 @@ namespace Exiv2 { class Value; class TiffVisitor; - template class TiffReader; + class TiffReader; class TiffMetadataDecoder; class TiffPrinter; class TiffIfdMakernote; @@ -224,6 +224,12 @@ namespace Exiv2 { }; // class TiffComponent + /*! + Type for a factory function to create new TIFF components. + */ + typedef TiffComponent::AutoPtr (*TiffCompFactoryFct)(uint32_t extendedTag, + uint16_t group); + /*! @brief This abstract base class provides the common functionality of an IFD directory entry and defines an extended interface for derived @@ -231,7 +237,6 @@ namespace Exiv2 { entry. */ class TiffEntryBase : public TiffComponent { - template friend class TiffReader; public: //! @name Creators @@ -247,11 +252,14 @@ namespace Exiv2 { //! @name Accessors //@{ - //! Return the Exiv2 type which corresponds to the field type. + //! Return the Exiv2 type which corresponds to the field type TypeId typeId() const { return TypeId(type_); } - //! Return the number of components in this entry. + //! Return the number of components in this entry uint32_t count() const { return count_; } - //! Return the offset relative to the start of the TIFF header. + /*! + Return the offset to the data area relative to the base for + the component (usually the start of the TIFF header) + */ uint32_t offset() const { return offset_; } //! Return the size of this component in bytes uint32_t size() const { return size_; } @@ -265,7 +273,7 @@ namespace Exiv2 { // DATA uint16_t type_; //!< Field Type uint32_t count_; //!< The number of values of the indicated Type - uint32_t offset_; //!< Offset to data area from start of TIFF header + uint32_t offset_; //!< Offset to the data area /*! Size of the data buffer holding the value in bytes, there is no minimum size. @@ -337,7 +345,6 @@ namespace Exiv2 { GPS tags. */ class TiffSubIfd : public TiffEntryBase { - template friend class TiffReader; public: //! @name Creators @@ -365,14 +372,12 @@ namespace Exiv2 { /*! @brief This class is the basis for Makernote support in TIFF. It contains - a pointer to a concrete Makernote. The TiffReader - visitor has the responsibility to create the correct Make/Model - specific Makernote for a particular TIFF file. Calls to child - management methods are forwarded to the concrete Makernote, if - there is one. + a pointer to a concrete Makernote. The TiffReader visitor has the + responsibility to create the correct Make/Model specific Makernote + for a particular TIFF file. Calls to child management methods are + forwarded to the concrete Makernote, if there is one. */ class TiffMnEntry : public TiffEntryBase { - template friend class TiffReader; friend class TiffMetadataDecoder; friend class TiffPrinter; diff --git a/src/tiffimage.cpp b/src/tiffimage.cpp index 68e5a0e9..4209ea68 100644 --- a/src/tiffimage.cpp +++ b/src/tiffimage.cpp @@ -135,7 +135,7 @@ namespace Exiv2 { io_->read(buf.pData_, len); if (io_->error() || io_->eof()) throw Error(14); - TiffParser::decode(this, buf.pData_, buf.size_); + TiffParser::decode(this, buf.pData_, buf.size_, TiffCreator::create); } // TiffImage::readMetadata void TiffImage::writeMetadata() diff --git a/src/tiffparse.cpp b/src/tiffparse.cpp index 545bc73e..d6db126b 100644 --- a/src/tiffparse.cpp +++ b/src/tiffparse.cpp @@ -39,16 +39,22 @@ try { TiffHeade2 tiffHeader; if (!tiffHeader.read(buf.pData_, buf.size_)) throw Error(3, "TIFF"); - TiffComponent::AutoPtr rootDir = TiffCreator::create(Tag::root, Group::none); + TiffCompFactoryFct createFct = TiffCreator::create; + + TiffComponent::AutoPtr rootDir = createFct(Tag::root, Group::none); if (0 == rootDir.get()) { throw Error(1, "No root element defined in TIFF structure"); } - rootDir->setStart(buf.pData_ + tiffHeader.offset()); - TiffReader reader(buf.pData_, - buf.size_, - tiffHeader.byteOrder(), - rootDir.get()); + + TiffRwState::AutoPtr state( + new TiffRwState(tiffHeader.byteOrder(), 0, createFct)); + + TiffReader reader(buf.pData_, + buf.size_, + rootDir.get(), + state); + rootDir->accept(reader); tiffHeader.print(std::cerr); diff --git a/src/tiffparser.cpp b/src/tiffparser.cpp index b85c9776..e47339c8 100644 --- a/src/tiffparser.cpp +++ b/src/tiffparser.cpp @@ -54,20 +54,11 @@ EXIV2_RCSID("@(#) $Id$"); + Add further child mgmt stuff to TIFF composite: remove + Review boundary checking, is it better to check the offsets? + Define and implement consistent error handling for recursive hierarchy - + Add Makernote support + Make TiffImage a template StandardImage, which can be parametrized with a parser and the necessary checking functions to cover all types of images which need to be loaded completely. - + Decide what tag and group should be assigned to TiffMnEntry and - concrete Makernotes and which of them should derive from base-entry - - TiffMnEntry tag 0x927c, group exif, derives from tiffentry: because - create needs the entry - - ConcreteMn tag 0, group Mn, derives from component so that the plain entry - is only kept in one place, - if it contains an Ifd, that has a different group (create fct knows which) - + Implementation of concrete makernotes: Base class TiffIfdMakernote? - Why is the hierarchy MnHeader needed? - + TiffComponent: should it have end() and setEnd() or pData and size?? + + TiffComponent: should it have end() and setEnd() or pData and size? + + Can NewTiffCompFct and TiffCompFactoryFct be combined? in crwimage.* : @@ -124,6 +115,32 @@ namespace Exiv2 { return tc; } // TiffCreator::create + void TiffParser::decode(Image* pImage, + const byte* pData, + uint32_t size, + TiffCompFactoryFct createFct) + { + assert(pImage != 0); + assert(pData != 0); + + TiffHeade2 tiffHeader; + if (!tiffHeader.read(pData, size) || tiffHeader.offset() >= size) { + throw Error(3, "TIFF"); + } + TiffComponent::AutoPtr rootDir = createFct(Tag::root, Group::none); + if (0 == rootDir.get()) return; + rootDir->setStart(pData + tiffHeader.offset()); + + TiffRwState::AutoPtr state( + new TiffRwState(tiffHeader.byteOrder(), 0, createFct)); + TiffReader reader(pData, size, rootDir.get(), state); + rootDir->accept(reader); + + TiffMetadataDecoder decoder(pImage); + rootDir->accept(decoder); + + } // TiffParser::decode + // ************************************************************************* // free functions diff --git a/src/tiffparser.hpp b/src/tiffparser.hpp index 7545c0a3..75675d9f 100644 --- a/src/tiffparser.hpp +++ b/src/tiffparser.hpp @@ -33,7 +33,6 @@ // included header files #include "tiffcomposite.hpp" #include "tiffvisitor.hpp" -#include "tiffvisitor_tmpl.hpp" #include "error.hpp" #include "types.hpp" @@ -55,9 +54,7 @@ namespace Exiv2 { // class definitions /*! - Type for a function pointer for functions to create TIFF components. - Todo: This may eventually need to also have access to the image or parse tree - in order to make decisions based on the value of other tags. + Type for a function pointer for a function to create a TIFF component. */ typedef TiffComponent::AutoPtr (*NewTiffCompFct)(const TiffStructure* ts); @@ -90,8 +87,7 @@ namespace Exiv2 { }; /*! - @brief TIFF component factory for standard TIFF components. This class is - used as a policy class. + @brief TIFF component factory for standard TIFF components. */ class TiffCreator { public: @@ -105,9 +101,7 @@ namespace Exiv2 { */ static TiffComponent::AutoPtr create(uint32_t extendedTag, uint16_t group); - protected: - //! Prevent destruction - ~TiffCreator() {} + private: static const TiffStructure tiffStructure_[]; // - class TiffParser : public CreationPolicy { + class TiffParser { public: /*! @brief Decode TIFF metadata from a data buffer \em pData of length @@ -128,14 +121,16 @@ namespace Exiv2 { parser uses classes TiffHeade2 and the TiffComponent and TiffVisitor hierarchies. - @param pImage Pointer to the image to hold the metadata - @param pData Pointer to the data buffer. Must point to data - in TIFF format; no checks are performed. - @param size Length of the data buffer. + @param pImage Pointer to the image to hold the metadata + @param pData Pointer to the data buffer. Must point to data + in TIFF format; no checks are performed. + @param size Length of the data buffer. + @param createFct Factory function to create new TIFF components. */ - static void decode( Image* pImage, - const byte* pData, - uint32_t size); + static void decode( Image* pImage, + const byte* pData, + uint32_t size, + TiffCompFactoryFct createFct); }; // class TiffParser // ***************************************************************************** @@ -150,34 +145,6 @@ namespace Exiv2 { //! Function to create and initialize a new TIFF makernote entry TiffComponent::AutoPtr newTiffMnEntry(const TiffStructure* ts); - template - void TiffParser::decode(Image* pImage, - const byte* pData, - uint32_t size) - { - assert(pImage != 0); - assert(pData != 0); - - TiffHeade2 tiffHeader; - if (!tiffHeader.read(pData, size) || tiffHeader.offset() >= size) { - throw Error(3, "TIFF"); - } - TiffComponent::AutoPtr rootDir = CreationPolicy::create(Tag::root, - Group::none); - if (0 == rootDir.get()) return; - rootDir->setStart(pData + tiffHeader.offset()); - - TiffReader reader(pData, - size, - tiffHeader.byteOrder(), - rootDir.get()); - rootDir->accept(reader); - - TiffMetadataDecoder decoder(pImage); - rootDir->accept(decoder); - - } // TiffParser::decode - } // namespace Exiv2 #endif // #ifndef TIFFPARSER_HPP_ diff --git a/src/tiffvisitor.cpp b/src/tiffvisitor.cpp index 6ceaeedc..be4ea9a7 100644 --- a/src/tiffvisitor.cpp +++ b/src/tiffvisitor.cpp @@ -44,10 +44,14 @@ EXIV2_RCSID("@(#) $Id$"); #include "tiffcomposite.hpp" #include "makernote2.hpp" #include "exif.hpp" +#include "value.hpp" #include "image.hpp" // + standard includes #include +#include +#include +#include // ***************************************************************************** // class member definitions @@ -211,7 +215,273 @@ namespace Exiv2 { } // TiffPrinter::printTiffEntry - // ************************************************************************* - // free functions + TiffReader::TiffReader(const byte* pData, + uint32_t size, + TiffComponent* pRoot, + TiffRwState::AutoPtr state) + : pData_(pData), + size_(size), + pLast_(pData + size - 1), + pRoot_(pRoot), + pState_(state.release()), + pOrigState_(pState_) + { + assert(pData_); + assert(size_ > 0); + + } // TiffReader::TiffReader + + TiffReader::~TiffReader() + { + if (pOrigState_ != pState_) delete pOrigState_; + delete pState_; + } + + void TiffReader::resetState() { + if (pOrigState_ != pState_) delete pState_; + pState_ = pOrigState_; + } + + void TiffReader::changeState(TiffRwState::AutoPtr state) + { + if (state.get() != 0) { + if (pOrigState_ != pState_) delete pState_; + pState_ = state.release(); + } + } + + ByteOrder TiffReader::byteOrder() const + { + assert(pState_); + return pState_->byteOrder_; + } + + uint32_t TiffReader::baseOffset() const + { + assert(pState_); + return pState_->baseOffset_; + } + + TiffComponent::AutoPtr TiffReader::create(uint32_t extendedTag, + uint16_t group) const + { + assert(pState_); + assert(pState_->createFct_); + return pState_->createFct_(extendedTag, group); + } + + void TiffReader::visitEntry(TiffEntry* object) + { + readTiffEntry(object); + } + + void TiffReader::visitDirectory(TiffDirectory* object) + { + assert(object != 0); + + const byte* p = object->start(); + assert(p >= pData_); + + if (p + 2 > pLast_) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: " + << "Directory " << object->groupName() << ": " + << " IFD exceeds data buffer, cannot read entry count.\n"; +#endif + return; + } + const uint16_t n = getUShort(p, byteOrder()); + p += 2; + for (uint16_t i = 0; i < n; ++i) { + if (p + 12 > pLast_) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: " + << "Directory " << object->groupName() << ": " + << " IFD entry " << i + << " lies outside of the data buffer.\n"; +#endif + return; + } + uint16_t tag = getUShort(p, byteOrder()); + TiffComponent::AutoPtr tc = create(tag, object->group()); + tc->setStart(p); + object->addChild(tc); + p += 12; + } + + if (p + 4 > pLast_) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: " + << "Directory " << object->groupName() << ": " + << " IFD exceeds data buffer, cannot read next pointer.\n"; +#endif + return; + } + uint32_t next = getLong(p, byteOrder()); + if (next) { + TiffComponent::AutoPtr tc = create(Tag::next, object->group()); + if (baseOffset() + next > size_) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: " + << "Directory " << object->groupName() << ": " + << " Next pointer is out of bounds.\n"; +#endif + return; + } + tc->setStart(pData_ + baseOffset() + next); + object->addNext(tc); + } + + } // TiffReader::visitDirectory + + void TiffReader::visitSubIfd(TiffSubIfd* object) + { + assert(object != 0); + + readTiffEntry(object); + if (object->typeId() == unsignedLong && object->count() >= 1) { + uint32_t offset = getULong(object->pData(), byteOrder()); + if (baseOffset() + offset > size_) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: " + << "Directory " << object->groupName() + << ", entry 0x" << std::setw(4) + << std::setfill('0') << std::hex << object->tag() + << " Sub-IFD pointer is out of bounds; ignoring it.\n"; +#endif + return; + } + object->ifd_.setStart(pData_ + baseOffset() + offset); + } +#ifndef SUPPRESS_WARNINGS + else { + std::cerr << "Warning: " + << "Directory " << object->groupName() + << ", entry 0x" << std::setw(4) + << std::setfill('0') << std::hex << object->tag() + << " doesn't look like a sub-IFD."; + } +#endif + + } // TiffReader::visitSubIfd + + void TiffReader::visitMnEntry(TiffMnEntry* object) + { + assert(object != 0); + + readTiffEntry(object); + // Find camera make + TiffFinder finder(0x010f, Group::ifd0); + pRoot_->accept(finder); + TiffEntryBase* te = dynamic_cast(finder.result()); + std::string make; + if (te && te->pValue()) { + make = te->pValue()->toString(); + // create concrete makernote, based on make and makernote contents + object->mn_ = TiffMnCreator::create(object->tag(), + object->mnGroup_, + make, + object->pData(), + object->size(), + byteOrder()); + } + if (object->mn_) object->mn_->setStart(object->pData()); + + } // TiffReader::visitMnEntry + + void TiffReader::visitIfdMakernote(TiffIfdMakernote* object) + { + assert(object != 0); + + object->readHeader(object->start(), pLast_ - object->start(), byteOrder()); + if (!object->checkHeader()) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: IFD Makernote header check failed.\n"; +#endif + return; // todo: signal error to parent, delete object + } + // Modify reader for Makernote peculiarities, byte order, offset, + // component factory + changeState(object->getState(object->start() - pData_)); + object->ifd_.setStart(object->start() + object->ifdOffset()); + + } // TiffReader::visitIfdMakernote + + void TiffReader::visitIfdMakernoteEnd(TiffIfdMakernote* object) + { + // Reset state (byte order, create function, offset) back to that + // for the image + resetState(); + } // TiffReader::visitIfdMakernoteEnd + + void TiffReader::readTiffEntry(TiffEntryBase* object) + { + assert(object != 0); + + const byte* p = object->start(); + assert(p >= pData_); + + if (p + 12 > pLast_) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: Entry in directory " << object->groupName() + << "requests access to memory beyond the data buffer. " + << "Skipping entry.\n"; +#endif + return; + } + // Component already has tag + p += 2; + object->type_ = getUShort(p, byteOrder()); + // todo: check type + p += 2; + object->count_ = getULong(p, byteOrder()); + p += 4; + object->size_ = TypeInfo::typeSize(object->typeId()) * object->count(); + object->offset_ = getULong(p, byteOrder()); + object->pData_ = p; + if (object->size() > 4) { + if (baseOffset() + object->offset() >= size_) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: Offset of " + << "directory " << object->groupName() << ", " + << " entry 0x" << std::setw(4) + << std::setfill('0') << std::hex << object->tag() + << " is out of bounds:\n" + << "Offset = 0x" << std::setw(8) + << std::setfill('0') << std::hex << object->offset() + << "; truncating the entry\n"; +#endif + object->size_ = 0; + object->count_ = 0; + object->offset_ = 0; + return; + } + object->pData_ = pData_ + baseOffset() + object->offset(); + if (object->pData() + object->size() > pLast_) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Warning: Upper boundary of data for " + << "directory " << object->groupName() << ", " + << " entry 0x" << std::setw(4) + << std::setfill('0') << std::hex << object->tag() + << " is out of bounds:\n" + << "Offset = 0x" << std::setw(8) + << std::setfill('0') << std::hex << object->offset() + << ", size = " << std::dec << object->size() + << ", exceeds buffer size by " + // cast to make MSVC happy + << static_cast(object->pData() + object->size() - pLast_) + << " Bytes; adjusting the size\n"; +#endif + object->size_ = pLast_ - object->pData() + 1; + // todo: adjust count_, make size_ a multiple of typeSize + } + } + Value::AutoPtr v = Value::create(object->typeId()); + if (v.get()) { + v->read(object->pData(), object->size(), byteOrder()); + object->pValue_ = v.release(); + } + + } // TiffReader::readTiffEntry } // namespace Exiv2 diff --git a/src/tiffvisitor.hpp b/src/tiffvisitor.hpp index 30e7f3e9..342e5d37 100644 --- a/src/tiffvisitor.hpp +++ b/src/tiffvisitor.hpp @@ -32,8 +32,10 @@ // ***************************************************************************** // included header files #include "types.hpp" +#include "tiffcomposite.hpp" // + standard includes +#include #include #include #include @@ -45,12 +47,6 @@ namespace Exiv2 { // ***************************************************************************** // class declarations - class TiffComponent; - class TiffEntryBase; - class TiffEntry; - class TiffDirectory; - class TiffSubIfd; - class TiffMnEntry; class TiffIfdMakernote; class Image; @@ -91,13 +87,13 @@ namespace Exiv2 { //! Operation to perform for a TIFF directory virtual void visitDirectory(TiffDirectory* object) =0; /*! - Operation to perform for a TIFF directory, after all components and - before the next entry is processed. + @brief Operation to perform for a TIFF directory, after all components + and before the next entry is processed. */ virtual void visitDirectoryNext(TiffDirectory* object) {} /*! - Operation to perform for a TIFF directory, at the end of the - processing. + @brief Operation to perform for a TIFF directory, at the end of the + processing. */ virtual void visitDirectoryEnd(TiffDirectory* object) {} //! Operation to perform for a TIFF sub-IFD @@ -106,6 +102,8 @@ namespace Exiv2 { virtual void visitMnEntry(TiffMnEntry* object) =0; //! Operation to perform for an IFD makernote virtual void visitIfdMakernote(TiffIfdMakernote* object) =0; + //! Operation to perform after processing an IFD makernote + virtual void visitIfdMakernoteEnd(TiffIfdMakernote* object) {} //@} //! @name Accessors @@ -209,14 +207,52 @@ namespace Exiv2 { }; // class TiffMetadataDecoder + /*! + @brief Simple state class containing relevant state information for + the TIFF reader. This is in a separate class so that the + reader can change state if needed (e.g., to read certain complex + makernotes). + */ + struct TiffRwState { + //! TiffRWState auto_ptr type + typedef std::auto_ptr AutoPtr; + //! Constructor. + explicit TiffRwState(ByteOrder byteOrder, + uint32_t baseOffset, + TiffCompFactoryFct createFct) + : byteOrder_(byteOrder), + baseOffset_(baseOffset), + createFct_(createFct) {} + /*! + Applicable byte order. May be different for the Makernote and the + rest of the TIFF entries. + */ + const ByteOrder byteOrder_; + /*! + Base offset. TIFF standard format uses byte offsets which are + always relative to the start of the TIFF file, i.e., relative to the + start of the TIFF image header. In this case, the base offset is 0. + However, some camera vendors encode their makernotes in TIFF IFDs + using offsets relative to (somewhere near) the start of the makernote + data. In this case, base offset added to the start of the TIFF image + header points to the basis for such makernote offsets. + */ + const uint32_t baseOffset_; + /*! + Factory function to create new TIFF components. Different create + functions may use different lookup tables, so that makernotes + can independently use their own factory function and lookup table, + which can be defined together with the makernote implementation. + */ + TiffCompFactoryFct createFct_; + }; // TiffRwState + /*! @brief TIFF composite visitor to read the TIFF structure from a block of memory and build the composite from it (Visitor pattern). Used by - TiffParser to read the TIFF data from a block of memory. Uses - the policy class CreationPolicy for the creation of TIFF components. + TiffParser to read the TIFF data from a block of memory. */ - template - class TiffReader : public TiffVisitor, public CreationPolicy { + class TiffReader : public TiffVisitor { public: //! @name Creators //@{ @@ -225,16 +261,17 @@ namespace Exiv2 { structure of the data are set in the constructor. @param pData Pointer to the data buffer, starting with a TIFF header. @param size Number of bytes in the data buffer. - @param byteOrder Applicable byte order (little or big endian). @param pRoot Root element of the TIFF composite. + @param state State object for creation function, byteorder and + base offset. */ - TiffReader(const byte* pData, - uint32_t size, - ByteOrder byteOrder, - TiffComponent* pRoot); + TiffReader(const byte* pData, + uint32_t size, + TiffComponent* pRoot, + TiffRwState::AutoPtr state); //! Virtual destructor - virtual ~TiffReader() {} + virtual ~TiffReader(); //@} //! @name Manipulators @@ -249,18 +286,35 @@ namespace Exiv2 { virtual void visitMnEntry(TiffMnEntry* object); //! Read an IFD makernote from the data buffer virtual void visitIfdMakernote(TiffIfdMakernote* object); + //! Reset reader to its original state, undo makernote specific settings + virtual void visitIfdMakernoteEnd(TiffIfdMakernote* object); //! Read a standard TIFF entry from the data buffer void readTiffEntry(TiffEntryBase* object); + //! Set the \em state class. Assumes ownership of the object passed in. + void changeState(TiffRwState::AutoPtr state); + //! Reset the state to the original state as set in the constructor. + void resetState(); + //@} + + //! @name Accessors + //@{ + //! Return the byte order. + ByteOrder byteOrder() const; + //! Return the base offset. See class TiffRwState for details + uint32_t baseOffset() const; + //! Create a TIFF component for \em extendedTag and group + TiffComponent::AutoPtr create(uint32_t extendedTag, uint16_t group) const; //@} private: // DATA - const byte* pData_; //!< Pointer to the memory buffer - const uint32_t size_; //!< Size of the buffer - const byte* pLast_; //!< Pointer to the last byte - const ByteOrder byteOrder_; //!< Byteorder for the image - TiffComponent* const pRoot_; //!< Root element of the composite + const byte* pData_; //!< Pointer to the memory buffer + const uint32_t size_; //!< Size of the buffer + const byte* pLast_; //!< Pointer to the last byte + TiffComponent* const pRoot_; //!< Root element of the composite + TiffRwState* pState_; //!< State class + TiffRwState* pOrigState_; //!< State class as set in the c'tor }; // class TiffReader @@ -319,9 +373,6 @@ namespace Exiv2 { static const std::string indent_; //!< Indent for one level }; // class TiffPrinter -// ***************************************************************************** -// template, inline and free functions - } // namespace Exiv2 #endif // #ifndef TIFFVISITOR_HPP_ diff --git a/src/tiffvisitor_tmpl.hpp b/src/tiffvisitor_tmpl.hpp deleted file mode 100644 index 38550534..00000000 --- a/src/tiffvisitor_tmpl.hpp +++ /dev/null @@ -1,277 +0,0 @@ -// ***************************************************************** -*- C++ -*- -/* - * Copyright (C) 2006 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., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. - */ -/*! - @file tiffvisitor_tmpl.hpp - @brief - @version $Rev$ - @author Andreas Huggel (ahu) - ahuggel@gmx.net - @date 11-Apr-06, ahu: created - */ -#ifndef TIFFVISITOR_TMPL_HPP_ -#define TIFFVISITOR_TMPL_HPP_ - -// ***************************************************************************** -// included header files -#include "tiffvisitor.hpp" -#include "tiffcomposite.hpp" -#include "makernote2.hpp" -#include "value.hpp" -#include "types.hpp" - -// + standard includes -#include -#include -#include - -// ***************************************************************************** -// namespace extensions -namespace Exiv2 { - -// ***************************************************************************** -// template, inline and free functions - - template - TiffReader::TiffReader(const byte* pData, - uint32_t size, - ByteOrder byteOrder, - TiffComponent* pRoot) - : pData_(pData), - size_(size), - pLast_(pData + size - 1), - byteOrder_(byteOrder), - pRoot_(pRoot) - { - assert(pData); - assert(size > 0); - } // TiffReader::TiffReader - - template - void TiffReader::visitEntry(TiffEntry* object) - { - readTiffEntry(object); - } - - template - void TiffReader::visitDirectory(TiffDirectory* object) - { - assert(object != 0); - - const byte* p = object->start(); - assert(p >= pData_); - - if (p + 2 > pLast_) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: " - << "Directory " << object->groupName() << ": " - << " IFD exceeds data buffer, cannot read entry count.\n"; -#endif - return; - } - const uint16_t n = getUShort(p, byteOrder_); - p += 2; - for (uint16_t i = 0; i < n; ++i) { - if (p + 12 > pLast_) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: " - << "Directory " << object->groupName() << ": " - << " IFD entry " << i - << " lies outside of the data buffer.\n"; -#endif - return; - } - uint16_t tag = getUShort(p, byteOrder_); - TiffComponent::AutoPtr tc = CreationPolicy::create(tag, object->group()); - tc->setStart(p); - object->addChild(tc); - p += 12; - } - - if (p + 4 > pLast_) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: " - << "Directory " << object->groupName() << ": " - << " IFD exceeds data buffer, cannot read next pointer.\n"; -#endif - return; - } - uint32_t next = getLong(p, byteOrder_); - if (next) { - TiffComponent::AutoPtr tc = CreationPolicy::create(Tag::next, object->group()); - if (next > size_) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: " - << "Directory " << object->groupName() << ": " - << " Next pointer is out of bounds.\n"; -#endif - return; - } - tc->setStart(pData_ + next); - object->addNext(tc); - } - - } // TiffReader::visitDirectory - - template - void TiffReader::visitSubIfd(TiffSubIfd* object) - { - assert(object != 0); - - readTiffEntry(object); - if (object->typeId() == unsignedLong && object->count() >= 1) { - uint32_t offset = getULong(object->pData(), byteOrder_); - if (offset > size_) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: " - << "Directory " << object->groupName() - << ", entry 0x" << std::setw(4) - << std::setfill('0') << std::hex << object->tag() - << " Sub-IFD pointer is out of bounds; ignoring it.\n"; -#endif - return; - } - object->ifd_.setStart(pData_ + offset); - } -#ifndef SUPPRESS_WARNINGS - else { - std::cerr << "Warning: " - << "Directory " << object->groupName() - << ", entry 0x" << std::setw(4) - << std::setfill('0') << std::hex << object->tag() - << " doesn't look like a sub-IFD."; - } -#endif - - } // TiffReader::visitSubIfd - - template - void TiffReader::visitMnEntry(TiffMnEntry* object) - { - assert(object != 0); - - readTiffEntry(object); - // Find the camera model - TiffFinder finder(0x010f, Group::ifd0); - pRoot_->accept(finder); - TiffEntryBase* te = dynamic_cast(finder.result()); - std::string make; - if (te && te->pValue()) { - make = te->pValue()->toString(); - // create concrete makernote, based on model and makernote contents - object->mn_ = TiffMnCreator::create(object->tag(), - object->mnGroup_, - make, - object->pData(), - object->size(), - byteOrder_); - } - if (object->mn_) object->mn_->setStart(object->pData()); - - } // TiffReader::visitMnEntry - - template - void TiffReader::visitIfdMakernote(TiffIfdMakernote* object) - { - object->readHeader(object->start(), pLast_ - object->start()); - if (!object->checkHeader()) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: IFD Makernote header check failed.\n"; -#endif - return; // todo: signal error to parent, delete object - } - object->ifd_.setStart(object->start() + object->ifdOffset()); - - } // TiffReader::visitIfdMakernote - - template - void TiffReader::readTiffEntry(TiffEntryBase* object) - { - assert(object != 0); - - byte* p = const_cast(object->start()); - assert(p >= pData_); - - if (p + 12 > pLast_) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: Entry in directory " << object->groupName() - << "requests access to memory beyond the data buffer. " - << "Skipping entry.\n"; -#endif - return; - } - // Component already has tag - p += 2; - object->type_ = getUShort(p, byteOrder_); - // todo: check type - p += 2; - object->count_ = getULong(p, byteOrder_); - p += 4; - object->size_ = TypeInfo::typeSize(object->typeId()) * object->count(); - object->offset_ = getULong(p, byteOrder_); - object->pData_ = p; - if (object->size() > 4) { - if (object->offset() >= size_) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: Offset of " - << "directory " << object->groupName() << ", " - << " entry 0x" << std::setw(4) - << std::setfill('0') << std::hex << object->tag() - << " is out of bounds:\n" - << "Offset = 0x" << std::setw(8) - << std::setfill('0') << std::hex << object->offset() - << "; truncating the entry\n"; -#endif - object->size_ = 0; - object->count_ = 0; - object->offset_ = 0; - return; - } - object->pData_ = pData_ + object->offset(); - if (object->pData() + object->size() > pLast_) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Warning: Upper boundary of data for " - << "directory " << object->groupName() << ", " - << " entry 0x" << std::setw(4) - << std::setfill('0') << std::hex << object->tag() - << " is out of bounds:\n" - << "Offset = 0x" << std::setw(8) - << std::setfill('0') << std::hex << object->offset() - << ", size = " << std::dec << object->size() - << ", exceeds buffer size by " - // cast to make MSVC happy - << static_cast(object->pData() + object->size() - pLast_) - << " Bytes; adjusting the size\n"; -#endif - object->size_ = size_ - object->offset(); - // todo: adjust count_, make size_ a multiple of typeSize - } - } - Value::AutoPtr v = Value::create(object->typeId()); - if (v.get()) { - v->read(object->pData(), object->size(), byteOrder_); - object->pValue_ = v.release(); - } - - } // TiffReader::readTiffEntry - -} // namespace Exiv2 - -#endif // #ifndef TIFFVISITOR_TMPL_HPP_