Blame src/iptc.cpp

Packit 01d647
// ***************************************************************** -*- C++ -*-
Packit 01d647
/*
Packit 01d647
 * Copyright (C) 2004-2018 Exiv2 authors
Packit 01d647
 * This program is part of the Exiv2 distribution.
Packit 01d647
 *
Packit 01d647
 * This program is free software; you can redistribute it and/or
Packit 01d647
 * modify it under the terms of the GNU General Public License
Packit 01d647
 * as published by the Free Software Foundation; either version 2
Packit 01d647
 * of the License, or (at your option) any later version.
Packit 01d647
 *
Packit 01d647
 * This program is distributed in the hope that it will be useful,
Packit 01d647
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 01d647
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 01d647
 * GNU General Public License for more details.
Packit 01d647
 *
Packit 01d647
 * You should have received a copy of the GNU General Public License
Packit 01d647
 * along with this program; if not, write to the Free Software
Packit 01d647
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
Packit 01d647
 */
Packit 01d647
/*
Packit 01d647
  File:      iptc.cpp
Packit 01d647
  Author(s): Brad Schick (brad) <brad@robotbattle.com>
Packit 01d647
  History:   31-July-04, brad: created
Packit 01d647
 */
Packit 01d647
// *****************************************************************************
Packit 01d647
// included header files
Packit 01d647
#include "iptc.hpp"
Packit 01d647
#include "types.hpp"
Packit 01d647
#include "error.hpp"
Packit 01d647
#include "value.hpp"
Packit 01d647
#include "datasets.hpp"
Packit 01d647
#include "jpgimage.hpp"
Packit 01d647
#include "image_int.hpp"
Packit 01d647
Packit 01d647
// + standard includes
Packit 01d647
#include <iostream>
Packit 01d647
#include <algorithm>
Packit 01d647
#include <iterator>
Packit 01d647
#include <fstream>      // write the temporary file
Packit 01d647
Packit 01d647
// *****************************************************************************
Packit 01d647
namespace {
Packit 01d647
    /*!
Packit 01d647
      @brief Read a single dataset payload and create a new metadata entry.
Packit 01d647
Packit 01d647
      @param iptcData IPTC metadata container to add the dataset to
Packit 01d647
      @param dataSet  DataSet number
Packit 01d647
      @param record   Record Id
Packit 01d647
      @param data     Pointer to the first byte of dataset payload
Packit 01d647
      @param sizeData Length in bytes of dataset payload
Packit 01d647
      @return 0 if successful.
Packit 01d647
     */
Packit 01d647
    int readData(
Packit 01d647
              Exiv2::IptcData& iptcData,
Packit 01d647
              uint16_t         dataSet,
Packit 01d647
              uint16_t         record,
Packit 01d647
        const Exiv2::byte*     data,
Packit 01d647
              uint32_t         sizeData
Packit 01d647
    );
Packit 01d647
Packit 01d647
    //! Unary predicate that matches an Iptcdatum with given record and dataset
Packit 01d647
    class FindIptcdatum {
Packit 01d647
    public:
Packit 01d647
        //! Constructor, initializes the object with the record and dataset id
Packit 01d647
        FindIptcdatum(uint16_t dataset, uint16_t record)
Packit 01d647
            : dataset_(dataset), record_(record) {}
Packit 01d647
        /*!
Packit 01d647
          @brief Returns true if the record and dataset id of the argument
Packit 01d647
                Iptcdatum is equal to that of the object.
Packit 01d647
        */
Packit 01d647
        bool operator()(const Exiv2::Iptcdatum& iptcdatum) const
Packit 01d647
        {
Packit 01d647
            return dataset_ == iptcdatum.tag() && record_ == iptcdatum.record();
Packit 01d647
        }
Packit 01d647
Packit 01d647
    private:
Packit 01d647
        // DATA
Packit 01d647
        uint16_t dataset_;
Packit 01d647
        uint16_t record_;
Packit 01d647
Packit 01d647
    }; // class FindIptcdatum
Packit 01d647
}
Packit 01d647
Packit 01d647
// *****************************************************************************
Packit 01d647
// class member definitions
Packit 01d647
namespace Exiv2 {
Packit 01d647
Packit 01d647
    Iptcdatum::Iptcdatum(const IptcKey& key,
Packit 01d647
                         const Value* pValue)
Packit 01d647
        : key_(key.clone())
Packit 01d647
    {
Packit 01d647
        if (pValue) value_ = pValue->clone();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    Iptcdatum::Iptcdatum(const Iptcdatum& rhs)
Packit 01d647
        : Metadatum(rhs)
Packit 01d647
    {
Packit 01d647
        if (rhs.key_.get() != 0) key_ = rhs.key_->clone(); // deep copy
Packit 01d647
        if (rhs.value_.get() != 0) value_ = rhs.value_->clone(); // deep copy
Packit 01d647
    }
Packit 01d647
Packit 01d647
    Iptcdatum::~Iptcdatum()
Packit 01d647
    {
Packit 01d647
    }
Packit 01d647
Packit 01d647
    long Iptcdatum::copy(byte* buf, ByteOrder byteOrder) const
Packit 01d647
    {
Packit 01d647
        return value_.get() == 0 ? 0 : value_->copy(buf, byteOrder);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    std::ostream& Iptcdatum::write(std::ostream& os, const ExifData*) const
Packit 01d647
    {
Packit 01d647
        return os << value();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    std::string Iptcdatum::key() const
Packit 01d647
    {
Packit 01d647
        return key_.get() == 0 ? "" : key_->key();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    std::string Iptcdatum::recordName() const
Packit 01d647
    {
Packit 01d647
        return key_.get() == 0 ? "" : key_->recordName();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    uint16_t Iptcdatum::record() const
Packit 01d647
    {
Packit 01d647
        return key_.get() == 0 ? 0 : key_->record();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    const char* Iptcdatum::familyName() const
Packit 01d647
    {
Packit 01d647
        return key_.get() == 0 ? "" : key_->familyName();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    std::string Iptcdatum::groupName() const
Packit 01d647
    {
Packit 01d647
        return key_.get() == 0 ? "" : key_->groupName();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    std::string Iptcdatum::tagName() const
Packit 01d647
    {
Packit 01d647
        return key_.get() == 0 ? "" : key_->tagName();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    std::string Iptcdatum::tagLabel() const
Packit 01d647
    {
Packit 01d647
        return key_.get() == 0 ? "" : key_->tagLabel();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    uint16_t Iptcdatum::tag() const
Packit 01d647
    {
Packit 01d647
        return key_.get() == 0 ? 0 : key_->tag();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    TypeId Iptcdatum::typeId() const
Packit 01d647
    {
Packit 01d647
        return value_.get() == 0 ? invalidTypeId : value_->typeId();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    const char* Iptcdatum::typeName() const
Packit 01d647
    {
Packit 01d647
        return TypeInfo::typeName(typeId());
Packit 01d647
    }
Packit 01d647
Packit 01d647
    long Iptcdatum::typeSize() const
Packit 01d647
    {
Packit 01d647
        return TypeInfo::typeSize(typeId());
Packit 01d647
    }
Packit 01d647
Packit 01d647
    long Iptcdatum::count() const
Packit 01d647
    {
Packit 01d647
        return value_.get() == 0 ? 0 : value_->count();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    long Iptcdatum::size() const
Packit 01d647
    {
Packit 01d647
        return value_.get() == 0 ? 0 : value_->size();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    std::string Iptcdatum::toString() const
Packit 01d647
    {
Packit 01d647
        return value_.get() == 0 ? "" : value_->toString();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    std::string Iptcdatum::toString(long n) const
Packit 01d647
    {
Packit 01d647
        return value_.get() == 0 ? "" : value_->toString(n);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    long Iptcdatum::toLong(long n) const
Packit 01d647
    {
Packit 01d647
        return value_.get() == 0 ? -1 : value_->toLong(n);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    float Iptcdatum::toFloat(long n) const
Packit 01d647
    {
Packit 01d647
        return value_.get() == 0 ? -1 : value_->toFloat(n);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    Rational Iptcdatum::toRational(long n) const
Packit 01d647
    {
Packit 01d647
        return value_.get() == 0 ? Rational(-1, 1) : value_->toRational(n);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    Value::AutoPtr Iptcdatum::getValue() const
Packit 01d647
    {
Packit 01d647
        return value_.get() == 0 ? Value::AutoPtr(0) : value_->clone();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    const Value& Iptcdatum::value() const
Packit 01d647
    {
Packit 01d647
        if (value_.get() == 0) throw Error(kerValueNotSet);
Packit 01d647
        return *value_;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    Iptcdatum& Iptcdatum::operator=(const Iptcdatum& rhs)
Packit 01d647
    {
Packit 01d647
        if (this == &rhs) return *this;
Packit 01d647
        Metadatum::operator=(rhs);
Packit 01d647
Packit 01d647
        key_.reset();
Packit 01d647
        if (rhs.key_.get() != 0) key_ = rhs.key_->clone(); // deep copy
Packit 01d647
Packit 01d647
        value_.reset();
Packit 01d647
        if (rhs.value_.get() != 0) value_ = rhs.value_->clone(); // deep copy
Packit 01d647
Packit 01d647
        return *this;
Packit 01d647
    } // Iptcdatum::operator=
Packit 01d647
Packit 01d647
    Iptcdatum& Iptcdatum::operator=(const uint16_t& value)
Packit 01d647
    {
Packit 01d647
        UShortValue::AutoPtr v(new UShortValue);
Packit 01d647
        v->value_.push_back(value);
Packit 01d647
        value_ = v;
Packit 01d647
        return *this;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    Iptcdatum& Iptcdatum::operator=(const std::string& value)
Packit 01d647
    {
Packit 01d647
        setValue(value);
Packit 01d647
        return *this;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    Iptcdatum& Iptcdatum::operator=(const Value& value)
Packit 01d647
    {
Packit 01d647
        setValue(&value);
Packit 01d647
        return *this;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Iptcdatum::setValue(const Value* pValue)
Packit 01d647
    {
Packit 01d647
        value_.reset();
Packit 01d647
        if (pValue) value_ = pValue->clone();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    int Iptcdatum::setValue(const std::string& value)
Packit 01d647
    {
Packit 01d647
        if (value_.get() == 0) {
Packit 01d647
            TypeId type = IptcDataSets::dataSetType(tag(), record());
Packit 01d647
            value_ = Value::create(type);
Packit 01d647
        }
Packit 01d647
        return value_->read(value);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    Iptcdatum& IptcData::operator[](const std::string& key)
Packit 01d647
    {
Packit 01d647
        IptcKey iptcKey(key);
Packit 01d647
        iterator pos = findKey(iptcKey);
Packit 01d647
        if (pos == end()) {
Packit 01d647
            add(Iptcdatum(iptcKey));
Packit 01d647
            pos = findKey(iptcKey);
Packit 01d647
        }
Packit 01d647
        return *pos;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    long IptcData::size() const
Packit 01d647
    {
Packit 01d647
        long newSize = 0;
Packit 01d647
        const_iterator iter = iptcMetadata_.begin();
Packit 01d647
        const_iterator end = iptcMetadata_.end();
Packit 01d647
        for ( ; iter != end; ++iter) {
Packit 01d647
            // marker, record Id, dataset num, first 2 bytes of size
Packit 01d647
            newSize += 5;
Packit 01d647
            long dataSize = iter->size();
Packit 01d647
            newSize += dataSize;
Packit 01d647
            if (dataSize > 32767) {
Packit 01d647
                // extended dataset (we always use 4 bytes)
Packit 01d647
                newSize += 4;
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        return newSize;
Packit 01d647
    } // IptcData::size
Packit 01d647
Packit 01d647
    int IptcData::add(const IptcKey& key, Value* value)
Packit 01d647
    {
Packit 01d647
        return add(Iptcdatum(key, value));
Packit 01d647
    }
Packit 01d647
Packit 01d647
    int IptcData::add(const Iptcdatum& iptcDatum)
Packit 01d647
    {
Packit 01d647
        if (!IptcDataSets::dataSetRepeatable(
Packit 01d647
               iptcDatum.tag(), iptcDatum.record()) &&
Packit 01d647
               findId(iptcDatum.tag(), iptcDatum.record()) != end()) {
Packit 01d647
             return 6;
Packit 01d647
        }
Packit 01d647
        // allow duplicates
Packit 01d647
        iptcMetadata_.push_back(iptcDatum);
Packit 01d647
        return 0;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    IptcData::const_iterator IptcData::findKey(const IptcKey& key) const
Packit 01d647
    {
Packit 01d647
        return std::find_if(iptcMetadata_.begin(), iptcMetadata_.end(),
Packit 01d647
                            FindIptcdatum(key.tag(), key.record()));
Packit 01d647
    }
Packit 01d647
Packit 01d647
    IptcData::iterator IptcData::findKey(const IptcKey& key)
Packit 01d647
    {
Packit 01d647
        return std::find_if(iptcMetadata_.begin(), iptcMetadata_.end(),
Packit 01d647
                            FindIptcdatum(key.tag(), key.record()));
Packit 01d647
    }
Packit 01d647
Packit 01d647
    IptcData::const_iterator IptcData::findId(uint16_t dataset, uint16_t record) const
Packit 01d647
    {
Packit 01d647
        return std::find_if(iptcMetadata_.begin(), iptcMetadata_.end(),
Packit 01d647
                            FindIptcdatum(dataset, record));
Packit 01d647
    }
Packit 01d647
Packit 01d647
    IptcData::iterator IptcData::findId(uint16_t dataset, uint16_t record)
Packit 01d647
    {
Packit 01d647
        return std::find_if(iptcMetadata_.begin(), iptcMetadata_.end(),
Packit 01d647
                            FindIptcdatum(dataset, record));
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void IptcData::sortByKey()
Packit 01d647
    {
Packit 01d647
        std::sort(iptcMetadata_.begin(), iptcMetadata_.end(), cmpMetadataByKey);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void IptcData::sortByTag()
Packit 01d647
    {
Packit 01d647
        std::sort(iptcMetadata_.begin(), iptcMetadata_.end(), cmpMetadataByTag);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    IptcData::iterator IptcData::erase(IptcData::iterator pos)
Packit 01d647
    {
Packit 01d647
        return iptcMetadata_.erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void IptcData::printStructure(std::ostream& out, const Slice<byte*>& bytes, uint32_t depth)
Packit 01d647
    {
Packit 01d647
        uint32_t i = 0;
Packit 01d647
        while (i < bytes.size() - 3 && bytes.at(i) != 0x1c)
Packit 01d647
            i++;
Packit 01d647
        depth++;
Packit 01d647
        out << Internal::indent(depth) << "Record | DataSet | Name                     | Length | Data" << std::endl;
Packit 01d647
        while (i < bytes.size() - 3) {
Packit 01d647
            if (bytes.at(i) != 0x1c) {
Packit 01d647
                break;
Packit 01d647
            }
Packit 01d647
            char buff[100];
Packit 01d647
            uint16_t record = bytes.at(i + 1);
Packit 01d647
            uint16_t dataset = bytes.at(i + 2);
Packit 01d647
            uint16_t len = getUShort(bytes.subSlice(i + 3, bytes.size()), bigEndian);
Packit 01d647
            sprintf(buff, "  %6d | %7d | %-24s | %6d | ", record, dataset,
Packit 01d647
                    Exiv2::IptcDataSets::dataSetName(dataset, record).c_str(), len);
Packit 01d647
Packit 01d647
            out << buff << Internal::binaryToString(makeSlice(bytes, i + 5, i + 5 + (len > 40 ? 40 : len)))
Packit 01d647
                << (len > 40 ? "..." : "")
Packit 01d647
                << std::endl;
Packit 01d647
            i += 5 + len;
Packit 01d647
        }
Packit 01d647
        depth--;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    const char *IptcData::detectCharset() const
Packit 01d647
    {
Packit 01d647
        const_iterator pos = findKey(IptcKey("Iptc.Envelope.CharacterSet"));
Packit 01d647
        if (pos != end()) {
Packit 01d647
            const std::string value = pos->toString();
Packit 01d647
            if (pos->value().ok()) {
Packit 01d647
                if (value == "\033%G") return "UTF-8";
Packit 01d647
                // other values are probably not practically relevant
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
Packit 01d647
        bool ascii = true;
Packit 01d647
        bool utf8 = true;
Packit 01d647
Packit 01d647
        for (pos = begin(); pos != end(); ++pos) {
Packit 01d647
            std::string value = pos->toString();
Packit 01d647
            if (pos->value().ok()) {
Packit 01d647
                int seqCount = 0;
Packit 01d647
                std::string::iterator i;
Packit 01d647
                for (i = value.begin(); i != value.end(); ++i) {
Packit 01d647
                    char c = *i;
Packit 01d647
                    if (seqCount) {
Packit 01d647
                        if ((c & 0xc0) != 0x80) {
Packit 01d647
                            utf8 = false;
Packit 01d647
                            break;
Packit 01d647
                        }
Packit 01d647
                        --seqCount;
Packit 01d647
                    }
Packit 01d647
                    else {
Packit 01d647
                        if (c & 0x80) ascii = false;
Packit 01d647
                        else continue; // ascii character
Packit 01d647
Packit 01d647
                        if      ((c & 0xe0) == 0xc0) seqCount = 1;
Packit 01d647
                        else if ((c & 0xf0) == 0xe0) seqCount = 2;
Packit 01d647
                        else if ((c & 0xf8) == 0xf0) seqCount = 3;
Packit 01d647
                        else if ((c & 0xfc) == 0xf8) seqCount = 4;
Packit 01d647
                        else if ((c & 0xfe) == 0xfc) seqCount = 5;
Packit 01d647
                        else {
Packit 01d647
                            utf8 = false;
Packit 01d647
                            break;
Packit 01d647
                        }
Packit 01d647
                    }
Packit 01d647
                }
Packit 01d647
                if (seqCount) utf8 = false; // unterminated seq
Packit 01d647
                if (!utf8) break;
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
Packit 01d647
        if (ascii) return "ASCII";
Packit 01d647
        if (utf8) return "UTF-8";
Packit 01d647
        return NULL;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    const byte IptcParser::marker_ = 0x1C;          // Dataset marker
Packit 01d647
Packit 01d647
    int IptcParser::decode(
Packit 01d647
              IptcData& iptcData,
Packit 01d647
        const byte*     pData,
Packit 01d647
              uint32_t  size
Packit 01d647
    )
Packit 01d647
    {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
        std::cerr << "IptcParser::decode, size = " << size << "\n";
Packit 01d647
#endif
Packit 01d647
        const byte* pRead = pData;
Packit 01d647
        const byte* const pEnd = pData + size;
Packit 01d647
        iptcData.clear();
Packit 01d647
Packit 01d647
        uint16_t record = 0;
Packit 01d647
        uint16_t dataSet = 0;
Packit 01d647
        uint32_t sizeData = 0;
Packit 01d647
        byte extTest = 0;
Packit 01d647
Packit 01d647
        while (6 <= static_cast<size_t>(pEnd - pRead)) {
Packit 01d647
            // First byte should be a marker. If it isn't, scan forward and skip
Packit 01d647
            // the chunk bytes present in some images. This deviates from the
Packit 01d647
            // standard, which advises to treat such cases as errors.
Packit 01d647
            if (*pRead++ != marker_) continue;
Packit 01d647
            record = *pRead++;
Packit 01d647
            dataSet = *pRead++;
Packit 01d647
Packit 01d647
            extTest = *pRead;
Packit 01d647
            if (extTest & 0x80) {
Packit 01d647
                // extended dataset
Packit 01d647
                uint16_t sizeOfSize = (getUShort(pRead, bigEndian) & 0x7FFF);
Packit 01d647
                if (sizeOfSize > 4) return 5;
Packit 01d647
                pRead += 2;
Packit 01d647
                if (sizeOfSize > static_cast<size_t>(pEnd - pRead)) return 6;
Packit 01d647
                sizeData = 0;
Packit 01d647
                for (; sizeOfSize > 0; --sizeOfSize) {
Packit 01d647
                    sizeData |= *pRead++ << (8 *(sizeOfSize-1));
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
            else {
Packit 01d647
                // standard dataset
Packit 01d647
                sizeData = getUShort(pRead, bigEndian);
Packit 01d647
                pRead += 2;
Packit 01d647
            }
Packit 01d647
            if (sizeData <= static_cast<size_t>(pEnd - pRead)) {
Packit 01d647
                int rc = 0;
Packit 01d647
                if ((rc = readData(iptcData, dataSet, record, pRead, sizeData)) != 0) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_WARNING << "Failed to read IPTC dataset "
Packit 01d647
                                << IptcKey(dataSet, record)
Packit 01d647
                                << " (rc = " << rc << "); skipped.\n";
Packit 01d647
#endif
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            else {
Packit 01d647
                EXV_WARNING << "IPTC dataset " << IptcKey(dataSet, record)
Packit 01d647
                            << " has invalid size " << sizeData << "; skipped.\n";
Packit 01d647
                return 7;
Packit 01d647
            }
Packit 01d647
#endif
Packit 01d647
            pRead += sizeData;
Packit 01d647
        }
Packit 01d647
Packit 01d647
        return 0;
Packit 01d647
    } // IptcParser::decode
Packit 01d647
Packit 01d647
    /*!
Packit 01d647
      @brief Compare two iptc items by record. Return true if the record of
Packit 01d647
             lhs is less than that of rhs.
Packit 01d647
Packit 01d647
      This is a helper function for IptcParser::encode().
Packit 01d647
     */
Packit 01d647
    bool cmpIptcdataByRecord(const Iptcdatum& lhs, const Iptcdatum& rhs)
Packit 01d647
    {
Packit 01d647
        return lhs.record() < rhs.record();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    DataBuf IptcParser::encode(const IptcData& iptcData)
Packit 01d647
    {
Packit 01d647
        DataBuf buf(iptcData.size());
Packit 01d647
        byte *pWrite = buf.pData_;
Packit 01d647
Packit 01d647
        // Copy the iptc data sets and sort them by record but preserve the order of datasets
Packit 01d647
        IptcMetadata sortedIptcData;
Packit 01d647
        std::copy(iptcData.begin(), iptcData.end(), std::back_inserter(sortedIptcData));
Packit 01d647
        std::stable_sort(sortedIptcData.begin(), sortedIptcData.end(), cmpIptcdataByRecord);
Packit 01d647
Packit 01d647
        IptcData::const_iterator iter = sortedIptcData.begin();
Packit 01d647
        IptcData::const_iterator end = sortedIptcData.end();
Packit 01d647
        for ( ; iter != end; ++iter) {
Packit 01d647
            // marker, record Id, dataset num
Packit 01d647
            *pWrite++ = marker_;
Packit 01d647
            *pWrite++ = static_cast<byte>(iter->record());
Packit 01d647
            *pWrite++ = static_cast<byte>(iter->tag());
Packit 01d647
Packit 01d647
            // extended or standard dataset?
Packit 01d647
            long dataSize = iter->size();
Packit 01d647
            if (dataSize > 32767) {
Packit 01d647
                // always use 4 bytes for extended length
Packit 01d647
                uint16_t sizeOfSize = 4 | 0x8000;
Packit 01d647
                us2Data(pWrite, sizeOfSize, bigEndian);
Packit 01d647
                pWrite += 2;
Packit 01d647
                ul2Data(pWrite, dataSize, bigEndian);
Packit 01d647
                pWrite += 4;
Packit 01d647
            }
Packit 01d647
            else {
Packit 01d647
                us2Data(pWrite, static_cast<uint16_t>(dataSize), bigEndian);
Packit 01d647
                pWrite += 2;
Packit 01d647
            }
Packit 01d647
            pWrite += iter->value().copy(pWrite, bigEndian);
Packit 01d647
        }
Packit 01d647
Packit 01d647
        return buf;
Packit 01d647
    } // IptcParser::encode
Packit 01d647
Packit 01d647
}                                       // namespace Exiv2
Packit 01d647
Packit 01d647
// *****************************************************************************
Packit 01d647
// local definitions
Packit 01d647
namespace {
Packit 01d647
Packit 01d647
    int readData(
Packit 01d647
              Exiv2::IptcData& iptcData,
Packit 01d647
              uint16_t         dataSet,
Packit 01d647
              uint16_t         record,
Packit 01d647
        const Exiv2::byte*     data,
Packit 01d647
              uint32_t         sizeData
Packit 01d647
    )
Packit 01d647
    {
Packit 01d647
        Exiv2::Value::AutoPtr value;
Packit 01d647
        Exiv2::TypeId type = Exiv2::IptcDataSets::dataSetType(dataSet, record);
Packit 01d647
        value = Exiv2::Value::create(type);
Packit 01d647
        int rc = value->read(data, sizeData, Exiv2::bigEndian);
Packit 01d647
        if (0 == rc) {
Packit 01d647
            Exiv2::IptcKey key(dataSet, record);
Packit 01d647
            iptcData.add(key, value.get());
Packit 01d647
        }
Packit 01d647
        else if (1 == rc) {
Packit 01d647
            // If the first attempt failed, try with a string value
Packit 01d647
            value = Exiv2::Value::create(Exiv2::string);
Packit 01d647
            rc = value->read(data, sizeData, Exiv2::bigEndian);
Packit 01d647
            if (0 == rc) {
Packit 01d647
                Exiv2::IptcKey key(dataSet, record);
Packit 01d647
                iptcData.add(key, value.get());
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        return rc;
Packit 01d647
    }
Packit 01d647
Packit 01d647
}