// ***************************************************************** -*- C++ -*-
/*
 * Copyright (C) 2004-2008 Andreas Huggel <ahuggel@gmx.net>
 *
 * This program is part of the Exiv2 distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
 */
/*
  File:      tiffcomposite.cpp
  Version:   $Rev: 1689 $
  Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
  History:   11-Apr-06, ahu: created
 */
// *****************************************************************************
#include "rcsid.hpp"
EXIV2_RCSID("@(#) $Id: tiffcomposite.cpp 1689 2008-12-02 15:04:34Z ahuggel $")

// *****************************************************************************
// included header files
#ifdef _MSC_VER
# include "exv_msvc.h"
#else
# include "exv_conf.h"
#endif

#include "tiffcomposite_int.hpp"
#include "tiffvisitor_int.hpp"
#include "makernote_int.hpp"
#include "value.hpp"
#include "error.hpp"

// + standard includes
#include <string>
#include <iostream>
#include <iomanip>
#include <algorithm>

// *****************************************************************************
// class member definitions
namespace Exiv2 {
    namespace Internal {

    //! Structure for group and group name info
    struct TiffGroupInfo {
        //! Comparison operator for group (id)
        bool operator==(const uint16_t& group) const;
        //! Comparison operator for group name
        bool operator==(const std::string& groupName) const;

        uint16_t group_;   //!< group
        const char* name_; //!< group name
    };

    // Todo: This mapping table probably belongs somewhere else - move it
    // Note: Names must be unique!
    //! List of groups and their names.
    extern const TiffGroupInfo tiffGroupInfo[] = {
        {   1, "Image"        },
        {   2, "Thumbnail"    },
        {   3, "Image2"       },
        {   4, "Photo"        },
        {   5, "GPSInfo"      },
        {   6, "Iop"          },
        {   7, "SubImage1"    },
        {   8, "SubImage2"    },
        {   9, "SubImage3"    },
        {  10, "SubImage4"    },
        { 256, "MakerNote"    },
        // 257 not needed (olympmn)
        { 258, "Fujifilm"     },
        { 259, "Canon"        },
        { 260, "CanonCs"      },
        { 261, "CanonSi"      },
        { 262, "CanonCf"      },
        // 263 not needed (nikonmn)
        { 264, "Nikon1"       },
        { 265, "Nikon2"       },
        { 266, "Nikon3"       },
        { 267, "Panasonic"    },
        { 268, "Sigma"        },
        // 269 not needed (sonymn)
        { 270, "Sony"         },
        { 271, "Sony"         }, // Todo: Need second Sony group!
        { 272, "Minolta"      },
        { 273, "MinoltaCsOld" },
        { 274, "MinoltaCsNew" },
        { 275, "MinoltaCs5D"  },
        { 276, "MinoltaCs7D"  },
        { 277, "CanonPi"      },
        { 278, "CanonPa"      },
        { 279, "Pentax"       },
        { 280, "NikonPreview" },
        { 281, "Olympus"      },
        { 282, "Olympus2"     },
        { 283, "OlympusCs"    },
        { 284, "OlympusEq"    },
        { 285, "OlympusRd"    },
        { 286, "OlympusRd2"   },
        { 287, "OlympusIp"    },
        { 288, "OlympusFi"    },
        { 289, "OlympusFe1"   },
        { 290, "OlympusFe2"   },
        { 291, "OlympusFe3"   },
        { 292, "OlympusFe4"   },
        { 293, "OlympusFe5"   },
        { 294, "OlympusFe6"   },
        { 295, "OlympusFe7"   },
        { 296, "OlympusFe8"   },
        { 297, "OlympusFe9"   },
        { 298, "OlympusRi"    }
    };

    bool TiffGroupInfo::operator==(const uint16_t& group) const
    {
        return group_ == group;
    }

    bool TiffGroupInfo::operator==(const std::string& groupName) const
    {
        std::string name(name_);
        return name == groupName;
    }

    const char* tiffGroupName(uint16_t group)
    {
        const TiffGroupInfo* gi = find(tiffGroupInfo, group);
        if (!gi) return "Unknown";
        return gi->name_;
    }

