|
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 |
}
|