MakerNote related changes and Entry clean-up
This commit is contained in:
+146
-54
@@ -20,14 +20,14 @@
|
||||
*/
|
||||
/*
|
||||
File: exif.cpp
|
||||
Version: $Name: $ $Revision: 1.20 $
|
||||
Version: $Name: $ $Revision: 1.21 $
|
||||
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
|
||||
History: 26-Jan-04, ahu: created
|
||||
11-Feb-04, ahu: isolated as a component
|
||||
*/
|
||||
// *****************************************************************************
|
||||
#include "rcsid.hpp"
|
||||
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.20 $ $RCSfile: exif.cpp,v $")
|
||||
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.21 $ $RCSfile: exif.cpp,v $")
|
||||
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
@@ -38,6 +38,7 @@ EXIV2_RCSID("@(#) $Name: $ $Revision: 1.20 $ $RCSfile: exif.cpp,v $")
|
||||
#include "ifd.hpp"
|
||||
#include "tags.hpp"
|
||||
#include "image.hpp"
|
||||
#include "makernote.hpp"
|
||||
|
||||
// + standard includes
|
||||
#include <iostream>
|
||||
@@ -49,23 +50,42 @@ EXIV2_RCSID("@(#) $Name: $ $Revision: 1.20 $ $RCSfile: exif.cpp,v $")
|
||||
#include <map>
|
||||
#include <cstring>
|
||||
|
||||
// *****************************************************************************
|
||||
// local declarations
|
||||
namespace {
|
||||
|
||||
/*
|
||||
Set the data of the entry identified by tag in ifd to an unsigned long
|
||||
with the value of offset. If no entry with this tag exists in ifd, an
|
||||
entry of type unsigned long with one component is created.
|
||||
*/
|
||||
void setOffsetTag(Exif::Ifd& ifd,
|
||||
Exif::uint16 tag,
|
||||
Exif::uint32 offset,
|
||||
Exif::ByteOrder byteOrder);
|
||||
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
namespace Exif {
|
||||
|
||||
Metadatum::Metadatum(const Entry& e, ByteOrder byteOrder)
|
||||
: tag_(e.tag()), ifdId_(e.ifdId()), ifdIdx_(e.ifdIdx()), value_(0)
|
||||
: tag_(e.tag()), ifdId_(e.ifdId()), makerNote_(e.makerNote()),
|
||||
value_(0)
|
||||
{
|
||||
value_ = Value::create(TypeId(e.type()));
|
||||
value_->read(e.data(), e.size(), byteOrder);
|
||||
key_ = ExifTags::makeKey(tag_, ifdId_);
|
||||
key_ = makeKey(tag_, ifdId_, makerNote_);
|
||||
}
|
||||
|
||||
Metadatum::Metadatum(const std::string& key, Value* value)
|
||||
: ifdIdx_(-1), value_(0), key_(key)
|
||||
Metadatum::Metadatum(const std::string& key,
|
||||
const Value* value,
|
||||
MakerNote* makerNote)
|
||||
: makerNote_(makerNote), value_(0), key_(key)
|
||||
{
|
||||
if (value) value_ = value->clone();
|
||||
std::pair<uint16, IfdId> p = ExifTags::decomposeKey(key);
|
||||
std::pair<uint16, IfdId> p = decomposeKey(key, makerNote);
|
||||
if (p.first == 0xffff) throw Error("Invalid key");
|
||||
tag_ = p.first;
|
||||
if (p.second == ifdIdNotSet) throw Error("Invalid key");
|
||||
@@ -75,11 +95,12 @@ namespace Exif {
|
||||
Metadatum::~Metadatum()
|
||||
{
|
||||
delete value_;
|
||||
// do *not* delete the MakerNote
|
||||
}
|
||||
|
||||
Metadatum::Metadatum(const Metadatum& rhs)
|
||||
: tag_(rhs.tag_), ifdId_(rhs.ifdId_),
|
||||
ifdIdx_(rhs.ifdIdx_), value_(0), key_(rhs.key_)
|
||||
: tag_(rhs.tag_), ifdId_(rhs.ifdId_), makerNote_(rhs.makerNote_),
|
||||
value_(0), key_(rhs.key_)
|
||||
{
|
||||
if (rhs.value_ != 0) value_ = rhs.value_->clone(); // deep copy
|
||||
}
|
||||
@@ -89,7 +110,7 @@ namespace Exif {
|
||||
if (this == &rhs) return *this;
|
||||
tag_ = rhs.tag_;
|
||||
ifdId_ = rhs.ifdId_;
|
||||
ifdIdx_ = rhs.ifdIdx_;
|
||||
makerNote_ = rhs.makerNote_;
|
||||
delete value_;
|
||||
value_ = 0;
|
||||
if (rhs.value_ != 0) value_ = rhs.value_->clone(); // deep copy
|
||||
@@ -109,6 +130,22 @@ namespace Exif {
|
||||
value_->read(buf);
|
||||
}
|
||||
|
||||
std::string Metadatum::tagName() const
|
||||
{
|
||||
if (ifdId_ == makerIfd && makerNote_ != 0) {
|
||||
return makerNote_->tagName(tag_);
|
||||
}
|
||||
return ExifTags::tagName(tag_, ifdId_);
|
||||
}
|
||||
|
||||
std::string Metadatum::sectionName() const
|
||||
{
|
||||
if (ifdId_ == makerIfd && makerNote_ != 0) {
|
||||
return makerNote_->sectionName(tag_);
|
||||
}
|
||||
return ExifTags::sectionName(tag_, ifdId_);
|
||||
}
|
||||
|
||||
Thumbnail::Thumbnail()
|
||||
: type_(none), size_(0), image_(0), ifd_(ifd1, 0, false)
|
||||
{
|
||||
@@ -392,7 +429,7 @@ namespace Exif {
|
||||
{
|
||||
Ifd::iterator pos = ifd1.findTag(0x0201);
|
||||
if (pos == ifd1.end()) throw Error("Bad thumbnail (0x0201)");
|
||||
pos->setOffset(ifd1.offset() + ifd1.size() + ifd1.dataSize(), byteOrder);
|
||||
pos->setValue(ifd1.offset() + ifd1.size() + ifd1.dataSize(), byteOrder);
|
||||
}
|
||||
|
||||
void Thumbnail::setTiffImageOffsets(Ifd& ifd1, ByteOrder byteOrder) const
|
||||
@@ -416,7 +453,7 @@ namespace Exif {
|
||||
} // Thumbnail::setTiffImageOffsets
|
||||
|
||||
ExifData::ExifData()
|
||||
: ifd0_(ifd0, 0, false), exifIfd_(exifIfd, 0, false),
|
||||
: makerNote_(0), ifd0_(ifd0, 0, false), exifIfd_(exifIfd, 0, false),
|
||||
iopIfd_(iopIfd, 0, false), gpsIfd_(gpsIfd, 0, false),
|
||||
ifd1_(ifd1, 0, false), valid_(false), size_(0), data_(0)
|
||||
{
|
||||
@@ -424,6 +461,7 @@ namespace Exif {
|
||||
|
||||
ExifData::~ExifData()
|
||||
{
|
||||
delete makerNote_;
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
@@ -469,8 +507,34 @@ namespace Exif {
|
||||
if (rc) return rc;
|
||||
}
|
||||
|
||||
// Find MakerNote in ExifIFD, create a MakerNote class
|
||||
Ifd::iterator pos = exifIfd_.findTag(0x927c);
|
||||
Ifd::iterator make = ifd0_.findTag(0x010f);
|
||||
Ifd::iterator model = ifd0_.findTag(0x0110);
|
||||
if (pos != exifIfd_.end() && make != ifd0_.end() && model != ifd0_.end()) {
|
||||
MakerNoteFactory& makerNoteFactory = MakerNoteFactory::instance();
|
||||
// Todo: The conversion to string assumes that there is a \0 at the end
|
||||
makerNote_ = makerNoteFactory.create(make->data(), model->data());
|
||||
}
|
||||
// Read the MakerNote
|
||||
if (makerNote_) {
|
||||
rc = makerNote_->read(pos->data(),
|
||||
pos->size(),
|
||||
byteOrder(),
|
||||
exifIfd_.offset() + pos->offset());
|
||||
if (rc) {
|
||||
delete makerNote_;
|
||||
makerNote_ = 0;
|
||||
}
|
||||
}
|
||||
// If we successfully parsed the MakerNote, delete the raw MakerNote,
|
||||
// the parsed MakerNote is the primary MakerNote from now on
|
||||
if (makerNote_) {
|
||||
exifIfd_.erase(pos);
|
||||
}
|
||||
|
||||
// Find and delete ExifIFD sub-IFD of IFD1
|
||||
Ifd::iterator pos = ifd1_.findTag(0x8769);
|
||||
pos = ifd1_.findTag(0x8769);
|
||||
if (pos != ifd1_.end()) {
|
||||
ifd1_.erase(pos);
|
||||
ret = -99;
|
||||
@@ -482,10 +546,13 @@ namespace Exif {
|
||||
ret = -99;
|
||||
}
|
||||
|
||||
// Copy all entries from the IFDs to the internal metadata
|
||||
// Copy all entries from the IFDs and the MakerNote to the metadata
|
||||
metadata_.clear();
|
||||
add(ifd0_.begin(), ifd0_.end(), byteOrder());
|
||||
add(exifIfd_.begin(), exifIfd_.end(), byteOrder());
|
||||
if (makerNote_) {
|
||||
add(makerNote_->begin(), makerNote_->end(), byteOrder());
|
||||
}
|
||||
add(iopIfd_.begin(), iopIfd_.end(), byteOrder());
|
||||
add(gpsIfd_.begin(), gpsIfd_.end(), byteOrder());
|
||||
add(ifd1_.begin(), ifd1_.end(), byteOrder());
|
||||
@@ -506,6 +573,7 @@ namespace Exif {
|
||||
}
|
||||
JpegImage img;
|
||||
img.setExifData(buf, actualSize);
|
||||
delete[] buf;
|
||||
return img.writeExifData(path);
|
||||
} // ExifData::write
|
||||
|
||||
@@ -551,7 +619,7 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n";
|
||||
// Set the offset to the Exif IFD in IFD0
|
||||
ifd0.erase(0x8769);
|
||||
if (exifIfd.size() > 0) {
|
||||
ifd0.setOffset(0x8769, exifIfdOffset, byteOrder());
|
||||
setOffsetTag(ifd0, 0x8769, exifIfdOffset, byteOrder());
|
||||
}
|
||||
|
||||
// Build Interoperability IFD from metadata
|
||||
@@ -562,7 +630,7 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n";
|
||||
// Set the offset to the Interoperability IFD in Exif IFD
|
||||
exifIfd.erase(0xa005);
|
||||
if (iopIfd.size() > 0) {
|
||||
exifIfd.setOffset(0xa005, iopIfdOffset, byteOrder());
|
||||
setOffsetTag(exifIfd, 0xa005, iopIfdOffset, byteOrder());
|
||||
}
|
||||
|
||||
// Build GPSInfo IFD from metadata
|
||||
@@ -573,7 +641,7 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n";
|
||||
// Set the offset to the GPSInfo IFD in IFD0
|
||||
ifd0.erase(0x8825);
|
||||
if (gpsIfd.size() > 0) {
|
||||
ifd0.setOffset(0x8825, gpsIfdOffset, byteOrder());
|
||||
setOffsetTag(ifd0, 0x8825, gpsIfdOffset, byteOrder());
|
||||
}
|
||||
|
||||
// Update Exif data from thumbnail, build IFD1 from updated metadata
|
||||
@@ -633,11 +701,11 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n";
|
||||
return size;
|
||||
}
|
||||
|
||||
void ExifData::add(Ifd::const_iterator begin,
|
||||
Ifd::const_iterator end,
|
||||
void ExifData::add(Entries::const_iterator begin,
|
||||
Entries::const_iterator end,
|
||||
ByteOrder byteOrder)
|
||||
{
|
||||
Ifd::const_iterator i = begin;
|
||||
Entries::const_iterator i = begin;
|
||||
for (; i != end; ++i) {
|
||||
add(Metadatum(*i, byteOrder));
|
||||
}
|
||||
@@ -650,13 +718,8 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n";
|
||||
|
||||
void ExifData::add(const Metadatum& metadatum)
|
||||
{
|
||||
iterator i = findKey(metadatum.key());
|
||||
if (i != end()) {
|
||||
i->setValue(&metadatum.value());
|
||||
}
|
||||
else {
|
||||
metadata_.push_back(metadatum);
|
||||
}
|
||||
// allow duplicates
|
||||
metadata_.push_back(metadatum);
|
||||
}
|
||||
|
||||
ExifData::const_iterator ExifData::findKey(const std::string& key) const
|
||||
@@ -681,12 +744,6 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n";
|
||||
std::sort(metadata_.begin(), metadata_.end(), cmpMetadataByTag);
|
||||
}
|
||||
|
||||
void ExifData::erase(const std::string& key)
|
||||
{
|
||||
iterator pos = findKey(key);
|
||||
if (pos != end()) erase(pos);
|
||||
}
|
||||
|
||||
void ExifData::erase(ExifData::iterator pos)
|
||||
{
|
||||
metadata_.erase(pos);
|
||||
@@ -714,7 +771,8 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n";
|
||||
Ifd::iterator end = ifd.end();
|
||||
for (Ifd::iterator entry = ifd.begin(); entry != end; ++entry) {
|
||||
// find the corresponding metadatum
|
||||
std::string key = ExifTags::makeKey(entry->tag(), entry->ifdId());
|
||||
std::string key =
|
||||
makeKey(entry->tag(), entry->ifdId(), entry->makerNote());
|
||||
const_iterator md = findKey(key);
|
||||
if (md == this->end()) {
|
||||
// corresponding metadatum was deleted: this is not (yet) a
|
||||
@@ -724,7 +782,7 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n";
|
||||
}
|
||||
char* buf = new char[md->size()];
|
||||
md->copy(buf, byteOrder());
|
||||
entry->setValue(md->typeId(), buf, md->size());
|
||||
entry->setValue(md->typeId(), md->count(), buf, md->size());
|
||||
delete[] buf;
|
||||
}
|
||||
return compatible;
|
||||
@@ -802,23 +860,14 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n";
|
||||
// Todo: Implement Assert (Stroustup 24.3.7.2)
|
||||
if (!ifd.alloc()) throw Error("Invariant violated in addToIfd");
|
||||
|
||||
RawEntry e;
|
||||
e.ifdId_ = metadatum.ifdId();
|
||||
e.ifdIdx_ = metadatum.ifdIdx();
|
||||
e.tag_ = metadatum.tag();
|
||||
e.type_ = metadatum.typeId();
|
||||
e.count_ = metadatum.count();
|
||||
e.size_ = metadatum.size();
|
||||
e.offset_ = 0; // will be calculated when the IFD is written
|
||||
char* buf = 0;
|
||||
if (e.size_ > 4) {
|
||||
buf = new char[e.size_];
|
||||
metadatum.copy(buf, byteOrder);
|
||||
}
|
||||
else {
|
||||
metadatum.copy(e.offsetData_, byteOrder);
|
||||
}
|
||||
ifd.add(Entry(e, buf, ifd.alloc()));
|
||||
Entry e(ifd.alloc());
|
||||
e.setIfdId(metadatum.ifdId());
|
||||
e.setTag(metadatum.tag());
|
||||
e.setOffset(0); // will be calculated when the IFD is written
|
||||
char* buf = new char[metadatum.size()];
|
||||
metadatum.copy(buf, byteOrder);
|
||||
e.setValue(metadatum.typeId(), metadatum.count(), buf, metadatum.size());
|
||||
ifd.add(e);
|
||||
delete[] buf;
|
||||
} // addToIfd
|
||||
|
||||
@@ -834,8 +883,51 @@ std::cout << "->>>>>> writing from metadata <<<<<<-\n";
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Metadatum& md)
|
||||
{
|
||||
PrintFct fct = ExifTags::printFct(md.tag(), md.ifdId());
|
||||
return fct(os, md.value());
|
||||
if (md.ifdId() == makerIfd && md.makerNote() != 0) {
|
||||
return md.makerNote()->printTag(os, md.tag(), md.value());
|
||||
}
|
||||
return ExifTags::printTag(os, md.tag(), md.ifdId(), md.value());
|
||||
}
|
||||
|
||||
std::string makeKey(uint16 tag, IfdId ifdId, const MakerNote* makerNote)
|
||||
{
|
||||
if (ifdId == makerIfd && makerNote != 0) {
|
||||
return makerNote->makeKey(tag);
|
||||
}
|
||||
return ExifTags::makeKey(tag, ifdId);
|
||||
}
|
||||
|
||||
std::pair<uint16, IfdId> decomposeKey(const std::string& key,
|
||||
const MakerNote* makerNote)
|
||||
{
|
||||
std::pair<uint16, IfdId> p = ExifTags::decomposeKey(key);
|
||||
if (p.second == makerIfd && makerNote != 0) {
|
||||
p.first = makerNote->decomposeKey(key);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace Exif
|
||||
|
||||
// *****************************************************************************
|
||||
// local definitions
|
||||
namespace {
|
||||
|
||||
void setOffsetTag(Exif::Ifd& ifd,
|
||||
Exif::uint16 tag,
|
||||
Exif::uint32 offset,
|
||||
Exif::ByteOrder byteOrder)
|
||||
{
|
||||
Exif::Ifd::iterator pos = ifd.findTag(tag);
|
||||
if (pos == ifd.end()) {
|
||||
Exif::Entry e(ifd.alloc());
|
||||
e.setIfdId(ifd.ifdId());
|
||||
e.setTag(tag);
|
||||
e.setOffset(0); // will be calculated when the IFD is written
|
||||
ifd.add(e);
|
||||
pos = ifd.findTag(tag);
|
||||
}
|
||||
pos->setValue(offset, byteOrder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+103
-75
@@ -21,7 +21,7 @@
|
||||
/*!
|
||||
@file exif.hpp
|
||||
@brief Encoding and decoding of %Exif data
|
||||
@version $Name: $ $Revision: 1.22 $
|
||||
@version $Name: $ $Revision: 1.23 $
|
||||
@author Andreas Huggel (ahu)
|
||||
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
|
||||
@date 09-Jan-04, ahu: created
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "value.hpp"
|
||||
#include "ifd.hpp"
|
||||
#include "tags.hpp"
|
||||
#include "makernote.hpp"
|
||||
|
||||
// + standard includes
|
||||
#include <string>
|
||||
@@ -73,10 +74,14 @@ namespace Exif {
|
||||
|
||||
@param key The key of the metadatum.
|
||||
@param value Pointer to a metadatum value.
|
||||
@param makerNote Pointer to the associated MakerNote (only needed for
|
||||
MakerNote tags).
|
||||
@throw Error ("Invalid key") if the key cannot be parsed and converted
|
||||
to a tag number and an IFD id or the section name does not match.
|
||||
*/
|
||||
explicit Metadatum(const std::string& key, Value* value =0);
|
||||
explicit Metadatum(const std::string& key,
|
||||
const Value* value =0,
|
||||
MakerNote* makerNote =0);
|
||||
//! Destructor
|
||||
~Metadatum();
|
||||
//! Copy constructor
|
||||
@@ -94,41 +99,52 @@ namespace Exif {
|
||||
*/
|
||||
void setValue(const std::string& buf);
|
||||
/*!
|
||||
@brief Return a pointer to a copy (clone) of the value. The caller
|
||||
is responsible to delete this copy when it's no longer needed.
|
||||
@brief Write value to a character data buffer and return the number
|
||||
of characters (bytes) written.
|
||||
|
||||
This method is provided for users who need full control over the
|
||||
value. A caller may, e.g., downcast the pointer to the appropriate
|
||||
subclass of Value to make use of the interface of the subclass to set
|
||||
or modify its contents.
|
||||
|
||||
@return A pointer to a copy (clone) of the value, 0 if the value is
|
||||
not set.
|
||||
*/
|
||||
Value* getValue() const { return value_ == 0 ? 0 : value_->clone(); }
|
||||
//! Return the name of the tag
|
||||
const char* tagName() const { return ExifTags::tagName(tag_, ifdId_); }
|
||||
//! Return the name of the type
|
||||
const char* typeName() const { return TypeInfo::typeName(typeId()); }
|
||||
//! Return the size in bytes of one component of this type
|
||||
long typeSize() const { return TypeInfo::typeSize(typeId()); }
|
||||
//! Return the name of the IFD
|
||||
const char* ifdName() const { return ExifTags::ifdName(ifdId_); }
|
||||
//! Return the related image item (image or thumbnail)
|
||||
const char* ifdItem() const { return ExifTags::ifdItem(ifdId_); }
|
||||
//! Return the name of the section
|
||||
const char* sectionName() const
|
||||
{ return ExifTags::sectionName(tag_, ifdId_); }
|
||||
The user must ensure that the buffer has enough memory. Otherwise
|
||||
the call results in undefined behaviour.
|
||||
|
||||
@param buf Data buffer to write to.
|
||||
@param byteOrder Applicable byte order (little or big endian).
|
||||
@return Number of characters written.
|
||||
*/
|
||||
long copy(char* buf, ByteOrder byteOrder) const
|
||||
{ return value_ == 0 ? 0 : value_->copy(buf, byteOrder); }
|
||||
//! @name Accessors
|
||||
//@{
|
||||
/*!
|
||||
@brief Return a key for the tag. The key is of the form
|
||||
'ifdItem.sectionName.tagName'.
|
||||
*/
|
||||
std::string key() const { return key_; }
|
||||
//! Return the related image item
|
||||
const char* ifdItem() const { return ExifTags::ifdItem(ifdId_); }
|
||||
//! Return the name of the section
|
||||
std::string sectionName() const;
|
||||
//! Return the name of the tag
|
||||
std::string tagName() const;
|
||||
//! Return the tag
|
||||
uint16 tag() const { return tag_; }
|
||||
//! Return the type id.
|
||||
TypeId typeId() const { return value_ == 0 ? invalid : value_->typeId(); }
|
||||
//! Return the name of the type
|
||||
const char* typeName() const { return TypeInfo::typeName(typeId()); }
|
||||
//! Return the size in bytes of one component of this type
|
||||
long typeSize() const { return TypeInfo::typeSize(typeId()); }
|
||||
//! Return the number of components in the value
|
||||
long count() const { return value_ == 0 ? 0 : value_->count(); }
|
||||
//! Return the size of the value in bytes
|
||||
long size() const { return value_ == 0 ? 0 : value_->size(); }
|
||||
//! Return the IFD id
|
||||
IfdId ifdId() const { return ifdId_; }
|
||||
//! Return the name of the IFD
|
||||
const char* ifdName() const { return ExifTags::ifdName(ifdId_); }
|
||||
//! Return the pointer to the associated MakerNote
|
||||
MakerNote* makerNote() const { return makerNote_; }
|
||||
//! Return the value as a string.
|
||||
std::string toString() const
|
||||
{ return value_ == 0 ? "" : value_->toString(); }
|
||||
/*!
|
||||
@brief Return the n-th component of the value converted to long. The
|
||||
return value is -1 if the value of the Metadatum is not set and
|
||||
@@ -153,26 +169,19 @@ namespace Exif {
|
||||
*/
|
||||
Rational toRational(long n =0) const
|
||||
{ return value_ == 0 ? Rational(-1, 1) : value_->toRational(n); }
|
||||
//! Return the value as a string.
|
||||
std::string toString() const
|
||||
{ return value_ == 0 ? "" : value_->toString(); }
|
||||
/*!
|
||||
@brief Write value to a character data buffer and return the number
|
||||
of characters (bytes) written.
|
||||
@brief Return a pointer to a copy (clone) of the value. The caller
|
||||
is responsible to delete this copy when it's no longer needed.
|
||||
|
||||
The user must ensure that the buffer has enough memory. Otherwise
|
||||
the call results in undefined behaviour.
|
||||
|
||||
@param buf Data buffer to write to.
|
||||
@param byteOrder Applicable byte order (little or big endian).
|
||||
@return Number of characters written.
|
||||
*/
|
||||
long copy(char* buf, ByteOrder byteOrder) const
|
||||
{ return value_ == 0 ? 0 : value_->copy(buf, byteOrder); }
|
||||
//! Return the IFD id
|
||||
IfdId ifdId() const { return ifdId_; }
|
||||
//! Return the position in the IFD (-1: not set)
|
||||
int ifdIdx() const { return ifdIdx_; }
|
||||
This method is provided for users who need full control over the
|
||||
value. A caller may, e.g., downcast the pointer to the appropriate
|
||||
subclass of Value to make use of the interface of the subclass to set
|
||||
or modify its contents.
|
||||
|
||||
@return A pointer to a copy (clone) of the value, 0 if the value is
|
||||
not set.
|
||||
*/
|
||||
Value* getValue() const { return value_ == 0 ? 0 : value_->clone(); }
|
||||
/*!
|
||||
@brief Return a constant reference to the value.
|
||||
|
||||
@@ -194,19 +203,14 @@ namespace Exif {
|
||||
*/
|
||||
const Value& value() const
|
||||
{ if (value_) return *value_; throw Error("Value not set"); }
|
||||
/*!
|
||||
@brief Return a unique key for the tag. The key is of the form
|
||||
'ifdItem.sectionName.tagName'.
|
||||
*/
|
||||
std::string key() const { return key_; }
|
||||
//@}
|
||||
|
||||
private:
|
||||
uint16 tag_; //!< Tag value
|
||||
IfdId ifdId_; //!< The IFD associated with this tag
|
||||
int ifdIdx_; //!< Position in the IFD (-1: not set)
|
||||
MakerNote* makerNote_; //!< Pointer to the associated MakerNote
|
||||
Value* value_; //!< Pointer to the value
|
||||
std::string key_; //!< Unique key
|
||||
std::string key_; //!< Key
|
||||
|
||||
}; // class Metadatum
|
||||
|
||||
@@ -344,7 +348,7 @@ namespace Exif {
|
||||
|
||||
Provide high-level access to the %Exif data of an image:
|
||||
- read %Exif information from JPEG files
|
||||
- access metadata through unique keys and standard C++ iterators
|
||||
- access metadata through keys and standard C++ iterators
|
||||
- add, modify and delete metadata
|
||||
- write %Exif data to JPEG files
|
||||
- extract %Exif metadata to files, insert from these files
|
||||
@@ -411,25 +415,25 @@ namespace Exif {
|
||||
*/
|
||||
long copy(char* buf);
|
||||
/*!
|
||||
@brief Add all IFD entries in the range from iterator position begin
|
||||
to iterator position end to the %Exif metadata. Checks for
|
||||
duplicates: if a metadatum already exists, its value is
|
||||
overwritten.
|
||||
@brief Add all (IFD) entries in the range from iterator position begin
|
||||
to iterator position end to the %Exif metadata. No duplicate
|
||||
checks are performed, i.e., it is possible to add multiple
|
||||
metadata with the same key.
|
||||
*/
|
||||
void add(Ifd::const_iterator begin,
|
||||
Ifd::const_iterator end,
|
||||
void add(Entries::const_iterator begin,
|
||||
Entries::const_iterator end,
|
||||
ByteOrder byteOrder);
|
||||
/*!
|
||||
@brief Add a metadatum from the supplied key and value pair.
|
||||
This method copies (clones) the value. If a metadatum with the
|
||||
given key already exists, its value is overwritten and no new
|
||||
metadatum is added.
|
||||
@brief Add a metadatum from the supplied key and value pair. This
|
||||
method copies (clones) the value. No duplicate checks are
|
||||
performed, i.e., it is possible to add multiple metadata with
|
||||
the same key.
|
||||
*/
|
||||
void add(const std::string& key, Value* value);
|
||||
/*!
|
||||
@brief Add a copy of the metadatum to the %Exif metadata. If a
|
||||
metadatum with the given key already exists, its value is
|
||||
overwritten and no new metadatum is added.
|
||||
@brief Add a copy of the metadatum to the %Exif metadata. No
|
||||
duplicate checks are performed, i.e., it is possible to add
|
||||
multiple metadata with the same key.
|
||||
*/
|
||||
void add(const Metadatum& metadatum);
|
||||
|
||||
@@ -455,16 +459,22 @@ namespace Exif {
|
||||
iterator begin() { return metadata_.begin(); }
|
||||
//! End of the metadata
|
||||
iterator end() { return metadata_.end(); }
|
||||
//! Find a metadatum by its key, return an iterator to it
|
||||
/*!
|
||||
@brief Find a metadatum with the given key, return an iterator to it.
|
||||
If multiple metadata with the same key exist, it is undefined
|
||||
which of the matching metadata is found.
|
||||
*/
|
||||
iterator findKey(const std::string& key);
|
||||
//! Find a metadatum by its key, return a const iterator to it
|
||||
/*!
|
||||
@brief Find a metadatum with the given key, return a const iterator to
|
||||
it. If multiple metadata with the same key exist, it is
|
||||
undefined which of the matching metadata is found.
|
||||
*/
|
||||
const_iterator findKey(const std::string& key) const;
|
||||
//! Sort metadata by key
|
||||
void sortByKey();
|
||||
//! Sort metadata by tag
|
||||
void sortByTag();
|
||||
//! Delete the metadatum with a given key
|
||||
void erase(const std::string& key);
|
||||
//! Delete the metadatum at iterator position pos
|
||||
void erase(iterator pos);
|
||||
|
||||
@@ -520,6 +530,7 @@ namespace Exif {
|
||||
TiffHeader tiffHeader_;
|
||||
Metadata metadata_;
|
||||
Thumbnail thumbnail_;
|
||||
MakerNote* makerNote_;
|
||||
|
||||
Ifd ifd0_;
|
||||
Ifd exifIfd_;
|
||||
@@ -539,17 +550,18 @@ namespace Exif {
|
||||
/*!
|
||||
@brief Add all metadata in the range from iterator position begin to
|
||||
iterator position end, which have an IFD id matching that of the
|
||||
IFD to the list of directory entries of ifd. Checks for duplicates:
|
||||
if an entry with the same tag already exists, the entry is
|
||||
overwritten.
|
||||
IFD to the list of directory entries of ifd. No duplicate checks
|
||||
are performed, i.e., it is possible to add multiple metadata with
|
||||
the same key to an IFD.
|
||||
*/
|
||||
void addToIfd(Ifd& ifd,
|
||||
Metadata::const_iterator begin,
|
||||
Metadata::const_iterator end,
|
||||
ByteOrder byteOrder);
|
||||
/*!
|
||||
@brief Add the metadatum to the IFD. Checks for duplicates: if an entry
|
||||
with the same tag already exists, the entry is overwritten.
|
||||
@brief Add the metadatum to the IFD. No duplicate checks are performed,
|
||||
i.e., it is possible to add multiple metadata with the same key to
|
||||
an IFD.
|
||||
*/
|
||||
void addToIfd(Ifd& ifd, const Metadatum& metadatum, ByteOrder byteOrder);
|
||||
/*!
|
||||
@@ -562,7 +574,23 @@ namespace Exif {
|
||||
lhs is less than that of rhs.
|
||||
*/
|
||||
bool cmpMetadataByKey(const Metadatum& lhs, const Metadatum& rhs);
|
||||
|
||||
/*!
|
||||
@brief Return a key for the tag and IFD id. The key is of the form
|
||||
'ifdItem.sectionName.tagName'. This function knows about
|
||||
MakerNotes, i.e., it will invoke MakerNote::makeKey if necessary.
|
||||
*/
|
||||
std::string makeKey(uint16 tag, IfdId ifdId, const MakerNote* makerNote);
|
||||
/*!
|
||||
@brief Return the tag and IFD id pair for the key. This function knows
|
||||
about MakerNotes, i.e., it will forward the request to
|
||||
MakerNote::decomposeKey if necessary.
|
||||
@return A pair consisting of the tag and IFD id.
|
||||
@throw Error ("Invalid key") if the key cannot be parsed into
|
||||
item item, section name and tag name parts.
|
||||
*/
|
||||
std::pair<uint16, IfdId> decomposeKey(const std::string& key,
|
||||
const MakerNote* makerNote);
|
||||
|
||||
} // namespace Exif
|
||||
|
||||
#endif // #ifndef EXIF_HPP_
|
||||
|
||||
+111
-125
@@ -20,14 +20,14 @@
|
||||
*/
|
||||
/*
|
||||
File: ifd.cpp
|
||||
Version: $Name: $ $Revision: 1.1 $
|
||||
Version: $Name: $ $Revision: 1.2 $
|
||||
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
|
||||
History: 26-Jan-04, ahu: created
|
||||
11-Feb-04, ahu: isolated as a component
|
||||
*/
|
||||
// *****************************************************************************
|
||||
#include "rcsid.hpp"
|
||||
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.1 $ $RCSfile: ifd.cpp,v $")
|
||||
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.2 $ $RCSfile: ifd.cpp,v $")
|
||||
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
@@ -41,55 +41,55 @@ EXIV2_RCSID("@(#) $Name: $ $Revision: 1.1 $ $RCSfile: ifd.cpp,v $")
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
// *****************************************************************************
|
||||
// local declarations
|
||||
namespace {
|
||||
|
||||
// Helper structure to build IFD entries
|
||||
struct PreEntry {
|
||||
Exif::uint16 tag_;
|
||||
Exif::uint16 type_;
|
||||
Exif::uint32 count_;
|
||||
long size_;
|
||||
long offsetLoc_;
|
||||
Exif::uint32 offset_;
|
||||
};
|
||||
|
||||
// Container for 'pre-entries'
|
||||
typedef std::vector<PreEntry> PreEntries;
|
||||
|
||||
/*
|
||||
Compare two 'pre-IFD entries' by offset, taking care of special cases
|
||||
where one or both of the entries don't have an offset. Return true if the
|
||||
offset of entry lhs is less than that of rhs, else false. By definition,
|
||||
entries without an offset are greater than those with an offset.
|
||||
*/
|
||||
bool cmpPreEntriesByOffset(const PreEntry& lhs, const PreEntry& rhs);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
namespace Exif {
|
||||
|
||||
RawEntry::RawEntry()
|
||||
: ifdId_(ifdIdNotSet), ifdIdx_(-1),
|
||||
tag_(0), type_(0), count_(0), offset_(0), size_(0)
|
||||
{
|
||||
memset(offsetData_, 0x0, 4);
|
||||
}
|
||||
|
||||
Entry::Entry(bool alloc)
|
||||
: alloc_(alloc), ifdId_(ifdIdNotSet), ifdIdx_(-1),
|
||||
: alloc_(alloc), ifdId_(ifdIdNotSet), makerNote_(0),
|
||||
tag_(0), type_(0), count_(0), offset_(0), size_(0), data_(0)
|
||||
{
|
||||
memset(offsetData_, 0x0, 4);
|
||||
}
|
||||
|
||||
Entry::Entry(const RawEntry& e, const char* buf, bool alloc)
|
||||
: alloc_(alloc), ifdId_(e.ifdId_), ifdIdx_(e.ifdIdx_),
|
||||
tag_(e.tag_), type_(e.type_), count_(e.count_), offset_(e.offset_),
|
||||
size_(e.size_), data_(0)
|
||||
{
|
||||
if (size_ > 4) {
|
||||
if (alloc_) {
|
||||
data_ = new char[size_];
|
||||
memcpy(data_, buf + offset_, size_);
|
||||
}
|
||||
else {
|
||||
data_ = const_cast<char*>(buf) + offset_;
|
||||
}
|
||||
}
|
||||
else {
|
||||
memcpy(offsetData_, e.offsetData_, 4);
|
||||
}
|
||||
}
|
||||
|
||||
Entry::~Entry()
|
||||
{
|
||||
if (alloc_) delete[] data_;
|
||||
// do *not* delete the MakerNote
|
||||
}
|
||||
|
||||
Entry::Entry(const Entry& rhs)
|
||||
: alloc_(rhs.alloc_), ifdId_(rhs.ifdId_),
|
||||
ifdIdx_(rhs.ifdIdx_), tag_(rhs.tag_), type_(rhs.type_),
|
||||
count_(rhs.count_), offset_(rhs.offset_), size_(rhs.size_), data_(0)
|
||||
: alloc_(rhs.alloc_), ifdId_(rhs.ifdId_), makerNote_(rhs.makerNote_),
|
||||
tag_(rhs.tag_), type_(rhs.type_), count_(rhs.count_),
|
||||
offset_(rhs.offset_), size_(rhs.size_), data_(0)
|
||||
{
|
||||
memcpy(offsetData_, rhs.offsetData_, 4);
|
||||
if (alloc_) {
|
||||
if (rhs.data_) {
|
||||
data_ = new char[rhs.size()];
|
||||
@@ -106,12 +106,11 @@ namespace Exif {
|
||||
if (this == &rhs) return *this;
|
||||
alloc_ = rhs.alloc_;
|
||||
ifdId_ = rhs.ifdId_;
|
||||
ifdIdx_ = rhs.ifdIdx_;
|
||||
makerNote_ = rhs.makerNote_;
|
||||
tag_ = rhs.tag_;
|
||||
type_ = rhs.type_;
|
||||
count_ = rhs.count_;
|
||||
offset_ = rhs.offset_;
|
||||
memcpy(offsetData_, rhs.offsetData_, 4);
|
||||
size_ = rhs.size_;
|
||||
if (alloc_) {
|
||||
delete[] data_;
|
||||
@@ -127,48 +126,47 @@ namespace Exif {
|
||||
return *this;
|
||||
} // Entry::operator=
|
||||
|
||||
const char* Entry::data() const
|
||||
void Entry::setValue(uint32 data, ByteOrder byteOrder)
|
||||
{
|
||||
if (size_ > 4) return data_;
|
||||
return offsetData_;
|
||||
if (data_ == 0) {
|
||||
if (!alloc_) {
|
||||
throw Error("Invariant alloc violated in Entry::setValue");
|
||||
}
|
||||
data_ = new char[4];
|
||||
}
|
||||
// No need to resize previously allocated memory
|
||||
ul2Data(data_, data, byteOrder);
|
||||
size_ = 4;
|
||||
type_ = unsignedLong;
|
||||
count_ = 1;
|
||||
}
|
||||
|
||||
void Entry::setOffset(uint32 offset, ByteOrder byteOrder)
|
||||
void Entry::setValue(uint16 type, uint32 count, const char* data, long size)
|
||||
{
|
||||
if (size_ > 4) {
|
||||
offset_ = offset;
|
||||
// Make sure size is always at least four bytes
|
||||
long newSize = std::max(long(4), size);
|
||||
if (alloc_) {
|
||||
delete[] data_;
|
||||
data_ = new char[newSize];
|
||||
memset(data_, 0x0, 4);
|
||||
memcpy(data_, data, size);
|
||||
}
|
||||
else {
|
||||
ul2Data(offsetData_, offset, byteOrder);
|
||||
if (size_ == 0) {
|
||||
// Set the data pointer of a virgin entry
|
||||
if (size < 4) throw Error("Size too small");
|
||||
data_ = const_cast<char*>(data);
|
||||
}
|
||||
else {
|
||||
// Overwrite existing data if it fits into the buffer
|
||||
if (newSize > size_) throw Error("Size too large");
|
||||
memset(data_, 0x0, std::max(long(4), size_));
|
||||
memcpy(data_, data, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Entry::setValue(uint16 type, const char* buf, long size)
|
||||
{
|
||||
if (size > 4 && alloc_) {
|
||||
delete[] data_;
|
||||
data_ = new char[size];
|
||||
memcpy(data_, buf, size);
|
||||
}
|
||||
if (size <= 4 && alloc_) {
|
||||
delete[] data_;
|
||||
data_ = 0;
|
||||
memset(offsetData_, 0x0, 4);
|
||||
memcpy(offsetData_, buf, size);
|
||||
}
|
||||
if (size > 4 && !alloc_) {
|
||||
if (size > size_) throw Error("Size too large");
|
||||
memset(data_, 0x0, size_);
|
||||
memcpy(data_, buf, size);
|
||||
}
|
||||
if (size <= 4 && !alloc_) {
|
||||
data_ = 0;
|
||||
memset(offsetData_, 0x0, 4);
|
||||
memcpy(offsetData_, buf, size);
|
||||
}
|
||||
size_ = size;
|
||||
type_ = type;
|
||||
count_ = size / TypeInfo::typeSize(TypeId(type));
|
||||
count_ = count;
|
||||
size_ = newSize;
|
||||
} // Entry::setValue
|
||||
|
||||
Ifd::Ifd(IfdId ifdId)
|
||||
@@ -189,22 +187,21 @@ namespace Exif {
|
||||
int Ifd::read(const char* buf, ByteOrder byteOrder, long offset)
|
||||
{
|
||||
offset_ = offset;
|
||||
int n = getUShort(buf, byteOrder);
|
||||
long o = 2;
|
||||
|
||||
// Create an array of raw entries
|
||||
RawEntries rawEntries;
|
||||
PreEntries preEntries;
|
||||
|
||||
int n = getUShort(buf, byteOrder);
|
||||
long o = 2;
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
RawEntry e;
|
||||
e.ifdId_ = ifdId_;
|
||||
e.ifdIdx_ = i;
|
||||
e.tag_ = getUShort(buf+o, byteOrder);
|
||||
e.type_ = getUShort(buf+o+2, byteOrder);
|
||||
e.count_ = getULong(buf+o+4, byteOrder);
|
||||
e.size_ = e.count_ * TypeInfo::typeSize(TypeId(e.type_));
|
||||
e.offset_ = e.size_ > 4 ? getULong(buf+o+8, byteOrder) : 0;
|
||||
memcpy(e.offsetData_, buf+o+8, 4);
|
||||
rawEntries.push_back(e);
|
||||
PreEntry pe;
|
||||
pe.tag_ = getUShort(buf+o, byteOrder);
|
||||
pe.type_ = getUShort(buf+o+2, byteOrder);
|
||||
pe.count_ = getULong(buf+o+4, byteOrder);
|
||||
pe.size_ = pe.count_ * TypeInfo::typeSize(TypeId(pe.type_));
|
||||
pe.offsetLoc_ = o + 8;
|
||||
pe.offset_ = pe.size_ > 4 ? getULong(buf+o+8, byteOrder) : 0;
|
||||
preEntries.push_back(pe);
|
||||
o += 12;
|
||||
}
|
||||
next_ = getULong(buf+o, byteOrder);
|
||||
@@ -213,11 +210,10 @@ namespace Exif {
|
||||
// on the assumption that the smallest offset points to a data buffer
|
||||
// directly following the IFD. Subsequently all offsets of IFD entries
|
||||
// will need to be recalculated.
|
||||
if (offset_ == 0 && rawEntries.size() > 0) {
|
||||
if (offset_ == 0 && preEntries.size() > 0) {
|
||||
// Find the entry with the smallest offset
|
||||
RawEntries::const_iterator i;
|
||||
i = std::min_element(
|
||||
rawEntries.begin(), rawEntries.end(), cmpRawEntriesByOffset);
|
||||
PreEntries::const_iterator i = std::min_element(
|
||||
preEntries.begin(), preEntries.end(), cmpPreEntriesByOffset);
|
||||
// Set the 'guessed' IFD offset, the test is needed for the case when
|
||||
// all entries have data sizes not exceeding 4.
|
||||
if (i->size_ > 4) {
|
||||
@@ -225,17 +221,21 @@ namespace Exif {
|
||||
}
|
||||
}
|
||||
|
||||
// Convert 'raw' IFD entries to the actual entries, assign the data
|
||||
// Convert the pre-IFD entries to the actual entries, assign the data
|
||||
// to each IFD entry and calculate relative offsets, relative to the
|
||||
// start of the IFD
|
||||
entries_.clear();
|
||||
const RawEntries::iterator begin = rawEntries.begin();
|
||||
const RawEntries::iterator end = rawEntries.end();
|
||||
for (RawEntries::iterator i = begin; i != end; ++i) {
|
||||
if (i->size_ > 4) {
|
||||
i->offset_ = i->offset_ - offset_;
|
||||
}
|
||||
add(Entry(*i, buf, alloc_));
|
||||
const PreEntries::iterator begin = preEntries.begin();
|
||||
const PreEntries::iterator end = preEntries.end();
|
||||
for (PreEntries::iterator i = begin; i != end; ++i) {
|
||||
Entry e(alloc_);
|
||||
e.setIfdId(ifdId_);
|
||||
e.setTag(i->tag_);
|
||||
// Set the offset to the data, relative to start of IFD
|
||||
e.setOffset(i->size_ > 4 ? i->offset_ - offset_ : i->offsetLoc_);
|
||||
e.setValue(i->type_, i->count_, buf + e.offset(),
|
||||
std::max(long(4), i->size_));
|
||||
this->add(e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -323,7 +323,7 @@ namespace Exif {
|
||||
if (ifdId_ != entry.ifdId()) {
|
||||
throw Error("Invariant ifdId violated in Ifd::add");
|
||||
}
|
||||
erase(entry.tag());
|
||||
// allow duplicates
|
||||
entries_.push_back(entry);
|
||||
}
|
||||
|
||||
@@ -335,29 +335,9 @@ namespace Exif {
|
||||
|
||||
void Ifd::erase(iterator pos)
|
||||
{
|
||||
if (alloc_) {
|
||||
entries_.erase(pos);
|
||||
}
|
||||
entries_.erase(pos);
|
||||
}
|
||||
|
||||
void Ifd::setOffset(uint16 tag, uint32 offset, ByteOrder byteOrder)
|
||||
{
|
||||
iterator pos = findTag(tag);
|
||||
if (pos == entries_.end()) {
|
||||
RawEntry e;
|
||||
e.ifdId_ = ifdId_;
|
||||
e.ifdIdx_ = 0;
|
||||
e.tag_ = tag;
|
||||
e.type_ = unsignedLong;
|
||||
e.count_ = 1;
|
||||
e.offset_ = 0;
|
||||
e.size_ = 4;
|
||||
add(Entry(e, 0, alloc_));
|
||||
pos = findTag(tag);
|
||||
}
|
||||
pos->setOffset(offset, byteOrder);
|
||||
} // Ifd::setOffset
|
||||
|
||||
long Ifd::size() const
|
||||
{
|
||||
if (entries_.size() == 0) return 0;
|
||||
@@ -435,7 +415,18 @@ namespace Exif {
|
||||
// *************************************************************************
|
||||
// free functions
|
||||
|
||||
bool cmpRawEntriesByOffset(const RawEntry& lhs, const RawEntry& rhs)
|
||||
bool cmpEntriesByTag(const Entry& lhs, const Entry& rhs)
|
||||
{
|
||||
return lhs.tag() < rhs.tag();
|
||||
}
|
||||
|
||||
} // namespace Exif
|
||||
|
||||
// *****************************************************************************
|
||||
// local definitions
|
||||
namespace {
|
||||
|
||||
bool cmpPreEntriesByOffset(const PreEntry& lhs, const PreEntry& rhs)
|
||||
{
|
||||
// We need to ignore entries with size <= 4, so by definition,
|
||||
// entries with size <= 4 are greater than those with size > 4
|
||||
@@ -449,9 +440,4 @@ namespace Exif {
|
||||
return lhs.offset_ < rhs.offset_;
|
||||
}
|
||||
|
||||
bool cmpEntriesByTag(const Entry& lhs, const Entry& rhs)
|
||||
{
|
||||
return lhs.tag() < rhs.tag();
|
||||
}
|
||||
|
||||
} // namespace Exif
|
||||
}
|
||||
|
||||
+77
-102
@@ -21,7 +21,7 @@
|
||||
/*!
|
||||
@file ifd.hpp
|
||||
@brief Encoding and decoding of IFD (Image File Directory) data
|
||||
@version $Name: $ $Revision: 1.2 $
|
||||
@version $Name: $ $Revision: 1.3 $
|
||||
@author Andreas Huggel (ahu)
|
||||
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
|
||||
@date 09-Jan-04, ahu: created
|
||||
@@ -43,37 +43,11 @@
|
||||
// namespace extensions
|
||||
namespace Exif {
|
||||
|
||||
class MakerNote;
|
||||
|
||||
// *****************************************************************************
|
||||
// class definitions
|
||||
|
||||
/*!
|
||||
@brief Simple structure for 'raw' IFD directory entries (without any data
|
||||
greater than four bytes)
|
||||
*/
|
||||
struct RawEntry {
|
||||
//! Default constructor
|
||||
RawEntry();
|
||||
//! The IFD id
|
||||
IfdId ifdId_;
|
||||
//! Position in the IFD
|
||||
int ifdIdx_;
|
||||
//! Tag
|
||||
uint16 tag_;
|
||||
//! Type
|
||||
uint16 type_;
|
||||
//! Number of components
|
||||
uint32 count_;
|
||||
//! Offset, unprocessed
|
||||
uint32 offset_;
|
||||
//! Data from the IFD offset field if size is less or equal to four
|
||||
char offsetData_[4];
|
||||
//! Size of the data in bytes
|
||||
long size_;
|
||||
}; // struct RawEntry
|
||||
|
||||
//! Container type to hold 'raw' IFD directory entries
|
||||
typedef std::vector<RawEntry> RawEntries;
|
||||
|
||||
/*!
|
||||
@brief Data structure for one IFD directory entry. See the description of
|
||||
class Ifd for an explanation of the supported modes for memory
|
||||
@@ -88,91 +62,107 @@ namespace Exif {
|
||||
it doesn't allocate or delete.
|
||||
*/
|
||||
explicit Entry(bool alloc =true);
|
||||
/*!
|
||||
@brief Constructor to create an %Entry from a raw entry structure
|
||||
and a data buffer.
|
||||
|
||||
@param e 'Raw' entry structure filled with the relevant data. The
|
||||
offset_ field will only be used if size_ is greater than four.
|
||||
@param buf Character buffer with the data of the tag. If size_ is
|
||||
less or equal four, the data from the original IFD offset
|
||||
field must be available in the field offsetData_. The buf is
|
||||
not needed in this case and can be 0.
|
||||
@param alloc Determines if memory management is required. If alloc
|
||||
is true, a data buffer will be allocated to store data
|
||||
of more than four bytes, else only the pointer will be
|
||||
stored. Data less or equal than four bytes is stored
|
||||
locally in the %Entry.
|
||||
*/
|
||||
Entry(const RawEntry& e, const char* buf, bool alloc =true);
|
||||
//! Destructor
|
||||
~Entry();
|
||||
//! Copy constructor
|
||||
Entry(const Entry& rhs);
|
||||
//! Assignment operator
|
||||
Entry& operator=(const Entry& rhs);
|
||||
|
||||
//! Set the tag
|
||||
void setTag(uint16 tag) { tag_ = tag; }
|
||||
//! Set the IFD id
|
||||
void setIfdId(IfdId ifdId) { ifdId_ = ifdId; }
|
||||
//! Set the pointer to the MakerNote
|
||||
void setMakerNote(MakerNote* makerNote) { makerNote_ = makerNote; }
|
||||
//! Set the offset. The offset is relative to the start of the IFD.
|
||||
void setOffset(uint32 offset) { offset_ = offset; }
|
||||
/*!
|
||||
@brief Set the value of the entry to a single unsigned long component,
|
||||
i.e., set the type of the entry to unsigned long, number of
|
||||
components to one, size to four bytes and the value according
|
||||
to the data provided. This method can be used to set the value
|
||||
of a tag which contains a pointer (offset) to a location in the
|
||||
%Exif data (like e.g., ExifTag, 0x8769 in IFD0, which contains a
|
||||
pointer to the Exif IFD). This method cannot be used to set the
|
||||
value of a newly created %Entry in non-alloc mode.
|
||||
*/
|
||||
void setValue(uint32 data, ByteOrder byteOrder);
|
||||
/*!
|
||||
@brief Set type, count, size and the data of the entry.
|
||||
|
||||
Copies the provided buffer when called in memory allocation mode. In
|
||||
non-alloc mode, use this method to set the data of a newly created
|
||||
%Entry. The data buffer provided must be at least four bytes to
|
||||
initialise an %Entry in non-alloc mode. In this case, only the pointer
|
||||
to the buffer is copied, i.e., the buffer must remain valid throughout
|
||||
the life of the %Entry. Subsequent calls in non-alloc mode overwrite
|
||||
the data pointed to by this pointer with the data provided, i.e., the
|
||||
buffer provided in subsequent calls can be deleted after the call.
|
||||
<BR>Todo: This sounds too complicated: should I isolate the init
|
||||
functionality into a separate method?
|
||||
|
||||
@param type The type of the data.
|
||||
@param count Number of components in the buffer.
|
||||
@param data Pointer to the data buffer.
|
||||
@param size Size of the data buffer in bytes.
|
||||
@throw Error ("Size too large") if no memory allocation is allowed and
|
||||
the size of the data in buf is greater than the existing size
|
||||
of the data of the entry.<BR>
|
||||
@throw Error ("Size too small") if an attempt is made to initialise an
|
||||
%Entry in non-alloc mode with a buffer with size less than four.
|
||||
*/
|
||||
void setValue(uint16 type, uint32 count, const char* data, long size);
|
||||
|
||||
//! @name Accessors
|
||||
//@{
|
||||
//! Return the IFD id
|
||||
IfdId ifdId() const { return ifdId_; }
|
||||
//! Return the index in the IFD
|
||||
int ifdIdx() const { return ifdIdx_; }
|
||||
//! Return the tag
|
||||
uint16 tag() const { return tag_; }
|
||||
//! Return the type id.
|
||||
uint16 type() const { return type_; }
|
||||
//! Return the name of the type
|
||||
const char* typeName() const
|
||||
{ return TypeInfo::typeName(TypeId(type_)); }
|
||||
//! Return the size in bytes of one element of this type
|
||||
long typeSize() const
|
||||
{ return TypeInfo::typeSize(TypeId(type_)); }
|
||||
//! Return the IFD id
|
||||
IfdId ifdId() const { return ifdId_; }
|
||||
//! Return the pointer to the associated MakerNote
|
||||
MakerNote* makerNote() const { return makerNote_; }
|
||||
//! Return the number of components in the value
|
||||
uint32 count() const { return count_; }
|
||||
/*!
|
||||
@brief Return the size of the value in bytes, it is at least four
|
||||
bytes unless it is 0.
|
||||
*/
|
||||
long size() const { return size_; }
|
||||
//! Return the offset from the start of the IFD
|
||||
uint32 offset() const { return offset_; }
|
||||
//! Return the size of the value in bytes
|
||||
long size() const { return size_; }
|
||||
/*!
|
||||
@brief Return a pointer to the data area. Do not attempt to write
|
||||
to this pointer.
|
||||
*/
|
||||
const char* data() const;
|
||||
const char* data() const { return data_; }
|
||||
//! Get the memory allocation mode
|
||||
bool alloc() const { return alloc_; }
|
||||
//@}
|
||||
|
||||
//! Return the size in bytes of one element of this type
|
||||
long typeSize() const
|
||||
{ return TypeInfo::typeSize(TypeId(type_)); }
|
||||
//! Return the name of the type
|
||||
const char* typeName() const
|
||||
{ return TypeInfo::typeName(TypeId(type_)); }
|
||||
|
||||
/*!
|
||||
@brief Set the offset. If the size of the data is not greater than
|
||||
four, the offset is written into the offset field as an
|
||||
unsigned long using the byte order given to encode it.
|
||||
*/
|
||||
void setOffset(uint32 offset, ByteOrder byteOrder);
|
||||
/*!
|
||||
@brief Set type, count and the data of the entry.
|
||||
@throw Error ("Size too large") if no memory allocation is allowed and
|
||||
the size of the data in buf is greater than the existing size
|
||||
of the data of the entry.
|
||||
*/
|
||||
void setValue(uint16 type, const char* buf, long size);
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief True: Requires memory allocation and deallocation,<BR>
|
||||
False: No memory management needed.
|
||||
*/
|
||||
bool alloc_;
|
||||
IfdId ifdId_; // Redundant IFD id (it is also at the IFD)
|
||||
int ifdIdx_; // Position in the IFD
|
||||
uint16 tag_; // Tag
|
||||
uint16 type_; // Type
|
||||
uint32 count_; // Number of components
|
||||
uint32 offset_; // Offset from the start of the IFD,
|
||||
// 0 if size <=4, i.e., if there is no offset
|
||||
char offsetData_[4]; // Data from the offset field if size <= 4
|
||||
long size_; // Size of the data in bytes
|
||||
char* data_; // Pointer to the data buffer
|
||||
IfdId ifdId_; // Redundant IFD id (it is also at the IFD)
|
||||
MakerNote* makerNote_; // Pointer to the associated MakerNote
|
||||
uint16 tag_; // Tag
|
||||
uint16 type_; // Type
|
||||
uint32 count_; // Number of components
|
||||
uint32 offset_; // Offset from the start of the IFD to the data
|
||||
long size_; // Size of the data in bytes, at least four bytes
|
||||
char* data_; // Pointer to the data buffer, which is always at
|
||||
// least four bytes big (or 0, if not allocated)
|
||||
}; // class Entry
|
||||
|
||||
//! Container type to hold all IFD directory entries
|
||||
@@ -256,19 +246,12 @@ namespace Exif {
|
||||
void erase(uint16 tag);
|
||||
//! Delete the directory entry at iterator position pos
|
||||
void erase(iterator pos);
|
||||
/*!
|
||||
@brief Set the offset of the entry identified by tag. If no entry with
|
||||
this tag exists, an entry of type unsigned long with one
|
||||
component is created. If the size of the data is greater than
|
||||
four, the offset of the entry is set to the value provided in
|
||||
offset, else it is written to the offset field of the entry as
|
||||
an unsigned long, encoded according to the byte order.
|
||||
*/
|
||||
void setOffset(uint16 tag, uint32 offset, ByteOrder byteOrder);
|
||||
|
||||
//! Set the offset of the next IFD
|
||||
void setNext(uint32 next) { next_ = next; }
|
||||
/*!
|
||||
@brief Add the Entry to the IFD. Checks for duplicates: if an entry
|
||||
@brief Add the Entry to the IFD. No duplicate-check is performed, i.e.,
|
||||
it is possible to add multiple entries with the same tag.
|
||||
with the same tag already exists, the entry is overwritten. The
|
||||
memory allocation mode of the entry to be added must match that
|
||||
of the IFD and the IFD ids of the IFD and Entry must match.
|
||||
@@ -369,14 +352,6 @@ namespace Exif {
|
||||
// *****************************************************************************
|
||||
// free functions
|
||||
|
||||
/*!
|
||||
@brief Compare two 'raw' IFD entries by offset, taking care of special
|
||||
cases where one or both of the entries don't have an offset.
|
||||
Return true if the offset of entry lhs is less than that of rhs,
|
||||
else false. By definition, entries without an offset are greater
|
||||
than those with an offset.
|
||||
*/
|
||||
bool cmpRawEntriesByOffset(const RawEntry& lhs, const RawEntry& rhs);
|
||||
/*!
|
||||
@brief Compare two IFD entries by tag. Return true if the tag of entry
|
||||
lhs is less than that of rhs.
|
||||
|
||||
Reference in New Issue
Block a user