    uint16_t tiffGroupId(const std::string& groupName)
    {
        const TiffGroupInfo* gi = find(tiffGroupInfo, groupName);
        if (!gi) return 0;
        return gi->group_;
    }

    bool TiffStructure::operator==(const TiffStructure::Key& key) const
    {
        return    (Tag::all == extendedTag_ || key.e_ == extendedTag_)
               && key.g_ == group_;
    }

    bool TiffMappingInfo::operator==(const TiffMappingInfo::Key& key) const
    {
        std::string make(make_);
        return    ("*" == make || make == key.m_.substr(0, make.length()))
               && (Tag::all == extendedTag_ || key.e_ == extendedTag_)
               && key.g_ == group_;
    }

    TiffComponent::TiffComponent(uint16_t tag, uint16_t group)
        : tag_(tag), group_(group), pStart_(0)
    {
    }

    TiffEntryBase::TiffEntryBase(uint16_t tag, uint16_t 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, uint16_t group, uint16_t newGroup)
        : TiffEntryBase(tag, group, ttUnsignedLong), newGroup_(newGroup)
    {
    }

    TiffMnEntry::TiffMnEntry(uint16_t tag, uint16_t group, uint16_t mnGroup)
        : TiffEntryBase(tag, group, ttUndefined), mnGroup_(mnGroup), mn_(0)
    {
    }

    TiffArrayEntry::TiffArrayEntry(uint16_t tag,
                                   uint16_t group,
                                   uint16_t elGroup,
                                   TiffType elTiffType,
                                   bool     addSizeElement)
        : TiffEntryBase(tag, group, elTiffType),
          elSize_(static_cast<uint16_t>(TypeInfo::typeSize(toTypeId(elTiffType, 0, elGroup)))),
          elGroup_(elGroup),
          addSizeElement_(addSizeElement)
    {
    }

    TiffDirectory::~TiffDirectory()
    {
        for (Components::iterator i = components_.begin(); i != components_.end(); ++i) {
            delete *i;
        }
        delete pNext_;
    } // TiffDirectory::~TiffDirectory

    TiffSubIfd::~TiffSubIfd()
    {
        for (Ifds::iterator i = ifds_.begin(); i != ifds_.end(); ++i) {
            delete *i;
        }
    } // TiffSubIfd::~TiffSubIfd

    TiffEntryBase::~TiffEntryBase()
    {
        if (isMalloced_) {
            delete[] pData_;
        }
        delete pValue_;
    } // TiffEntryBase::~TiffEntryBase

    TiffMnEntry::~TiffMnEntry()
    {
        delete mn_;
    } // TiffMnEntry::~TiffMnEntry

    TiffArrayEntry::~TiffArrayEntry()
    {
        for (Components::iterator i = elements_.begin(); i != elements_.end(); ++i) {
            delete *i;
        }
    } // TiffArrayEntry::~TiffArrayEntry

    void TiffEntryBase::allocData(uint32_t len)
    {
        if (isMalloced_) {
            delete[] pData_;
        }
        pData_ = new byte[len];
        size_ = len;
        isMalloced_ = true;
    } // TiffEntryBase::allocData

