diff --git a/src/Makefile b/src/Makefile index 3971b8d0..080d2967 100644 --- a/src/Makefile +++ b/src/Makefile @@ -47,14 +47,15 @@ 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 +CCHDR = exv_conf.h exv_msvc.h mn.hpp rcsid.hpp tiffvisitor_tmpl.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 jpgimage.cpp \ - makernote.cpp metadatum.cpp nikonmn.cpp olympusmn.cpp panasonicmn.cpp \ - sigmamn.cpp sonymn.cpp tags.cpp tiffimage.cpp tiffparser.cpp types.cpp \ - value.cpp + futils.cpp fujimn.cpp ifd.cpp image.cpp imgreg.cpp iptc.cpp \ + jpgimage.cpp makernote.cpp makernote2.cpp metadatum.cpp nikonmn.cpp \ + olympusmn.cpp panasonicmn.cpp sigmamn.cpp sonymn.cpp tags.cpp \ + tiffcomposite.cpp tiffimage.cpp tiffparser.cpp tiffvisitor.cpp \ + types.cpp value.cpp # Add library C source files to this list ifndef HAVE_TIMEGM @@ -65,7 +66,7 @@ endif BINSRC = addmoddel.cpp crwedit.cpp crwparse.cpp dataarea-test.cpp \ exifcomment.cpp exifdata-test.cpp exifprint.cpp ifd-test.cpp iotest.cpp \ iptceasy.cpp iptcprint.cpp iptctest.cpp key-test.cpp makernote-test.cpp \ - taglist.cpp write-test.cpp write2-test.cpp tiffparse.cpp + taglist.cpp write-test.cpp write2-test.cpp tiffparse.cpp # Main source file of the Exiv2 application EXIV2MAIN = exiv2.cpp diff --git a/src/makernote2.cpp b/src/makernote2.cpp new file mode 100644 index 00000000..9eca37ab --- /dev/null +++ b/src/makernote2.cpp @@ -0,0 +1,134 @@ +// ***************************************************************** -*- 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: makernote2.cpp + Version: $Rev$ + Author(s): Andreas Huggel (ahu) + History: 11-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 "makernote2.hpp" +#include "tiffcomposite.hpp" +#include "tiffvisitor.hpp" + +// + standard includes +#include +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + + const TiffMnRegistry TiffMnCreator::registry_[] = { + { "OLYMPUS", newOlympusMn } + }; + + bool TiffMnRegistry::operator==(const TiffMnRegistry::Key& key) const + { + std::string make(make_); + return make == key.make_.substr(0, make.length()); + } + + TiffComponent* TiffMnCreator::create(uint16_t tag, + uint16_t group, + std::string make, + const byte* pData, + uint32_t size, + ByteOrder byteOrder) + { + TiffComponent* tc = 0; + const TiffMnRegistry* tmr = find(registry_, TiffMnRegistry::Key(make)); + if (tmr) tc = tmr->newMnFct_(tag, group, pData, size, byteOrder); + return tc; + } // TiffMnCreator::create + + const char* OlympusMnHeader::signature_ = "OLYMP\0\1\0"; + const uint32_t OlympusMnHeader::size_ = 8; + + OlympusMnHeader::OlympusMnHeader() + { + read(reinterpret_cast(signature_), size_); + } + + bool OlympusMnHeader::read(const byte* pData, uint32_t size) + { + assert (pData != 0); + + if (size < size_) return false; + + header_.alloc(size_); + memcpy(header_.pData_, pData, header_.size_); + return true; + } // OlympusMnHeader::read + + bool OlympusMnHeader::check() const + { + if ( static_cast(header_.size_) < size_ + || 0 != memcmp(header_.pData_, signature_, 5)) { + return false; + } + return true; + } // OlympusMnHeader::check + + void TiffOlympusMn::doAddChild(TiffComponent::AutoPtr tiffComponent) + { + ifd_.addChild(tiffComponent); + } + + void TiffOlympusMn::doAddNext(TiffComponent::AutoPtr tiffComponent) + { + ifd_.addNext(tiffComponent); + } + + void TiffOlympusMn::doAccept(TiffVisitor& visitor) + { + visitor.visitOlympusMn(this); + ifd_.accept(visitor); + } + + // ************************************************************************* + // free functions + + TiffComponent* newOlympusMn(uint16_t tag, + uint16_t group, + const byte* /*pData*/, + uint32_t /*size*/, + ByteOrder /*byteOrder*/) + { + return new TiffOlympusMn(tag, group); + } + +} // namespace Exiv2 diff --git a/src/makernote2.hpp b/src/makernote2.hpp new file mode 100644 index 00000000..a55c6ab3 --- /dev/null +++ b/src/makernote2.hpp @@ -0,0 +1,211 @@ +// ***************************************************************** -*- 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 makernote2.hpp + @brief + @version $Rev$ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @date 11-Apr-06, ahu: created + */ +#ifndef MAKERNOTE2_HPP_ +#define MAKERNOTE2_HPP_ + +// ***************************************************************************** +// included header files +#include "tiffcomposite.hpp" +#include "types.hpp" + +// + standard includes +#include + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class declarations + + class TiffComponent; + template class TiffReader; + +// ***************************************************************************** +// class definitions + + namespace Group { + const uint16_t olympmn = 257; //!< Olympus makernote + } + + //! Type for a pointer to a function creating a makernote + typedef TiffComponent* (*NewMnFct)(uint16_t tag, + uint16_t group, + const byte* pData, + uint32_t size, + ByteOrder byteOrder); + + //! Makernote registry + struct TiffMnRegistry { + struct Key; + //! Compare a TiffMnRegistry structure with a TiffMnRegistry::Key + bool operator==(const Key& key) const; + + // DATA + const char* make_; //!< Camera make + NewMnFct newMnFct_; //!< Makernote create function + }; + + //! Search key for TIFF structure. + struct TiffMnRegistry::Key { + //! Constructor + Key(const std::string& make) : make_(make) {} + std::string make_; //!< Camera make + }; + + /*! + @brief TIFF makernote factory for concrete TIFF makernotes. + */ + class TiffMnCreator { + public: + /*! + @brief Create the Makernote for camera \em make and details from + the makernote entry itself if needed. Return a pointer to + the newly created TIFF component. Set tag and group of the + new component to \em tag and \em group. + @note Ownership for the component is transferred to the caller, + who is responsible to delete the component. No smart pointer + is used to indicate this transfer here in order to reduce + file dependencies. + */ + static TiffComponent* create(uint16_t tag, + uint16_t group, + std::string make, + const byte* pData, + uint32_t size, + ByteOrder byteOrder); + protected: + //! Prevent destruction (needed if used as a policy class) + ~TiffMnCreator() {} + private: + static const TiffMnRegistry registry_[]; // + friend class TiffReader; + public: + //! @name Creators + //@{ + //! Default constructor + TiffOlympusMn(uint16_t tag, uint16_t group) + : TiffComponent(tag, group), ifd_(tag, Group::olympmn) {} + //! Virtual destructor + virtual ~TiffOlympusMn() {} + //@} + + private: + //! @name Manipulators + //@{ + virtual void doAddChild(TiffComponent::AutoPtr tiffComponent); + virtual void doAddNext(TiffComponent::AutoPtr tiffComponent); + virtual void doAccept(TiffVisitor& visitor); + //@} + + private: + // DATA + OlympusMnHeader header_; //!< Makernote header + TiffDirectory ifd_; //!< Makernote IFD + + }; // TiffOlympusMn + +// ***************************************************************************** +// template, inline and free functions + + //! Function to create an Olympus makernote + TiffComponent* newOlympusMn(uint16_t tag, + uint16_t group, + const byte* pData, + uint32_t size, + ByteOrder byteOrder); + +} // namespace Exiv2 + +#endif // #ifndef MAKERNOTE2_HPP_ diff --git a/src/tiffcomposite.cpp b/src/tiffcomposite.cpp new file mode 100644 index 00000000..8fd65f39 --- /dev/null +++ b/src/tiffcomposite.cpp @@ -0,0 +1,214 @@ +// ***************************************************************** -*- 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: tiffcomposite.cpp + Version: $Rev$ + Author(s): Andreas Huggel (ahu) + History: 11-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 tiffcomposite.o +//#define DEBUG + +// ***************************************************************************** +// included header files +#ifdef _MSC_VER +# include "exv_msvc.h" +#else +# include "exv_conf.h" +#endif + +#include "tiffcomposite.hpp" +#include "tiffvisitor.hpp" +#include "value.hpp" + +// + standard includes +#include +#include +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + + void TiffHeade2::print(std::ostream& os, const std::string& prefix) const + { + os << prefix + << "Header, offset = 0x" << std::setw(8) << std::setfill('0') + << std::hex << std::right << offset_; + + switch (byteOrder_) { + case littleEndian: os << ", little endian encoded"; break; + case bigEndian: os << ", big endian encoded"; break; + case invalidByteOrder: break; + } + os << "\n"; + + } // TiffHeade2::print + + TiffDirectory::~TiffDirectory() + { + Components::iterator b = components_.begin(); + Components::iterator e = components_.end(); + for (Components::iterator i = b; i != e; ++i) { + delete *i; + } + delete pNext_; + } // TiffDirectory::~TiffDirectory + + TiffEntryBase::~TiffEntryBase() + { + if (isAllocated_) { + delete[] pData_; + } + delete pValue_; + } // TiffEntryBase::~TiffEntryBase + + TiffMakernote::~TiffMakernote() + { + delete mn_; + } // TiffMakernote::~TiffMakernote + + const uint16_t TiffHeade2::tag_ = 42; + + bool TiffHeade2::read(const byte* pData, uint32_t size) + { + if (size < 8) return false; + + if (pData[0] == 0x49 && pData[1] == 0x49) { + byteOrder_ = littleEndian; + } + else if (pData[0] == 0x4d && pData[1] == 0x4d) { + byteOrder_ = bigEndian; + } + else { + return false; + } + if (tag_ != getUShort(pData + 2, byteOrder_)) return false; + offset_ = getULong(pData + 4, byteOrder_); + + return true; + } // TiffHeade2::read + + std::string TiffComponent::groupName() const + { + // Todo: This mapping should be a table and it belongs somewhere else + // Possibly the whole function shouldn't be in this class... + std::string group; + switch (group_) { + case 1: group = "Image"; break; + case 2: group = "Thumbnail"; break; + case 3: group = "Photo"; break; + case 4: group = "GPSInfo"; break; + case 5: group = "Iop"; break; + case 257: group = "Olympus"; break; + default: group = "Unknown"; break; + } + + return group; + } + + void TiffComponent::addChild(TiffComponent::AutoPtr tiffComponent) + { + doAddChild(tiffComponent); + } // TiffComponent::addChild + + void TiffDirectory::doAddChild(TiffComponent::AutoPtr tiffComponent) + { + components_.push_back(tiffComponent.release()); + } // TiffDirectory::doAddChild + + void TiffSubIfd::doAddChild(TiffComponent::AutoPtr tiffComponent) + { + ifd_.addChild(tiffComponent); + } // TiffSubIfd::doAddChild + + void TiffMakernote::doAddChild(TiffComponent::AutoPtr tiffComponent) + { + if (mn_) mn_->addChild(tiffComponent); + } // TiffMakernote::doAddChild + + void TiffComponent::addNext(TiffComponent::AutoPtr tiffComponent) + { + doAddNext(tiffComponent); + } // TiffComponent::addNext + + void TiffDirectory::doAddNext(TiffComponent::AutoPtr tiffComponent) + { + pNext_ = tiffComponent.release(); + } // TiffDirectory::doAddNext + + void TiffSubIfd::doAddNext(TiffComponent::AutoPtr tiffComponent) + { + ifd_.addNext(tiffComponent); + } // TiffSubIfd::doAddNext + + void TiffMakernote::doAddNext(TiffComponent::AutoPtr tiffComponent) + { + if (mn_) mn_->addNext(tiffComponent); + } // TiffMakernote::doAddNext + + void TiffComponent::accept(TiffVisitor& visitor) + { + if (visitor.go()) doAccept(visitor); // one for NVI :) + } // TiffComponent::accept + + void TiffEntry::doAccept(TiffVisitor& visitor) + { + visitor.visitEntry(this); + } // TiffEntry::doAccept + + void TiffDirectory::doAccept(TiffVisitor& visitor) + { + visitor.visitDirectory(this); + Components::const_iterator b = components_.begin(); + Components::const_iterator e = components_.end(); + for (Components::const_iterator i = b; visitor.go() && i != e; ++i) { + (*i)->accept(visitor); + } + if (visitor.go()) visitor.visitDirectoryNext(this); + if (pNext_) { + pNext_->accept(visitor); + } + if (visitor.go()) visitor.visitDirectoryEnd(this); + + } // TiffDirectory::doAccept + + void TiffSubIfd::doAccept(TiffVisitor& visitor) + { + visitor.visitSubIfd(this); + ifd_.accept(visitor); + } // TiffSubIfd::doAccept + + void TiffMakernote::doAccept(TiffVisitor& visitor) + { + visitor.visitMakernote(this); + if (mn_) mn_->accept(visitor); + } // TiffMakernote::doAccept + + // ************************************************************************* + // free functions + +} // namespace Exiv2 diff --git a/src/tiffcomposite.hpp b/src/tiffcomposite.hpp new file mode 100644 index 00000000..1b08917f --- /dev/null +++ b/src/tiffcomposite.hpp @@ -0,0 +1,408 @@ +// ***************************************************************** -*- 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 tiffcomposite.hpp + @brief + @version $Rev$ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @date 11-Apr-06, ahu: created + */ +#ifndef TIFFCOMPOSITE_HPP_ +#define TIFFCOMPOSITE_HPP_ + +// ***************************************************************************** +// included header files +#include "image.hpp" // for Blob +#include "types.hpp" + +// + standard includes +#include +#include +#include + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class declarations + + class Value; + class TiffVisitor; + template class TiffReader; + class TiffMetadataDecoder; + class TiffPrinter; + +// ***************************************************************************** +// class definitions + + /*! + Known TIFF groups + + Todo: what exactly are these and where should they go? + Are they going to be mapped to the second part of an Exif key or are they + the second part of the key? + */ + namespace Group { + const uint16_t none = 0; //!< Dummy group + const uint16_t ifd0 = 1; //!< Exif IFD0 + const uint16_t ifd1 = 2; //!< Thumbnail IFD + const uint16_t exif = 3; //!< Exif IFD + const uint16_t gps = 4; //!< GPS IFD + const uint16_t iop = 5; //!< Interoperability IFD + const uint16_t mn = 256; //!< Makernote + } + + /*! + Special TIFF tags for the use in TIFF structures only + + Todo: Same Q as above... + */ + namespace Tag { + const uint32_t none = 0x10000; //!< Dummy tag + const uint32_t root = 0x20000; //!< Special tag: root IFD + const uint32_t next = 0x30000; //!< Special tag: next IFD + } + + /*! + @brief This class models a TIFF header structure. + */ + class TiffHeade2 { + public: + //! @name Creators + //@{ + //! Default constructor + TiffHeade2() + : byteOrder_ (littleEndian), + offset_ (0x00000008) + {} + //@} + + //! @name Manipulators + //@{ + /*! + @brief Read the TIFF header from a data buffer. Return false if the + data buffer does not contain a TIFF header, else true. + + @param pData Pointer to the data buffer. + @param size Number of bytes in the data buffer. + */ + bool read(const byte* pData, uint32_t size); + //@} + + //! @name Accessors + //@{ + /*! + @brief Write the TIFF header to the binary image \em blob. + This method appends to the blob. + + @param blob Binary image to add to. + + @throw Error If the header cannot be written. + */ + void write(Blob& blob) const; + /*! + @brief Print debug info for the TIFF header to \em os. + + @param os Output stream to write to. + @param prefix Prefix to be written before each line of output. + */ + void print(std::ostream& os, const std::string& prefix ="") const; + //! Return the byte order (little or big endian). + ByteOrder byteOrder() const { return byteOrder_; } + //! Return the offset to the start of the root directory + uint32_t offset() const { return offset_; } + //@} + + private: + // DATA + ByteOrder byteOrder_; //!< Applicable byte order + uint32_t offset_; //!< Offset to the start of the root dir + + static const uint16_t tag_; //!< 42, identifies the buffer as TIFF data + + }; // class TiffHeade2 + + /*! + @brief Interface class for components of a TIFF directory hierarchy + (Composite pattern). Both TIFF directories as well as entries + implement this interface. A component can be un iquely identified + by a tag, group tupel. This class is implemented as a NVI + (Non-Virtual Interface) and it has an interface for visitors (Visitor + pattern). + */ + class TiffComponent { + public: + //! TiffComponent auto_ptr type + typedef std::auto_ptr AutoPtr; + //! Container type to hold all metadata + typedef std::vector Components; + + //! @name Creators + //@{ + //! Constructor + TiffComponent(uint16_t tag, uint16_t group) + : tag_(tag), group_(group), pData_(0) {} + + //! Virtual destructor. + virtual ~TiffComponent() {} + //@} + + //! @name Manipulators + //@{ + //! Add a child to the component. Default is to do nothing. + void addChild(AutoPtr tiffComponent); + //! Add a "next" component to the component. Default is to do nothing. + void addNext(AutoPtr tiffComponent); + /*! + @brief Interface to accept visitors (Visitor pattern). + + @param visitor The visitor. + */ + void accept(TiffVisitor& visitor); + /*! + @brief Set a pointer to the start of the binary representation of the + component in a memory buffer. The buffer must be allocated and + freed outside of this class. + */ + void setStart(const byte* pData) { pData_ = const_cast(pData); } + //@} + + //! @name Accessors + //@{ + //! Return the tag of this entry. + uint16_t tag() const { return tag_; } + //! Return the group id of this component + uint16_t group() const { return group_; } + //! Return the group name of this component + std::string groupName() const; + //! Return a pointer to the start of the binary representation of the component + const byte* start() const { return pData_; } + + protected: + //! @name Manipulators + //@{ + //! Implements addChild(). + virtual void doAddChild(AutoPtr tiffComponent) {} + + //! Implements addNext(). + virtual void doAddNext(AutoPtr tiffComponent) {} + + //! Implements accept() + virtual void doAccept(TiffVisitor& visitor) =0; + //@} + + private: + // DATA + uint16_t tag_; //!< Tag that identifies the component + uint16_t group_; //!< Group id for this component + /*! + Pointer to the start of the binary representation of the component in + a memory buffer. The buffer is allocated and freed outside of this class. + */ + byte* pData_; + + }; // class TiffComponent + + /*! + @brief This abstract base class provides the common functionality of an + IFD directory entry and defines an extended interface for derived + concrete entries, which allows access to the attributes of the + entry. + */ + class TiffEntryBase : public TiffComponent { + template + friend class TiffReader; + public: + //! @name Creators + //@{ + //! Default constructor + TiffEntryBase(uint16_t tag, uint16_t group) + : TiffComponent(tag, group), + type_(0), count_(0), offset_(0), + size_(0), pData_(0), isAllocated_(false), pValue_(0) {} + //! Virtual destructor. + virtual ~TiffEntryBase(); + //@} + + //! @name Accessors + //@{ + //! Return the Exiv2 type which corresponds to the field type. + TypeId typeId() const { return TypeId(type_); } + //! 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. + uint32_t offset() const { return offset_; } + //! Return the size of this component in bytes + uint32_t size() const { return size_; } + //! Return a pointer to the data area of this component + const byte* pData() const { return pData_; } + //! Return a pointer to the converted value of this component + const Value* pValue() const { return pValue_; } + //@} + + private: + // 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 + /*! + Size of the data buffer holding the value in bytes, there is no + minimum size. + */ + uint32_t size_; + const byte* pData_; //!< Pointer to the data area + bool isAllocated_; //!< True if this entry owns the value data + Value* pValue_; //!< Converted data value + + }; // class TiffEntryBase + + /*! + @brief A standard TIFF IFD entry. + */ + class TiffEntry : public TiffEntryBase { + public: + //! @name Creators + //@{ + //! Constructor + TiffEntry(uint16_t tag, uint16_t group) : TiffEntryBase(tag, group) {} + //! Virtual destructor. + virtual ~TiffEntry() {} + //@} + + private: + //! @name Manipulators + //@{ + virtual void doAccept(TiffVisitor& visitor); + //@} + + }; // class TiffEntry + + /*! + @brief This class models a TIFF directory (%Ifd). It is a composite + component of the TIFF tree. + */ + class TiffDirectory : public TiffComponent { + friend class TiffPrinter; + public: + //! @name Creators + //@{ + //! Default constructor + TiffDirectory(uint16_t tag, uint16_t group) + : TiffComponent(tag, group), pNext_(0) {} + //! Virtual destructor + virtual ~TiffDirectory(); + //@} + + private: + //! @name Manipulators + //@{ + virtual void doAddChild(TiffComponent::AutoPtr tiffComponent); + virtual void doAddNext(TiffComponent::AutoPtr tiffComponent); + virtual void doAccept(TiffVisitor& visitor); + //@} + + private: + // DATA + Components components_; //!< List of components in this directory + TiffComponent* pNext_; //!< Pointer to the next IFD + + }; // class TiffDirectory + + /*! + @brief This class models a TIFF sub-directory (sub-IFD). A sub-IFD + is an entry with a value that is a pointer to an IFD + structure and contains this IFD. The TIFF standard defines + some important tags to be sub-IFDs, including the %Exif and + GPS tags. + */ + class TiffSubIfd : public TiffEntryBase { + template + friend class TiffReader; + public: + //! @name Creators + //@{ + //! Default constructor + TiffSubIfd(uint16_t tag, uint16_t group, uint16_t newGroup) + : TiffEntryBase(tag, group), ifd_(tag, newGroup) {} + //! Virtual destructor + virtual ~TiffSubIfd() {} + //@} + + private: + //! @name Manipulators + //@{ + virtual void doAddChild(TiffComponent::AutoPtr tiffComponent); + virtual void doAddNext(TiffComponent::AutoPtr tiffComponent); + virtual void doAccept(TiffVisitor& visitor); + //@} + + private: + // DATA + TiffDirectory ifd_; //!< The subdirectory + + }; // class TiffSubIfd + + /*! + @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. + */ + class TiffMakernote : public TiffEntryBase { + template + friend class TiffReader; + friend class TiffMetadataDecoder; + friend class TiffPrinter; + public: + //! @name Creators + //@{ + //! Default constructor + TiffMakernote(uint16_t tag, uint16_t group, uint16_t newGroup) + : TiffEntryBase(tag, group), newGroup_(newGroup), mn_(0) {} + //! Virtual destructor + virtual ~TiffMakernote(); + //@} + + private: + //! @name Manipulators + //@{ + virtual void doAddChild(TiffComponent::AutoPtr tiffComponent); + virtual void doAddNext(TiffComponent::AutoPtr tiffComponent); + virtual void doAccept(TiffVisitor& visitor); + //@} + + private: + // DATA + uint16_t newGroup_; //!< New group for concrete mn + TiffComponent* mn_; //!< The Makernote + + }; // class TiffMakernote + +// ***************************************************************************** +// template, inline and free functions + +} // namespace Exiv2 + +#endif // #ifndef TIFFCOMPOSITE_HPP_ diff --git a/src/tiffparse.cpp b/src/tiffparse.cpp index 557fff2a..545bc73e 100644 --- a/src/tiffparse.cpp +++ b/src/tiffparse.cpp @@ -43,13 +43,17 @@ try { if (0 == rootDir.get()) { throw Error(1, "No root element defined in TIFF structure"); } - TiffReader reader(buf.pData_, buf.size_, tiffHeader.byteOrder()); rootDir->setStart(buf.pData_ + tiffHeader.offset()); + TiffReader reader(buf.pData_, + buf.size_, + tiffHeader.byteOrder(), + rootDir.get()); rootDir->accept(reader); tiffHeader.print(std::cerr); - rootDir->print(std::cerr, tiffHeader.byteOrder()); + TiffPrinter tiffPrinter(std::cerr); + rootDir->accept(tiffPrinter); return 0; } diff --git a/src/tiffparser.cpp b/src/tiffparser.cpp index 1513383a..88df2b61 100644 --- a/src/tiffparser.cpp +++ b/src/tiffparser.cpp @@ -42,28 +42,32 @@ EXIV2_RCSID("@(#) $Id$"); #endif #include "tiffparser.hpp" -#include "image.hpp" -#include "exif.hpp" -#include "tags.hpp" -#include "error.hpp" -#include "futils.hpp" +#include "tiffcomposite.hpp" // + standard includes -#include -#include #include /* -------------------------------------------------------------------------- Todo: - + Add further child mgmt stuff to TIFF composite: remove, find + + 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 TiffMakernote and + concrete Makernotes and which of them should derive from base-entry + - TiffMakernote 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?? in crwimage.* : @@ -79,11 +83,23 @@ EXIV2_RCSID("@(#) $Id$"); // class member definitions namespace Exiv2 { + /* + This table describes the standard TIFF layout and determines the + corresponding Exiv2 TIFF components. The key of the table consists of the + first two attributes, (extended) tag and group. Tag is the TIFF tag or one + of a few extended tags, group identifies the IFD or any other composite + TIFF component. Each entry of the table defines for a particular tag and + group combination, which create function is used and what the group of the + new component is. + */ const TiffStructure TiffCreator::tiffStructure_[] = { + // ext. tag group create function new group + //--------- ----------- ---------------- ----------- { Tag::root, Group::none, newTiffDirectory, Group::ifd0 }, { 0x8769, Group::ifd0, newTiffSubIfd, Group::exif }, { 0x8825, Group::ifd0, newTiffSubIfd, Group::gps }, { 0xa005, Group::exif, newTiffSubIfd, Group::iop }, + { 0x927c, Group::exif, newTiffMakernote, Group::mn }, { Tag::next, Group::ifd0, newTiffDirectory, Group::ifd0 } }; @@ -108,229 +124,6 @@ namespace Exiv2 { return tc; } // TiffCreator::create - TiffDirectory::~TiffDirectory() - { - Components::iterator b = components_.begin(); - Components::iterator e = components_.end(); - for (Components::iterator i = b; i != e; ++i) { - delete *i; - } - delete pNext_; - } // TiffDirectory::~TiffDirectory - - TiffEntryBase::~TiffEntryBase() - { - if (isAllocated_) { - delete[] pData_; - } - delete pValue_; - } // TiffEntryBase::~TiffEntryBase - - const uint16_t TiffHeade2::tag_ = 42; - - bool TiffHeade2::read(const byte* pData, uint32_t size) - { - if (size < 8) return false; - - if (pData[0] == 0x49 && pData[1] == 0x49) { - byteOrder_ = littleEndian; - } - else if (pData[0] == 0x4d && pData[1] == 0x4d) { - byteOrder_ = bigEndian; - } - else { - return false; - } - if (tag_ != getUShort(pData + 2, byteOrder_)) return false; - offset_ = getULong(pData + 4, byteOrder_); - - return true; - } // TiffHeade2::read - - std::string TiffComponent::groupName() const - { - // Todo: This mapping should be a table and it belongs somewhere else - // Possibly the whole function shouldn't be in this class... - std::string group; - switch (group_) { - case 1: group = "Image"; break; - case 2: group = "Thumbnail"; break; - case 3: group = "Photo"; break; - case 4: group = "GPSInfo"; break; - case 5: group = "Iop"; break; - default: group = "Unknown"; break; - } - - return group; - } - - void TiffComponent::addChild(TiffComponent::AutoPtr tiffComponent) - { - doAddChild(tiffComponent); - } // TiffComponent::addChild - - void TiffDirectory::doAddChild(TiffComponent::AutoPtr tiffComponent) - { - components_.push_back(tiffComponent.release()); - } // TiffDirectory::doAddChild - - void TiffSubIfd::doAddChild(TiffComponent::AutoPtr tiffComponent) - { - ifd_.addChild(tiffComponent); - } // TiffSubIfd::doAddChild - - void TiffComponent::addNext(TiffComponent::AutoPtr tiffComponent) - { - doAddNext(tiffComponent); - } // TiffComponent::addNext - - void TiffDirectory::doAddNext(TiffComponent::AutoPtr tiffComponent) - { - pNext_ = tiffComponent.release(); - } // TiffDirectory::doAddNext - - void TiffSubIfd::doAddNext(TiffComponent::AutoPtr tiffComponent) - { - ifd_.addNext(tiffComponent); - } // TiffSubIfd::doAddNext - - void TiffHeade2::print(std::ostream& os, const std::string& prefix) const - { - os << prefix - << "Header, offset = 0x" << std::setw(8) << std::setfill('0') - << std::hex << std::right << offset_; - - switch (byteOrder_) { - case littleEndian: os << ", little endian encoded"; break; - case bigEndian: os << ", big endian encoded"; break; - case invalidByteOrder: break; - } - os << "\n"; - - } // TiffHeade2::print - - void TiffComponent::print(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const - { - doPrint(os, byteOrder, prefix); - } // TiffComponent::print - - void TiffEntryBase::printEntry(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const - { - os << prefix - << "tag 0x" << std::setw(4) << std::setfill('0') - << std::hex << std::right << tag() - << ", type " << TypeInfo::typeName(typeId()) - << ", " << std::dec << count() << " component"; - if (count() > 1) os << "s"; - os <<" in " << size() << " bytes"; - if (size() > 4) os << ", offset " << offset(); - os << "\n"; - if (pValue_ && pValue_->count() < 100) os << prefix << *pValue_; - else os << prefix << "..."; - os << "\n"; - - } // TiffEntryBase::printEntry - - void TiffEntry::doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const - { - TiffEntryBase::printEntry(os, byteOrder, prefix); - } // TiffEntry::doPrint - - void TiffDirectory::doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const - { - os << prefix << groupName() << " directory with " - // cast to make MSVC happy - << std::dec << static_cast(components_.size()); - if (components_.size() == 1) os << " entry:\n"; - else os << " entries:\n"; - Components::const_iterator b = components_.begin(); - Components::const_iterator e = components_.end(); - for (Components::const_iterator i = b; i != e; ++i) { - (*i)->print(os, byteOrder, prefix + " "); - } - if (pNext_) { - os << prefix << "Next directory:\n"; - pNext_->print(os, byteOrder, prefix); - } - else { - os << prefix << "No next directory.\n"; - } - - } // TiffDirectory::doPrint - - void TiffSubIfd::doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const - { - TiffEntryBase::printEntry(os, byteOrder, prefix); - ifd_.print(os, byteOrder, prefix); - } // TiffSubIfd::doPrint - - void TiffComponent::accept(TiffVisitor& visitor) - { - doAccept(visitor); - } // TiffComponent::accept - - void TiffEntry::doAccept(TiffVisitor& visitor) - { - visitor.visitEntry(this); - } // TiffEntry::doAccept - - void TiffDirectory::doAccept(TiffVisitor& visitor) - { - visitor.visitDirectory(this); - - Components::const_iterator b = components_.begin(); - Components::const_iterator e = components_.end(); - for (Components::const_iterator i = b; i != e; ++i) { - (*i)->accept(visitor); - } - if (pNext_) { - pNext_->accept(visitor); - } - - } // TiffDirectory::doAccept - - void TiffSubIfd::doAccept(TiffVisitor& visitor) - { - visitor.visitSubIfd(this); - ifd_.accept(visitor); - } // TiffSubIfd::doAccept - - void TiffMetadataDecoder::visitEntry(TiffEntry* object) - { - decodeTiffEntry(object); - } - - void TiffMetadataDecoder::visitDirectory(TiffDirectory* object) - { - // Nothing to do - } - - void TiffMetadataDecoder::visitSubIfd(TiffSubIfd* object) - { - decodeTiffEntry(object); - } - - void TiffMetadataDecoder::decodeTiffEntry(const TiffEntryBase* object) - { - assert(object != 0); - - // Todo: ExifKey should have an appropriate c'tor, it should not be - // necessary to use groupName here - ExifKey k(object->tag(), object->groupName()); - assert(pImage_ != 0); - pImage_->exifData().add(k, object->pValue()); - } // TiffMetadataDecoder::decodeTiffEntry - // ************************************************************************* // free functions @@ -348,4 +141,12 @@ namespace Exiv2 { ts->newGroup_)); } + TiffComponent::AutoPtr newTiffMakernote(const TiffStructure* ts) + { + assert(ts); + return TiffComponent::AutoPtr(new TiffMakernote(ts->tag(), + ts->group_, + ts->newGroup_)); + } + } // namespace Exiv2 diff --git a/src/tiffparser.hpp b/src/tiffparser.hpp index d83120c7..df7afb5c 100644 --- a/src/tiffparser.hpp +++ b/src/tiffparser.hpp @@ -31,17 +31,14 @@ // ***************************************************************************** // included header files -#include "image.hpp" -#include "exif.hpp" -#include "iptc.hpp" -#include "types.hpp" +#include "tiffcomposite.hpp" +#include "tiffvisitor.hpp" +#include "tiffvisitor_tmpl.hpp" #include "error.hpp" +#include "types.hpp" // + standard includes -#include -#include -#include -#include +#include #include // ***************************************************************************** @@ -52,487 +49,11 @@ namespace Exiv2 { // class declarations struct TiffStructure; - class TiffDirectory; - class TiffEntryBase; - class TiffEntry; - class TiffSubIfd; - -// ***************************************************************************** -// type definitions + class Image; // ***************************************************************************** // class definitions - /*! - Known TIFF groups - - Todo: what exactly are these and where should they go? - Are they going to be mapped to the second part of an Exif key or are they - the second part of the key? - */ - namespace Group { - const uint16_t none = 0; //!< Dummy group - const uint16_t ifd0 = 1; //!< Exif IFD0 - const uint16_t ifd1 = 2; //!< Thumbnail IFD - const uint16_t exif = 3; //!< Exif IFD - const uint16_t gps = 4; //!< GPS IFD - const uint16_t iop = 5; //!< Interoperability IFD - const uint16_t makernote = 256; //!< Makernote - const uint16_t canonmn = 257; //!< Canon makernote - } - - /*! - Special TIFF tags for the use in TIFF structures only - - Todo: Same Q as above... - */ - namespace Tag { - const uint32_t none = 0x10000; //!< Dummy tag - const uint32_t root = 0x20000; //!< Special tag: root IFD - const uint32_t next = 0x30000; //!< Special tag: next IFD - } - - /*! - @brief This class models a TIFF header structure. - */ - class TiffHeade2 { - public: - //! @name Creators - //@{ - //! Default constructor - TiffHeade2() - : byteOrder_ (littleEndian), - offset_ (0x00000008) - {} - //@} - - //! @name Manipulators - //@{ - /*! - @brief Read the TIFF header from a data buffer. Return false if the - data buffer does not contain a TIFF header, else true. - - @param pData Pointer to the data buffer. - @param size Number of bytes in the data buffer. - */ - bool read(const byte* pData, uint32_t size); - //@} - - //! @name Accessors - //@{ - /*! - @brief Write the TIFF header to the binary image \em blob. - This method appends to the blob. - - @param blob Binary image to add to. - - @throw Error If the header cannot be written. - */ - void write(Blob& blob) const; - /*! - @brief Print debug info for the TIFF header to \em os. - - @param os Output stream to write to. - @param prefix Prefix to be written before each line of output. - */ - void print(std::ostream& os, const std::string& prefix ="") const; - //! Return the byte order (little or big endian). - ByteOrder byteOrder() const { return byteOrder_; } - //! Return the offset to the start of the root directory - uint32_t offset() const { return offset_; } - //@} - - private: - // DATA - ByteOrder byteOrder_; //!< Applicable byte order - uint32_t offset_; //!< Offset to the start of the root dir - - static const uint16_t tag_; //!< 42, identifies the buffer as TIFF data - - }; // class TiffHeade2 - - /*! - @brief Abstract base class for TIFF composite vistors (Visitor pattern) - - A concrete visitor class is used as shown in the example below. Accept() - will invoke the member function corresponding to the concrete type of each - component in the composite. - - @code - void visitorExample(Exiv2::TiffComponent* tiffComponent, Exiv2::TiffVisitor& visitor) - { - tiffComponent->accept(visitor); - } - @endcode - */ - class TiffVisitor { - public: - //! @name Creators - //@{ - virtual ~TiffVisitor() {} - //@} - - //! @name Manipulators - //@{ - //! Operation to perform for a TIFF entry - virtual void visitEntry(TiffEntry* object) =0; - //! Operation to perform for a TIFF directory - virtual void visitDirectory(TiffDirectory* object) =0; - //! Operation to perform for a TIFF sub-IFD - virtual void visitSubIfd(TiffSubIfd* object) =0; - //@} - - }; // class TiffVisitor - - /*! - @brief TIFF composite visitor to decode metadata from the TIFF tree and - add it to an Image, which is supplied in the constructor (Visitor - pattern). Used by TiffParser to decode the metadata from a - TIFF composite. - */ - class TiffMetadataDecoder : public TiffVisitor { - public: - //! @name Creators - //@{ - //! Constructor, taking the image to add the metadata to - TiffMetadataDecoder(Image* pImage) : pImage_(pImage) {} - //! Virtual destructor - virtual ~TiffMetadataDecoder() {} - //@} - - //! @name Manipulators - //@{ - //! Decode a TIFF entry - virtual void visitEntry(TiffEntry* object); - //! Decode a TIFF directory - virtual void visitDirectory(TiffDirectory* object); - //! Decode a TIFF sub-IFD - virtual void visitSubIfd(TiffSubIfd* object); - //! Decode a standard TIFF entry - void decodeTiffEntry(const TiffEntryBase* object); - //@} - - private: - // DATA - Image* pImage_; //!< Pointer to the image to which the metadata is added - - }; // class TiffMetadataDecoder - - /*! - @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. - */ - template - class TiffReader : public TiffVisitor, public CreationPolicy { - public: - //! @name Creators - //@{ - /*! - @brief Constructor. The data buffer and table describing the TIFF - 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). - */ - TiffReader(const byte* pData, - uint32_t size, - ByteOrder byteOrder); - - //! Virtual destructor - virtual ~TiffReader() {} - //@} - - //! @name Manipulators - //@{ - //! Read a TIFF entry from the data buffer - virtual void visitEntry(TiffEntry* object); - //! Read a TIFF directory from the data buffer - virtual void visitDirectory(TiffDirectory* object); - //! Read a TIFF sub-IFD from the data buffer - virtual void visitSubIfd(TiffSubIfd* object); - //! Read a standard TIFF entry from the data buffer - void readTiffEntry(TiffEntryBase* object); - //@} - - 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 - - }; // class TiffReader - - /*! - @brief Interface class for components of a TIFF directory hierarchy - (Composite pattern). Both TIFF directories as well as entries - implement this interface. A component can be un iquely identified - by a tag, group tupel. This class is implemented as a NVI - (Non-Virtual Interface) and it has an interface for visitors (Visitor - pattern). - */ - class TiffComponent { - public: - //! TiffComponent auto_ptr type - typedef std::auto_ptr AutoPtr; - //! Container type to hold all metadata - typedef std::vector Components; - - //! @name Creators - //@{ - //! Constructor - TiffComponent(uint16_t tag, uint16_t group) - : tag_(tag), group_(group), pData_(0) {} - - //! Virtual destructor. - virtual ~TiffComponent() {} - //@} - - //! @name Manipulators - //@{ - //! Add a child to the component. Default is to do nothing. - void addChild(AutoPtr tiffComponent); - //! Add a "next" component to the component. Default is to do nothing. - void addNext(AutoPtr tiffComponent); - /*! - @brief Interface to accept visitors (Visitor pattern). - - @param visitor The visitor. - */ - void accept(TiffVisitor& visitor); - /*! - @brief Set a pointer to the start of the binary representation of the - component in a memory buffer. The buffer must be allocated and - freed outside of this class. - */ - void setStart(const byte* pData) { pData_ = const_cast(pData); } - //@} - - //! @name Accessors - //@{ - //! Return the tag of this entry. - uint16_t tag() const { return tag_; } - //! Return the group id of this component - uint16_t group() const { return group_; } - //! Return the group name of this component - std::string groupName() const; - //! Return a pointer to the start of the binary representation of the component - const byte* start() const { return pData_; } - /*! - @brief Print debug info about a component to \em os. - - @param os Output stream to write to - @param byteOrder Byte order - @param prefix Prefix to be written before each line of output - */ - void print(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix ="") const; - //@} - - protected: - //! @name Manipulators - //@{ - //! Implements addChild(). - virtual void doAddChild(AutoPtr tiffComponent) {} - - //! Implements addNext(). - virtual void doAddNext(AutoPtr tiffComponent) {} - - //! Implements accept() - virtual void doAccept(TiffVisitor& visitor) =0; - //@} - - //! @name Accessors - //@{ - //! Implements print() - virtual void doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const =0; - //@} - - private: - // DATA - uint16_t tag_; //!< Tag that identifies the component - uint16_t group_; //!< Group id for this component - /*! - Pointer to the start of the binary representation of the component in - a memory buffer. The buffer is allocated and freed outside of this class. - */ - byte* pData_; - - }; // class TiffComponent - - /*! - @brief This abstract base class provides the common functionality of an - IFD directory entry and defines an extended interface for derived - concrete entries, which allows access to the attributes of the - entry. - */ - class TiffEntryBase : public TiffComponent { - template - friend void TiffReader::readTiffEntry(TiffEntryBase* object); - public: - //! @name Creators - //@{ - //! Default constructor - TiffEntryBase(uint16_t tag, uint16_t group) - : TiffComponent(tag, group), - type_(0), count_(0), offset_(0), - size_(0), pData_(0), isAllocated_(false), pValue_(0) {} - //! Virtual destructor. - virtual ~TiffEntryBase(); - //@} - - //! @name Accessors - //@{ - //! Return the Exiv2 type which corresponds to the field type. - TypeId typeId() const { return TypeId(type_); } - //! 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. - uint32_t offset() const { return offset_; } - //! Return the size of this component in bytes - uint32_t size() const { return size_; } - //! Return a pointer to the data area of this component - const byte* pData() const { return pData_; } - //! Return a pointer to the converted value of this component - const Value* pValue() const { return pValue_; } - - //! Print base entry - void printEntry(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const; - //@} - - private: - // 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 - /*! - Size of the data buffer holding the value in bytes, there is no - minimum size. - */ - uint32_t size_; - const byte* pData_; //!< Pointer to the data area - bool isAllocated_; //!< True if this entry owns the value data - Value* pValue_; //!< Converted data value - - }; // class TiffEntryBase - - /*! - @brief A standard TIFF IFD entry. - */ - class TiffEntry : public TiffEntryBase { - public: - //! @name Creators - //@{ - //! Constructor - TiffEntry(uint16_t tag, uint16_t group) : TiffEntryBase(tag, group) {} - //! Virtual destructor. - virtual ~TiffEntry() {} - //@} - - private: - //! @name Manipulators - //@{ - virtual void doAccept(TiffVisitor& visitor); - //@} - - //! @name Accessors - //@{ - virtual void doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const; - //@} - - }; // class TiffEntry - - /*! - @brief This class models a TIFF directory (%Ifd). It is a composite - component of the TIFF tree. - */ - class TiffDirectory : public TiffComponent { - public: - //! @name Creators - //@{ - //! Default constructor - TiffDirectory(uint16_t tag, uint16_t group) - : TiffComponent(tag, group), pNext_(0) {} - //! Virtual destructor - virtual ~TiffDirectory(); - //@} - - private: - //! @name Manipulators - //@{ - virtual void doAddChild(TiffComponent::AutoPtr tiffComponent); - - virtual void doAddNext(TiffComponent::AutoPtr tiffComponent); - - virtual void doAccept(TiffVisitor& visitor); - //@} - - //! @name Accessors - //@{ - virtual void doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const; - //@} - - private: - // DATA - Components components_; //!< List of components in this directory - TiffComponent* pNext_; //!< Pointer to the next IFD - - }; // class TiffDirectory - - /*! - @brief This class models a TIFF sub-directory (sub-IFD). A sub-IFD - is an entry with a value that is a pointer to an IFD - structure and contains this IFD. The TIFF standard defines - some important tags to be sub-IFDs, including the %Exif and - GPS tags. - */ - class TiffSubIfd : public TiffEntryBase { - template - friend void TiffReader::visitSubIfd(TiffSubIfd* object); - public: - //! @name Creators - //@{ - //! Default constructor - TiffSubIfd(uint16_t tag, uint16_t group, uint16_t newGroup) - : TiffEntryBase(tag, group), ifd_(tag, newGroup) {} - //! Virtual destructor - virtual ~TiffSubIfd() {} - //@} - - private: - //! @name Manipulators - //@{ - virtual void doAddChild(TiffComponent::AutoPtr tiffComponent); - - virtual void doAddNext(TiffComponent::AutoPtr tiffComponent); - - virtual void doAccept(TiffVisitor& visitor); - //@} - - //! @name Accessors - //@{ - virtual void doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const; - //@} - - private: - // DATA - TiffDirectory ifd_; //!< The subdirectory - - }; // class TiffSubIfd - /*! 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 @@ -547,17 +68,17 @@ namespace Exiv2 { based image formats. */ struct TiffStructure { + struct Key; + //! Comparison operator to compare a TiffStructure with a TiffStructure::Key + bool operator==(const Key& key) const; //! Return the tag corresponding to the extended tag uint16_t tag() const { return static_cast(extendedTag_ & 0xffff); } + // DATA uint32_t extendedTag_; //!< Tag (32 bit so that it can contain special tags) uint16_t group_; //!< Group that contains the tag NewTiffCompFct newTiffCompFct_; //!< Function to create the correct TIFF component uint16_t newGroup_; //!< Group of the newly created component - - struct Key; - //! Comparison operator to compare a TiffStructure with a TiffStructure::Key - bool operator==(const Key& key) const; }; //! Search key for TIFF structure. @@ -570,7 +91,7 @@ namespace Exiv2 { /*! @brief TIFF component factory for standard TIFF components. This class is - meant to be used as a policy class. + used as a policy class. */ class TiffCreator { public: @@ -626,6 +147,9 @@ namespace Exiv2 { //! Function to create and initialize a new TIFF sub-directory TiffComponent::AutoPtr newTiffSubIfd(const TiffStructure* ts); + //! Function to create and initialize a new TIFF makernote + TiffComponent::AutoPtr newTiffMakernote(const TiffStructure* ts); + template void TiffParser::decode(Image* pImage, const byte* pData, @@ -645,7 +169,8 @@ namespace Exiv2 { TiffReader reader(pData, size, - tiffHeader.byteOrder()); + tiffHeader.byteOrder(), + rootDir.get()); rootDir->accept(reader); TiffMetadataDecoder decoder(pImage); @@ -653,188 +178,6 @@ namespace Exiv2 { } // TiffParser::decode - template - TiffReader::TiffReader(const byte* pData, - uint32_t size, - ByteOrder byteOrder) - : pData_(pData), - size_(size), - pLast_(pData + size - 1), - byteOrder_(byteOrder) - { - assert(pData); - assert(size > 0); - } // TiffReader::TiffReader - - template - void TiffReader::visitEntry(TiffEntry* object) - { - readTiffEntry(object); - } - - template - void TiffReader::visitDirectory(TiffDirectory* object) - { - assert(object != 0); - - byte* p = const_cast(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::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 TIFFPARSER_HPP_ diff --git a/src/tiffvisitor.cpp b/src/tiffvisitor.cpp new file mode 100644 index 00000000..e682eaf2 --- /dev/null +++ b/src/tiffvisitor.cpp @@ -0,0 +1,217 @@ +// ***************************************************************** -*- 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.cpp + Version: $Rev$ + Author(s): Andreas Huggel (ahu) + History: 11-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 tiffvisitor.o +//#define DEBUG + +// ***************************************************************************** +// included header files +#ifdef _MSC_VER +# include "exv_msvc.h" +#else +# include "exv_conf.h" +#endif + +#include "tiffvisitor.hpp" +#include "tiffcomposite.hpp" +#include "makernote2.hpp" +#include "exif.hpp" +#include "image.hpp" + +// + standard includes +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + + void TiffFinder::init(uint16_t tag, uint16_t group) + { + tag_ = tag; + group_ = group; + tiffComponent_ = 0; + } + + void TiffFinder::findObject(TiffComponent* object) + { + if (object->tag() == tag_ && object->group() == group_) { + tiffComponent_ = object; + setGo(false); + } + } + + void TiffFinder::visitEntry(TiffEntry* object) + { + findObject(object); + } + + void TiffFinder::visitDirectory(TiffDirectory* object) + { + findObject(object); + } + + void TiffFinder::visitSubIfd(TiffSubIfd* object) + { + findObject(object); + } + + void TiffFinder::visitMakernote(TiffMakernote* object) + { + findObject(object); + } + + void TiffFinder::visitOlympusMn(TiffOlympusMn* object) + { + findObject(object); + } + + void TiffMetadataDecoder::visitEntry(TiffEntry* object) + { + decodeTiffEntry(object); + } + + void TiffMetadataDecoder::visitDirectory(TiffDirectory* object) + { + // Nothing to do + } + + void TiffMetadataDecoder::visitSubIfd(TiffSubIfd* object) + { + decodeTiffEntry(object); + } + + void TiffMetadataDecoder::visitMakernote(TiffMakernote* object) + { + if (!object->mn_) decodeTiffEntry(object); + } + + void TiffMetadataDecoder::visitOlympusMn(TiffOlympusMn* object) + { + // Nothing to do + } + + void TiffMetadataDecoder::decodeTiffEntry(const TiffEntryBase* object) + { + assert(object != 0); + + // Todo: ExifKey should have an appropriate c'tor, it should not be + // necessary to use groupName here + ExifKey k(object->tag(), object->groupName()); + assert(pImage_ != 0); + pImage_->exifData().add(k, object->pValue()); + } // TiffMetadataDecoder::decodeTiffEntry + + const std::string TiffPrinter::indent_(" "); + + void TiffPrinter::incIndent() + { + prefix_ += indent_; + } // TiffPrinter::incIndent + + void TiffPrinter::decIndent() + { + if (prefix_.length() >= indent_.length()) { + prefix_.erase(prefix_.length() - indent_.length(), indent_.length()); + } + } // TiffPrinter::decIndent + + void TiffPrinter::visitEntry(TiffEntry* object) + { + printTiffEntry(object, prefix()); + } // TiffPrinter::visitEntry + + void TiffPrinter::visitDirectory(TiffDirectory* object) + { + assert(object != 0); + + os_ << prefix() << object->groupName() << " directory with " + // cast to make MSVC happy + << std::dec << static_cast(object->components_.size()); + if (object->components_.size() == 1) os_ << " entry:\n"; + else os_ << " entries:\n"; + incIndent(); + + } // TiffPrinter::visitDirectory + + void TiffPrinter::visitDirectoryNext(TiffDirectory* object) + { + decIndent(); + if (object->pNext_) os_ << prefix() << "Next directory:\n"; + else os_ << prefix() << "No next directory\n"; + } // TiffPrinter::visitDirectoryNext + + void TiffPrinter::visitDirectoryEnd(TiffDirectory* object) + { + // Nothing to do + } // TiffPrinter::visitDirectoryEnd + + void TiffPrinter::visitSubIfd(TiffSubIfd* object) + { + os_ << prefix() << "Sub-IFD "; + printTiffEntry(object); + } // TiffPrinter::visitSubIfd + + void TiffPrinter::visitMakernote(TiffMakernote* object) + { + if (!object->mn_) printTiffEntry(object, prefix()); + else os_ << prefix() << "Makernote "; + } // TiffPrinter::visitMakernote + + void TiffPrinter::visitOlympusMn(TiffOlympusMn* object) + { + os_ << prefix() << "Todo: Olympus Makernote header\n"; + } // TiffPrinter::visitOlympusMn + + void TiffPrinter::printTiffEntry(TiffEntryBase* object, + const std::string& px) const + { + assert(object != 0); + + os_ << px << object->groupName() + << " tag 0x" << std::setw(4) << std::setfill('0') + << std::hex << std::right << object->tag() + << ", type " << TypeInfo::typeName(object->typeId()) + << ", " << std::dec << object->count() << " component"; + if (object->count() > 1) os_ << "s"; + os_ << " in " << object->size() << " bytes"; + if (object->size() > 4) os_ << ", offset " << object->offset(); + os_ << "\n"; + const Value* vp = object->pValue(); + if (vp && vp->count() < 100) os_ << prefix() << *vp; + else os_ << prefix() << "..."; + os_ << "\n"; + + } // TiffPrinter::printTiffEntry + + // ************************************************************************* + // free functions + +} // namespace Exiv2 diff --git a/src/tiffvisitor.hpp b/src/tiffvisitor.hpp new file mode 100644 index 00000000..71fad2be --- /dev/null +++ b/src/tiffvisitor.hpp @@ -0,0 +1,327 @@ +// ***************************************************************** -*- 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.hpp + @brief + @version $Rev$ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @date 11-Apr-06, ahu: created + */ +#ifndef TIFFVISITOR_HPP_ +#define TIFFVISITOR_HPP_ + +// ***************************************************************************** +// included header files +#include "types.hpp" + +// + standard includes +#include +#include +#include + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class declarations + + class TiffComponent; + class TiffEntryBase; + class TiffEntry; + class TiffDirectory; + class TiffSubIfd; + class TiffMakernote; + class TiffOlympusMn; + class Image; + +// ***************************************************************************** +// class definitions + + /*! + @brief Abstract base class defining the interface for TIFF composite + vistors (Visitor pattern) + + A concrete visitor class is used as shown in the example below. Accept() + will invoke the member function corresponding to the concrete type of each + component in the composite. + + @code + void visitorExample(Exiv2::TiffComponent* tiffComponent, Exiv2::TiffVisitor& visitor) + { + tiffComponent->accept(visitor); + } + @endcode + */ + class TiffVisitor { + public: + //! @name Creators + //@{ + //! Default constructor + TiffVisitor() : go_(true) {} + //! Virtual destructor + virtual ~TiffVisitor() {} + //@} + + //! @name Manipulators + //@{ + //! Set the stop/go flag: true for go, false for stop + void setGo(bool go) { go_ = go; } + //! Operation to perform for a TIFF entry + virtual void visitEntry(TiffEntry* object) =0; + //! 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. + */ + virtual void visitDirectoryNext(TiffDirectory* object) {} + /*! + 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 + virtual void visitSubIfd(TiffSubIfd* object) =0; + //! Operation to perform for the makernote component + virtual void visitMakernote(TiffMakernote* object) =0; + //! Operation to perform for an Olympus makernote + virtual void visitOlympusMn(TiffOlympusMn* object) =0; + //@} + + //! @name Accessors + //@{ + //! Check if stop flag is clear, return true if it's clear. + bool go() { return go_; } + //@} + + private: + bool go_; //!< Set this to false to abort the iteration + + }; // class TiffVisitor + + /*! + @brief Search the composite for a component with \em tag and \em group. + Return a pointer to the component or 0, if not found. The class + is ready for a first search after construction and can be + re-initialized with init(). + */ + class TiffFinder : public TiffVisitor { + public: + //! @name Creators + //@{ + //! Constructor, taking the image to add the metadata to + TiffFinder(uint16_t tag, uint16_t group) + : tag_(tag), group_(group), tiffComponent_(0) {} + //! Virtual destructor + virtual ~TiffFinder() {} + //@} + + //! @name Manipulators + //@{ + //! Find tag and group in a TIFF entry + virtual void visitEntry(TiffEntry* object); + //! Find tag and group in a TIFF directory + virtual void visitDirectory(TiffDirectory* object); + //! Find tag and group in a TIFF sub-IFD + virtual void visitSubIfd(TiffSubIfd* object); + //! Find tag and group in a TIFF makernote + virtual void visitMakernote(TiffMakernote* object); + //! Find tag and group in an Olympus makernote + virtual void visitOlympusMn(TiffOlympusMn* object); + + //! Check if \em object matches \em tag and \em group + void findObject(TiffComponent* object); + //! Initialize the Finder for a new search. + void init(uint16_t tag, uint16_t group); + //@} + + //! @name Accessors + //@{ + /*! + @brief Return the search result. 0 if no TIFF component was found + for the tag and group combination. + */ + TiffComponent* result() const { return tiffComponent_; } + //@} + + private: + uint16_t tag_; + uint16_t group_; + TiffComponent* tiffComponent_; + }; // class TiffFinder + + /*! + @brief TIFF composite visitor to decode metadata from the TIFF tree and + add it to an Image, which is supplied in the constructor (Visitor + pattern). Used by TiffParser to decode the metadata from a + TIFF composite. + */ + class TiffMetadataDecoder : public TiffVisitor { + public: + //! @name Creators + //@{ + //! Constructor, taking the image to add the metadata to + TiffMetadataDecoder(Image* pImage) : pImage_(pImage) {} + //! Virtual destructor + virtual ~TiffMetadataDecoder() {} + //@} + + //! @name Manipulators + //@{ + //! Decode a TIFF entry + virtual void visitEntry(TiffEntry* object); + //! Decode a TIFF directory + virtual void visitDirectory(TiffDirectory* object); + //! Decode a TIFF sub-IFD + virtual void visitSubIfd(TiffSubIfd* object); + //! Decode a TIFF makernote + virtual void visitMakernote(TiffMakernote* object); + //! Decode an Olympus makernote + virtual void visitOlympusMn(TiffOlympusMn* object); + + //! Decode a standard TIFF entry + void decodeTiffEntry(const TiffEntryBase* object); + //@} + + private: + // DATA + Image* pImage_; //!< Pointer to the image to which the metadata is added + + }; // class TiffMetadataDecoder + + /*! + @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. + */ + template + class TiffReader : public TiffVisitor, public CreationPolicy { + public: + //! @name Creators + //@{ + /*! + @brief Constructor. The data buffer and table describing the TIFF + 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. + */ + TiffReader(const byte* pData, + uint32_t size, + ByteOrder byteOrder, + TiffComponent* pRoot); + + //! Virtual destructor + virtual ~TiffReader() {} + //@} + + //! @name Manipulators + //@{ + //! Read a TIFF entry from the data buffer + virtual void visitEntry(TiffEntry* object); + //! Read a TIFF directory from the data buffer + virtual void visitDirectory(TiffDirectory* object); + //! Read a TIFF sub-IFD from the data buffer + virtual void visitSubIfd(TiffSubIfd* object); + //! Read a TIFF makernote entry from the data buffer + virtual void visitMakernote(TiffMakernote* object); + //! Read an Olympus makernote from the data buffer + virtual void visitOlympusMn(TiffOlympusMn* object); + + //! Read a standard TIFF entry from the data buffer + void readTiffEntry(TiffEntryBase* object); + //@} + + 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 + + }; // class TiffReader + + /*! + @brief TIFF composite visitor to print the TIFF structure to an output + stream. + */ + class TiffPrinter : public TiffVisitor { + public: + //! @name Creators + //@{ + //! Constructor, takes an output stream to write to. + TiffPrinter(std::ostream& os, const std::string& prefix ="") + : os_(os), prefix_(prefix) {} + //! Virtual destructor + virtual ~TiffPrinter() {} + //@} + + //! @name Manipulators + //@{ + //! Print a TIFF entry. + virtual void visitEntry(TiffEntry* object); + //! Print a TIFF directory + virtual void visitDirectory(TiffDirectory* object); + //! Print header before next directory + virtual void visitDirectoryNext(TiffDirectory* object); + //! Cleanup before leaving this directory + virtual void visitDirectoryEnd(TiffDirectory* object); + //! Print a TIFF sub-IFD + virtual void visitSubIfd(TiffSubIfd* object); + //! Print a TIFF makernote + virtual void visitMakernote(TiffMakernote* object); + //! Print an Olympus makernote + virtual void visitOlympusMn(TiffOlympusMn* object); + + //! Increment the indent by one level + void incIndent(); + //! Decrement the indent by one level + void decIndent(); + //@} + + //! @name Accessors + //@{ + //! Print a standard TIFF entry. + void printTiffEntry(TiffEntryBase* object, + const std::string& prefix ="") const; + //! Return the current prefix + std::string prefix() const { return prefix_; } + //@} + + private: + // DATA + std::ostream& os_; //!< Output stream to write to + std::string prefix_; //!< Current prefix + + 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 new file mode 100644 index 00000000..348f437a --- /dev/null +++ b/src/tiffvisitor_tmpl.hpp @@ -0,0 +1,277 @@ +// ***************************************************************** -*- 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::visitMakernote(TiffMakernote* 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->newGroup_, + make, + object->pData(), + object->size(), + byteOrder_); + } + if (object->mn_) object->mn_->setStart(object->pData()); + + } // TiffReader::visitMakernote + + template + void TiffReader::visitOlympusMn(TiffOlympusMn* object) + { + object->header_.read(object->start(), pLast_ - object->start()); + if (!object->header_.check()) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: Olympus Makernote header check failed.\n"; +#endif + return; // todo: signal error to parent, delete object + } + object->ifd_.setStart(object->start() + object->header_.offset()); + + } // TiffReader::visitOlympusMn + + 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_