// ***************************************************************** -*- C++ -*- /* * Copyright (C) 2004-2018 Exiv2 authors * This program is part of the Exiv2 distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. */ /* File: tiffcomposite.cpp Author(s): Andreas Huggel (ahu) History: 11-Apr-06, ahu: created */ // ***************************************************************************** // included header files #include "config.h" #include "tiffimage_int.hpp" #include "tiffcomposite_int.hpp" #include "tiffvisitor_int.hpp" #include "makernote_int.hpp" #include "value.hpp" #include "error.hpp" #include "enforce.hpp" // + standard includes #include #include #include #include #include // ***************************************************************************** namespace { //! Add \em tobe - \em curr 0x00 filler bytes if necessary uint32_t fillGap(Exiv2::Internal::IoWrapper& ioWrapper, uint32_t curr, uint32_t tobe); } // ***************************************************************************** // class member definitions namespace Exiv2 { namespace Internal { bool TiffMappingInfo::operator==(const TiffMappingInfo::Key& key) const { return ( 0 == strcmp("*", make_) || 0 == strncmp(make_, key.m_.c_str(), strlen(make_))) && (Tag::all == extendedTag_ || key.e_ == extendedTag_) && key.g_ == group_; } IoWrapper::IoWrapper(BasicIo& io, const byte* pHeader, long size, OffsetWriter* pow) : io_(io), pHeader_(pHeader), size_(size), wroteHeader_(false), pow_(pow) { if (pHeader_ == 0 || size_ == 0) wroteHeader_ = true; } long IoWrapper::write(const byte* pData, long wcount) { if (!wroteHeader_ && wcount > 0) { io_.write(pHeader_, size_); wroteHeader_ = true; } return io_.write(pData, wcount); } int IoWrapper::putb(byte data) { if (!wroteHeader_) { io_.write(pHeader_, size_); wroteHeader_ = true; } return io_.putb(data); } void IoWrapper::setTarget(int id, uint32_t target) { if (pow_) pow_->setTarget(OffsetWriter::OffsetId(id), target); } TiffComponent::TiffComponent(uint16_t tag, IfdId group) : tag_(tag), group_(group), pStart_(0) { } TiffEntryBase::TiffEntryBase(uint16_t tag, IfdId group, TiffType tiffType) : TiffComponent(tag, group), tiffType_(tiffType), count_(0), offset_(0), size_(0), pData_(0), isMalloced_(false), idx_(0), pValue_(0) { } TiffSubIfd::TiffSubIfd(uint16_t tag, IfdId group, IfdId newGroup) : TiffEntryBase(tag, group, ttUnsignedLong), newGroup_(newGroup) { } TiffMnEntry::TiffMnEntry(uint16_t tag, IfdId group, IfdId mnGroup) : TiffEntryBase(tag, group, ttUndefined), mnGroup_(mnGroup), mn_(0) { } TiffIfdMakernote::TiffIfdMakernote(uint16_t tag, IfdId group, IfdId mnGroup, MnHeader* pHeader, bool hasNext) : TiffComponent(tag, group), pHeader_(pHeader), ifd_(tag, mnGroup, hasNext), mnOffset_(0), imageByteOrder_(invalidByteOrder) { } TiffBinaryArray::TiffBinaryArray(uint16_t tag, IfdId group, const ArrayCfg* arrayCfg, const ArrayDef* arrayDef, int defSize) : TiffEntryBase(tag, group, arrayCfg->elTiffType_), cfgSelFct_(0), arraySet_(0), arrayCfg_(arrayCfg), arrayDef_(arrayDef), defSize_(defSize), setSize_(0), origData_(0), origSize_(0), pRoot_(0), decoded_(false) { assert(arrayCfg != 0); } TiffBinaryArray::TiffBinaryArray(uint16_t tag, IfdId group, const ArraySet* arraySet, int setSize, CfgSelFct cfgSelFct) : TiffEntryBase(tag, group), // Todo: Does it make a difference that there is no type? cfgSelFct_(cfgSelFct), arraySet_(arraySet), arrayCfg_(0), arrayDef_(0), defSize_(0), setSize_(setSize), origData_(0), origSize_(0), pRoot_(0), decoded_(false) { // We'll figure out the correct cfg later assert(cfgSelFct != 0); assert(arraySet_ != 0); } TiffBinaryElement::TiffBinaryElement(uint16_t tag, IfdId group) : TiffEntryBase(tag, group), elByteOrder_(invalidByteOrder) { elDef_.idx_ = 0; elDef_.tiffType_ = ttUndefined; elDef_.count_ = 0; } TiffComponent::~TiffComponent() { } TiffDirectory::~TiffDirectory() { for (Components::iterator i = components_.begin(); i != components_.end(); ++i) { delete *i; } delete pNext_; } TiffSubIfd::~TiffSubIfd() { for (Ifds::iterator i = ifds_.begin(); i != ifds_.end(); ++i) { delete *i; } } TiffEntryBase::~TiffEntryBase() { if (isMalloced_) { delete[] pData_; } delete pValue_; } TiffEntry::~TiffEntry() { } TiffDataEntryBase::~TiffDataEntryBase() { } TiffDataEntry::~TiffDataEntry() { } TiffImageEntry::~TiffImageEntry() { } TiffSizeEntry::~TiffSizeEntry() { } TiffMnEntry::~TiffMnEntry() { delete mn_; } TiffIfdMakernote::~TiffIfdMakernote() { delete pHeader_; } TiffBinaryArray::~TiffBinaryArray() { for (Components::iterator i = elements_.begin(); i != elements_.end(); ++i) { delete *i; } } TiffBinaryElement::~TiffBinaryElement() { } TiffEntryBase::TiffEntryBase(const TiffEntryBase& rhs) : TiffComponent(rhs), tiffType_(rhs.tiffType_), count_(rhs.count_), offset_(rhs.offset_), size_(rhs.size_), pData_(rhs.pData_), isMalloced_(rhs.isMalloced_), idx_(rhs.idx_), pValue_(rhs.pValue_ ? rhs.pValue_->clone().release() : 0) { if (rhs.isMalloced_) { pData_ = new byte[rhs.size_]; memcpy(pData_, rhs.pData_, rhs.size_); } } TiffDirectory::TiffDirectory(const TiffDirectory& rhs) : TiffComponent(rhs), hasNext_(rhs.hasNext_), pNext_(0) { } TiffSubIfd::TiffSubIfd(const TiffSubIfd& rhs) : TiffEntryBase(rhs), newGroup_(rhs.newGroup_) { } TiffBinaryArray::TiffBinaryArray(const TiffBinaryArray& rhs) : TiffEntryBase(rhs), cfgSelFct_(rhs.cfgSelFct_), arraySet_(rhs.arraySet_), arrayCfg_(rhs.arrayCfg_), arrayDef_(rhs.arrayDef_), defSize_(rhs.defSize_), setSize_(rhs.setSize_), origData_(rhs.origData_), origSize_(rhs.origSize_), pRoot_(rhs.pRoot_), decoded_(false) { } TiffComponent::AutoPtr TiffComponent::clone() const { return AutoPtr(doClone()); } TiffEntry* TiffEntry::doClone() const { return new TiffEntry(*this); } TiffDataEntry* TiffDataEntry::doClone() const { return new TiffDataEntry(*this); } TiffImageEntry* TiffImageEntry::doClone() const { return new TiffImageEntry(*this); } TiffSizeEntry* TiffSizeEntry::doClone() const { return new TiffSizeEntry(*this); } TiffDirectory* TiffDirectory::doClone() const { return new TiffDirectory(*this); } TiffSubIfd* TiffSubIfd::doClone() const { return new TiffSubIfd(*this); } TiffMnEntry* TiffMnEntry::doClone() const { assert(false); // Not implemented return 0; } TiffIfdMakernote* TiffIfdMakernote::doClone() const { assert(false); // Not implemented return 0; } TiffBinaryArray* TiffBinaryArray::doClone() const { return new TiffBinaryArray(*this); } TiffBinaryElement* TiffBinaryElement::doClone() const { return new TiffBinaryElement(*this); } int TiffComponent::idx() const { return 0; } int TiffEntryBase::idx() const { return idx_; } void TiffEntryBase::setData(DataBuf buf) { std::pair p = buf.release(); setData(p.first, p.second); isMalloced_ = true; } void TiffEntryBase::setData(byte* pData, int32_t size) { if (isMalloced_) { delete[] pData_; } pData_ = pData; size_ = size; if (pData_ == 0) size_ = 0; } void TiffEntryBase::updateValue(Value::AutoPtr value, ByteOrder byteOrder) { if (value.get() == 0) return; uint32_t newSize = value->size(); if (newSize > size_) { setData(DataBuf(newSize)); } if (pData_ != NULL) { memset(pData_, 0x0, size_); } size_ = value->copy(pData_, byteOrder); assert(size_ == newSize); setValue(value); } // TiffEntryBase::updateValue void TiffEntryBase::setValue(Value::AutoPtr value) { if (value.get() == 0) return; tiffType_ = toTiffType(value->typeId()); count_ = value->count(); delete pValue_; pValue_ = value.release(); } // TiffEntryBase::setValue void TiffDataEntry::setStrips(const Value* pSize, const byte* pData, uint32_t sizeData, uint32_t baseOffset) { if (!pValue() || !pSize) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << ": Size or data offset value not set, ignoring them.\n"; #endif return; } if (pValue()->count() == 0) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << ": Data offset entry value is empty, ignoring it.\n"; #endif return; } if (pValue()->count() != pSize->count()) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << ": Size and data offset entries have different" << " number of components, ignoring them.\n"; #endif return; } uint32_t size = 0; for (int i = 0; i < pSize->count(); ++i) { size += static_cast(pSize->toLong(i)); } uint32_t offset = static_cast(pValue()->toLong(0)); // Todo: Remove limitation of JPEG writer: strips must be contiguous // Until then we check: last offset + last size - first offset == size? if ( static_cast(pValue()->toLong(pValue()->count()-1)) + static_cast(pSize->toLong(pSize->count()-1)) - offset != size) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << ": Data area is not contiguous, ignoring it.\n"; #endif return; } if ( offset > sizeData || size > sizeData || baseOffset + offset > sizeData - size) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << ": Data area exceeds data buffer, ignoring it.\n"; #endif return; } pDataArea_ = const_cast(pData) + baseOffset + offset; sizeDataArea_ = size; const_cast(pValue())->setDataArea(pDataArea_, sizeDataArea_); } // TiffDataEntry::setStrips void TiffImageEntry::setStrips(const Value* pSize, const byte* pData, uint32_t sizeData, uint32_t baseOffset) { if (!pValue() || !pSize) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << ": Size or data offset value not set, ignoring them.\n"; #endif return; } if (pValue()->count() != pSize->count()) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << ": Size and data offset entries have different" << " number of components, ignoring them.\n"; #endif return; } for (int i = 0; i < pValue()->count(); ++i) { const uint32_t offset = static_cast(pValue()->toLong(i)); const byte* pStrip = pData + baseOffset + offset; const uint32_t size = static_cast(pSize->toLong(i)); if ( offset > sizeData || size > sizeData || baseOffset + offset > sizeData - size) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << ": Strip " << std::dec << i << " is outside of the data area; ignored.\n"; #endif } else if (size != 0) { strips_.push_back(std::make_pair(pStrip, size)); } } } // TiffImageEntry::setStrips uint32_t TiffIfdMakernote::ifdOffset() const { if (!pHeader_) return 0; return pHeader_->ifdOffset(); } ByteOrder TiffIfdMakernote::byteOrder() const { assert(imageByteOrder_ != invalidByteOrder); if (!pHeader_ || pHeader_->byteOrder() == invalidByteOrder) { return imageByteOrder_; } return pHeader_->byteOrder(); } uint32_t TiffIfdMakernote::mnOffset() const { return mnOffset_; } uint32_t TiffIfdMakernote::baseOffset() const { if (!pHeader_) return 0; return pHeader_->baseOffset(mnOffset_); } bool TiffIfdMakernote::readHeader(const byte* pData, uint32_t size, ByteOrder byteOrder) { if (!pHeader_) return true; return pHeader_->read(pData, size, byteOrder); } void TiffIfdMakernote::setByteOrder(ByteOrder byteOrder) { if (pHeader_) pHeader_->setByteOrder(byteOrder); } uint32_t TiffIfdMakernote::sizeHeader() const { if (!pHeader_) return 0; return pHeader_->size(); } uint32_t TiffIfdMakernote::writeHeader(IoWrapper& ioWrapper, ByteOrder byteOrder) const { if (!pHeader_) return 0; return pHeader_->write(ioWrapper, byteOrder); } uint32_t ArrayDef::size(uint16_t tag, IfdId group) const { TypeId typeId = toTypeId(tiffType_, tag, group); return count_ * TypeInfo::typeSize(typeId); } bool TiffBinaryArray::initialize(IfdId group) { if (arrayCfg_ != 0) return true; // Not a complex array or already initialized for (int idx = 0; idx < setSize_; ++idx) { if (arraySet_[idx].cfg_.group_ == group) { arrayCfg_ = &arraySet_[idx].cfg_; arrayDef_ = arraySet_[idx].def_; defSize_ = arraySet_[idx].defSize_; return true; } } return false; } bool TiffBinaryArray::initialize(TiffComponent* const pRoot) { if (cfgSelFct_ == 0) return true; // Not a complex array int idx = cfgSelFct_(tag(), pData(), TiffEntryBase::doSize(), pRoot); if (idx > -1) { arrayCfg_ = &arraySet_[idx].cfg_; arrayDef_ = arraySet_[idx].def_; defSize_ = arraySet_[idx].defSize_; } return idx > -1; } void TiffBinaryArray::iniOrigDataBuf() { origData_ = const_cast(pData()); origSize_ = TiffEntryBase::doSize(); } bool TiffBinaryArray::updOrigDataBuf(const byte* pData, uint32_t size) { assert(pData != 0); if (origSize_ != size) return false; if (origData_ == pData) return true; memcpy(origData_, pData, origSize_); return true; } uint32_t TiffBinaryArray::addElement(uint32_t idx, const ArrayDef& def) { uint16_t tag = static_cast(idx / cfg()->tagStep()); int32_t sz = EXV_MIN(def.size(tag, cfg()->group_), TiffEntryBase::doSize() - idx); TiffComponent::AutoPtr tc = TiffCreator::create(tag, cfg()->group_); TiffBinaryElement* tp = dynamic_cast(tc.get()); // The assertion typically fails if a component is not configured in // the TIFF structure table (TiffCreator::tiffTreeStruct_) assert(tp); tp->setStart(pData() + idx); tp->setData(const_cast(pData() + idx), sz); tp->setElDef(def); tp->setElByteOrder(cfg()->byteOrder_); addChild(tc); return sz; } // TiffBinaryArray::addElement TiffComponent* TiffComponent::addPath(uint16_t tag, TiffPath& tiffPath, TiffComponent* const pRoot, TiffComponent::AutoPtr object) { return doAddPath(tag, tiffPath, pRoot, object); } // TiffComponent::addPath TiffComponent* TiffComponent::doAddPath(uint16_t /*tag*/, TiffPath& /*tiffPath*/, TiffComponent* const /*pRoot*/, TiffComponent::AutoPtr /*object*/) { return this; } // TiffComponent::doAddPath TiffComponent* TiffDirectory::doAddPath(uint16_t tag, TiffPath& tiffPath, TiffComponent* const pRoot, TiffComponent::AutoPtr object) { assert(tiffPath.size() > 1); tiffPath.pop(); const TiffPathItem tpi = tiffPath.top(); TiffComponent* tc = 0; // Try to use an existing component if there is still at least one // composite tag on the stack or the tag to add is the MakerNote tag. // This is used to prevent duplicate entries. Sub-IFDs also, but the > 1 // condition takes care of them, see below. if ( tiffPath.size() > 1 || (tpi.extendedTag() == 0x927c && tpi.group() == exifId)) { if (tpi.extendedTag() == Tag::next) { tc = pNext_; } else { for (Components::iterator i = components_.begin(); i != components_.end(); ++i) { if ((*i)->tag() == tpi.tag() && (*i)->group() == tpi.group()) { tc = *i; break; } } } } if (tc == 0) { TiffComponent::AutoPtr atc; if (tiffPath.size() == 1 && object.get() != 0) { atc = object; } else { atc = TiffCreator::create(tpi.extendedTag(), tpi.group()); } assert(atc.get() != 0); // Prevent dangling sub-IFD tags: Do not add a sub-IFD component without children. // Todo: How to check before creating the component? if (tiffPath.size() == 1 && dynamic_cast(atc.get()) != 0) return 0; if (tpi.extendedTag() == Tag::next) { tc = this->addNext(atc); } else { tc = this->addChild(atc); } } return tc->addPath(tag, tiffPath, pRoot, object); } // TiffDirectory::doAddPath TiffComponent* TiffSubIfd::doAddPath(uint16_t tag, TiffPath& tiffPath, TiffComponent* const pRoot, TiffComponent::AutoPtr object) { assert(!tiffPath.empty()); const TiffPathItem tpi1 = tiffPath.top(); tiffPath.pop(); if (tiffPath.empty()) { // If the last element in the path is the sub-IFD tag itself we're done. // But that shouldn't happen - see TiffDirectory::doAddPath return this; } const TiffPathItem tpi2 = tiffPath.top(); tiffPath.push(tpi1); TiffComponent* tc = 0; for (Ifds::iterator i = ifds_.begin(); i != ifds_.end(); ++i) { if ((*i)->group() == tpi2.group()) { tc = *i; break; } } if (tc == 0) { if (tiffPath.size() == 1 && object.get() != 0) { tc = addChild(object); } else { TiffComponent::AutoPtr atc(new TiffDirectory(tpi1.tag(), tpi2.group())); tc = addChild(atc); } setCount(static_cast(ifds_.size())); } return tc->addPath(tag, tiffPath, pRoot, object); } // TiffSubIfd::doAddPath TiffComponent* TiffMnEntry::doAddPath(uint16_t tag, TiffPath& tiffPath, TiffComponent* const pRoot, TiffComponent::AutoPtr object) { assert(!tiffPath.empty()); const TiffPathItem tpi1 = tiffPath.top(); tiffPath.pop(); if (tiffPath.empty()) { // If the last element in the path is the makernote tag itself we're done return this; } const TiffPathItem tpi2 = tiffPath.top(); tiffPath.push(tpi1); if (mn_ == 0) { mnGroup_ = tpi2.group(); mn_ = TiffMnCreator::create(tpi1.tag(), tpi1.group(), mnGroup_); assert(mn_); } return mn_->addPath(tag, tiffPath, pRoot, object); } // TiffMnEntry::doAddPath TiffComponent* TiffIfdMakernote::doAddPath(uint16_t tag, TiffPath& tiffPath, TiffComponent* const pRoot, TiffComponent::AutoPtr object) { return ifd_.addPath(tag, tiffPath, pRoot, object); } TiffComponent* TiffBinaryArray::doAddPath(uint16_t tag, TiffPath& tiffPath, TiffComponent* const pRoot, TiffComponent::AutoPtr object) { pRoot_ = pRoot; if (tiffPath.size() == 1) { // An unknown complex binary array has no children and acts like a standard TIFF entry return this; } tiffPath.pop(); const TiffPathItem tpi = tiffPath.top(); // Initialize the binary array (if it is a complex array) initialize(tpi.group()); TiffComponent* tc = 0; // Todo: Duplicates are not allowed! // To allow duplicate entries, we only check if the new component already // exists if there is still at least one composite tag on the stack if (tiffPath.size() > 1) { for (Components::iterator i = elements_.begin(); i != elements_.end(); ++i) { if ((*i)->tag() == tpi.tag() && (*i)->group() == tpi.group()) { tc = *i; break; } } } if (tc == 0) { TiffComponent::AutoPtr atc; if (tiffPath.size() == 1 && object.get() != 0) { atc = object; } else { atc = TiffCreator::create(tpi.extendedTag(), tpi.group()); } assert(atc.get() != 0); assert(tpi.extendedTag() != Tag::next); tc = addChild(atc); setCount(static_cast(elements_.size())); } return tc->addPath(tag, tiffPath, pRoot, object); } // TiffBinaryArray::doAddPath TiffComponent* TiffComponent::addChild(TiffComponent::AutoPtr tiffComponent) { return doAddChild(tiffComponent); } // TiffComponent::addChild TiffComponent* TiffComponent::doAddChild(AutoPtr /*tiffComponent*/) { return 0; } // TiffComponent::doAddChild TiffComponent* TiffDirectory::doAddChild(TiffComponent::AutoPtr tiffComponent) { TiffComponent* tc = tiffComponent.release(); components_.push_back(tc); return tc; } // TiffDirectory::doAddChild TiffComponent* TiffSubIfd::doAddChild(TiffComponent::AutoPtr tiffComponent) { TiffDirectory* d = dynamic_cast(tiffComponent.release()); assert(d); ifds_.push_back(d); return d; } // TiffSubIfd::doAddChild TiffComponent* TiffMnEntry::doAddChild(TiffComponent::AutoPtr tiffComponent) { TiffComponent* tc = 0; if (mn_) { tc = mn_->addChild(tiffComponent); } return tc; } // TiffMnEntry::doAddChild TiffComponent* TiffIfdMakernote::doAddChild(TiffComponent::AutoPtr tiffComponent) { return ifd_.addChild(tiffComponent); } TiffComponent* TiffBinaryArray::doAddChild(TiffComponent::AutoPtr tiffComponent) { TiffComponent* tc = tiffComponent.release(); elements_.push_back(tc); setDecoded(true); return tc; } // TiffBinaryArray::doAddChild TiffComponent* TiffComponent::addNext(TiffComponent::AutoPtr tiffComponent) { return doAddNext(tiffComponent); } // TiffComponent::addNext TiffComponent* TiffComponent::doAddNext(AutoPtr /*tiffComponent*/) { return 0; } // TiffComponent::doAddNext TiffComponent* TiffDirectory::doAddNext(TiffComponent::AutoPtr tiffComponent) { TiffComponent* tc = 0; if (hasNext_) { tc = tiffComponent.release(); pNext_ = tc; } return tc; } // TiffDirectory::doAddNext TiffComponent* TiffMnEntry::doAddNext(TiffComponent::AutoPtr tiffComponent) { TiffComponent* tc = 0; if (mn_) { tc = mn_->addNext(tiffComponent); } return tc; } // TiffMnEntry::doAddNext TiffComponent* TiffIfdMakernote::doAddNext(TiffComponent::AutoPtr tiffComponent) { return ifd_.addNext(tiffComponent); } void TiffComponent::accept(TiffVisitor& visitor) { if (visitor.go(TiffVisitor::geTraverse)) doAccept(visitor); // one for NVI :) } // TiffComponent::accept void TiffEntry::doAccept(TiffVisitor& visitor) { visitor.visitEntry(this); } // TiffEntry::doAccept void TiffDataEntry::doAccept(TiffVisitor& visitor) { visitor.visitDataEntry(this); } // TiffDataEntry::doAccept void TiffImageEntry::doAccept(TiffVisitor& visitor) { visitor.visitImageEntry(this); } // TiffImageEntry::doAccept void TiffSizeEntry::doAccept(TiffVisitor& visitor) { visitor.visitSizeEntry(this); } // TiffSizeEntry::doAccept void TiffDirectory::doAccept(TiffVisitor& visitor) { visitor.visitDirectory(this); for (Components::const_iterator i = components_.begin(); visitor.go(TiffVisitor::geTraverse) && i != components_.end(); ++i) { (*i)->accept(visitor); } if (visitor.go(TiffVisitor::geTraverse)) visitor.visitDirectoryNext(this); if (pNext_) pNext_->accept(visitor); if (visitor.go(TiffVisitor::geTraverse)) visitor.visitDirectoryEnd(this); } // TiffDirectory::doAccept void TiffSubIfd::doAccept(TiffVisitor& visitor) { visitor.visitSubIfd(this); for (Ifds::iterator i = ifds_.begin(); visitor.go(TiffVisitor::geTraverse) && i != ifds_.end(); ++i) { (*i)->accept(visitor); } } // TiffSubIfd::doAccept void TiffMnEntry::doAccept(TiffVisitor& visitor) { visitor.visitMnEntry(this); if (mn_) mn_->accept(visitor); if (!visitor.go(TiffVisitor::geKnownMakernote)) { delete mn_; mn_ = 0; } } // TiffMnEntry::doAccept void TiffIfdMakernote::doAccept(TiffVisitor& visitor) { if (visitor.go(TiffVisitor::geTraverse)) visitor.visitIfdMakernote(this); if (visitor.go(TiffVisitor::geKnownMakernote)) ifd_.accept(visitor); if ( visitor.go(TiffVisitor::geKnownMakernote) && visitor.go(TiffVisitor::geTraverse)) visitor.visitIfdMakernoteEnd(this); } void TiffBinaryArray::doAccept(TiffVisitor& visitor) { visitor.visitBinaryArray(this); for (Components::const_iterator i = elements_.begin(); visitor.go(TiffVisitor::geTraverse) && i != elements_.end(); ++i) { (*i)->accept(visitor); } if (visitor.go(TiffVisitor::geTraverse)) visitor.visitBinaryArrayEnd(this); } // TiffBinaryArray::doAccept void TiffBinaryElement::doAccept(TiffVisitor& visitor) { visitor.visitBinaryElement(this); } // TiffBinaryElement::doAccept void TiffEntryBase::encode(TiffEncoder& encoder, const Exifdatum* datum) { doEncode(encoder, datum); } // TiffComponent::encode void TiffBinaryElement::doEncode(TiffEncoder& encoder, const Exifdatum* datum) { encoder.encodeBinaryElement(this, datum); } // TiffBinaryElement::doEncode void TiffBinaryArray::doEncode(TiffEncoder& encoder, const Exifdatum* datum) { encoder.encodeBinaryArray(this, datum); } // TiffBinaryArray::doEncode void TiffDataEntry::doEncode(TiffEncoder& encoder, const Exifdatum* datum) { encoder.encodeDataEntry(this, datum); } // TiffDataEntry::doEncode void TiffEntry::doEncode(TiffEncoder& encoder, const Exifdatum* datum) { encoder.encodeTiffEntry(this, datum); } // TiffEntry::doEncode void TiffImageEntry::doEncode(TiffEncoder& encoder, const Exifdatum* datum) { encoder.encodeImageEntry(this, datum); } // TiffImageEntry::doEncode void TiffMnEntry::doEncode(TiffEncoder& encoder, const Exifdatum* datum) { encoder.encodeMnEntry(this, datum); } // TiffMnEntry::doEncode void TiffSizeEntry::doEncode(TiffEncoder& encoder, const Exifdatum* datum) { encoder.encodeSizeEntry(this, datum); } // TiffSizeEntry::doEncode void TiffSubIfd::doEncode(TiffEncoder& encoder, const Exifdatum* datum) { encoder.encodeSubIfd(this, datum); } // TiffSubIfd::doEncode uint32_t TiffComponent::count() const { return doCount(); } uint32_t TiffDirectory::doCount() const { return static_cast(components_.size()); } uint32_t TiffEntryBase::doCount() const { return count_; } uint32_t TiffMnEntry::doCount() const { if (!mn_) { return TiffEntryBase::doCount(); } // Count of IFD makernote in tag Exif.Photo.MakerNote is the size of the // Makernote in bytes assert(tiffType() == ttUndefined || tiffType() == ttUnsignedByte || tiffType() == ttSignedByte); return mn_->size(); } uint32_t TiffIfdMakernote::doCount() const { return ifd_.count(); } // TiffIfdMakernote::doCount uint32_t TiffBinaryArray::doCount() const { if (cfg() == 0 || !decoded()) return TiffEntryBase::doCount(); if (elements_.empty()) return 0; TypeId typeId = toTypeId(tiffType(), tag(), group()); long typeSize = TypeInfo::typeSize(typeId); if (0 == typeSize) { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << " has unknown Exif (TIFF) type " << std::dec << tiffType() << "; setting type size 1.\n"; #endif typeSize = 1; } return static_cast(static_cast(size()) / typeSize + 0.5); } uint32_t TiffBinaryElement::doCount() const { return elDef_.count_; } uint32_t TiffComponent::write(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t valueIdx, uint32_t dataIdx, uint32_t& imageIdx) { return doWrite(ioWrapper, byteOrder, offset, valueIdx, dataIdx, imageIdx); } // TiffComponent::write uint32_t TiffDirectory::doWrite(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t valueIdx, uint32_t dataIdx, uint32_t& imageIdx) { bool isRootDir = (imageIdx == uint32_t(-1)); // Number of components to write const uint32_t compCount = count(); if (compCount > 0xffff) throw Error(kerTooManyTiffDirectoryEntries, groupName(group())); // Size of next IFD, if any uint32_t sizeNext = 0; if (pNext_) sizeNext = pNext_->size(); // Nothing to do if there are no entries and the size of the next IFD is 0 if (compCount == 0 && sizeNext == 0) return 0; // Remember the offset of the CR2 RAW IFD if (group() == ifd3Id) { #ifdef EXIV2_DEBUG_MESSAGES std::cerr << "Directory " << groupName(group()) << " offset is 0x" << std::setw(8) << std::setfill('0') << std::hex << offset << std::dec << "\n"; #endif ioWrapper.setTarget(OffsetWriter::cr2RawIfdOffset, offset); } // Size of all directory entries, without values and additional data const uint32_t sizeDir = 2 + 12 * compCount + (hasNext_ ? 4 : 0); // TIFF standard requires IFD entries to be sorted in ascending order by tag. // Not sorting makernote directories sometimes preserves them better. if (group() < mnId) { std::sort(components_.begin(), components_.end(), cmpTagLt); } // Size of IFD values and additional data uint32_t sizeValue = 0; uint32_t sizeData = 0; for (Components::const_iterator i = components_.begin(); i != components_.end(); ++i) { uint32_t sv = (*i)->size(); if (sv > 4) { sv += sv & 1; // Align value to word boundary sizeValue += sv; } // Also add the size of data, but only if needed if (isRootDir) { uint32_t sd = (*i)->sizeData(); sd += sd & 1; // Align data to word boundary sizeData += sd; } } uint32_t idx = 0; // Current IFD index / bytes written valueIdx = sizeDir; // Offset to the current IFD value dataIdx = sizeDir + sizeValue; // Offset to the entry's data area if (isRootDir) { // Absolute offset to the image data imageIdx = offset + dataIdx + sizeData + sizeNext; imageIdx += imageIdx & 1; // Align image data to word boundary } // 1st: Write the IFD, a) Number of directory entries byte buf[4]; us2Data(buf, static_cast(compCount), byteOrder); ioWrapper.write(buf, 2); idx += 2; // b) Directory entries - may contain pointers to the value or data for (Components::const_iterator i = components_.begin(); i != components_.end(); ++i) { idx += writeDirEntry(ioWrapper, byteOrder, offset, *i, valueIdx, dataIdx, imageIdx); uint32_t sv = (*i)->size(); if (sv > 4) { sv += sv & 1; // Align value to word boundary valueIdx += sv; } uint32_t sd = (*i)->sizeData(); sd += sd & 1; // Align data to word boundary dataIdx += sd; } // c) Pointer to the next IFD if (hasNext_) { memset(buf, 0x0, 4); if (pNext_ && sizeNext) { l2Data(buf, offset + dataIdx, byteOrder); } ioWrapper.write(buf, 4); idx += 4; } assert(idx == sizeDir); // 2nd: Write IFD values - may contain pointers to additional data valueIdx = sizeDir; dataIdx = sizeDir + sizeValue; for (Components::const_iterator i = components_.begin(); i != components_.end(); ++i) { uint32_t sv = (*i)->size(); if (sv > 4) { uint32_t d = (*i)->write(ioWrapper, byteOrder, offset, valueIdx, dataIdx, imageIdx); enforce(sv == d, kerImageWriteFailed); if ((sv & 1) == 1) { ioWrapper.putb(0x0); // Align value to word boundary sv += 1; } idx += sv; valueIdx += sv; } uint32_t sd = (*i)->sizeData(); sd += sd & 1; // Align data to word boundary dataIdx += sd; } assert(idx == sizeDir + sizeValue); // 3rd: Write data - may contain offsets too (eg sub-IFD) dataIdx = sizeDir + sizeValue; idx += writeData(ioWrapper, byteOrder, offset, dataIdx, imageIdx); // 4th: Write next-IFD if (pNext_ && sizeNext) { idx += pNext_->write(ioWrapper, byteOrder, offset + idx, uint32_t(-1), uint32_t(-1), imageIdx); } // 5th, at the root directory level only: write image data if (isRootDir) { idx += writeImage(ioWrapper, byteOrder); } return idx; } // TiffDirectory::doWrite uint32_t TiffDirectory::writeDirEntry(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, TiffComponent* pTiffComponent, uint32_t valueIdx, uint32_t dataIdx, uint32_t& imageIdx) const { assert(pTiffComponent); TiffEntryBase* pDirEntry = dynamic_cast(pTiffComponent); assert(pDirEntry); byte buf[8]; us2Data(buf, pDirEntry->tag(), byteOrder); us2Data(buf + 2, pDirEntry->tiffType(), byteOrder); ul2Data(buf + 4, pDirEntry->count(), byteOrder); ioWrapper.write(buf, 8); if (pDirEntry->size() > 4) { pDirEntry->setOffset(offset + static_cast(valueIdx)); l2Data(buf, pDirEntry->offset(), byteOrder); ioWrapper.write(buf, 4); } else { const uint32_t len = pDirEntry->write(ioWrapper, byteOrder, offset, valueIdx, dataIdx, imageIdx); assert(len <= 4); if (len < 4) { memset(buf, 0x0, 4); ioWrapper.write(buf, 4 - len); } } return 12; } // TiffDirectory::writeDirEntry uint32_t TiffEntryBase::doWrite(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t /*offset*/, uint32_t /*valueIdx*/, uint32_t /*dataIdx*/, uint32_t& /*imageIdx*/) { if (!pValue_) return 0; DataBuf buf(pValue_->size()); pValue_->copy(buf.pData_, byteOrder); ioWrapper.write(buf.pData_, buf.size_); return buf.size_; } // TiffEntryBase::doWrite uint32_t TiffEntryBase::writeOffset(byte* buf, int32_t offset, TiffType tiffType, ByteOrder byteOrder) { uint32_t rc = 0; switch(tiffType) { case ttUnsignedShort: case ttSignedShort: if (static_cast(offset) > 0xffff) throw Error(kerOffsetOutOfRange); rc = s2Data(buf, static_cast(offset), byteOrder); break; case ttUnsignedLong: case ttSignedLong: rc = l2Data(buf, static_cast(offset), byteOrder); break; default: throw Error(kerUnsupportedDataAreaOffsetType); break; } return rc; } // TiffEntryBase::writeOffset uint32_t TiffDataEntry::doWrite(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t /*valueIdx*/, uint32_t dataIdx, uint32_t& /*imageIdx*/) { if (!pValue() || pValue()->count() == 0) return 0; DataBuf buf(pValue()->size()); uint32_t idx = 0; const long prevOffset = pValue()->toLong(0); for (uint32_t i = 0; i < count(); ++i) { const long newDataIdx = pValue()->toLong(i) - prevOffset + static_cast(dataIdx); idx += writeOffset(buf.pData_ + idx, offset + newDataIdx, tiffType(), byteOrder); } ioWrapper.write(buf.pData_, buf.size_); return buf.size_; } // TiffDataEntry::doWrite uint32_t TiffImageEntry::doWrite(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t /*valueIdx*/, uint32_t dataIdx, uint32_t& imageIdx) { uint32_t o2 = imageIdx; // For makernotes, write TIFF image data to the data area if (group() > mnId) o2 = offset + dataIdx; #ifdef EXIV2_DEBUG_MESSAGES std::cerr << "TiffImageEntry, Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << std::dec << ": Writing offset " << o2 << "\n"; #endif DataBuf buf(static_cast(strips_.size()) * 4); memset(buf.pData_, 0x0, buf.size_); uint32_t idx = 0; for (Strips::const_iterator i = strips_.begin(); i != strips_.end(); ++i) { idx += writeOffset(buf.pData_ + idx, o2, tiffType(), byteOrder); o2 += i->second; o2 += i->second & 1; // Align strip data to word boundary if (!(group() > mnId)) { // Todo: FIX THIS!! SHOULDN'T USE > imageIdx += i->second; imageIdx += i->second & 1; // Align strip data to word boundary } } ioWrapper.write(buf.pData_, buf.size_); return buf.size_; } // TiffImageEntry::doWrite uint32_t TiffSubIfd::doWrite(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t /*valueIdx*/, uint32_t dataIdx, uint32_t& /*imageIdx*/) { DataBuf buf(static_cast(ifds_.size()) * 4); uint32_t idx = 0; // Sort IFDs by group, needed if image data tags were copied first std::sort(ifds_.begin(), ifds_.end(), cmpGroupLt); for (Ifds::const_iterator i = ifds_.begin(); i != ifds_.end(); ++i) { idx += writeOffset(buf.pData_ + idx, offset + dataIdx, tiffType(), byteOrder); dataIdx += (*i)->size(); } ioWrapper.write(buf.pData_, buf.size_); return buf.size_; } // TiffSubIfd::doWrite uint32_t TiffMnEntry::doWrite(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t valueIdx, uint32_t dataIdx, uint32_t& imageIdx) { if (!mn_) { return TiffEntryBase::doWrite(ioWrapper, byteOrder, offset, valueIdx, dataIdx, imageIdx); } return mn_->write(ioWrapper, byteOrder, offset + valueIdx, uint32_t(-1), uint32_t(-1), imageIdx); } // TiffMnEntry::doWrite uint32_t TiffIfdMakernote::doWrite(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t /*valueIdx*/, uint32_t /*dataIdx*/, uint32_t& imageIdx) { mnOffset_ = offset; setImageByteOrder(byteOrder); uint32_t len = writeHeader(ioWrapper, this->byteOrder()); len += ifd_.write(ioWrapper, this->byteOrder(), offset - baseOffset() + len, uint32_t(-1), uint32_t(-1), imageIdx); return len; } // TiffIfdMakernote::doWrite uint32_t TiffBinaryArray::doWrite(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t valueIdx, uint32_t dataIdx, uint32_t& imageIdx) { if (cfg() == 0 || !decoded()) return TiffEntryBase::doWrite(ioWrapper, byteOrder, offset, valueIdx, dataIdx, imageIdx); if (cfg()->byteOrder_ != invalidByteOrder) byteOrder = cfg()->byteOrder_; // Tags must be sorted in ascending order std::sort(elements_.begin(), elements_.end(), cmpTagLt); uint32_t idx = 0; MemIo mio; IoWrapper mioWrapper(mio, 0, 0, 0); // Some array entries need to have the size in the first element if (cfg()->hasSize_) { byte buf[4]; long elSize = TypeInfo::typeSize(toTypeId(cfg()->elTiffType_, 0, cfg()->group_)); switch (elSize) { case 2: idx += us2Data(buf, size(), byteOrder); break; case 4: idx += ul2Data(buf, size(), byteOrder); break; default: assert(false); } mioWrapper.write(buf, elSize); } // write all tags of the array (Todo: assumes that there are no duplicates, need check) for (Components::const_iterator i = elements_.begin(); i != elements_.end(); ++i) { // Skip the manufactured tag, if it exists if (cfg()->hasSize_ && (*i)->tag() == 0) continue; uint32_t newIdx = (*i)->tag() * cfg()->tagStep(); idx += fillGap(mioWrapper, idx, newIdx); idx += (*i)->write(mioWrapper, byteOrder, offset + newIdx, valueIdx, dataIdx, imageIdx); } if (cfg()->hasFillers_ && def()) { const ArrayDef* lastDef = def() + defSize() - 1; uint16_t lastTag = static_cast(lastDef->idx_ / cfg()->tagStep()); idx += fillGap(mioWrapper, idx, lastDef->idx_ + lastDef->size(lastTag, cfg()->group_)); } DataBuf buf; if (cfg()->cryptFct_) { buf = cfg()->cryptFct_(tag(), mio.mmap(), static_cast(mio.size()), pRoot_); } if (buf.size_ > 0) { ioWrapper.write(buf.pData_, buf.size_); } else { ioWrapper.write(mio.mmap(), static_cast(mio.size())); } return idx; } // TiffBinaryArray::doWrite uint32_t TiffBinaryElement::doWrite(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t /*offset*/, uint32_t /*valueIdx*/, uint32_t /*dataIdx*/, uint32_t& /*imageIdx*/) { Value const* pv = pValue(); if (!pv || pv->count() == 0) return 0; DataBuf buf(pv->size()); pv->copy(buf.pData_, byteOrder); ioWrapper.write(buf.pData_, buf.size_); return buf.size_; } // TiffBinaryElement::doWrite uint32_t TiffComponent::writeData(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t dataIdx, uint32_t& imageIdx) const { return doWriteData(ioWrapper, byteOrder, offset, dataIdx, imageIdx); } // TiffComponent::writeData uint32_t TiffDirectory::doWriteData(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t dataIdx, uint32_t& imageIdx) const { uint32_t len = 0; for (Components::const_iterator i = components_.begin(); i != components_.end(); ++i) { len += (*i)->writeData(ioWrapper, byteOrder, offset, dataIdx + len, imageIdx); } return len; } // TiffDirectory::doWriteData uint32_t TiffEntryBase::doWriteData(IoWrapper&/*ioWrapper*/, ByteOrder /*byteOrder*/, int32_t /*offset*/, uint32_t /*dataIdx*/, uint32_t& /*imageIdx*/) const { return 0; } // TiffEntryBase::doWriteData uint32_t TiffImageEntry::doWriteData(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t /*offset*/, uint32_t /*dataIdx*/, uint32_t& /*imageIdx*/) const { uint32_t len = 0; // For makernotes, write TIFF image data to the data area if (group() > mnId) { // Todo: FIX THIS HACK!!! len = writeImage(ioWrapper, byteOrder); } return len; } // TiffImageEntry::doWriteData uint32_t TiffDataEntry::doWriteData(IoWrapper& ioWrapper, ByteOrder /*byteOrder*/, int32_t /*offset*/, uint32_t /*dataIdx*/, uint32_t& /*imageIdx*/) const { if (!pValue()) return 0; DataBuf buf = pValue()->dataArea(); ioWrapper.write(buf.pData_, buf.size_); // Align data to word boundary uint32_t align = (buf.size_ & 1); if (align) ioWrapper.putb(0x0); return buf.size_ + align; } // TiffDataEntry::doWriteData uint32_t TiffSubIfd::doWriteData(IoWrapper& ioWrapper, ByteOrder byteOrder, int32_t offset, uint32_t dataIdx, uint32_t& imageIdx) const { uint32_t len = 0; for (Ifds::const_iterator i = ifds_.begin(); i != ifds_.end(); ++i) { len += (*i)->write(ioWrapper, byteOrder, offset + dataIdx + len, uint32_t(-1), uint32_t(-1), imageIdx); } // Align data to word boundary uint32_t align = (len & 1); if (align) ioWrapper.putb(0x0); return len + align; } // TiffSubIfd::doWriteData uint32_t TiffIfdMakernote::doWriteData(IoWrapper&/*ioWrapper*/, ByteOrder /*byteOrder*/, int32_t /*offset*/, uint32_t /*dataIdx*/, uint32_t& /*imageIdx*/) const { assert(false); return 0; } // TiffIfdMakernote::doWriteData uint32_t TiffComponent::writeImage(IoWrapper& ioWrapper, ByteOrder byteOrder) const { return doWriteImage(ioWrapper, byteOrder); } // TiffComponent::writeImage uint32_t TiffDirectory::doWriteImage(IoWrapper& ioWrapper, ByteOrder byteOrder) const { uint32_t len = 0; TiffComponent* pSubIfd = 0; for (Components::const_iterator i = components_.begin(); i != components_.end(); ++i) { if ((*i)->tag() == 0x014a) { // Hack: delay writing of sub-IFD image data to get the order correct assert(pSubIfd == 0); pSubIfd = *i; continue; } len += (*i)->writeImage(ioWrapper, byteOrder); } if (pSubIfd) { len += pSubIfd->writeImage(ioWrapper, byteOrder); } if (pNext_) { len += pNext_->writeImage(ioWrapper, byteOrder); } return len; } // TiffDirectory::doWriteImage uint32_t TiffEntryBase::doWriteImage(IoWrapper&/*ioWrapper*/, ByteOrder /*byteOrder*/) const { return 0; } // TiffEntryBase::doWriteImage uint32_t TiffSubIfd::doWriteImage(IoWrapper& ioWrapper, ByteOrder byteOrder) const { uint32_t len = 0; for (Ifds::const_iterator i = ifds_.begin(); i != ifds_.end(); ++i) { len += (*i)->writeImage(ioWrapper, byteOrder); } return len; } // TiffSubIfd::doWriteImage uint32_t TiffIfdMakernote::doWriteImage(IoWrapper& ioWrapper, ByteOrder byteOrder) const { if (this->byteOrder() != invalidByteOrder) { byteOrder = this->byteOrder(); } uint32_t len = ifd_.writeImage(ioWrapper, byteOrder); return len; } // TiffIfdMakernote::doWriteImage uint32_t TiffImageEntry::doWriteImage(IoWrapper& ioWrapper, ByteOrder /*byteOrder*/) const { if ( !pValue() ) throw Error(kerImageWriteFailed); // #1296 uint32_t len = pValue()->sizeDataArea(); if (len > 0) { #ifdef EXIV2_DEBUG_MESSAGES std::cerr << "TiffImageEntry, Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << std::dec << ": Writing data area, size = " << len; #endif DataBuf buf = pValue()->dataArea(); ioWrapper.write(buf.pData_, buf.size_); uint32_t align = len & 1; // Align image data to word boundary if (align) ioWrapper.putb(0x0); len += align; } else { #ifdef EXIV2_DEBUG_MESSAGES std::cerr << "TiffImageEntry, Directory " << groupName(group()) << ", entry 0x" << std::setw(4) << std::setfill('0') << std::hex << tag() << std::dec << ": Writing " << strips_.size() << " strips"; #endif len = 0; for (Strips::const_iterator i = strips_.begin(); i != strips_.end(); ++i) { ioWrapper.write(i->first, i->second); len += i->second; uint32_t align = i->second & 1; // Align strip data to word boundary if (align) ioWrapper.putb(0x0); len += align; } } #ifdef EXIV2_DEBUG_MESSAGES std::cerr << ", len = " << len << " bytes\n"; #endif return len; } // TiffImageEntry::doWriteImage uint32_t TiffComponent::size() const { return doSize(); } // TiffComponent::size uint32_t TiffDirectory::doSize() const { uint32_t compCount = count(); // Size of the directory, without values and additional data uint32_t len = 2 + 12 * compCount + (hasNext_ ? 4 : 0); // Size of IFD values and data for (Components::const_iterator i = components_.begin(); i != components_.end(); ++i) { uint32_t sv = (*i)->size(); if (sv > 4) { sv += sv & 1; // Align value to word boundary len += sv; } uint32_t sd = (*i)->sizeData(); sd += sd & 1; // Align data to word boundary len += sd; } // Size of next-IFD, if any uint32_t sizeNext = 0; if (pNext_) { sizeNext = pNext_->size(); len += sizeNext; } // Reset size of IFD if it has no entries and no or empty next IFD. if (compCount == 0 && sizeNext == 0) len = 0; return len; } // TiffDirectory::doSize uint32_t TiffEntryBase::doSize() const { return size_; } // TiffEntryBase::doSize uint32_t TiffImageEntry::doSize() const { return static_cast(strips_.size()) * 4; } // TiffImageEntry::doSize uint32_t TiffSubIfd::doSize() const { return static_cast(ifds_.size()) * 4; } // TiffSubIfd::doSize uint32_t TiffMnEntry::doSize() const { if (!mn_) { return TiffEntryBase::doSize(); } return mn_->size(); } // TiffMnEntry::doSize uint32_t TiffIfdMakernote::doSize() const { return sizeHeader() + ifd_.size(); } // TiffIfdMakernote::doSize uint32_t TiffBinaryArray::doSize() const { if (cfg() == 0 || !decoded()) return TiffEntryBase::doSize(); if (elements_.empty()) return 0; // Remaining assumptions: // - array elements don't "overlap" // - no duplicate tags in the array uint32_t idx = 0; uint32_t sz = cfg()->tagStep(); for (Components::const_iterator i = elements_.begin(); i != elements_.end(); ++i) { if ((*i)->tag() > idx) { idx = (*i)->tag(); sz = (*i)->size(); } } idx = idx * cfg()->tagStep() + sz; if (cfg()->hasFillers_ && def()) { const ArrayDef* lastDef = def() + defSize() - 1; uint16_t lastTag = static_cast(lastDef->idx_ / cfg()->tagStep()); idx = EXV_MAX(idx, lastDef->idx_ + lastDef->size(lastTag, cfg()->group_)); } return idx; } // TiffBinaryArray::doSize uint32_t TiffBinaryElement::doSize() const { if (!pValue()) return 0; return pValue()->size(); } // TiffBinaryElement::doSize uint32_t TiffComponent::sizeData() const { return doSizeData(); } // TiffComponent::sizeData uint32_t TiffDirectory::doSizeData() const { assert(false); return 0; } // TiffDirectory::doSizeData uint32_t TiffEntryBase::doSizeData() const { return 0; } // TiffEntryBase::doSizeData uint32_t TiffImageEntry::doSizeData() const { uint32_t len = 0; // For makernotes, TIFF image data is written to the data area if (group() > mnId) { // Todo: Fix this hack!! len = sizeImage(); } return len; } // TiffImageEntry::doSizeData uint32_t TiffDataEntry::doSizeData() const { if (!pValue()) return 0; return pValue()->sizeDataArea(); } // TiffDataEntry::doSizeData uint32_t TiffSubIfd::doSizeData() const { uint32_t len = 0; for (Ifds::const_iterator i = ifds_.begin(); i != ifds_.end(); ++i) { len += (*i)->size(); } return len; } // TiffSubIfd::doSizeData uint32_t TiffIfdMakernote::doSizeData() const { assert(false); return 0; } // TiffIfdMakernote::doSizeData uint32_t TiffComponent::sizeImage() const { return doSizeImage(); } // TiffComponent::sizeImage uint32_t TiffDirectory::doSizeImage() const { uint32_t len = 0; for (Components::const_iterator i = components_.begin(); i != components_.end(); ++i) { len += (*i)->sizeImage(); } if (pNext_) { len += pNext_->sizeImage(); } return len; } // TiffDirectory::doSizeImage uint32_t TiffSubIfd::doSizeImage() const { uint32_t len = 0; for (Ifds::const_iterator i = ifds_.begin(); i != ifds_.end(); ++i) { len += (*i)->sizeImage(); } return len; } // TiffSubIfd::doSizeImage uint32_t TiffIfdMakernote::doSizeImage() const { return ifd_.sizeImage(); } // TiffIfdMakernote::doSizeImage uint32_t TiffEntryBase::doSizeImage() const { return 0; } // TiffEntryBase::doSizeImage uint32_t TiffImageEntry::doSizeImage() const { if (!pValue()) return 0; uint32_t len = pValue()->sizeDataArea(); if (len == 0) { for (Strips::const_iterator i = strips_.begin(); i != strips_.end(); ++i) { len += i->second; } } return len; } // TiffImageEntry::doSizeImage static const TagInfo* findTagInfo(uint16_t tag,IfdId group) { const TagInfo* result = NULL ; const TagInfo* tags = group == exifId ? Internal::exifTagList() : group == gpsId ? Internal::gpsTagList() : NULL ; if ( tags ) { for ( size_t idx = 0; result==NULL && tags[idx].tag_ != 0xffff; ++idx) { if ( tags[idx].tag_ == tag ) { result = tags+idx; } } } return result; } // ************************************************************************* // free functions TypeId toTypeId(TiffType tiffType, uint16_t tag, IfdId group) { TypeId ti = TypeId(tiffType); // On the fly type conversion for Exif.Photo.UserComment, Exif.GPSProcessingMethod, GPSAreaInformation if ( const TagInfo* pTag = ti == undefined ? findTagInfo(tag,group) : NULL ) { if ( pTag->typeId_ == comment ) { ti = comment; } } // http://dev.exiv2.org/boards/3/topics/1337 change unsignedByte to signedByte // Exif.NikonAFT.AFFineTuneAdj || Exif.Pentax.Temperature if ( ti == Exiv2::unsignedByte ) { if ( (tag == 0x0002 && group == nikonAFTId ) || (tag == 0x0047 && group == pentaxId) ) { ti = Exiv2::signedByte; } } return ti; } TiffType toTiffType(TypeId typeId) { if (static_cast(typeId) > 0xffff) { #ifndef SUPPRESS_WARNINGS EXV_ERROR << "'" << TypeInfo::typeName(typeId) << "' is not a valid Exif (TIFF) type; using type '" << TypeInfo::typeName(undefined) << "'.\n"; #endif return undefined; } return static_cast(typeId); } bool cmpTagLt(TiffComponent const* lhs, TiffComponent const* rhs) { assert(lhs != 0); assert(rhs != 0); if (lhs->tag() != rhs->tag()) return lhs->tag() < rhs->tag(); return lhs->idx() < rhs->idx(); } bool cmpGroupLt(TiffComponent const* lhs, TiffComponent const* rhs) { assert(lhs != 0); assert(rhs != 0); return lhs->group() < rhs->group(); } TiffComponent::AutoPtr newTiffEntry(uint16_t tag, IfdId group) { return TiffComponent::AutoPtr(new TiffEntry(tag, group)); } TiffComponent::AutoPtr newTiffMnEntry(uint16_t tag, IfdId group) { return TiffComponent::AutoPtr(new TiffMnEntry(tag, group, mnId)); } TiffComponent::AutoPtr newTiffBinaryElement(uint16_t tag, IfdId group) { return TiffComponent::AutoPtr(new TiffBinaryElement(tag, group)); } }} // namespace Internal, Exiv2 // ***************************************************************************** // local definitions namespace { uint32_t fillGap(Exiv2::Internal::IoWrapper& ioWrapper, uint32_t curr, uint32_t tobe) { if (curr < tobe) { Exiv2::DataBuf buf(tobe - curr); memset(buf.pData_, 0x0, buf.size_); ioWrapper.write(buf.pData_, buf.size_); return tobe - curr; } return 0; } // fillGap }