    void TiffEntryBase::setData(byte* pData, int32_t size)
    {
        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_) {
            allocData(newSize);
        }
        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)
    {
        assert(pSize);
        assert(pValue());

        if (pValue()->count() == 0) {
#ifndef SUPPRESS_WARNINGS
            std::cerr << "Warning: "
                      << "Directory " << tiffGroupName(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
            std::cerr << "Warning: "
                      << "Directory " << tiffGroupName(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<uint32_t>(pSize->toLong(i));
        }
        uint32_t offset = static_cast<uint32_t>(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<uint32_t>(pValue()->toLong(pValue()->count()-1))
            + static_cast<uint32_t>(pSize->toLong(pSize->count()-1))
            - offset != size) {
#ifndef SUPPRESS_WARNINGS
            std::cerr << "Warning: "
                      << "Directory " << tiffGroupName(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
            std::cerr << "Warning: "
                      << "Directory " << tiffGroupName(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<byte*>(pData) + baseOffset + offset;
        sizeDataArea_ = size;
        const_cast<Value*>(pValue())->setDataArea(pDataArea_, sizeDataArea_);
    } // TiffDataEntry::setStrips

    void TiffImageEntry::setStrips(const Value* pSize,
                                   const byte*  pData,
                                   uint32_t     sizeData,
                                   uint32_t     baseOffset)
    {
        assert(pSize);
        assert(pValue());

        if (pValue()->count() != pSize->count()) {
#ifndef SUPPRESS_WARNINGS
            std::cerr << "Warning: "
                      << "Directory " << tiffGroupName(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<uint32_t>(pValue()->toLong(i));
            const byte* pStrip = pData + baseOffset + offset;
            const uint32_t size = static_cast<uint32_t>(pSize->toLong(i));

            if (   offset > sizeData
                || size > sizeData
                || baseOffset + offset > sizeData - size) {
#ifndef SUPPRESS_WARNINGS
                std::cerr << "Warning: "
                          << "Directory " << tiffGroupName(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

    TiffComponent* TiffComponent::addPath(uint16_t tag, TiffPath& tiffPath)
    {
        return doAddPath(tag, tiffPath);
    } // TiffComponent::addPath

    TiffComponent* TiffDirectory::doAddPath(uint16_t tag, TiffPath& tiffPath)
    {
        tiffPath.pop();
        assert(!tiffPath.empty());
        const TiffStructure* ts = tiffPath.top();
        assert(ts != 0);
        // Prevent dangling subIFD tags: Do not add a subIFD tag if it has no child
        if (tiffPath.size() == 1 && ts->newTiffCompFct_ == newTiffSubIfd) return 0;
        TiffComponent* tc = 0;
        // Allow duplicate entries but not for subIFDs and the MakerNote tag. So we
        // only check if the new component already exists if there is still at least
        // one composite tag on the stack, or it is a subIFD or the MakerNote tag.
        if (   tiffPath.size() > 1
            || ts->newTiffCompFct_ == newTiffSubIfd
            || (ts->extendedTag_ == 0x927c && ts->group_ == Group::exif)) {
            if (ts->extendedTag_ == Tag::next) {
                tc = pNext_;
            }
            else {
                for (Components::iterator i = components_.begin(); i != components_.end(); ++i) {
                    if ((*i)->tag() == ts->tag() && (*i)->group() == ts->group_) {
                        tc = *i;
                        break;
                    }
                }
            }
        }
        if (tc == 0) {
            assert(ts->newTiffCompFct_ != 0);
            uint16_t tg = tiffPath.size() == 1 ? tag : ts->tag();
            TiffComponent::AutoPtr atc(ts->newTiffCompFct_(tg, ts));
            if (ts->extendedTag_ == Tag::next) {
                tc = this->addNext(atc);
            }
            else {
                tc = this->addChild(atc);
            }
        }
        return tc->addPath(tag, tiffPath);
    } // TiffDirectory::doAddPath

    TiffComponent* TiffSubIfd::doAddPath(uint16_t tag, TiffPath& tiffPath)
    {
        const TiffStructure* ts1 = tiffPath.top();
        assert(ts1 != 0);
        tiffPath.pop();
        if (tiffPath.empty()) {
            // If the last element in the path is the sub-IFD tag itself we're done
            return this;
        }
        const TiffStructure* ts2 = tiffPath.top();
        assert(ts2 != 0);
        tiffPath.push(ts1);
        uint16_t dt = ts1->tag();
        TiffComponent* tc = 0;
        for (Ifds::iterator i = ifds_.begin(); i != ifds_.end(); ++i) {
            if ((*i)->group() == ts2->group_) {
                tc = *i;
                break;
            }
        }
        if (tc == 0) {
            TiffComponent::AutoPtr atc(new TiffDirectory(dt, ts2->group_));
            tc = addChild(atc);
            setCount(static_cast<uint32_t>(ifds_.size()));
        }
        return tc->addPath(tag, tiffPath);
    } // TiffSubIfd::doAddPath

    TiffComponent* TiffMnEntry::doAddPath(uint16_t tag, TiffPath& tiffPath)
    {
        const TiffStructure* ts1 = tiffPath.top();
        assert(ts1 != 0);
        tiffPath.pop();
        if (tiffPath.empty()) {
            // If the last element in the path is the makernote tag itself we're done
            return this;
        }
        const TiffStructure* ts2 = tiffPath.top();
        assert(ts2 != 0);
        tiffPath.push(ts1);
        if (mn_ == 0) {
            mnGroup_ = ts2->group_;
            mn_ = TiffMnCreator::create(ts1->tag(), ts1->group_, mnGroup_);
            assert(mn_);
        }
        return mn_->addPath(tag, tiffPath);
    } // TiffMnEntry::doAddPath

    TiffComponent* TiffArrayEntry::doAddPath(uint16_t tag, TiffPath& tiffPath)
    {
        tiffPath.pop();
        assert(!tiffPath.empty());
        const TiffStructure* ts = tiffPath.top();
        assert(ts != 0);
        TiffComponent* tc = 0;
        // 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() == ts->tag() && (*i)->group() == ts->group_) {
                    tc = *i;
                    break;
                }
            }
        }
        if (tc == 0) {
            assert(ts->newTiffCompFct_ != 0);
            uint16_t tg = tiffPath.size() == 1 ? tag : ts->tag();
            TiffComponent::AutoPtr atc(ts->newTiffCompFct_(tg, ts));
            assert(ts->extendedTag_ != Tag::next);
            tc = addChild(atc);
            setCount(static_cast<uint32_t>(elements_.size()));
        }
        return tc->addPath(tag, tiffPath);
    } // TiffArrayEntry::doAddPath

    TiffComponent* TiffComponent::addChild(TiffComponent::AutoPtr tiffComponent)
    {
        return doAddChild(tiffComponent);
    } // TiffComponent::addChild

    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<TiffDirectory*>(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* TiffArrayEntry::doAddChild(TiffComponent::AutoPtr tiffComponent)
    {
        TiffComponent* tc = tiffComponent.release();
        elements_.push_back(tc);
        return tc;
    } // TiffArrayEntry::doAddChild

    TiffComponent* TiffComponent::addNext(TiffComponent::AutoPtr tiffComponent)
    {
        return doAddNext(tiffComponent);
    } // TiffComponent::addNext

    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

    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 TiffArrayEntry::doAccept(TiffVisitor& visitor)
    {
        visitor.visitArrayEntry(this);
        for (Components::const_iterator i = elements_.begin();
             visitor.go(TiffVisitor::geTraverse) && i != elements_.end(); ++i) {
            (*i)->accept(visitor);
        }
    } // TiffArrayEntry::doAccept

    void TiffArrayElement::doAccept(TiffVisitor& visitor)
    {
        visitor.visitArrayElement(this);
    } // TiffArrayElement::doAccept

    void TiffEntryBase::encode(TiffEncoder& encoder, const Exifdatum* datum)
    {
        doEncode(encoder, datum);
    } // TiffComponent::encode

    void TiffArrayElement::doEncode(TiffEncoder& encoder, const Exifdatum* datum)
    {
        encoder.encodeArrayElement(this, datum);
    } // TiffArrayElement::doEncode

    void TiffArrayEntry::doEncode(TiffEncoder& encoder, const Exifdatum* datum)
    {
        encoder.encodeArrayEntry(this, datum);
    } // TiffArrayEntry::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<uint32_t>(components_.size());
    }

    uint32_t TiffEntryBase::doCount() const
    {
        return count_;
    }

    uint32_t TiffMnEntry::doCount() const
    {
        // Count of tag Exif.Photo.MakerNote is the size of the Makernote in bytes
        assert(tiffType() == ttUndefined);
        return size();
    }

    uint32_t TiffArrayEntry::doCount() const
    {
        if (elements_.empty()) return 0;

        uint16_t maxTag = 0;
        for (Components::const_iterator i = elements_.begin(); i != elements_.end(); ++i) {
            uint32_t mt = (*i)->tag();
            if ((*i)->count() > 1) mt += (*i)->count() - 1;
            if (mt > maxTag) maxTag = mt;
        }
        return maxTag + 1;
    }

    uint32_t TiffComponent::write(Blob&     blob,
                                  ByteOrder byteOrder,
                                  int32_t   offset,
                                  uint32_t  valueIdx,
                                  uint32_t  dataIdx,
                                  uint32_t& imageIdx)
    {
        return doWrite(blob, byteOrder, offset, valueIdx, dataIdx, imageIdx);
    } // TiffComponent::write

    uint32_t TiffDirectory::doWrite(Blob&     blob,
                                    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(49, tiffGroupName(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;

        // 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() < Group::mn) {
            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<uint16_t>(compCount), byteOrder);
        append(blob, 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(blob, 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);
            }
            append(blob, 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(blob, byteOrder, offset, valueIdx, dataIdx, imageIdx);
                assert(sv == d);
                if ((sv & 1) == 1) {
                    blob.push_back(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(blob, byteOrder, offset, dataIdx, imageIdx);

        // 4th: Write next-IFD
        if (pNext_ && sizeNext) {
            idx += pNext_->write(blob, byteOrder, offset + idx, uint32_t(-1), uint32_t(-1), imageIdx);
        }

        // 5th, at the root directory level only: write image data
        if (isRootDir) {
            idx += writeImage(blob, byteOrder);
        }

        return idx;
    } // TiffDirectory::doWrite

    uint32_t TiffDirectory::writeDirEntry(Blob&          blob,
                                          ByteOrder      byteOrder,
                                          int32_t        offset,
                                          TiffComponent* pTiffComponent,
                                          uint32_t       valueIdx,
                                          uint32_t       dataIdx,
                                          uint32_t&      imageIdx) const
    {
        assert(pTiffComponent);
        TiffEntryBase* pDirEntry = dynamic_cast<TiffEntryBase*>(pTiffComponent);
        assert(pDirEntry);
        byte buf[8];
        us2Data(buf,     pDirEntry->tag(),      byteOrder);
        us2Data(buf + 2, pDirEntry->tiffType(), byteOrder);
        ul2Data(buf + 4, pDirEntry->count(),    byteOrder);
        append(blob, buf, 8);
        if (pDirEntry->size() > 4) {
            pDirEntry->setOffset(offset + static_cast<int32_t>(valueIdx));
            l2Data(buf, pDirEntry->offset(), byteOrder);
            append(blob, buf, 4);
        }
        else {
            const uint32_t len = pDirEntry->write(blob,
                                                  byteOrder,
                                                  offset,
                                                  valueIdx,
                                                  dataIdx,
                                                  imageIdx);
            assert(len <= 4);
            if (len < 4) {
                memset(buf, 0x0, 4);
                append(blob, buf, 4 - len);
            }
        }
        return 12;
    } // TiffDirectory::writeDirEntry

    uint32_t TiffEntryBase::doWrite(Blob&     blob,
                                    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);
        append(blob, 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<uint32_t>(offset) > 0xffff) throw Error(26);
            rc = s2Data(buf, static_cast<int16_t>(offset), byteOrder);
            break;
        case ttUnsignedLong:
        case ttSignedLong:
            rc = l2Data(buf, static_cast<int32_t>(offset), byteOrder);
            break;
        default:
            throw Error(27);
            break;
        }
        return rc;
    } // TiffEntryBase::writeOffset

    uint32_t TiffDataEntry::doWrite(Blob&     blob,
                                    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<long>(dataIdx);
            idx += writeOffset(buf.pData_ + idx,
                               offset + newDataIdx,
                               tiffType(),
                               byteOrder);
        }
        append(blob, buf.pData_, buf.size_);
        return buf.size_;
    } // TiffDataEntry::doWrite

    uint32_t TiffImageEntry::doWrite(Blob&     blob,
                                     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() > Group::mn) o2 = offset + dataIdx;
#ifdef DEBUG
        std::cerr << "TiffImageEntry, Directory " << tiffGroupName(group())
                  << ", entry 0x" << std::setw(4)
                  << std::setfill('0') << std::hex << tag() << std::dec
                  << ": Writing offset " << o2 << "\n";
#endif
        DataBuf buf(static_cast<long>(strips_.size()) * 4);
        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() > Group::mn)) {
                imageIdx += i->second;
                imageIdx += i->second & 1;      // Align strip data to word boundary
            }
        }
        append(blob, buf.pData_, buf.size_);
        return buf.size_;
    } // TiffImageEntry::doWrite

    uint32_t TiffSubIfd::doWrite(Blob&     blob,
                                 ByteOrder byteOrder,
                                 int32_t   offset,
                                 uint32_t  /*valueIdx*/,
                                 uint32_t  dataIdx,
                                 uint32_t& /*imageIdx*/)
    {
        DataBuf buf(static_cast<long>(ifds_.size()) * 4);
        uint32_t idx = 0;
        for (Ifds::const_iterator i = ifds_.begin(); i != ifds_.end(); ++i) {
            idx += writeOffset(buf.pData_ + idx, offset + dataIdx, tiffType(), byteOrder);
            dataIdx += (*i)->size();
        }
        append(blob, buf.pData_, buf.size_);
        return buf.size_;
    } // TiffSubIfd::doWrite

    uint32_t TiffMnEntry::doWrite(Blob&     blob,
                                  ByteOrder byteOrder,
                                  int32_t   offset,
                                  uint32_t  valueIdx,
                                  uint32_t  dataIdx,
                                  uint32_t& imageIdx)
    {
        if (!mn_) {
            return TiffEntryBase::doWrite(blob, byteOrder, offset, valueIdx, dataIdx, imageIdx);
        }
        return mn_->write(blob, byteOrder, offset + valueIdx, uint32_t(-1), uint32_t(-1), imageIdx);
    } // TiffMnEntry::doWrite

    uint32_t TiffArrayEntry::doWrite(Blob&     blob,
                                     ByteOrder byteOrder,
                                     int32_t   offset,
                                     uint32_t  valueIdx,
                                     uint32_t  dataIdx,
                                     uint32_t& imageIdx)
    {
        const uint32_t cnt = count();
        if (cnt == 0) return 0;

        uint32_t idx = 0;
        int32_t nextTag = 0;

        // Some array entries need to have the size in the first element
        if (addSizeElement_) {
            byte buf[4];
            switch (elSize_) {
            case 2:
                idx += us2Data(buf, size(), byteOrder);
                break;
            case 4:
                idx += ul2Data(buf, size(), byteOrder);
                break;
            default:
                assert(false);
            }
            append(blob, buf, elSize_);
            nextTag = 1;
        }

        // Tags must be sorted in ascending order
        std::sort(elements_.begin(), elements_.end(), cmpTagLt);

        uint32_t seq = 0;
        for (Components::const_iterator i = elements_.begin(); i != elements_.end(); ++i) {
            // Skip deleted entries at the end of the array
            if (seq++ > cnt) break;
            // Skip the manufactured tag, if it exists
            if (addSizeElement_ && (*i)->tag() == 0x0000) continue;
            // Fill gaps. Repeated tags will cause an exception
            int32_t gap = ((*i)->tag() - nextTag) * elSize_;
            if (gap < 0) throw Error(50, (*i)->tag());
            if (gap > 0) {
                blob.insert(blob.end(), gap, 0);
                idx += gap;
            }
            idx += (*i)->write(blob, byteOrder, offset + idx, valueIdx, dataIdx, imageIdx);
            nextTag = (*i)->tag() + 1;
            if ((*i)->count() > 1) nextTag += (*i)->count() - 1;
        }
        return idx;
    } // TiffArrayEntry::doWrite

    uint32_t TiffArrayElement::doWrite(Blob&     blob,
                                       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;
        if (toTiffType(pv->typeId()) != elTiffType_) {
            throw Error(51, tag());
        }
        DataBuf buf(pv->size());
        if (elByteOrder_ != invalidByteOrder) byteOrder = elByteOrder_;
        pv->copy(buf.pData_, byteOrder);
        append(blob, buf.pData_, buf.size_);
        return buf.size_;
    } // TiffArrayElement::doWrite

    uint32_t TiffComponent::writeData(Blob&     blob,
                                      ByteOrder byteOrder,
                                      int32_t   offset,
                                      uint32_t  dataIdx,
                                      uint32_t& imageIdx) const
    {
        return doWriteData(blob, byteOrder, offset, dataIdx, imageIdx);
    } // TiffComponent::writeData

    uint32_t TiffDirectory::doWriteData(Blob&     blob,
                                        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(blob, byteOrder, offset, dataIdx + len, imageIdx);
        }
        return len;
    } // TiffDirectory::doWriteData

    uint32_t TiffEntryBase::doWriteData(Blob&     /*blob*/,
                                        ByteOrder /*byteOrder*/,
                                        int32_t   /*offset*/,
                                        uint32_t  /*dataIdx*/,
                                        uint32_t& /*imageIdx*/) const
    {
        return 0;
    } // TiffEntryBase::doWriteData

    uint32_t TiffImageEntry::doWriteData(Blob&     blob,
                                         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() > Group::mn) {
            len = writeImage(blob, byteOrder);
        }
        return len;
    } // TiffImageEntry::doWriteData

    uint32_t TiffDataEntry::doWriteData(Blob&     blob,
                                        ByteOrder /*byteOrder*/,
                                        int32_t   /*offset*/,
                                        uint32_t  /*dataIdx*/,
                                        uint32_t& /*imageIdx*/) const
    {
        if (!pValue()) return 0;

        DataBuf buf = pValue()->dataArea();
        append(blob, buf.pData_, buf.size_);
        // Align data to word boundary
        uint32_t align = (buf.size_ & 1);
        if (align) blob.push_back(0x0);

        return buf.size_ + align;
    } // TiffDataEntry::doWriteData

    uint32_t TiffSubIfd::doWriteData(Blob&     blob,
                                     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(blob, byteOrder, offset + dataIdx + len, uint32_t(-1), uint32_t(-1), imageIdx);
        }
        // Align data to word boundary
        uint32_t align = (len & 1);
        if (align) blob.push_back(0x0);

        return len + align;
    } // TiffSubIfd::doWriteData

    uint32_t TiffComponent::writeImage(Blob&     blob,
                                       ByteOrder byteOrder) const
    {
        return doWriteImage(blob, byteOrder);
    } // TiffComponent::writeImage

    uint32_t TiffDirectory::doWriteImage(Blob&     blob,
                                         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(blob, byteOrder);
        }
        if (pSubIfd) {
            len += pSubIfd->writeImage(blob, byteOrder);
        }
        if (pNext_) {
            len += pNext_->writeImage(blob, byteOrder);
        }
        return len;
    } // TiffDirectory::doWriteImage

