Implemented readTiffImage and various fixes and additions

v0.27.3
Andreas Huggel 22 years ago
parent 7450d04061
commit 552ce410d8

@ -20,7 +20,7 @@
*/ */
/* /*
File: exif.cpp File: exif.cpp
Version: $Name: $ $Revision: 1.10 $ Version: $Name: $ $Revision: 1.11 $
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net> Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
History: 26-Jan-04, ahu: created History: 26-Jan-04, ahu: created
*/ */
@ -38,16 +38,6 @@
#include <cstring> #include <cstring>
// *****************************************************************************
// local declarations
namespace {
// Compare two IFD entries by offset, taking care of special cases
// where one or both of the entries don't have an offset.
bool cmpOffset(const Exif::Ifd::Entry& lhs, const Exif::Ifd::Entry& rhs);
}
// ***************************************************************************** // *****************************************************************************
// class member definitions // class member definitions
namespace Exif { namespace Exif {
@ -139,8 +129,8 @@ namespace Exif {
return 0; return 0;
} // JpegImage::readExifData } // JpegImage::readExifData
TiffHeader::TiffHeader() TiffHeader::TiffHeader(ByteOrder byteOrder)
: byteOrder_(littleEndian), tag_(0x002a), offset_(0x00000008) : byteOrder_(byteOrder), tag_(0x002a), offset_(0x00000008)
{ {
} }
@ -361,7 +351,7 @@ namespace Exif {
return *this; return *this;
} // Metadatum::operator= } // Metadatum::operator=
void Metadatum::setValue(Value* value) void Metadatum::setValue(const Value* value)
{ {
delete value_; delete value_;
value_ = value->clone(); value_ = value->clone();
@ -374,8 +364,8 @@ namespace Exif {
} }
Ifd::Entry::Entry() Ifd::Entry::Entry()
: ifdIdx_(-1), tag_(0), type_(0), count_(0), offset_(0), : ifdId_(ifdIdNotSet), ifdIdx_(-1), tag_(0), type_(0), count_(0),
data_(0), size_(0) offset_(0), data_(0), size_(0)
{ {
} }
@ -385,8 +375,8 @@ namespace Exif {
} }
Ifd::Entry::Entry(const Entry& rhs) Ifd::Entry::Entry(const Entry& rhs)
: ifdIdx_(rhs.ifdIdx_), tag_(rhs.tag_), type_(rhs.type_), : ifdId_(rhs.ifdId_), ifdIdx_(rhs.ifdIdx_), tag_(rhs.tag_),
count_(rhs.count_), offset_(rhs.offset_), type_(rhs.type_), count_(rhs.count_), offset_(rhs.offset_),
data_(0), size_(rhs.size_) data_(0), size_(rhs.size_)
{ {
if (rhs.data_) { if (rhs.data_) {
@ -398,6 +388,7 @@ namespace Exif {
Ifd::Entry::Entry& Ifd::Entry::operator=(const Entry& rhs) Ifd::Entry::Entry& Ifd::Entry::operator=(const Entry& rhs)
{ {
if (this == &rhs) return *this; if (this == &rhs) return *this;
ifdId_ = rhs.ifdId_;
ifdIdx_ = rhs.ifdIdx_; ifdIdx_ = rhs.ifdIdx_;
tag_ = rhs.tag_; tag_ = rhs.tag_;
type_ = rhs.type_; type_ = rhs.type_;
@ -414,7 +405,7 @@ namespace Exif {
} }
Ifd::Ifd(IfdId ifdId) Ifd::Ifd(IfdId ifdId)
: ifdId_(ifdId), offset_(0), next_(0), size_(0) : ifdId_(ifdId), offset_(0), next_(0)
{ {
} }
@ -427,6 +418,7 @@ namespace Exif {
entries_.clear(); entries_.clear();
for (int i=0; i<n; ++i) { for (int i=0; i<n; ++i) {
Entry e; Entry e;
e.ifdId_ = ifdId_;
e.ifdIdx_ = i; e.ifdIdx_ = i;
e.tag_ = getUShort(buf+o, byteOrder); e.tag_ = getUShort(buf+o, byteOrder);
e.type_ = getUShort(buf+o+2, byteOrder); e.type_ = getUShort(buf+o+2, byteOrder);
@ -439,21 +431,20 @@ namespace Exif {
o += 12; o += 12;
} }
next_ = getULong(buf+o, byteOrder); next_ = getULong(buf+o, byteOrder);
size_ = 2 + 12 * entries_.size() + 4;
// Guess the offset if it was not given. The guess is based // Guess the offset if it was not given. The guess is based
// on the assumption that the smallest offset points to a data // on the assumption that the smallest offset points to a data
// buffer directly following the IFD. // buffer directly following the IFD.
// Subsequently all offsets of IFD entries need to be recalculated. // Subsequently all offsets of IFD entries need to be recalculated.
const Entries::iterator eb = entries_.begin(); const iterator eb = entries_.begin();
const Entries::iterator ee = entries_.end(); const iterator ee = entries_.end();
Entries::iterator i = eb; iterator i = eb;
if (offset_ == 0 && i != ee) { if (offset_ == 0 && i != ee) {
// Find the entry with the smallest offset // Find the entry with the smallest offset
i = std::min_element(eb, ee, cmpOffset); i = std::min_element(eb, ee, cmpOffset);
// Set the guessed IFD offset // Set the guessed IFD offset
if (i->size_ > 4) { if (i->size_ > 4) {
offset_ = i->offset_ - size_; offset_ = i->offset_ - size();
} }
} }
@ -475,24 +466,29 @@ namespace Exif {
return 0; return 0;
} // Ifd::read } // Ifd::read
Ifd::Entries::const_iterator Ifd::findTag(uint16 tag) const Ifd::const_iterator Ifd::findTag(uint16 tag) const
{ {
return std::find_if(entries_.begin(), entries_.end(), return std::find_if(entries_.begin(), entries_.end(),
FindEntryByTag(tag)); FindEntryByTag(tag));
} }
Ifd::Entries::iterator Ifd::findTag(uint16 tag) Ifd::iterator Ifd::findTag(uint16 tag)
{ {
return std::find_if(entries_.begin(), entries_.end(), return std::find_if(entries_.begin(), entries_.end(),
FindEntryByTag(tag)); FindEntryByTag(tag));
} }
void Ifd::sortByTag()
{
sort(entries_.begin(), entries_.end(), cmpTag);
}
int Ifd::readSubIfd( int Ifd::readSubIfd(
Ifd& dest, const char* buf, ByteOrder byteOrder, uint16 tag Ifd& dest, const char* buf, ByteOrder byteOrder, uint16 tag
) const ) const
{ {
int rc = 0; int rc = 0;
Entries::const_iterator pos = findTag(tag); const_iterator pos = findTag(tag);
if (pos != entries_.end()) { if (pos != entries_.end()) {
rc = dest.read(buf + pos->offset_, byteOrder, pos->offset_); rc = dest.read(buf + pos->offset_, byteOrder, pos->offset_);
} }
@ -509,15 +505,15 @@ namespace Exif {
// Add all directory entries to the data buffer // Add all directory entries to the data buffer
long dataSize = 0; long dataSize = 0;
const Entries::const_iterator b = entries_.begin(); const const_iterator b = entries_.begin();
const Entries::const_iterator e = entries_.end(); const const_iterator e = entries_.end();
Entries::const_iterator i = b; const_iterator i = b;
for (; i != e; ++i) { for (; i != e; ++i) {
us2Data(buf+o, i->tag_, byteOrder); us2Data(buf+o, i->tag_, byteOrder);
us2Data(buf+o+2, i->type_, byteOrder); us2Data(buf+o+2, i->type_, byteOrder);
ul2Data(buf+o+4, i->count_, byteOrder); ul2Data(buf+o+4, i->count_, byteOrder);
if (i->size_ > 4) { if (i->size_ > 4) {
ul2Data(buf+o+8, offset + size_ + dataSize, byteOrder); ul2Data(buf+o+8, offset + size() + dataSize, byteOrder);
dataSize += i->size_; dataSize += i->size_;
} }
else { else {
@ -529,7 +525,7 @@ namespace Exif {
// Add the offset to the next IFD to the data buffer pointing // Add the offset to the next IFD to the data buffer pointing
// directly behind this IFD and its data // directly behind this IFD and its data
if (next_ != 0) { if (next_ != 0) {
ul2Data(buf+o, offset + size_ + dataSize, byteOrder); ul2Data(buf+o, offset + size() + dataSize, byteOrder);
} }
else { else {
ul2Data(buf+o, 0, byteOrder); ul2Data(buf+o, 0, byteOrder);
@ -545,7 +541,59 @@ namespace Exif {
} }
return o; return o;
} // Ifd::data } // Ifd::copy
void Ifd::add(Metadata::const_iterator begin,
Metadata::const_iterator end,
ByteOrder byteOrder)
{
for (Metadata::const_iterator i = begin; i != end; ++i) {
// add only metadata with matching IFD id
if (i->ifdId() == ifdId_) {
add(*i, byteOrder);
}
}
} // Ifd::add
void Ifd::add(const Metadatum& metadatum, ByteOrder byteOrder)
{
Entry e;
e.ifdId_ = metadatum.ifdId();
e.ifdIdx_ = metadatum.ifdIdx();
e.tag_ = metadatum.tag();
e.type_ = metadatum.typeId();
e.count_ = metadatum.count();
e.offset_ = 0; // will be calculated when the IFD is written
long len = std::max(metadatum.size(), long(4));
e.data_ = new char[len];
::memset(e.data_, 0x0, len);
metadatum.copy(e.data_, byteOrder);
e.size_ = metadatum.size();
erase(metadatum.tag());
entries_.push_back(e);
}
void Ifd::erase(uint16 tag)
{
iterator pos = findTag(tag);
if (pos != end()) erase(pos);
}
void Ifd::erase(iterator pos)
{
entries_.erase(pos);
}
long Ifd::dataSize() const
{
long dataSize = 0;
const_iterator end = this->end();
for (const_iterator i = begin(); i != end; ++i) {
if (i->size_ > 4) dataSize += i->size_;
}
return dataSize;
}
void Ifd::print(std::ostream& os, const std::string& prefix) const void Ifd::print(std::ostream& os, const std::string& prefix) const
{ {
@ -560,9 +608,9 @@ namespace Exif {
<< prefix << "Entry Tag Format (Bytes each) Number Offset\n" << prefix << "Entry Tag Format (Bytes each) Number Offset\n"
<< prefix << "----- ------ --------------------- ------ -----------\n"; << prefix << "----- ------ --------------------- ------ -----------\n";
const Entries::const_iterator b = entries_.begin(); const const_iterator b = entries_.begin();
const Entries::const_iterator e = entries_.end(); const const_iterator e = entries_.end();
Entries::const_iterator i = b; const_iterator i = b;
for (; i != e; ++i) { for (; i != e; ++i) {
std::ostringstream offset; std::ostringstream offset;
if (i->size_ <= 4) { if (i->size_ <= 4) {
@ -605,43 +653,110 @@ namespace Exif {
} // Ifd::print } // Ifd::print
// Todo: Finish the implementation (TIFF thumbnails) int Thumbnail::read(const char* buf,
// - do we need to sum up the strips?? const ExifData& exifData,
int Thumbnail::read( ByteOrder byteOrder)
const char* buf, const ExifData& exifData, ByteOrder byteOrder
)
{ {
int rc = 0;
std::string key = "Thumbnail.ImageStructure.Compression"; std::string key = "Thumbnail.ImageStructure.Compression";
ExifData::const_iterator pos = exifData.findKey(key); ExifData::const_iterator pos = exifData.findKey(key);
if (pos == exifData.end()) return 1; if (pos == exifData.end()) return -1; // no thumbnail
long compression = pos->toLong(); long compression = pos->toLong();
if (compression == 6) { if (compression == 6) {
key = "Thumbnail.RecordingOffset.JPEGInterchangeFormat"; rc = readJpegImage(buf, exifData);
pos = exifData.findKey(key);
if (pos == exifData.end()) return 2;
long offset = pos->toLong();
key = "Thumbnail.RecordingOffset.JPEGInterchangeFormatLength";
pos = exifData.findKey(key);
if (pos == exifData.end()) return 3;
long size = pos->toLong();
thumbnail_ = std::string(buf + offset, size);
}
else if (compression == 1) {
// Todo: implement me!
return 4;
} }
else { else {
// invalid compression value rc = readTiffImage(buf, exifData, byteOrder);
return 5; }
return rc;
} // Thumbnail::read
int Thumbnail::readJpegImage(const char* buf, const ExifData& exifData)
{
std::string key = "Thumbnail.RecordingOffset.JPEGInterchangeFormat";
ExifData::const_iterator pos = exifData.findKey(key);
if (pos == exifData.end()) return 1;
long offset = pos->toLong();
key = "Thumbnail.RecordingOffset.JPEGInterchangeFormatLength";
pos = exifData.findKey(key);
if (pos == exifData.end()) return 1;
long size = pos->toLong();
image_ = std::string(buf + offset, size);
type_ = JPEG;
return 0;
}
int Thumbnail::readTiffImage(const char* buf,
const ExifData& exifData,
ByteOrder byteOrder)
{
char* data = new char[64*1024]; // temporary buffer Todo: handle larger
::memset(data, 0x0, 64*1024); // images (which violate the Exif Std)
long len = 0; // number of bytes in the buffer
// Copy the TIFF header
TiffHeader tiffHeader(byteOrder);
len += tiffHeader.copy(data);
// Create IFD (without Exif and GPS tags) from metadata
Ifd ifd1(ifd1);
ifd1.add(exifData.begin(), exifData.end(), tiffHeader.byteOrder());
Ifd::iterator i = ifd1.findTag(0x8769);
if (i != ifd1.end()) ifd1.erase(i);
i = ifd1.findTag(0x8825);
if (i != ifd1.end()) ifd1.erase(i);
// Do not copy the IFD yet, remember the location and leave a gap
long ifdOffset = len;
len += ifd1.size() + ifd1.dataSize();
// Copy thumbnail image data, remember the offsets used
std::string key = "Thumbnail.RecordingOffset.StripOffsets";
ExifData::const_iterator offsets = exifData.findKey(key);
if (offsets == exifData.end()) return 2;
key = "Thumbnail.RecordingOffset.StripByteCounts";
ExifData::const_iterator sizes = exifData.findKey(key);
if (sizes == exifData.end()) return 2;
std::ostringstream os; // for the new strip offsets
for (long k = 0; k < offsets->count(); ++k) {
long offset = offsets->toLong(k);
long size = sizes->toLong(k);
::memcpy(data + len, buf + offset, size);
os << len << " ";
len += size;
} }
// Update the IFD with the actual strip offsets (replace existing entry)
Metadatum newOffsets(*offsets);
newOffsets.setValue(os.str());
ifd1.add(newOffsets, tiffHeader.byteOrder());
// Finally, sort and copy the IFD
ifd1.sortByTag();
ifd1.copy(data + ifdOffset, tiffHeader.byteOrder(), ifdOffset);
image_ = std::string(data, len);
delete[] data;
type_ = TIFF;
return 0; return 0;
} }
int Thumbnail::write(const std::string& path) const int Thumbnail::write(const std::string& path) const
{ {
std::ofstream file(path.c_str(), std::ios::binary | std::ios::out); std::string p;
switch (type_) {
case JPEG:
p = path + ".jpg";
break;
case TIFF:
p = path + ".tif";
break;
}
std::ofstream file(p.c_str(), std::ios::binary | std::ios::out);
if (!file) return 1; if (!file) return 1;
file.write(thumbnail_.data(), thumbnail_.size()); file.write(image_.data(), image_.size());
if (!file.good()) return 2; if (!file.good()) return 2;
return 0; return 0;
} }
@ -706,14 +821,14 @@ namespace Exif {
// Copy all entries from the IFDs to the internal metadata // Copy all entries from the IFDs to the internal metadata
metadata_.clear(); metadata_.clear();
add(ifd0, byteOrder()); add(ifd0.begin(), ifd0.end(), byteOrder());
add(exifIfd, byteOrder()); add(exifIfd.begin(), exifIfd.end(), byteOrder());
add(iopIfd, byteOrder()); add(iopIfd.begin(), iopIfd.end(), byteOrder());
add(gpsIfd, byteOrder()); add(gpsIfd.begin(), gpsIfd.end(), byteOrder());
add(ifd1, byteOrder()); add(ifd1.begin(), ifd1.end(), byteOrder());
add(ifd1ExifIfd, byteOrder()); add(ifd1ExifIfd.begin(), ifd1ExifIfd.end(), byteOrder());
add(ifd1IopIfd, byteOrder()); add(ifd1IopIfd.begin(), ifd1IopIfd.end(), byteOrder());
add(ifd1GpsIfd, byteOrder()); add(ifd1GpsIfd.begin(), ifd1GpsIfd.end(), byteOrder());
// Read the thumbnail // Read the thumbnail
thumbnail_.read(buf, *this, byteOrder()); thumbnail_.read(buf, *this, byteOrder());
@ -733,42 +848,36 @@ namespace Exif {
return 0; return 0;
} }
void ExifData::add(const Ifd& ifd, ByteOrder byteOrder) void ExifData::add(Ifd::const_iterator begin,
Ifd::const_iterator end,
ByteOrder byteOrder)
{ {
Ifd::const_iterator i = ifd.begin(); Ifd::const_iterator i = begin;
Ifd::const_iterator end = ifd.end();
for (; i != end; ++i) { for (; i != end; ++i) {
Value* value = Value::create(TypeId(i->type_)); Value* value = Value::create(TypeId(i->type_));
value->read(i->data_, i->size_, byteOrder); value->read(i->data_, i->size_, byteOrder);
Metadatum md(i->tag_, i->type_, ifd.ifdId(), i->ifdIdx_); Metadatum md(i->tag_, i->type_, i->ifdId_, i->ifdIdx_, value);
iterator k = findKey(md.key());
if (k != this->end()) {
k->setValue(value);
}
else {
md.setValue(value);
add(md);
}
delete value; delete value;
add(md);
} }
} }
void ExifData::add(const std::string& key, Value* value) void ExifData::add(const std::string& key, Value* value)
{ {
iterator i = findKey(key); add(Metadatum(key, value));
}
void ExifData::add(const Metadatum& metadatum)
{
iterator i = findKey(metadatum.key());
if (i != end()) { if (i != end()) {
i->setValue(value); i->setValue(&metadatum.value());
} }
else { else {
add(Metadatum(key, value)); metadata_.push_back(metadatum);
} }
} }
void ExifData::add(const Metadatum& src)
{
metadata_.push_back(src);
}
ExifData::const_iterator ExifData::findKey(const std::string& key) const ExifData::const_iterator ExifData::findKey(const std::string& key) const
{ {
return std::find_if(metadata_.begin(), metadata_.end(), return std::find_if(metadata_.begin(), metadata_.end(),
@ -950,13 +1059,7 @@ namespace Exif {
os << std::dec << std::setfill(' '); os << std::dec << std::setfill(' ');
} }
} // namespace Exif bool cmpOffset(const Ifd::Entry& lhs, const Ifd::Entry& rhs)
// *****************************************************************************
// local definitions
namespace {
bool cmpOffset(const Exif::Ifd::Entry& lhs, const Exif::Ifd::Entry& rhs)
{ {
// We need to ignore entries with size <= 4, so by definition, // We need to ignore entries with size <= 4, so by definition,
// entries with size <= 4 are greater than those with size > 4 // entries with size <= 4 are greater than those with size > 4
@ -970,4 +1073,9 @@ namespace {
return lhs.offset_ < rhs.offset_; return lhs.offset_ < rhs.offset_;
} }
} bool cmpTag(const Ifd::Entry& lhs, const Ifd::Entry& rhs)
{
return lhs.tag_ < rhs.tag_;
}
} // namespace Exif

@ -21,7 +21,7 @@
/*! /*!
@file exif.hpp @file exif.hpp
@brief Encoding and decoding of %Exif data @brief Encoding and decoding of %Exif data
@version $Name: $ $Revision: 1.11 $ @version $Name: $ $Revision: 1.12 $
@author Andreas Huggel (ahu) @author Andreas Huggel (ahu)
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a> <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
@date 09-Jan-04, ahu: created @date 09-Jan-04, ahu: created
@ -127,19 +127,22 @@ namespace Exif {
//! Type to express the byte order (little or big endian) //! Type to express the byte order (little or big endian)
enum ByteOrder { littleEndian, bigEndian }; enum ByteOrder { littleEndian, bigEndian };
//! Helper class modelling the Tiff header structure. //! Helper class modelling the TIFF header structure.
class TiffHeader { class TiffHeader {
public: public:
//! Default constructor. /*!
TiffHeader(); @brief Default constructor. Optionally sets the byte order
//! Read the Tiff header from a data buffer. Returns 0 if successful. (default: little endian).
*/
explicit TiffHeader(ByteOrder byteOrder =littleEndian);
//! Read the TIFF header from a data buffer. Returns 0 if successful.
int read(const char* buf); int read(const char* buf);
/*! /*!
@brief Write the Tiff header into buf as a data string, return number @brief Write the TIFF header into buf as a data string, return number
of bytes copied. of bytes copied.
*/ */
long copy(char* buf) const; long copy(char* buf) const;
//! Return the lengths of the Tiff header in bytes. //! Return the lengths of the TIFF header in bytes.
long size() const { return 8; } long size() const { return 8; }
//! @name Accessors //! @name Accessors
//@{ //@{
@ -148,9 +151,9 @@ namespace Exif {
//! Return the tag value. //! Return the tag value.
uint16 tag() const { return tag_; } uint16 tag() const { return tag_; }
/*! /*!
@brief Return the offset to IFD0 from the start of the Tiff header. @brief Return the offset to IFD0 from the start of the TIFF header.
The offset is 0x00000008 if IFD0 begins immediately after the The offset is 0x00000008 if IFD0 begins immediately after the
Tiff header. TIFF header.
*/ */
uint32 offset() const { return offset_; } uint32 offset() const { return offset_; }
//@} //@}
@ -441,7 +444,7 @@ namespace Exif {
/*! /*!
@brief Set the value. This method copies (clones) the value. @brief Set the value. This method copies (clones) the value.
*/ */
void setValue(Value* value); void setValue(const Value* value);
/*! /*!
@brief Set the value to the string buf. @brief Set the value to the string buf.
Uses Value::read(const std::string& buf). If the metadatum does Uses Value::read(const std::string& buf). If the metadatum does
@ -493,6 +496,19 @@ namespace Exif {
//! Return the value as a string. //! Return the value as a string.
std::string toString() const std::string toString() const
{ return value_ == 0 ? "" : value_->toString(); } { return value_ == 0 ? "" : value_->toString(); }
/*!
@brief Write value to a character data buffer and return the number
of characters (bytes) written.
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 //! Return the IFD id
IfdId ifdId() const { return ifdId_; } IfdId ifdId() const { return ifdId_; }
//! Return the position in the IFD (-1: not set) //! Return the position in the IFD (-1: not set)
@ -576,6 +592,8 @@ namespace Exif {
const char* typeName() const const char* typeName() const
{ return ExifTags::typeName(TypeId(type_)); } { return ExifTags::typeName(TypeId(type_)); }
//! The IFD id (redundant, it is also at the IFD itself)
IfdId ifdId_;
//! Position in the IFD //! Position in the IFD
int ifdIdx_; int ifdIdx_;
//! Tag //! Tag
@ -607,6 +625,33 @@ namespace Exif {
iterator begin() { return entries_.begin(); } iterator begin() { return entries_.begin(); }
//! End of the entries //! End of the entries
iterator end() { return entries_.end(); } iterator end() { return entries_.end(); }
//! Find an IFD entry by tag, return a const iterator into the entries list
const_iterator findTag(uint16 tag) const;
//! Find an IFD entry by tag, return an iterator into the entries list
iterator findTag(uint16 tag);
//! Sort the IFD entries by tag
void sortByTag();
//! Delete the directory entry with the given tag
void erase(uint16 tag);
//! Delete the directory entry at iterator position pos
void erase(iterator pos);
/*!
@brief Add all metadata in the range from iterator position begin to
iterator position end, which have an IFD id matching that of
this IFD to the list of directory entries. Checks for
duplicates: if an entry with the same tag already exists, the
entry is overwritten.
*/
void add(Metadata::const_iterator begin,
Metadata::const_iterator end,
ByteOrder byteOrder);
/*!
@brief Add the metadatum to the list of directory entries. Does not
check for a matching IFD id. Checks for duplicates: if an
entry with the same tag already exists, the entry is
overwritten.
*/
void add(const Metadatum& metadatum, ByteOrder byteOrder);
//! Unary predicate that matches an Entry with a given tag //! Unary predicate that matches an Entry with a given tag
class FindEntryByTag { class FindEntryByTag {
@ -628,9 +673,9 @@ namespace Exif {
@brief Read a complete IFD and its data from a data buffer @brief Read a complete IFD and its data from a data buffer
@param buf Pointer to the data to decode. The buffer must start with the @param buf Pointer to the data to decode. The buffer must start with the
IFD data (as opposed to readSubIfd). IFD data (unlike the readSubIfd() method).
@param byteOrder Applicable byte order (little or big endian). @param byteOrder Applicable byte order (little or big endian).
@param offset (Optional) offset of the IFD from the start of the Tiff @param offset (Optional) offset of the IFD from the start of the TIFF
header, if known. If not given, the offset will be guessed header, if known. If not given, the offset will be guessed
using the assumption that the smallest offset of all IFD using the assumption that the smallest offset of all IFD
directory entries points to a data buffer immediately follwing directory entries points to a data buffer immediately follwing
@ -645,7 +690,7 @@ namespace Exif {
@param dest References the destination IFD. @param dest References the destination IFD.
@param buf The data buffer to read from. The buffer must contain all Exif @param buf The data buffer to read from. The buffer must contain all Exif
data starting from the Tiff header (as opposed to read). data starting from the TIFF header (unlike the read() method).
@param byteOrder Applicable byte order (little or big endian). @param byteOrder Applicable byte order (little or big endian).
@param tag Tag to look for. @param tag Tag to look for.
@ -655,64 +700,93 @@ namespace Exif {
Ifd& dest, const char* buf, ByteOrder byteOrder, uint16 tag Ifd& dest, const char* buf, ByteOrder byteOrder, uint16 tag
) const; ) const;
/*! /*!
@brief Copy the IFD to a data array, returns a reference to the @brief Copy the IFD to a data array, returns the number of bytes
data buffer. The pointer to the next IFD will be adjusted to an written. If the pointer to the next IFD is not 0, it will be
offset from the start of the Tiff header to the position adjusted to an offset from the start of the TIFF header to the
immediately following the converted IFD. position immediately following this IFD.
@param buf Pointer to the data buffer. @param buf Pointer to the data buffer.
@param byteOrder Applicable byte order (little or big endian). @param byteOrder Applicable byte order (little or big endian).
@param offset Target offset from the start of the Tiff header of the @param offset Target offset from the start of the TIFF header of the
data array. The IFD offsets will be adjusted as data array. The IFD offsets will be adjusted as
necessary. If not given, then it is assumed that the IFD necessary. If not given, then it is assumed that the IFD
will remain at its original position. will remain at its original position, i.e., the offset
of the IFD will be used.
@return Returns the number of characters written. @return Returns the number of characters written.
*/ */
long copy(char* buf, ByteOrder byteOrder, long offset =0) const; long copy(char* buf, ByteOrder byteOrder, long offset =0) const;
/*!
@brief Print the IFD in human readable format to the given stream;
begin each line with prefix.
*/
void print(std::ostream& os, const std::string& prefix ="") const;
//! @name Accessors //! @name Accessors
//@{ //@{
//! Ifd id of the IFD //! Ifd id of the IFD
IfdId ifdId() const { return ifdId_; } IfdId ifdId() const { return ifdId_; }
//! Offset of the IFD from SOI //! Offset of the IFD from SOI
long offset() const { return offset_; } long offset() const { return offset_; }
//! Find an IFD entry by tag, return a const iterator into the entries list //! Get the offset to the next IFD from the start of the TIFF header
Entries::const_iterator findTag(uint16 tag) const;
//! Find an IFD entry by tag, return an iterator into the entries list
Entries::iterator findTag(uint16 tag);
//! Get the offset to the next IFD from the start of the Tiff header
long next() const { return next_; } long next() const { return next_; }
//! Get the size of this IFD in bytes (IFD only, without data)
long size() const { return size_; }
//@} //@}
//! Get the size of this IFD in bytes (IFD only, without data)
long size() const { return 2 + 12 * entries_.size() + 4; }
/*!
@brief Return the total size of the data of this IFD in bytes,
sums the size of all directory entries where size is greater
than four (i.e., only data that requires memory outside the
IFD directory entries is counted).
*/
long dataSize() const;
/*!
@brief Print the IFD in human readable format to the given stream;
begin each line with prefix.
*/
void print(std::ostream& os, const std::string& prefix ="") const;
private: private:
Entries entries_; // IFD entries Entries entries_; // IFD entries
IfdId ifdId_; // IFD Id IfdId ifdId_; // IFD Id
long offset_; // offset of the IFD from the start of long offset_; // offset of the IFD from the start of
// Tiff header // TIFF header
long next_; // offset of next IFD from the start of long next_; // offset of next IFD from the start of
// the Tiff header // the TIFF header
long size_; // size of the IFD in bytes
}; // class Ifd }; // class Ifd
//! %Thumbnail data Todo: implement this properly //! %Thumbnail data Todo: implement this properly
class Thumbnail { class Thumbnail {
public: public:
//! Read the thumbnail from the data buffer, return 0 if successfull //! %Thumbnail image types
int read(const char* buf, const ExifData& exifData, ByteOrder byteOrder); enum Type { JPEG, TIFF };
/*!
@brief Read the thumbnail from the data buffer buf, using %Exif
metadata exifData. Return 0 if successful.
@param buf Data buffer containing the thumbnail data. The buffer must
start with the TIFF header.
@param exifData %Exif data corresponding to the data buffer.
@param byteOrder The byte order used for the encoding of TIFF
thumbnails. It determines the byte order of the resulting
thumbnail image, if it is in TIFF format. For JPEG thumbnails
the byte order is not used.
@return 0 if successful<br>
-1 if there is no thumbnail image according to the %Exif data<br>
1 in case of inconsistent JPEG thumbnail %Exif data<br>
2 in case of inconsistent TIFF thumbnail %Exif data<br>
*/
int read(const char* buf,
const ExifData& exifData,
ByteOrder byteOrder =littleEndian);
//! Write thumbnail to file path, return 0 if successful //! Write thumbnail to file path, return 0 if successful
int write(const std::string& path) const; int write(const std::string& path) const;
private: private:
std::string thumbnail_; //! Read a compressed (JPEG) thumbnail image from the data buffer
int readJpegImage(const char* buf, const ExifData& exifData);
//! Read an uncompressed (TIFF) thumbnail image from the data buffer
int readTiffImage(const char* buf,
const ExifData& exifData,
ByteOrder byteOrder);
std::string image_;
Type type_;
}; // class Thumbnail }; // class Thumbnail
/*! /*!
@ -728,7 +802,6 @@ namespace Exif {
Todo: Todo:
- A constructor which creates a minimal valid set of %Exif data - A constructor which creates a minimal valid set of %Exif data
- Support to add, delete, edit, read data (maybe also in Metadata)
*/ */
class ExifData { class ExifData {
public: public:
@ -752,16 +825,19 @@ namespace Exif {
int read(const char* buf, long len); int read(const char* buf, long len);
//! Write %Exif data to a data buffer, return number of bytes written //! Write %Exif data to a data buffer, return number of bytes written
long copy(char* buf) const; long copy(char* buf) const;
//! Returns the size of all %Exif data (Tiff header plus metadata) //! Returns the size of all %Exif data (TIFF header plus metadata)
long size() const; long size() const;
//! Returns the byte order as specified in the Tiff header //! Returns the byte order as specified in the TIFF header
ByteOrder byteOrder() const { return tiffHeader_.byteOrder(); } ByteOrder byteOrder() const { return tiffHeader_.byteOrder(); }
/*! /*!
@brief Add all entries of an IFD to the Exif metadata. Checks for @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 duplicates: if a metadatum already exists, its value is
overwritten. overwritten.
*/ */
void add(const Ifd& ifd, ByteOrder byteOrder); void add(Ifd::const_iterator begin,
Ifd::const_iterator end,
ByteOrder byteOrder);
/*! /*!
@brief Add a metadatum from the supplied key and value pair. @brief Add a metadatum from the supplied key and value pair.
This method copies (clones) the value. If a metadatum with the This method copies (clones) the value. If a metadatum with the
@ -769,6 +845,13 @@ namespace Exif {
metadatum is added. metadatum is added.
*/ */
void add(const std::string& key, Value* value); 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.
*/
void add(const Metadatum& metadatum);
//! Metadata iterator type //! Metadata iterator type
typedef Metadata::iterator iterator; typedef Metadata::iterator iterator;
//! Metadata const iterator type //! Metadata const iterator type
@ -790,17 +873,15 @@ namespace Exif {
//! Delete the metadatum at iterator position pos //! Delete the metadatum at iterator position pos
void erase(iterator pos); void erase(iterator pos);
//! Write the thumbnail image to a file /*!
@brief Write the thumbnail image to a file. The filename extension
will be set according to the image type of the thumbnail, so
the path should not include an extension.
*/
int writeThumbnail(const std::string& path) const int writeThumbnail(const std::string& path) const
{ return thumbnail_.write(path); } { return thumbnail_.write(path); }
private: private:
/*!
@brief Add metadatum src to the Exif metadata. No duplicate check
is done. (That's why the method is private.)
*/
void add(const Metadatum& src);
long offset_; // Original abs offset of the Exif data long offset_; // Original abs offset of the Exif data
TiffHeader tiffHeader_; TiffHeader tiffHeader_;
Metadata metadata_; Metadata metadata_;
@ -858,6 +939,20 @@ namespace Exif {
//! Print len bytes from buf in hex and ASCII format to the given stream //! Print len bytes from buf in hex and ASCII format to the given stream
void hexdump(std::ostream& os, const char* buf, long len); void hexdump(std::ostream& os, const char* buf, long len);
/*!
@brief Compare two 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 cmpOffset(const Ifd::Entry& lhs, const Ifd::Entry& rhs);
/*!
@brief Compare two IFD entries by tag. Return true if the tag of entry
lhs is less than that of rhs.
*/
bool cmpTag(const Ifd::Entry& lhs, const Ifd::Entry& rhs);
// ***************************************************************************** // *****************************************************************************
// template and inline definitions // template and inline definitions

@ -1,29 +1,9 @@
// ***************************************************************** -*- C++ -*- // ***************************************************************** -*- C++ -*-
/* /*
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net> Abstract : This is playground code, do what you want with it.
*
* This program is part of the Exiv2 distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
Abstract : Sample code to add, modify and delete Exif metadata
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net> Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
Version : $Name: $ $Revision: 1.8 $ Version : $Name: $ $Revision: 1.9 $
History : 26-Jan-04, ahu: created
*/ */
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
@ -40,88 +20,23 @@ void exifPrint(const ExifData& exifData);
// ***************************************************************************** // *****************************************************************************
// Main // Main
int main() int main(int argc, char* const argv[])
try { try {
// ExifData is the container for all metadata
ExifData exifData; ExifData exifData;
// ************************************************************************* if (argc != 2) {
// Add metadata to the Exif data std::cout << "Usage: exiftest file\n";
return 1;
// Create a value of the required type }
Value* v = Value::create(asciiString);
// Set the value to a string
v->read("1999:12:31 23:59:59");
// Add the value together with its key to the Exif data container
std::string key = "Image.DateTime.DateTimeOriginal";
exifData.add(key, v);
std::cout << "Added key \"" << key << "\", value \"" << *v << "\"\n";
// Delete the memory allocated by Value::create
delete v;
// Now create a more interesting value
v = Value::create(unsignedRational);
// Set two rational components from a string
v->read("1/2 1/3");
// Downcast the Value to its actual type
URationalValue* rv = dynamic_cast<URationalValue*>(v);
if (rv == 0) throw Error("Downcast failed");
// Add more elements through the extended interface of the actual type
rv->value_.push_back(std::make_pair(2,3));
rv->value_.push_back(std::make_pair(3,4));
// Add the key and value pair to the Exif data
key = "Image.ImageCharacteristics.PrimaryChromaticities";
exifData.add(key, rv);
std::cout << "Added key \"" << key << "\", value \"" << *v << "\"\n";
// Delete the memory allocated by Value::create
delete v;
// *************************************************************************
// Modify Exif data
// Find a metadatum by its key
key = "Image.DateTime.DateTimeOriginal";
ExifData::iterator pos = exifData.findKey(key);
if (pos == exifData.end()) throw Error("Key not found");
// Set the new value
pos->setValue("2000:01:01 00:00:00");
// Find another key int rc = exifData.read(argv[1]);
key = "Image.ImageCharacteristics.PrimaryChromaticities"; if (rc) throw Error("Reading Exif data failed");
pos = exifData.findKey(key);
if (pos == exifData.end()) throw Error("Key not found");
// Get a pointer to a copy of the value
v = pos->getValue();
// Downcast the Value to its actual type
rv = dynamic_cast<URationalValue*>(v);
if (rv == 0) throw Error("Downcast failed");
// Modify elements through the extended interface of the actual type
rv->value_[2] = std::make_pair(88,77);
// Set the value of the metadatum to the modified value
pos->setValue(rv);
// Delete the memory allocated by getValue
delete v;
exifPrint(exifData); exifPrint(exifData);
// ************************************************************************* exifData.writeThumbnail("thumb");
// Delete metadata from the Exif data container
// Delete a metadatum by its key
key = "Image.DateTime.DateTimeOriginal";
exifData.erase(key);
// Delete the metadatum at iterator position pos
key = "Image.ImageCharacteristics.PrimaryChromaticities";
pos = exifData.findKey(key);
if (pos == exifData.end()) throw Error("Key not found");
exifData.erase(pos);
exifPrint(exifData);
return 0; return rc;
} }
catch (Error& e) { catch (Error& e) {
std::cout << "Caught Exif exception '" << e << "'\n"; std::cout << "Caught Exif exception '" << e << "'\n";
@ -149,7 +64,7 @@ void exifPrint(const ExifData& exifData)
<< i->count() << " " << i->count() << " "
<< std::dec << i->value() << std::dec << i->value()
<< " | " << i->key() // << " | " << i->key()
<< " | " << i->ifdName() << " | " << i->ifdName()
<< " | " << i->ifdIdx() << " | " << i->ifdIdx()

Loading…
Cancel
Save