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); - - } // TiffEntryBase::readEntry + components_.push_back(tiffComponent.release()); + } // TiffDirectory::doAddChild - 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; - - 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); - } + doAddNext(tiffComponent); + } // TiffComponent::addNext - } // TiffDirectory::doRead + void TiffDirectory::doAddNext(TiffComponent::AutoPtr tiffComponent) + { + pNext_ = tiffComponent.release(); + } // TiffDirectory::doAddNext - void TiffSubIfd::doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder) + void TiffSubIfd::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 + 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,12 +90,187 @@ namespace Exiv2 { 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. + */ + 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 - bya tag, group tupel. This class is implemented as a NVI - (Non-Virtual Interface). It has an interface for visitors (Visitor + 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 { @@ -105,10 +283,8 @@ namespace Exiv2 { //! @name Creators //@{ //! Constructor - TiffComponent(uint16_t tag, - uint16_t group, - const TiffStructure* pTiffStructure) - : tag_(tag), group_(group), pTiffStructure_(pTiffStructure) {} + TiffComponent(uint16_t tag, uint16_t group) + : tag_(tag), group_(group), pData_(0) {} //! Virtual destructor. virtual ~TiffComponent() {} @@ -116,30 +292,32 @@ namespace Exiv2 { //! @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 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). + @brief Interface to accept visitors (Visitor pattern). - @throw Error If the component cannot be parsed. + @param visitor The visitor. */ - void read(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder); + 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_; } + 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_; } + 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. @@ -150,22 +328,19 @@ namespace Exiv2 { 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; + //! Implements addChild(). + virtual void doAddChild(AutoPtr tiffComponent) {} + + //! Implements addNext(). + virtual void doAddNext(AutoPtr tiffComponent) {} + + //! Implements accept() + virtual void doAccept(TiffVisitor& visitor) =0; //@} //! @name Accessors @@ -174,32 +349,35 @@ namespace Exiv2 { 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 + /*! + 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 baseclass provides the common functionality of an IFD - directory entry and defines the interface for derived concrete - entries. + @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, 0), - type_(0), count_(0), offset_(0), + 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(); @@ -226,15 +404,6 @@ namespace Exiv2 { 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 @@ -252,7 +421,7 @@ namespace Exiv2 { }; // class TiffEntryBase /*! - @brief A standard TIFF IFD entry. The value is kept in a data buffer. + @brief A standard TIFF IFD entry. */ class TiffEntry : public TiffEntryBase { public: @@ -267,11 +436,7 @@ namespace Exiv2 { private: //! @name Manipulators //@{ - //! Implements read(). - virtual void doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder); + virtual void doAccept(TiffVisitor& visitor); //@} //! @name Accessors @@ -279,19 +444,21 @@ namespace Exiv2 { 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). + /*! + @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, const TiffStructure* pTiffStructure) - : TiffComponent(tag, group, pTiffStructure), pNext_(0) {} + TiffDirectory(uint16_t tag, uint16_t group) + : TiffComponent(tag, group), pNext_(0) {} //! Virtual destructor virtual ~TiffDirectory(); //@} @@ -299,10 +466,11 @@ namespace Exiv2 { private: //! @name Manipulators //@{ - virtual void doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder); + virtual void doAddChild(TiffComponent::AutoPtr tiffComponent); + + virtual void doAddNext(TiffComponent::AutoPtr tiffComponent); + + virtual void doAccept(TiffVisitor& visitor); //@} //! @name Accessors @@ -310,8 +478,6 @@ namespace Exiv2 { virtual void doPrint(std::ostream& os, ByteOrder byteOrder, const std::string& prefix) const; - - virtual void doAccept(TiffVisitor& visitor) const; //@} private: @@ -321,17 +487,21 @@ namespace Exiv2 { }; // class TiffDirectory - //! This class models a TIFF sub-directory (%SubIfd). + /*! + @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, - const TiffStructure* pTiffStructure) - : TiffEntryBase(tag, group), ifd_(tag, newGroup, pTiffStructure) {} + TiffSubIfd(uint16_t tag, uint16_t group, uint16_t newGroup) + : TiffEntryBase(tag, group), ifd_(tag, newGroup) {} //! Virtual destructor virtual ~TiffSubIfd() {} //@} @@ -339,10 +509,11 @@ namespace Exiv2 { private: //! @name Manipulators //@{ - virtual void doRead(const byte* pData, - uint32_t size, - uint32_t start, - ByteOrder byteOrder); + virtual void doAddChild(TiffComponent::AutoPtr tiffComponent); + + virtual void doAddNext(TiffComponent::AutoPtr tiffComponent); + + virtual void doAccept(TiffVisitor& visitor); //@} //! @name Accessors @@ -350,179 +521,72 @@ namespace Exiv2 { 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 + }; // class TiffSubIfd /*! - @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 - - /*! 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 + 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, int i); + typedef TiffComponent::AutoPtr (*NewTiffCompFct)(const TiffStructure* ts); - /*! - 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. + /*! + @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 + 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 - //@{ - virtual ~TiffVisitor() {} - //@} - - //! @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; - //@} - - }; // class TiffVisitor - - /*! - TIFF composite visitor to decode metadata from the composite and add it - to an Image, which is supplied in the constructor. - */ - class TiffMetadataDecoder : public TiffVisitor { - public: - //! @name Creators - //@{ - //! Constructor - TiffMetadataDecoder(Image* pImage) : pImage_(pImage) {} - //! Virtual destructor - virtual ~TiffMetadataDecoder() {} - //@} - - //! @name Manipulators - //@{ - virtual void visitEntry(const TiffEntry* object); - virtual void visitDirectory(const TiffDirectory* object); - virtual void visitSubIfd(const 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 - /*! - Stateless parser class for data in TIFF format. + @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