diff --git a/src/tiffimage.cpp b/src/tiffimage.cpp new file mode 100644 index 00000000..9c38672a --- /dev/null +++ b/src/tiffimage.cpp @@ -0,0 +1,226 @@ +// ***************************************************************** -*- 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: tiffimage.cpp + Version: $Rev$ + Author(s): Andreas Huggel (ahu) + History: 15-Mar-06, ahu: created + + */ +// ***************************************************************************** +#include "rcsid.hpp" +EXIV2_RCSID("@(#) $Id: tiffparser.cpp 675 2006-01-25 04:16:58Z ahuggel $"); + +// Define DEBUG to output debug information to std::cerr, e.g, by calling make +// like this: make DEFS=-DDEBUG tiffparser.o +//#define DEBUG + +// ***************************************************************************** +// included header files +#ifdef _MSC_VER +# include "exv_msvc.h" +#else +# include "exv_conf.h" +#endif + +#include "tiffimage.hpp" +#include "tiffparser.hpp" +#include "image.hpp" +#include "error.hpp" +#include "futils.hpp" + +// + standard includes +#include +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + + const TiffStructure TiffImage::tiffStructure_[] = { + { 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 }, + { Tag::next, Group::ifd0, newTiffDirectory, Group::ifd0 }, + // End of list marker + { Tag::none, Group::none, 0, Group::none } + }; + + TiffImage::TiffImage(BasicIo::AutoPtr io, bool create) + : Image(mdExif | mdComment), io_(io) + { + if (create) { + IoCloser closer(*io_); + io_->open(); + } + } // TiffImage::TiffImage + + bool TiffImage::good() const + { + if (io_->open() != 0) return false; + IoCloser closer(*io_); + return isThisType(*io_, false); + } + + void TiffImage::clearMetadata() + { + clearExifData(); + clearComment(); + } + + void TiffImage::setMetadata(const Image& image) + { + setExifData(image.exifData()); + setComment(image.comment()); + } + + void TiffImage::clearExifData() + { + exifData_.clear(); + } + + void TiffImage::setExifData(const ExifData& exifData) + { + exifData_ = exifData; + } + + void TiffImage::clearIptcData() + { + // not supported + } + + void TiffImage::setIptcData(const IptcData& /*iptcData*/) + { + // not supported + } + + void TiffImage::clearComment() + { + comment_.erase(); + } + + void TiffImage::setComment(const std::string& comment) + { + comment_ = comment; + } + + void TiffImage::readMetadata() + { +#ifdef DEBUG + std::cerr << "Reading TIFF file " << io_->path() << "\n"; +#endif + if (io_->open() != 0) { + throw Error(9, io_->path(), strError()); + } + IoCloser closer(*io_); + // Ensure that this is the correct image type + if (!isThisType(*io_, false)) { + if (io_->error() || io_->eof()) throw Error(14); + throw Error(33); + } + clearMetadata(); + + // Read the image into a memory buffer + long len = io_->size(); + DataBuf buf(len); + io_->read(buf.pData_, len); + if (io_->error() || io_->eof()) throw Error(14); + + TiffParser::decode(this, tiffStructure_, buf.pData_, buf.size_); + } // TiffImage::readMetadata + + void TiffImage::writeMetadata() + { +/* + + Todo: implement me! + +#ifdef DEBUG + std::cerr << "Writing TIFF file " << io_->path() << "\n"; +#endif + // Read existing image + DataBuf buf; + if (io_->open() == 0) { + IoCloser closer(*io_); + // Ensure that this is the correct image type + if (isThisType(*io_, false)) { + // Read the image into a memory buffer + buf.alloc(io_->size()); + io_->read(buf.pData_, buf.size_); + if (io_->error() || io_->eof()) { + buf.reset(); + } + } + } + + // Parse image, starting with a TIFF header component + TiffHeade2::AutoPtr head(new TiffHeade2); + if (buf.size_ != 0) { + head->read(buf.pData_, buf.size_); + } + + Blob blob; + TiffParser::encode(blob, head.get(), this); + + // Write new buffer to file + BasicIo::AutoPtr tempIo(io_->temporary()); // may throw + assert (tempIo.get() != 0); + tempIo->write(&blob[0], static_cast(blob.size())); + io_->close(); + io_->transfer(*tempIo); // may throw +*/ + } // TiffImage::writeMetadata + + bool TiffImage::isThisType(BasicIo& iIo, bool advance) const + { + return isTiffType(iIo, advance); + } + + // ************************************************************************* + // free functions + + Image::AutoPtr newTiffInstance(BasicIo::AutoPtr io, bool create) + { + Image::AutoPtr image(new TiffImage(io, create)); + if (!image->good()) { + image.reset(); + } + return image; + } + + bool isTiffType(BasicIo& iIo, bool advance) + { + const uint32_t len = 8; + byte buf[len]; + iIo.read(buf, len); + if (iIo.error() || iIo.eof()) { + return false; + } + TiffHeade2 tiffHeader; + bool rc = tiffHeader.read(buf, len); + if (!advance || !rc) { + iIo.seek(-len, BasicIo::cur); + } + return rc; + } + +} // namespace Exiv2 diff --git a/src/tiffimage.hpp b/src/tiffimage.hpp new file mode 100644 index 00000000..356f9a7e --- /dev/null +++ b/src/tiffimage.hpp @@ -0,0 +1,191 @@ +// ***************************************************************** -*- 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 tiffimage.hpp + @brief Class TiffImage + @version $Rev$ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @date 15-Mar-06, ahu: created + */ +#ifndef TIFFIMAGE_HPP_ +#define TIFFIMAGE_HPP_ + +// ***************************************************************************** +// included header files +#include "exif.hpp" +#include "iptc.hpp" +#include "image.hpp" + +// + standard includes +#include + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class declarations + + struct TiffStructure; + +// ***************************************************************************** +// type definitions + +// ***************************************************************************** +// class definitions + + // Add TIFF to the supported image formats + namespace ImageType { + const int tiff = 4; //!< TIFF image type (see class TiffImage) + } + + /*! + @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 { + friend bool isTiffType(BasicIo& iIo, bool advance); + + //! @name NOT Implemented + //@{ + //! Copy constructor + TiffImage(const TiffImage& rhs); + //! Assignment operator + TiffImage& operator=(const TiffImage& rhs); + //@} + + public: + //! @name Creators + //@{ + /*! + @brief Constructor that can either open an existing TIFF image or create + a new image from scratch. If a new image is to be created, any + existing data is overwritten. Since the constructor can not return + a result, callers should check the good() method after object + construction to determine success or failure. + @param io An auto-pointer that owns a BasicIo instance used for + reading and writing image metadata. \b Important: The constructor + takes ownership of the passed in BasicIo instance through the + auto-pointer. Callers should not continue to use the BasicIo + instance after it is passed to this method. Use the Image::io() + method to get a temporary reference. + @param create Specifies if an existing image should be read (false) + or if a new file should be created (true). + */ + TiffImage(BasicIo::AutoPtr io, bool create); + //! Destructor + ~TiffImage() {} + //@} + + //! @name Manipulators + //@{ + void readMetadata(); + /*! + @brief Todo: Write metadata back to the image. This method is not + yet implemented. + */ + void writeMetadata(); + void setExifData(const ExifData& exifData); + void clearExifData(); + /*! + @brief Not supported. TIFF format does not contain IPTC metadata. + Calling this function will do nothing. + */ + void setIptcData(const IptcData& iptcData); + /*! + @brief Not supported. TIFF format does not contain IPTC metadata. + Calling this function will do nothing. + */ + void clearIptcData(); + void setComment(const std::string& comment); + void clearComment(); + void setMetadata(const Image& image); + void clearMetadata(); + ExifData& exifData() { return exifData_; } + IptcData& iptcData() { return iptcData_; } + //@} + + //! @name Accessors + //@{ + bool good() const; + const ExifData& exifData() const { return exifData_; } + const IptcData& iptcData() const { return iptcData_; } + std::string comment() const { return comment_; } + BasicIo& io() const { return *io_; } + //@} + + private: + //! @name Accessors + //@{ + /*! + @brief Determine if the content of the BasicIo instance is a TIFF image. + + The advance flag determines if the read position in the stream is + moved (see below). This applies only if the type matches and the + function returns true. If the type does not match, the stream + position is not changed. However, if reading from the stream fails, + the stream position is undefined. Consult the stream state to obtain + more information in this case. + + @param iIo BasicIo instance to read from. + @param advance Flag indicating whether the position of the io + should be advanced by the number of characters read to + analyse the data (true) or left at its original + position (false). This applies only if the type matches. + @return true if the data matches the type of this class;
+ false if the data does not match + */ + bool isThisType(BasicIo& iIo, bool advance) const; + /*! + @brief Todo: Write TIFF header. Not implemented yet. + */ + int writeHeader(BasicIo& oIo) const; + //@} + + // DATA + BasicIo::AutoPtr io_; //!< Image data io pointer + ExifData exifData_; //!< Exif data container + IptcData iptcData_; //!< IPTC data container + std::string comment_; //!< User comment + + static const TiffStructure tiffStructure_[]; // + * + * 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: tiffparser.cpp + Version: $Rev$ + Author(s): Andreas Huggel (ahu) + History: 15-Mar-06, ahu: created + + */ +// ***************************************************************************** +#include "rcsid.hpp" +EXIV2_RCSID("@(#) $Id: tiffparser.cpp 675 2006-01-25 04:16:58Z ahuggel $"); + +// Define DEBUG to output debug information to std::cerr, e.g, by calling make +// like this: make DEFS=-DDEBUG tiffparser.o +//#define DEBUG + +// ***************************************************************************** +// included header files +#ifdef _MSC_VER +# include "exv_msvc.h" +#else +# include "exv_conf.h" +#endif + +#include "tiffparser.hpp" +#include "image.hpp" +#include "exif.hpp" +#include "tags.hpp" +#include "error.hpp" +#include "futils.hpp" + +// + standard includes +#include +#include +#include + + +/* -------------------------------------------------------------------------- + + Todo: + + + Fix CiffHeader according to TiffHeade2 + + Combine Error(15) and Error(33), add format argument %1 + + Search crwimage for todos, fix writeMetadata comment + + rename all Ciff stuff to Crw for easier reference + + -------------------------------------------------------------------------- */ + + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + + void TiffParser::decode(Image* pImage, + const TiffStructure* pTiffStructure, + const byte* pData, + uint32_t size) + { + assert(pImage != 0); + assert(pData != 0); + + TiffHeade2 tiffHeader; + if (!tiffHeader.read(pData, size) || tiffHeader.offset() >= size) { + throw Error(3, "TIFF"); + } + + TiffComponent::AutoPtr rootDir + = create(Tag::root, Group::none, pTiffStructure); + if (0 == rootDir.get()) return; + rootDir->read(pData, size, tiffHeader.offset(), tiffHeader.byteOrder()); + +#ifdef DEBUG + tiffHeader.print(std::cerr); + rootDir->print(std::cerr, tiffHeader.byteOrder()); +#endif + + rootDir->decode(*pImage, tiffHeader.byteOrder()); + + } // TiffParser::decode + + TiffComponent::AutoPtr TiffParser::create(int32_t tag, + uint16_t group, + const TiffStructure* pTiffStructure) + { + const TiffStructure* ts = 0; + for (int i = 0; pTiffStructure[i].tag_ != Tag::none; ++i) { + if (tag == pTiffStructure[i].tag_ && group == pTiffStructure[i].group_) { + ts = &pTiffStructure[i]; + break; + } + } + TiffComponent::AutoPtr tc(0); + if (ts && ts->newTiffCompFct_) { + tc = ts->newTiffCompFct_(ts->newGroup_, pTiffStructure); + } + if (!ts) { + tc = TiffComponent::AutoPtr(new TiffEntry); + } + return tc; + } // TiffParser::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_; + } // 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 + + void TiffComponent::read(const byte* pData, + uint32_t size, + uint32_t start, + ByteOrder byteOrder) + { + doRead(pData, size, start, byteOrder); + } // TiffComponent::read + + void TiffEntryBase::doRead(const byte* pData, + uint32_t size, + uint32_t start, + ByteOrder byteOrder) + { + if (size - start < 12) throw Error(3, "TIFF"); + const byte* p = pData + start; + tag_ = getUShort(p, byteOrder); + p += 2; + type_ = getUShort(p, byteOrder); + // todo: check type + p += 2; + count_ = getULong(p, byteOrder); + p += 4; + offset_ = getULong(p, byteOrder); +#ifdef DEBUG + std::cout << "TiffEntryBase for " + << "tag 0x" << std::hex << tag_ + << ", type " << std::dec << type_ + << ", count " << count_ + << ", offset 0x" << std::hex << offset_ + << std::dec"\n"; +#endif + 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; + } + + } // TiffEntryBase::doRead + + void TiffEntry::doRead(const byte* pData, + uint32_t size, + uint32_t start, + ByteOrder byteOrder) + { + TiffEntryBase::doRead(pData, size, start, byteOrder); + } // TiffEntry::doRead + + void TiffDirectory::doRead(const byte* pData, + uint32_t size, + uint32_t start, + ByteOrder byteOrder) + { + 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); + } + + } // TiffDirectory::doRead + + void TiffSubIfd::doRead(const byte* pData, + uint32_t size, + uint32_t start, + ByteOrder byteOrder) + { + TiffEntryBase::doRead(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 + + void TiffComponent::decode(Image& image, ByteOrder byteOrder) const + { + doDecode(image, byteOrder); + } // TiffComponent::decode + + void TiffEntryBase::doDecode(Image& image, ByteOrder byteOrder) const + { + ExifKey k(tag(), "Image"); // todo needs ifdItem + TypeId t = typeId(); + Value::AutoPtr v = Value::create(t); + v->read(pData_, size_, byteOrder); + image.exifData().add(k, v.get()); + } // TiffEntryBase::doDecode + + void TiffEntry::doDecode(Image& image, ByteOrder byteOrder) const + { + TiffEntryBase::doDecode(image, byteOrder); + } // TiffEntry::doDecode + + void TiffDirectory::doDecode(Image& image, ByteOrder byteOrder) const + { + Components::const_iterator b = components_.begin(); + Components::const_iterator e = components_.end(); + for (Components::const_iterator i = b; i != e; ++i) { + (*i)->decode(image, byteOrder); + } + if (pNext_) pNext_->decode(image, byteOrder); + } // TiffDirectory::doDecode + + void TiffSubIfd::doDecode(Image& image, ByteOrder byteOrder) const + { + ifd_.decode(image, byteOrder); + } // TiffSubIfd::doDecode + + 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"; + } // TiffHeade2::print + + void TiffComponent::print(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix) const + { + doPrint(os, byteOrder, prefix); + } // TiffComponent::print + + void TiffEntryBase::doPrint(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()) + << ", count = " << std::dec << count() + << ", offset = " << offset() << "\n"; + + TypeId t = typeId(); + Value::AutoPtr v = Value::create(t); + v->read(pData_, size_, byteOrder); + if (v->size() < 100) { + os << prefix << *v << "\n"; + } + } // TiffEntryBase::doPrint + + void TiffEntry::doPrint(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix) const + { + TiffEntryBase::doPrint(os, byteOrder, prefix); + } // TiffEntry::doPrint + + void TiffDirectory::doPrint(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix) const + { + os << prefix << "Directory " << group() + << " with " << components_.size() << " 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::doPrint(os, byteOrder, prefix); + ifd_.print(os, byteOrder, prefix); + } // TiffSubIfd::doPrint + + // ************************************************************************* + // free functions + + TiffComponent::AutoPtr newTiffDirectory(uint16_t group, + const TiffStructure* pTiffStructure) + { + return TiffComponent::AutoPtr(new TiffDirectory(group, pTiffStructure)); + } + + TiffComponent::AutoPtr newTiffSubIfd(uint16_t group, + const TiffStructure* pTiffStructure) + { + return TiffComponent::AutoPtr(new TiffSubIfd(group, pTiffStructure)); + } + +} // namespace Exiv2 diff --git a/src/tiffparser.hpp b/src/tiffparser.hpp new file mode 100644 index 00000000..015fbca1 --- /dev/null +++ b/src/tiffparser.hpp @@ -0,0 +1,491 @@ +// ***************************************************************** -*- 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 tiffparser.hpp + @brief Class TiffParser to parse TIFF data. + @version $Rev$ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @date 15-Mar-06, ahu: created + */ +#ifndef TIFFPARSER_HPP_ +#define TIFFPARSER_HPP_ + +// ***************************************************************************** +// included header files +#include "exif.hpp" +#include "iptc.hpp" +#include "image.hpp" +#include "types.hpp" + +// + standard includes +#include +#include +#include +#include + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class declarations + + struct TiffStructure; + +// ***************************************************************************** +// type definitions + +// ***************************************************************************** +// 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 + } + + /*! + Known TIFF tags + + Todo: Same Q as above... + */ + namespace Tag { + const int32_t none = -1; //!< Dummy tag + const int32_t root = -2; //!< Special tag: root IFD + const int32_t next = -3; //!< Special tag: next IFD + } + + /*! + @brief Interface class for components of a TIFF directory hierarchy. Both + TIFF directories as well as entries implement this interface. This + class is implemented as NVI (non-virtual interface). + */ + 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 group, const TiffStructure* pTiffStructure) + : 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 group id of this component + uint16_t group() const { return group_; } + //*! Return the TIFF structure + const TiffStructure* pTiffStructure() const { return pTiffStructure_; } + /*! + @brief Decode metadata from the component and add it to + \em image. + + @param image Image to add the metadata to + @param byteOrder Byte order + */ + void decode(Image& image, ByteOrder byteOrder) const; + /*! + @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 read(). + virtual void doRead(const byte* pData, + uint32_t size, + uint32_t start, + ByteOrder byteOrder) =0; + //@} + + //! @name Accessors + //@{ + //! Implements decode() + virtual void doDecode(Image& image, + ByteOrder byteOrder) const =0; + //! Implements print() + virtual void doPrint(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix) const =0; + //@} + + private: + // DATA + 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. + + todo: make sure this class is an ABC + */ + class TiffEntryBase : public TiffComponent { + public: + //! @name Creators + //@{ + //! Default constructor + TiffEntryBase() + : TiffComponent(Group::none, 0), + tag_(0), type_(0), count_(0), offset_(0), + size_(0), pData_(0), isAllocated_(false) {} + //! Virtual destructor. + virtual ~TiffEntryBase(); + //@} + + //! @name Accessors + //@{ + //! Return the tag of this entry. + uint16_t tag() const { return tag_; } + //! 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_; } + //@} + + protected: + //! @name Manipulators + //@{ + //! Implements read(). + virtual void doRead(const byte* pData, + uint32_t size, + uint32_t start, + ByteOrder byteOrder); + //@} + + //! @name Accessors + //@{ + //! Implements decode() for a TIFF IFD entry + virtual void doDecode(Image& image, ByteOrder byteOrder) const; + //! Implements print() for a TIFF IFD entry + virtual void doPrint(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix) const; + //@} + + private: + // DATA + uint16_t tag_; //!< Tag that identifies the field + uint16_t type_; //!< Field Type + uint32_t count_; //!< The number of values of the indicated Type + uint32_t offset_; //!< Offset to the data area from start of the 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 + + }; // class TiffEntryBase + + /*! + @brief A standard TIFF IFD entry. The value is kept in a data buffer. + */ + class TiffEntry : public TiffEntryBase { + public: + //! @name Creators + //@{ + //! 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 doDecode(Image& image, ByteOrder byteOrder) const; + //! Implements print() for a TIFF IFD entry + virtual void doPrint(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix) const; + //@} + + }; // class TiffEntry + + //! This class models a TIFF directory (%Ifd). + class TiffDirectory : public TiffComponent { + public: + //! @name Creators + //@{ + //! Default constructor + TiffDirectory(uint16_t group, const TiffStructure* pTiffStructure) + : TiffComponent(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 doDecode(Image& image, + ByteOrder byteOrder) const; + + 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 + + //! This class models a TIFF sub-directory (%SubIfd). + class TiffSubIfd : public TiffEntryBase { + public: + //! @name Creators + //@{ + //! Default constructor + TiffSubIfd(uint16_t group, const TiffStructure* pTiffStructure) + : ifd_(group, 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 doDecode(Image& image, + ByteOrder byteOrder) const; + + virtual void doPrint(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix) const; + //@} + + private: + // DATA + TiffDirectory ifd_; //!< The subdirectory + + }; // class TiffDirectory + + /*! + @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 + in order to make decisions based on the value of other tags. + */ + typedef TiffComponent::AutoPtr (*NewTiffCompFct)(uint16_t group, + const TiffStructure* tiffStructure); + + /*! + 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 { + int32_t tag_; //!< 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 + }; + + /*! + Stateless parser class for data in TIFF format. + */ + class TiffParser { + public: + /*! + @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. + + @param pImage Pointer to the %Exiv2 TIFF image to hold the + metadata read from the buffer. + @param pTiffStructure Pointer to a table describing the TIFF structure + used to decode the 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. + + @throw Error If the data buffer cannot be parsed. + */ + static void decode( Image* pImage, + const TiffStructure* pTiffStructure, + const byte* pData, + uint32_t size); + /*! + @brief Create the appropriate TiffComponent to handle the \em tag in + \em group. + + 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 tag should be ignored. + */ + static TiffComponent::AutoPtr create( int32_t tag, + uint16_t group, + const TiffStructure* pTiffStructure); + }; // class TiffParser + +// ***************************************************************************** +// template, inline and free functions + + //!< Function to create and initialize a new TIFF directory + TiffComponent::AutoPtr newTiffDirectory(uint16_t group, + const TiffStructure* pTiffStructure); + + //!< Function to create and initialize a new TIFF sub-directory + TiffComponent::AutoPtr newTiffSubIfd(uint16_t group, + const TiffStructure* pTiffStructure); + +} // namespace Exiv2 + +#endif // #ifndef TIFFPARSER_HPP_