    uint32_t TiffEntryBase::doWriteImage(Blob&     /*blob*/,
                                         ByteOrder /*byteOrder*/) const
    {
        return 0;
    } // TiffEntryBase::doWriteImage

    uint32_t TiffSubIfd::doWriteImage(Blob&     blob,
                                      ByteOrder byteOrder) const
    {
        uint32_t len = 0;
        for (Ifds::const_iterator i = ifds_.begin(); i != ifds_.end(); ++i) {
            len  += (*i)->writeImage(blob, byteOrder);
        }
        return len;
    } // TiffSubIfd::doWriteImage

    uint32_t TiffImageEntry::doWriteImage(Blob&     blob,
                                          ByteOrder /*byteOrder*/) const
    {
        // Adjust blob capacity - speeds up copying of large data areas
        uint32_t sz = sizeImage();
        Blob::size_type size = blob.size();
        if (blob.capacity() - size < sz) {
            blob.reserve(size + sz + 65536);
        }

        uint32_t len = pValue()->sizeDataArea();
        if (len > 0) {
#ifdef DEBUG
            std::cerr << "TiffImageEntry, Directory " << tiffGroupName(group())
                      << ", entry 0x" << std::setw(4)
                      << std::setfill('0') << std::hex << tag() << std::dec
                      << ": Writing data area, blob-size = " << blob.size();
#endif
            DataBuf buf = pValue()->dataArea();
            append(blob, buf.pData_, buf.size_);
            uint32_t align = len & 1;       // Align image data to word boundary
            if (align) blob.push_back(0x0);
            len += align;
        }
        else {
#ifdef DEBUG
            std::cerr << "TiffImageEntry, Directory " << tiffGroupName(group())
                      << ", entry 0x" << std::setw(4)
                      << std::setfill('0') << std::hex << tag() << std::dec
                      << ": Writing data area, blob-size = " << blob.size();
#endif
            len = 0;
            for (Strips::const_iterator i = strips_.begin(); i != strips_.end(); ++i) {
                append(blob, i->first, i->second);
                len += i->second;
                uint32_t align = i->second & 1; // Align strip data to word boundary
                if (align) blob.push_back(0x0);
                len += align;
            }
        }
#ifdef DEBUG
        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<uint32_t>(strips_.size()) * 4;
    } // TiffImageEntry::doSize

