From 70695ded9202dd5bbeeabcfde02ef3190ec03b5c Mon Sep 17 00:00:00 2001 From: Andreas Huggel Date: Thu, 6 Apr 2006 12:12:53 +0000 Subject: [PATCH] Experimental TIFF read-only support, without Makernotes. As a side-effect, the parser also handles NEF images. Only tried on Linux so far. --- src/Makefile | 5 +- src/error.cpp | 4 +- src/imgreg.cpp | 4 +- src/tiffimage.hpp | 4 +- src/tiffparse.cpp | 10 +- src/tiffparser.cpp | 384 ++++++++++++++---------- src/tiffparser.hpp | 716 ++++++++++++++++++++++++--------------------- 7 files changed, 632 insertions(+), 495 deletions(-) diff --git a/src/Makefile b/src/Makefile index a294d13c..3971b8d0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -53,7 +53,8 @@ CCHDR = exv_conf.h exv_msvc.h mn.hpp rcsid.hpp 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 types.cpp value.cpp + sigmamn.cpp sonymn.cpp tags.cpp tiffimage.cpp tiffparser.cpp types.cpp \ + value.cpp # Add library C source files to this list ifndef HAVE_TIMEGM @@ -64,7 +65,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 + taglist.cpp write-test.cpp write2-test.cpp tiffparse.cpp # Main source file of the Exiv2 application EXIV2MAIN = exiv2.cpp diff --git a/src/error.cpp b/src/error.cpp index 47218c66..5fcdacb9 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -43,10 +43,8 @@ namespace Exiv2 { ErrMsg( -1, "Error %0: arg1=%1, arg2=%2, arg3=%3."), ErrMsg( 0, "Success"), ErrMsg( 1, "%1"), // %1=error message - ErrMsg( 2, "%1: %2 (%3)"), // %1=path, %2=strerror, %3=function that failed - // ErrMsg( 3, ""), - + ErrMsg( 3, "This does not look like a %1 image"), // %1=Image type ErrMsg( 4, "Invalid dataset name `%1'"), // %1=dataset name ErrMsg( 5, "Invalid record name `%1'"), // %1=record name ErrMsg( 6, "Invalid key `%1'"), // %1=key diff --git a/src/imgreg.cpp b/src/imgreg.cpp index 941fde06..e0bfed68 100644 --- a/src/imgreg.cpp +++ b/src/imgreg.cpp @@ -34,6 +34,7 @@ EXIV2_RCSID("@(#) $Id$"); #include "image.hpp" #include "jpgimage.hpp" #include "crwimage.hpp" +#include "tiffimage.hpp" // + standard includes @@ -44,7 +45,8 @@ namespace Exiv2 { ImageFactory::Registry ImageFactory::registry_[] = { Registry(ImageType::jpeg, newJpegInstance, isJpegType), Registry(ImageType::exv, newExvInstance, isExvType), - Registry(ImageType::crw, newCrwInstance, isCrwType) + Registry(ImageType::crw, newCrwInstance, isCrwType), + Registry(ImageType::tiff, newTiffInstance, isTiffType) }; } // namespace Exiv2 diff --git a/src/tiffimage.hpp b/src/tiffimage.hpp index 356f9a7e..3c5ba006 100644 --- a/src/tiffimage.hpp +++ b/src/tiffimage.hpp @@ -44,7 +44,7 @@ namespace Exiv2 { // ***************************************************************************** // class declarations - + struct TiffStructure; // ***************************************************************************** @@ -59,7 +59,7 @@ namespace Exiv2 { } /*! - @brief Class to access raw TIFF images. Only Exif metadata and a comment + @brief Class to access raw TIFF images. Only Exif metadata and a comment are supported. TIFF format does not contain IPTC metadata. */ class TiffImage : public Image { diff --git a/src/tiffparse.cpp b/src/tiffparse.cpp index c15ab6f7..c7eb141b 100644 --- a/src/tiffparse.cpp +++ b/src/tiffparse.cpp @@ -49,12 +49,18 @@ try { TiffHeade2 tiffHeader; if (!tiffHeader.read(buf.pData_, buf.size_)) throw Error(3, "TIFF"); - TiffComponent::AutoPtr rootDir + TiffComponent::AutoPtr rootDir = TiffParser::create(Tag::root, Group::none, tiffStructure); if (0 == rootDir.get()) { throw Error(1, "No root element defined in TIFF structure"); } - rootDir->read(buf.pData_, buf.size_, tiffHeader.offset(), tiffHeader.byteOrder()); + TiffReader reader(buf.pData_, + buf.size_, + tiffHeader.byteOrder(), + tiffStructure); + + rootDir->setStart(buf.pData_ + tiffHeader.offset()); + rootDir->accept(reader); tiffHeader.print(std::cerr); rootDir->print(std::cerr, tiffHeader.byteOrder()); diff --git a/src/tiffparser.cpp b/src/tiffparser.cpp index c6fee7aa..a95877cf 100644 --- a/src/tiffparser.cpp +++ b/src/tiffparser.cpp @@ -58,10 +58,9 @@ EXIV2_RCSID("@(#) $Id$"); Todo: - + Add child mgmt stuff to TIFF composite: add, remove, find - + Better encapsulate TiffStructure - + Remove read methods from Composite and turn them into a visitor - + Remove TiffStructure from Composite + + Add further child mgmt stuff to TIFF composite: remove, find + + Better handling of TiffStructure + + Add Makernote support in crwimage.* : @@ -77,7 +76,7 @@ EXIV2_RCSID("@(#) $Id$"); // class member definitions namespace Exiv2 { - void TiffParser::decode(const byte* pData, + void TiffParser::decode(const byte* pData, uint32_t size, const TiffStructure* pTiffStructure, TiffVisitor& decoder) @@ -89,10 +88,16 @@ namespace Exiv2 { throw Error(3, "TIFF"); } - TiffComponent::AutoPtr rootDir + TiffComponent::AutoPtr rootDir = TiffParser::create(Tag::root, Group::none, pTiffStructure); if (0 == rootDir.get()) return; - rootDir->read(pData, size, tiffHeader.offset(), tiffHeader.byteOrder()); + + TiffReader reader(pData, + size, + tiffHeader.byteOrder(), + pTiffStructure); + rootDir->setStart(pData + tiffHeader.offset()); + rootDir->accept(reader); #ifdef DEBUG tiffHeader.print(std::cerr); @@ -110,7 +115,7 @@ namespace Exiv2 { const TiffStructure* ts = 0; int idx = 0; for (; pTiffStructure[idx].extendedTag_ != Tag::none; ++idx) { - if ( extendedTag == pTiffStructure[idx].extendedTag_ + if ( extendedTag == pTiffStructure[idx].extendedTag_ && group == pTiffStructure[idx].group_) { ts = &pTiffStructure[idx]; break; @@ -119,7 +124,7 @@ namespace Exiv2 { TiffComponent::AutoPtr tc(0); if (ts && ts->newTiffCompFct_) { - tc = ts->newTiffCompFct_(pTiffStructure, idx); + tc = ts->newTiffCompFct_(ts); } if (!ts) { uint16_t tag = static_cast(extendedTag & 0xffff); @@ -137,7 +142,7 @@ namespace Exiv2 { } delete pNext_; } // TiffDirectory::~TiffDirectory - + TiffEntryBase::~TiffEntryBase() { if (isAllocated_) { @@ -167,142 +172,49 @@ namespace Exiv2 { return true; } // TiffHeade2::read - void TiffComponent::read(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder) + void TiffComponent::addChild(TiffComponent::AutoPtr tiffComponent) { - doRead(pData, size, start, byteOrder); - } // TiffComponent::read + doAddChild(tiffComponent); + } // TiffComponent::addChild - void TiffEntryBase::readEntry(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder) + void TiffDirectory::doAddChild(TiffComponent::AutoPtr tiffComponent) { - if (size - start < 12) throw Error(3, "TIFF"); - const byte* p = pData + start; - // Component already has tag - p += 2; - type_ = getUShort(p, byteOrder); - // todo: check type - p += 2; - count_ = getULong(p, byteOrder); - p += 4; - offset_ = getULong(p, byteOrder); - size_ = TypeInfo::typeSize(typeId()) * count(); - if (size_ > 4) { - if (size < offset() + size_) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Warning: Upper boundary of data for " - << "directory " << group() << ", " // todo: ExifTags::ifdName(ifdId_) - << " entry 0x" << std::setw(4) - << std::setfill('0') << std::hex << tag() - << " is out of bounds:\n" - << " Offset = 0x" << std::setw(8) - << std::setfill('0') << std::hex << offset() - << ", size = " << std::dec << size_ - << ", exceeds buffer size by " - << offset() + size_ - size - << " Bytes; adjusting the size\n"; -#endif - size_ = size - offset(); - // todo: adjust count_, make size_ a multiple of typeSize - } - pData_ = pData + offset(); - } - else { - pData_ = pData + start + 8; - } - pValue_ = Value::create(typeId()).release(); - if (pValue_) pValue_->read(pData_, size_, byteOrder); + components_.push_back(tiffComponent.release()); + } // TiffDirectory::doAddChild - } // TiffEntryBase::readEntry - - void TiffEntry::doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder) + void TiffSubIfd::doAddChild(TiffComponent::AutoPtr tiffComponent) { - TiffEntryBase::readEntry(pData, size, start, byteOrder); - } // TiffEntry::doRead + ifd_.addChild(tiffComponent); + } // TiffSubIfd::doAddChild - void TiffDirectory::doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder) + void TiffComponent::addNext(TiffComponent::AutoPtr tiffComponent) { - if (size < start + 2) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: " - << "Directory " << group() << ": " // todo: ExifTags::ifdName(ifdId_) - << " IFD exceeds data buffer, cannot read entry count.\n"; -#endif - return; - } - uint32_t o = start; - const uint16_t n = getUShort(pData + o, byteOrder); - o += 2; + doAddNext(tiffComponent); + } // TiffComponent::addNext - for (uint16_t i = 0; i < n; ++i) { - if (size < o + 12) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: " - << "Directory " << group() << ": " // todo: ExifTags::ifdName(ifdId_) - << " IFD entry " << i - << " lies outside of the data buffer.\n"; -#endif - return; - } - uint16_t tag = getUShort(pData + o, byteOrder); - TiffComponent::AutoPtr tc - = TiffParser::create(tag, group(), pTiffStructure()); - tc->read(pData, size, o, byteOrder); - components_.push_back(tc.release()); - o += 12; - } - if (size < o + 4) { -#ifndef SUPPRESS_WARNINGS - std::cerr << "Error: " - << "Directory " << group() << ": " // todo: ExifTags::ifdName(ifdId_) - << " IFD exceeds data buffer, cannot read next pointer.\n"; -#endif - return; - } - uint32_t next = getLong(pData + o, byteOrder); - if (next) { - pNext_ = TiffParser::create(Tag::next, group(), pTiffStructure()).release(); - pNext_->read(pData, size, next, byteOrder); - } - - } // TiffDirectory::doRead - - void TiffSubIfd::doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder) + void TiffDirectory::doAddNext(TiffComponent::AutoPtr tiffComponent) { - TiffEntryBase::readEntry(pData, size, start, byteOrder); - if (typeId() == unsignedLong && count() >= 1) { - uint32_t offset = getULong(this->pData(), byteOrder); - ifd_.read(pData, size, offset, byteOrder); - } -#ifndef SUPPRESS_WARNINGS - else { - std::cerr << "Warning: " - << "Directory " << group() << ", " // todo: ExifTags::ifdName(ifdId_) - << " entry 0x" << std::setw(4) - << std::setfill('0') << std::hex << tag() - << " doesn't look like a sub-IFD."; - } -#endif - } // TiffSubIfd::read + 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_ << "\n"; + << 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, @@ -317,15 +229,17 @@ namespace Exiv2 { const std::string& prefix) const { os << prefix - << "tag = 0x" << std::setw(4) << std::setfill('0') + << "tag 0x" << std::setw(4) << std::setfill('0') << std::hex << std::right << tag() - << ", type = " << TypeInfo::typeName(typeId()) - << ", count = " << std::dec << count() - << ", offset = " << offset() << "\n"; - - if (pValue_ && pValue_->size() < 100) { - os << prefix << *pValue_ << "\n"; - } + << ", 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 @@ -340,7 +254,7 @@ namespace Exiv2 { ByteOrder byteOrder, const std::string& prefix) const { - os << prefix << "Directory " << group() + os << prefix << "Directory " << group() << " with " << components_.size() << " entries.\n"; Components::const_iterator b = components_.begin(); Components::const_iterator e = components_.end(); @@ -365,17 +279,17 @@ namespace Exiv2 { ifd_.print(os, byteOrder, prefix); } // TiffSubIfd::doPrint - void TiffComponent::accept(TiffVisitor& visitor) const + void TiffComponent::accept(TiffVisitor& visitor) { doAccept(visitor); } // TiffComponent::accept - void TiffEntry::doAccept(TiffVisitor& visitor) const + void TiffEntry::doAccept(TiffVisitor& visitor) { visitor.visitEntry(this); } // TiffEntry::doAccept - void TiffDirectory::doAccept(TiffVisitor& visitor) const + void TiffDirectory::doAccept(TiffVisitor& visitor) { visitor.visitDirectory(this); @@ -387,33 +301,35 @@ namespace Exiv2 { if (pNext_) { pNext_->accept(visitor); } - + } // TiffDirectory::doAccept - void TiffSubIfd::doAccept(TiffVisitor& visitor) const + void TiffSubIfd::doAccept(TiffVisitor& visitor) { visitor.visitSubIfd(this); ifd_.accept(visitor); } // TiffSubIfd::doAccept - void TiffMetadataDecoder::visitEntry(const TiffEntry* object) + void TiffMetadataDecoder::visitEntry(TiffEntry* object) { - decodeTiffEntry(object); + decodeTiffEntry(object); } - void TiffMetadataDecoder::visitDirectory(const TiffDirectory* object) + void TiffMetadataDecoder::visitDirectory(TiffDirectory* object) { // Nothing to do } - void TiffMetadataDecoder::visitSubIfd(const TiffSubIfd* object) + void TiffMetadataDecoder::visitSubIfd(TiffSubIfd* object) { decodeTiffEntry(object); } void TiffMetadataDecoder::decodeTiffEntry(const TiffEntryBase* object) { - // Todo: ExifKey should have an appropriate c'tor, this mapping should + assert(object != 0); + + // Todo: ExifKey should have an appropriate c'tor, this mapping should // be a table and it belongs somewhere else std::string group; switch (object->group()) { @@ -427,22 +343,172 @@ namespace Exiv2 { ExifKey k(object->tag(), group); assert(pImage_ != 0); pImage_->exifData().add(k, object->pValue()); - } // TiffEntryBase::decodeTiffEntry + } // TiffMetadataDecoder::decodeTiffEntry + + TiffReader::TiffReader(const byte* pData, + uint32_t size, + ByteOrder byteOrder, + const TiffStructure* pTiffStructure) + : pData_(pData), + size_(size), + pLast_(pData + size - 1), + byteOrder_(byteOrder), + pTiffStructure_(pTiffStructure) + { + assert(pData); + assert(size > 0); + assert(pTiffStructure); + } // TiffReader::TiffReader + + void TiffReader::visitEntry(TiffEntry* object) + { + readTiffEntry(object); + } + + 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->group() << ": " // todo: ExifTags::ifdName(ifdId_) + << " 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->group() << ": " // todo: ExifTags::ifdName(ifdId_) + << " IFD entry " << i + << " lies outside of the data buffer.\n"; +#endif + return; + } + uint16_t tag = getUShort(p, byteOrder_); + TiffComponent::AutoPtr tc + = TiffParser::create(tag, object->group(), pTiffStructure_); + tc->setStart(p); + object->addChild(tc); + + p += 12; + } + + if (p + 4 > pLast_) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: " + << "Directory " << object->group() << ": " // todo: ExifTags::ifdName(ifdId_) + << " IFD exceeds data buffer, cannot read next pointer.\n"; +#endif + return; + } + uint32_t next = getLong(p, byteOrder_); + if (next) { + TiffComponent::AutoPtr tc + = TiffParser::create(Tag::next, object->group(), pTiffStructure_); + tc->setStart(p); + 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_); + object->ifd_.setStart(pData_ + offset); + } +#ifndef SUPPRESS_WARNINGS + else { + std::cerr << "Warning: " + << "Directory " << object->group() << ", " // todo: ExifTags::ifdName(ifdId_) + << " entry 0x" << std::setw(4) + << std::setfill('0') << std::hex << object->tag() + << " doesn't look like a sub-IFD."; + } +#endif + + } // TiffReader::visitSubIfd + + 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->group() // todo: ExifTags::ifdName(ifdId_) + << "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->offset_ = getULong(p, byteOrder_); + object->size_ = TypeInfo::typeSize(object->typeId()) * object->count(); + object->pData_ = p; + if (object->size() > 4) { + object->pData_ = pData_ + object->offset(); + if (object->pData() + object->size() > pLast_) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Warning: Upper boundary of data for " + << "directory " << object->group() << ", " // todo: ExifTags::ifdName(ifdId_) + << " 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 " + << 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 // ************************************************************************* // free functions - TiffComponent::AutoPtr newTiffDirectory(const TiffStructure* ts, int i) + TiffComponent::AutoPtr newTiffDirectory(const TiffStructure* ts) { - return TiffComponent::AutoPtr(new TiffDirectory(ts[i].tag(), ts[i].newGroup_, ts)); + assert(ts); + return TiffComponent::AutoPtr(new TiffDirectory(ts->tag(), ts->newGroup_)); } - TiffComponent::AutoPtr newTiffSubIfd(const TiffStructure* ts, int i) + TiffComponent::AutoPtr newTiffSubIfd(const TiffStructure* ts) { - return TiffComponent::AutoPtr(new TiffSubIfd(ts[i].tag(), - ts[i].group_, - ts[i].newGroup_, - ts)); + assert(ts); + return TiffComponent::AutoPtr(new TiffSubIfd(ts->tag(), + ts->group_, + ts->newGroup_)); } } // namespace Exiv2 diff --git a/src/tiffparser.hpp b/src/tiffparser.hpp index 6e08b64c..bb3c6910 100644 --- a/src/tiffparser.hpp +++ b/src/tiffparser.hpp @@ -50,7 +50,10 @@ namespace Exiv2 { // class declarations struct TiffStructure; - class TiffVisitor; + class TiffDirectory; + class TiffEntryBase; + class TiffEntry; + class TiffSubIfd; // ***************************************************************************** // type definitions @@ -62,7 +65,7 @@ namespace Exiv2 { 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 + 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 { @@ -72,11 +75,11 @@ namespace Exiv2 { 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 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... @@ -87,279 +90,6 @@ namespace Exiv2 { const uint32_t next = 0x30000; //!< Special tag: next IFD } - /*! - @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 - bya tag, group tupel. This class is implemented as a NVI - (Non-Virtual Interface). 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, - const TiffStructure* pTiffStructure) - : tag_(tag), group_(group), pTiffStructure_(pTiffStructure) {} - - //! Virtual destructor. - virtual ~TiffComponent() {} - //@} - - //! @name Manipulators - //@{ - /*! - @brief Read a component from a data buffer - - @param pData Pointer to the data buffer, starting with a TIFF header. - @param size Number of bytes in the data buffer. - @param start Component starts at \em pData + \em start. - @param byteOrder Applicable byte order (little or big endian). - - @throw Error If the component cannot be parsed. - */ - void read(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder); - //@} - - //! @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 TIFF structure - const TiffStructure* pTiffStructure() const { return pTiffStructure_; } - /*! - @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; - /*! - @brief Interface to accept visitors (Visitor pattern). - - @param visitor The visitor. - */ - void accept(TiffVisitor& visitor) const; - //@} - - protected: - //! @name Manipulators - //@{ - //! Implements read(). - virtual void doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder) =0; - //@} - - //! @name Accessors - //@{ - //! Implements print() - virtual void doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const =0; - - //! Implements accept() - virtual void doAccept(TiffVisitor& visitor) const =0; - //@} - - private: - // DATA - uint16_t tag_; //!< Tag that identifies the component - uint16_t group_; //!< Group id for this component - const TiffStructure* pTiffStructure_; //!< TIFF structure for this component - - }; // class TiffComponent - - /*! - @brief This baseclass provides the common functionality of an IFD - directory entry and defines the interface for derived concrete - entries. - */ - class TiffEntryBase : public TiffComponent { - public: - //! @name Creators - //@{ - //! Default constructor - TiffEntryBase(uint16_t tag, uint16_t group) - : TiffComponent(tag, group, 0), - 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; - //@} - - //! @name Manipulators - //@{ - //! Read base entry - void readEntry(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder); - //@} - - 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. The value is kept in a data buffer. - */ - 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 - //@{ - //! Implements read(). - virtual void doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder); - //@} - - //! @name Accessors - //@{ - virtual void doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const; - virtual void doAccept(TiffVisitor& visitor) const; - //@} - - }; // class TiffEntry - - //! This class models a TIFF directory (%Ifd). - class TiffDirectory : public TiffComponent { - public: - //! @name Creators - //@{ - //! Default constructor - TiffDirectory(uint16_t tag, uint16_t group, const TiffStructure* pTiffStructure) - : TiffComponent(tag, group, pTiffStructure), pNext_(0) {} - //! Virtual destructor - virtual ~TiffDirectory(); - //@} - - private: - //! @name Manipulators - //@{ - virtual void doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder); - //@} - - //! @name Accessors - //@{ - virtual void doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const; - - virtual void doAccept(TiffVisitor& visitor) const; - //@} - - private: - // DATA - Components components_; //!< List of components in this directory - TiffComponent* pNext_; //!< Pointer to the next IFD - - }; // class TiffDirectory - - //! This class models a TIFF sub-directory (%SubIfd). - class TiffSubIfd : public TiffEntryBase { - public: - //! @name Creators - //@{ - //! Default constructor - TiffSubIfd(uint16_t tag, - uint16_t group, - uint16_t newGroup, - const TiffStructure* pTiffStructure) - : TiffEntryBase(tag, group), ifd_(tag, newGroup, pTiffStructure) {} - //! Virtual destructor - virtual ~TiffSubIfd() {} - //@} - - private: - //! @name Manipulators - //@{ - virtual void doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder); - //@} - - //! @name Accessors - //@{ - virtual void doPrint(std::ostream& os, - ByteOrder byteOrder, - const std::string& prefix) const; - - virtual void doAccept(TiffVisitor& visitor) const; - //@} - - private: - // DATA - TiffDirectory ifd_; //!< The subdirectory - - }; // class TiffDirectory - /*! @brief This class models a TIFF header structure. */ @@ -377,7 +107,7 @@ namespace Exiv2 { //! @name Manipulators //@{ /*! - @brief Read the TIFF header from a data buffer. Return false if the + @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. @@ -389,7 +119,7 @@ namespace Exiv2 { //! @name Accessors //@{ /*! - @brief Write the TIFF header to the binary image \em blob. + @brief Write the TIFF header to the binary image \em blob. This method appends to the blob. @param blob Binary image to add to. @@ -419,29 +149,20 @@ namespace Exiv2 { }; // class TiffHeade2 - /*! - 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. + /*! + @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 */ - typedef TiffComponent::AutoPtr (*NewTiffCompFct)(const TiffStructure* ts, int i); - - /*! - This structure is meant to be used as an entry (row) of a table describing - the TIFF structure of an image format for reading and writing. Different - tables can be used to support different TIFF based image formats. - */ - struct TiffStructure { - //! Return the tag corresponding to the extended tag - uint16_t tag() const { return static_cast(extendedTag_ & 0xffff); } - - 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 - }; - - //! Abstract base class for TIFF composite vistors (Visitor pattern) class TiffVisitor { public: //! @name Creators @@ -451,25 +172,27 @@ namespace Exiv2 { //! @name Manipulators //@{ - //! Decode a TIFF entry - virtual void visitEntry(const TiffEntry* object) =0; - //! Decode a TIFF directory - virtual void visitDirectory(const TiffDirectory* object) =0; - //! Decode a TIFF sub-IFD - virtual void visitSubIfd(const TiffSubIfd* object) =0; + //! 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 /*! - TIFF composite visitor to decode metadata from the composite and add it - to an Image, which is supplied in the constructor. + @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 + //! Constructor, taking the image to add the metadata to TiffMetadataDecoder(Image* pImage) : pImage_(pImage) {} //! Virtual destructor virtual ~TiffMetadataDecoder() {} @@ -477,9 +200,12 @@ namespace Exiv2 { //! @name Manipulators //@{ - virtual void visitEntry(const TiffEntry* object); - virtual void visitDirectory(const TiffDirectory* object); - virtual void visitSubIfd(const TiffSubIfd* object); + //! 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); //@} @@ -491,38 +217,376 @@ namespace Exiv2 { }; // class TiffMetadataDecoder /*! - Stateless parser class for data in TIFF format. + @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. + */ + class TiffReader : public TiffVisitor { + 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 pTiffStructure Pointer to a table describing the TIFF structure + used to decode the data. + */ + TiffReader(const byte* pData, + uint32_t size, + ByteOrder byteOrder, + const TiffStructure* pTiffStructure); + + //! 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 + const TiffStructure* pTiffStructure_; //!< Pointer to the TIFF structure + + }; // 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 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 { + 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 { + 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 + in order to make decisions based on the value of other tags. + */ + typedef TiffComponent::AutoPtr (*NewTiffCompFct)(const TiffStructure* ts); + + /*! + @brief Data structure used as a row (element) of a table (array) + describing the TIFF structure of an image format for reading and + writing. Different tables can be used to support different TIFF + based image formats. + */ + struct TiffStructure { + //! Return the tag corresponding to the extended tag + uint16_t tag() const { return static_cast(extendedTag_ & 0xffff); } + + 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 + }; + + /*! + @brief Stateless parser class for data in TIFF format. Images use this + class to decode and encode TIFF-based data. */ class TiffParser { public: /*! - @brief Decode TIFF metadata from a data buffer \em pData of length + @brief Decode TIFF metadata from a data buffer \em pData of length \em size into \em image. This is the entry point to access image data in TIFF format. The - parser uses classes TiffHeade2, TiffEntry, TiffDirectory. + parser uses classes TiffHeade2 and the TiffComponent and TiffVisitor + hierarchies. - @param pData Pointer to the data buffer. Must point to data + @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 pTiffStructure Pointer to a table describing the TIFF structure + @param pTiffStructure Pointer to a table describing the TIFF structure used to decode the data. @param decoder Reference to a TIFF visitor to decode and extract the metadata from the TIFF composite structure. @throw Error If the data buffer cannot be parsed. */ - static void decode(const byte* pData, + static void decode(const byte* pData, uint32_t size, const TiffStructure* pTiffStructure, TiffVisitor& decoder); /*! - @brief Create the appropriate TiffComponent to handle the \em tag in - \em group. + @brief Create the TiffComponent for TIFF entry \em tag in \em group + based on the lookup list \em pTiffStructure. - Uses table \em pTiffStructure to derive the correct component. If a - tag, group tupel is not found in the table, a TiffEntry is created. If - the pointer that is returned is 0, then the TIFF entry should be ignored. + If a tag, group tupel is not found in the table, a TiffEntry is + created. If the pointer that is returned is 0, then the TIFF entry + should be ignored. */ static TiffComponent::AutoPtr create( uint32_t extendedTag, uint16_t group, @@ -533,10 +597,10 @@ namespace Exiv2 { // template, inline and free functions //! Function to create and initialize a new TIFF directory - TiffComponent::AutoPtr newTiffDirectory(const TiffStructure* ts, int i); + TiffComponent::AutoPtr newTiffDirectory(const TiffStructure* ts); //! Function to create and initialize a new TIFF sub-directory - TiffComponent::AutoPtr newTiffSubIfd(const TiffStructure* ts, int i); + TiffComponent::AutoPtr newTiffSubIfd(const TiffStructure* ts); } // namespace Exiv2