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
Version: $Name: $ $Revision: 1.10 $
Version: $Name: $ $Revision: 1.11 $
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
History: 26-Jan-04, ahu: created
*/
@ -38,16 +38,6 @@
#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
namespace Exif {
@ -139,8 +129,8 @@ namespace Exif {
return 0;
} // JpegImage::readExifData
TiffHeader::TiffHeader()
: byteOrder_(littleEndian), tag_(0x002a), offset_(0x00000008)
TiffHeader::TiffHeader(ByteOrder byteOrder)
: byteOrder_(byteOrder), tag_(0x002a), offset_(0x00000008)
{
}
@ -361,7 +351,7 @@ namespace Exif {
return *this;
} // Metadatum::operator=
void Metadatum::setValue(Value* value)
void Metadatum::setValue(const Value* value)
{
delete value_;
value_ = value->clone();
@ -374,8 +364,8 @@ namespace Exif {
}
Ifd::Entry::Entry()
: ifdIdx_(-1), tag_(0), type_(0), count_(0), offset_(0),
data_(0), size_(0)
: ifdId_(ifdIdNotSet), ifdIdx_(-1), tag_(0), type_(0), count_(0),
offset_(0), data_(0), size_(0)
{
}
@ -385,8 +375,8 @@ namespace Exif {
}
Ifd::Entry::Entry(const Entry& rhs)
: ifdIdx_(rhs.ifdIdx_), tag_(rhs.tag_), type_(rhs.type_),
count_(rhs.count_), offset_(rhs.offset_),
: ifdId_(rhs.ifdId_), ifdIdx_(rhs.ifdIdx_), tag_(rhs.tag_),
type_(rhs.type_), count_(rhs.count_), offset_(rhs.offset_),
data_(0), size_(rhs.size_)
{
if (rhs.data_) {
@ -398,6 +388,7 @@ namespace Exif {
Ifd::Entry::Entry& Ifd::Entry::operator=(const Entry& rhs)
{
if (this == &rhs) return *this;
ifdId_ = rhs.ifdId_;
ifdIdx_ = rhs.ifdIdx_;
tag_ = rhs.tag_;
type_ = rhs.type_;
@ -414,7 +405,7 @@ namespace Exif {
}
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();
for (int i=0; i<n; ++i) {
Entry e;
e.ifdId_ = ifdId_;
e.ifdIdx_ = i;
e.tag_ = getUShort(buf+o, byteOrder);
e.type_ = getUShort(buf+o+2, byteOrder);
@ -439,21 +431,20 @@ namespace Exif {
o += 12;
}
next_ = getULong(buf+o, byteOrder);
size_ = 2 + 12 * entries_.size() + 4;
// Guess the offset if it was not given. The guess is based
// on the assumption that the smallest offset points to a data
// buffer directly following the IFD.
// Subsequently all offsets of IFD entries need to be recalculated.
const Entries::iterator eb = entries_.begin();
const Entries::iterator ee = entries_.end();
Entries::iterator i = eb;
const iterator eb = entries_.begin();
const iterator ee = entries_.end();
iterator i = eb;
if (offset_ == 0 && i != ee) {
// Find the entry with the smallest offset
i = std::min_element(eb, ee, cmpOffset);
// Set the guessed IFD offset
if (i->size_ > 4) {
offset_ = i->offset_ - size_;
offset_ = i->offset_ - size();
}
}
@ -475,24 +466,29 @@ namespace Exif {
return 0;
} // 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(),
FindEntryByTag(tag));
}
Ifd::Entries::iterator Ifd::findTag(uint16 tag)
Ifd::iterator Ifd::findTag(uint16 tag)
{
return std::find_if(entries_.begin(), entries_.end(),
FindEntryByTag(tag));
}
void Ifd::sortByTag()
{
sort(entries_.begin(), entries_.end(), cmpTag);
}
int Ifd::readSubIfd(
Ifd& dest, const char* buf, ByteOrder byteOrder, uint16 tag
) const
{
int rc = 0;
Entries::const_iterator pos = findTag(tag);
const_iterator pos = findTag(tag);
if (pos != entries_.end()) {
rc = dest.read(buf + pos->offset_, byteOrder, pos->offset_);
}
@ -509,15 +505,15 @@ namespace Exif {
// Add all directory entries to the data buffer
long dataSize = 0;
const Entries::const_iterator b = entries_.begin();
const Entries::const_iterator e = entries_.end();
Entries::const_iterator i = b;
const const_iterator b = entries_.begin();
const const_iterator e = entries_.end();
const_iterator i = b;
for (; i != e; ++i) {
us2Data(buf+o, i->tag_, byteOrder);
us2Data(buf+o+2, i->type_, byteOrder);
ul2Data(buf+o+4, i->count_, byteOrder);
if (i->size_ > 4) {
ul2Data(buf+o+8, offset + size_ + dataSize, byteOrder);
ul2Data(buf+o+8, offset + size() + dataSize, byteOrder);
dataSize += i->size_;
}
else {
@ -529,7 +525,7 @@ namespace Exif {
// Add the offset to the next IFD to the data buffer pointing
// directly behind this IFD and its data
if (next_ != 0) {
ul2Data(buf+o, offset + size_ + dataSize, byteOrder);
ul2Data(buf+o, offset + size() + dataSize, byteOrder);
}
else {
ul2Data(buf+o, 0, byteOrder);
@ -545,7 +541,59 @@ namespace Exif {
}
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
{
@ -560,9 +608,9 @@ namespace Exif {
<< prefix << "Entry Tag Format (Bytes each) Number Offset\n"
<< prefix << "----- ------ --------------------- ------ -----------\n";
const Entries::const_iterator b = entries_.begin();
const Entries::const_iterator e = entries_.end();
Entries::const_iterator i = b;
const const_iterator b = entries_.begin();
const const_iterator e = entries_.end();
const_iterator i = b;
for (; i != e; ++i) {
std::ostringstream offset;
if (i->size_ <= 4) {
@ -605,43 +653,110 @@ namespace Exif {
} // Ifd::print
// Todo: Finish the implementation (TIFF thumbnails)
// - do we need to sum up the strips??
int Thumbnail::read(
const char* buf, const ExifData& exifData, ByteOrder byteOrder
)
int Thumbnail::read(const char* buf,
const ExifData& exifData,
ByteOrder byteOrder)
{
int rc = 0;
std::string key = "Thumbnail.ImageStructure.Compression";
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();
if (compression == 6) {
key = "Thumbnail.RecordingOffset.JPEGInterchangeFormat";
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;
rc = readJpegImage(buf, exifData);
}
else {
// invalid compression value
return 5;
rc = readTiffImage(buf, exifData, byteOrder);
}
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;
}
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;
file.write(thumbnail_.data(), thumbnail_.size());
file.write(image_.data(), image_.size());
if (!file.good()) return 2;
return 0;
}
@ -706,14 +821,14 @@ namespace Exif {
// Copy all entries from the IFDs to the internal metadata
metadata_.clear();
add(ifd0, byteOrder());
add(exifIfd, byteOrder());
add(iopIfd, byteOrder());
add(gpsIfd, byteOrder());
add(ifd1, byteOrder());
add(ifd1ExifIfd, byteOrder());
add(ifd1IopIfd, byteOrder());
add(ifd1GpsIfd, byteOrder());
add(ifd0.begin(), ifd0.end(), byteOrder());
add(exifIfd.begin(), exifIfd.end(), byteOrder());
add(iopIfd.begin(), iopIfd.end(), byteOrder());
add(gpsIfd.begin(), gpsIfd.end(), byteOrder());
add(ifd1.begin(), ifd1.end(), byteOrder());
add(ifd1ExifIfd.begin(), ifd1ExifIfd.end(), byteOrder());
add(ifd1IopIfd.begin(), ifd1IopIfd.end(), byteOrder());
add(ifd1GpsIfd.begin(), ifd1GpsIfd.end(), byteOrder());
// Read the thumbnail
thumbnail_.read(buf, *this, byteOrder());
@ -733,42 +848,36 @@ namespace Exif {
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 end = ifd.end();
Ifd::const_iterator i = begin;
for (; i != end; ++i) {
Value* value = Value::create(TypeId(i->type_));
value->read(i->data_, i->size_, byteOrder);
Metadatum md(i->tag_, i->type_, ifd.ifdId(), i->ifdIdx_);
iterator k = findKey(md.key());
if (k != this->end()) {
k->setValue(value);
}
else {
md.setValue(value);
add(md);
}
Metadatum md(i->tag_, i->type_, i->ifdId_, i->ifdIdx_, value);
delete value;
add(md);
}
}
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()) {
i->setValue(value);
i->setValue(&metadatum.value());
}
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
{
return std::find_if(metadata_.begin(), metadata_.end(),
@ -950,13 +1059,7 @@ namespace Exif {
os << std::dec << std::setfill(' ');
}
} // namespace Exif
// *****************************************************************************
// local definitions
namespace {
bool cmpOffset(const Exif::Ifd::Entry& lhs, const Exif::Ifd::Entry& rhs)
bool cmpOffset(const Ifd::Entry& lhs, const Ifd::Entry& rhs)
{
// We need to ignore entries with size <= 4, so by definition,
// entries with size <= 4 are greater than those with size > 4
@ -970,4 +1073,9 @@ namespace {
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
@brief Encoding and decoding of %Exif data
@version $Name: $ $Revision: 1.11 $
@version $Name: $ $Revision: 1.12 $
@author Andreas Huggel (ahu)
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
@date 09-Jan-04, ahu: created
@ -127,19 +127,22 @@ namespace Exif {
//! Type to express the byte order (little or big endian)
enum ByteOrder { littleEndian, bigEndian };
//! Helper class modelling the Tiff header structure.
//! Helper class modelling the TIFF header structure.
class TiffHeader {
public:
//! Default constructor.
TiffHeader();
//! Read the Tiff header from a data buffer. Returns 0 if successful.
/*!
@brief Default constructor. Optionally sets the byte order
(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);
/*!
@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.
*/
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; }
//! @name Accessors
//@{
@ -148,9 +151,9 @@ namespace Exif {
//! Return the tag value.
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
Tiff header.
TIFF header.
*/
uint32 offset() const { return offset_; }
//@}
@ -441,7 +444,7 @@ namespace Exif {
/*!
@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.
Uses Value::read(const std::string& buf). If the metadatum does
@ -493,6 +496,19 @@ namespace Exif {
//! 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.
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)
@ -576,6 +592,8 @@ namespace Exif {
const char* typeName() const
{ return ExifTags::typeName(TypeId(type_)); }
//! The IFD id (redundant, it is also at the IFD itself)
IfdId ifdId_;
//! Position in the IFD
int ifdIdx_;
//! Tag
@ -607,6 +625,33 @@ namespace Exif {
iterator begin() { return entries_.begin(); }
//! End of the entries
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
class FindEntryByTag {
@ -628,9 +673,9 @@ namespace Exif {
@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
IFD data (as opposed to readSubIfd).
IFD data (unlike the readSubIfd() method).
@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
using the assumption that the smallest offset of all IFD
directory entries points to a data buffer immediately follwing
@ -645,7 +690,7 @@ namespace Exif {
@param dest References the destination IFD.
@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 tag Tag to look for.
@ -655,64 +700,93 @@ namespace Exif {
Ifd& dest, const char* buf, ByteOrder byteOrder, uint16 tag
) const;
/*!
@brief Copy the IFD to a data array, returns a reference to the
data buffer. The pointer to the next IFD will be adjusted to an
offset from the start of the Tiff header to the position
immediately following the converted IFD.
@brief Copy the IFD to a data array, returns the number of bytes
written. If the pointer to the next IFD is not 0, it will be
adjusted to an offset from the start of the TIFF header to the
position immediately following this IFD.
@param buf Pointer to the data buffer.
@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
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.
*/
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
//@{
//! Ifd id of the IFD
IfdId ifdId() const { return ifdId_; }
//! Offset of the IFD from SOI
long offset() const { return offset_; }
//! Find an IFD entry by tag, return a const iterator into the entries list
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
//! Get the offset to the next IFD from the start of the TIFF header
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:
Entries entries_; // IFD entries
IfdId ifdId_; // IFD Id
long offset_; // offset of the IFD from the start of
// Tiff header
// TIFF header
long next_; // offset of next IFD from the start of
// the Tiff header
long size_; // size of the IFD in bytes
// the TIFF header
}; // class Ifd
//! %Thumbnail data Todo: implement this properly
class Thumbnail {
public:
//! Read the thumbnail from the data buffer, return 0 if successfull
int read(const char* buf, const ExifData& exifData, ByteOrder byteOrder);
//! %Thumbnail image types
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
int write(const std::string& path) const;
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
/*!
@ -728,7 +802,6 @@ namespace Exif {
Todo:
- A constructor which creates a minimal valid set of %Exif data
- Support to add, delete, edit, read data (maybe also in Metadata)
*/
class ExifData {
public:
@ -752,16 +825,19 @@ namespace Exif {
int read(const char* buf, long len);
//! Write %Exif data to a data buffer, return number of bytes written
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;
//! 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(); }
/*!
@brief Add all entries of an IFD to the Exif metadata. Checks for
duplicates: if a metadatum already exists, its value is
@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.
*/
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.
This method copies (clones) the value. If a metadatum with the
@ -769,6 +845,13 @@ namespace Exif {
metadatum is added.
*/
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
typedef Metadata::iterator iterator;
//! Metadata const iterator type
@ -790,17 +873,15 @@ namespace Exif {
//! Delete the metadatum at iterator position 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
{ return thumbnail_.write(path); }
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
TiffHeader tiffHeader_;
Metadata metadata_;
@ -858,6 +939,20 @@ namespace Exif {
//! Print len bytes from buf in hex and ASCII format to the given stream
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

@ -1,29 +1,9 @@
// ***************************************************************** -*- C++ -*-
/*
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
*
* This program is part of the Exiv2 distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
Abstract : Sample code to add, modify and delete Exif metadata
Abstract : This is playground code, do what you want with it.
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
Version : $Name: $ $Revision: 1.8 $
History : 26-Jan-04, ahu: created
Version : $Name: $ $Revision: 1.9 $
*/
// *****************************************************************************
// included header files
@ -40,88 +20,23 @@ void exifPrint(const ExifData& exifData);
// *****************************************************************************
// Main
int main()
int main(int argc, char* const argv[])
try {
// ExifData is the container for all metadata
ExifData exifData;
// *************************************************************************
// Add metadata to the Exif data
// 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
key = "Image.ImageCharacteristics.PrimaryChromaticities";
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);
// *************************************************************************
// 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);
if (argc != 2) {
std::cout << "Usage: exiftest file\n";
return 1;
}
int rc = exifData.read(argv[1]);
if (rc) throw Error("Reading Exif data failed");
exifPrint(exifData);
exifData.writeThumbnail("thumb");
return 0;
return rc;
}
catch (Error& e) {
std::cout << "Caught Exif exception '" << e << "'\n";
@ -149,7 +64,7 @@ void exifPrint(const ExifData& exifData)
<< i->count() << " "
<< std::dec << i->value()
<< " | " << i->key()
// << " | " << i->key()
<< " | " << i->ifdName()
<< " | " << i->ifdIdx()

Loading…
Cancel
Save