    uint32_t TiffSubIfd::doSize() const
    {
        return static_cast<uint32_t>(ifds_.size()) * 4;
    } // TiffSubIfd::doSize

    uint32_t TiffMnEntry::doSize() const
    {
        if (!mn_) {
            return TiffEntryBase::doSize();
        }
        return mn_->size();
    } // TiffMnEntry::doSize

    uint32_t TiffArrayEntry::doSize() const
    {
        return count() * elSize_;
    } // TiffArrayEntry::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() > Group::mn) {
            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 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 TiffEntryBase::doSizeImage() const
    {
        return 0;
    } // TiffEntryBase::doSizeImage

    uint32_t TiffImageEntry::doSizeImage() const
    {
        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

    // *************************************************************************
    // free functions

    TypeId toTypeId(TiffType tiffType, uint16_t tag, uint16_t group)
    {
        TypeId ti = TypeId(tiffType);
        // On the fly type conversion for Exif.Photo.UserComment
        if (tag == 0x9286 && group == Group::exif && ti == undefined) {
            ti = comment;
        }
        return ti;
    }

    TiffType toTiffType(TypeId typeId)
    {
        if (static_cast<uint32_t>(typeId) > 0xffff) {
#ifndef SUPPRESS_WARNINGS
            std::cerr << "Error: '" << TypeInfo::typeName(typeId)
                      << "' is not a valid Exif (TIFF) type; using type '"
                      << TypeInfo::typeName(undefined) << "'.\n";
#endif
            return undefined;
        }
        return static_cast<uint16_t>(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();
    }

    TiffComponent::AutoPtr newTiffDirectory(uint16_t tag,
                                            const TiffStructure* ts)
    {
        assert(ts);
        return TiffComponent::AutoPtr(new TiffDirectory(tag, ts->newGroup_));
    }

    TiffComponent::AutoPtr newTiffEntry(uint16_t tag,
                                        const TiffStructure* ts)
    {
        assert(ts);
        return TiffComponent::AutoPtr(new TiffEntry(tag, ts->newGroup_));
    }

    TiffComponent::AutoPtr newTiffSubIfd(uint16_t tag,
                                         const TiffStructure* ts)
    {
        assert(ts);
        return TiffComponent::AutoPtr(new TiffSubIfd(tag,
                                                     ts->group_,
                                                     ts->newGroup_));
    }

    TiffComponent::AutoPtr newTiffMnEntry(uint16_t tag,
                                          const TiffStructure* ts)
    {
        assert(ts);
        return TiffComponent::AutoPtr(new TiffMnEntry(tag,
                                                      ts->group_,
                                                      ts->newGroup_));
    }

}}                                      // namespace Internal, Exiv2
