Implemented ExifData::copy() and related Thumbnail stuff

This commit is contained in:
Andreas Huggel 2004-02-03 12:24:35 +00:00
parent c2f61538d8
commit 39172050a8
3 changed files with 524 additions and 223 deletions

View File

@ -20,13 +20,13 @@
*/
/*
File: exif.cpp
Version: $Name: $ $Revision: 1.13 $
Version: $Name: $ $Revision: 1.14 $
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
History: 26-Jan-04, ahu: created
*/
// *****************************************************************************
#include "rcsid.hpp"
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.13 $ $RCSfile: exif.cpp,v $")
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.14 $ $RCSfile: exif.cpp,v $")
// *****************************************************************************
// included header files
@ -297,11 +297,11 @@ namespace Exif {
return os << value_;
}
Metadatum::Metadatum(uint16 tag, uint16 type,
IfdId ifdId, int ifdIdx, Value* value)
: tag_(tag), ifdId_(ifdId), ifdIdx_(ifdIdx), value_(0)
Metadatum::Metadatum(const Entry& e, ByteOrder byteOrder)
: tag_(e.tag()), ifdId_(e.ifdId()), ifdIdx_(e.ifdIdx()), value_(0)
{
if (value) value_ = value->clone();
value_ = Value::create(TypeId(e.type()));
value_->read(e.data(), e.size(), byteOrder);
key_ = std::string(ifdItem())
+ "." + std::string(sectionName())
@ -368,21 +368,21 @@ namespace Exif {
value_->read(buf);
}
Ifd::RawEntry::RawEntry()
RawEntry::RawEntry()
: ifdId_(ifdIdNotSet), ifdIdx_(-1),
tag_(0), type_(0), count_(0), offset_(0), size_(0)
{
memset(offsetData_, 0x0, 4);
}
Ifd::Entry::Entry(bool alloc)
Entry::Entry(bool alloc)
: alloc_(alloc), status_(valid), ifdId_(ifdIdNotSet), ifdIdx_(-1),
tag_(0), type_(0), count_(0), offset_(0), size_(0), data_(0)
{
memset(offsetData_, 0x0, 4);
}
Ifd::Entry::Entry(const RawEntry& e, char* buf, bool alloc)
Entry::Entry(const RawEntry& e, const char* buf, bool alloc)
: alloc_(alloc), status_(valid), ifdId_(e.ifdId_), ifdIdx_(e.ifdIdx_),
tag_(e.tag_), type_(e.type_), count_(e.count_), offset_(e.offset_),
size_(e.size_), data_(0)
@ -393,7 +393,7 @@ namespace Exif {
memcpy(data_, buf + offset_, size_);
}
else {
data_ = buf + offset_;
data_ = const_cast<char*>(buf) + offset_;
}
}
else {
@ -401,12 +401,12 @@ namespace Exif {
}
}
Ifd::Entry::~Entry()
Entry::~Entry()
{
if (alloc_) delete[] data_;
}
Ifd::Entry::Entry(const Entry& rhs)
Entry::Entry(const Entry& rhs)
: alloc_(rhs.alloc_), status_(rhs.status_), ifdId_(rhs.ifdId_),
ifdIdx_(rhs.ifdIdx_), tag_(rhs.tag_), type_(rhs.type_),
count_(rhs.count_), offset_(rhs.offset_), size_(rhs.size_), data_(0)
@ -423,7 +423,7 @@ namespace Exif {
}
}
Ifd::Entry::Entry& Ifd::Entry::operator=(const Entry& rhs)
Entry::Entry& Entry::operator=(const Entry& rhs)
{
if (this == &rhs) return *this;
alloc_ = rhs.alloc_;
@ -448,20 +448,30 @@ namespace Exif {
data_ = rhs.data_;
}
return *this;
} // Ifd::Entry::operator=
} // Entry::operator=
const char* Ifd::Entry::data() const
const char* Entry::data() const
{
if (size_ > 4) return data_;
return offsetData_;
}
Ifd::Ifd(IfdId ifdId, bool alloc)
: alloc_(alloc), ifdId_(ifdId), offset_(0), next_(0)
void Entry::setOffset(uint32 offset, ByteOrder byteOrder)
{
if (size_ > 4) {
offset_ = offset;
}
else {
ul2Data(offsetData_, offset, byteOrder);
}
}
Ifd::Ifd(IfdId ifdId, uint32 offset, bool alloc)
: alloc_(alloc), ifdId_(ifdId), offset_(offset), next_(0)
{
}
int Ifd::read(char* buf, ByteOrder byteOrder, long offset)
int Ifd::read(const char* buf, ByteOrder byteOrder, long offset)
{
offset_ = offset;
int n = getUShort(buf, byteOrder);
@ -574,15 +584,8 @@ namespace Exif {
o += 12;
}
// 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);
}
else {
ul2Data(buf+o, 0, byteOrder);
}
o += 4;
// Add the offset to the next IFD to the data buffer
o += ul2Data(buf+o, next_, byteOrder);
// Add the data of all IFD entries to the data buffer
for (i = b; i != e; ++i) {
@ -649,6 +652,30 @@ namespace Exif {
}
}
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;
entries_.push_back(Entry(e, 0, alloc_));
pos = findTag(tag);
}
pos->setOffset(offset, byteOrder);
} // Ifd::setOffset
long Ifd::size() const
{
if (entries_.size() == 0) return 0;
return 2 + 12 * entries_.size() + 4;
}
long Ifd::dataSize() const
{
long dataSize = 0;
@ -748,7 +775,7 @@ namespace Exif {
image_ = std::string(buf + offset, size);
type_ = JPEG;
return 0;
}
} // Thumbnail::readJpegImage
int Thumbnail::readTiffImage(const char* buf,
const ExifData& exifData,
@ -795,9 +822,6 @@ namespace Exif {
newOffsets.setValue(os.str());
ifd1.add(newOffsets, tiffHeader.byteOrder());
// ahu: Todo: remove this
ifd1.print(std::cout);
// Finally, sort and copy the IFD
ifd1.sortByTag();
ifd1.copy(data + ifdOffset, tiffHeader.byteOrder(), ifdOffset);
@ -807,7 +831,7 @@ namespace Exif {
type_ = TIFF;
return 0;
}
} // Thumbnail::readTiffImage
int Thumbnail::write(const std::string& path) const
{
@ -825,8 +849,152 @@ namespace Exif {
file.write(image_.data(), image_.size());
if (!file.good()) return 2;
return 0;
} // Thumbnail::write
void Thumbnail::update(ExifData& exifData) const
{
// Todo: properly synchronize the Exif data with the actual thumbnail,
// i.e., synch all relevant metadata
switch (type_) {
case JPEG:
updateJpegImage(exifData);
break;
case TIFF:
updateTiffImage(exifData);
break;
}
} // Thumbnail::update
void Thumbnail::updateJpegImage(ExifData& exifData) const
{
std::string key = "Thumbnail.RecordingOffset.JPEGInterchangeFormat";
ExifData::iterator pos = exifData.findKey(key);
if (pos == exifData.end()) {
Value *value = Value::create(unsignedLong);
exifData.add(key, value);
delete value;
pos = exifData.findKey(key);
}
pos->setValue("0");
key = "Thumbnail.RecordingOffset.JPEGInterchangeFormatLength";
pos = exifData.findKey(key);
if (pos == exifData.end()) {
Value *value = Value::create(unsignedLong);
exifData.add(key, value);
delete value;
pos = exifData.findKey(key);
}
std::ostringstream os;
os << image_.size();
pos->setValue(os.str());
} // Thumbnail::updateJpegImage
void Thumbnail::updateTiffImage(ExifData& exifData) const
{
TiffHeader tiffHeader;
tiffHeader.read(image_.data());
long offset = tiffHeader.offset();
Ifd ifd1(ifd1);
ifd1.read(image_.data() + offset, tiffHeader.byteOrder(), offset);
// Create metadata from the StripOffsets and StripByteCounts entries
// and add these to the Exif data, replacing existing entries
Ifd::const_iterator pos = ifd1.findTag(0x0111);
if (pos == ifd1.end()) throw Error("Bad thumbnail (0x0111)");
exifData.add(Metadatum(*pos, tiffHeader.byteOrder()));
pos = ifd1.findTag(0x0117);
if (pos == ifd1.end()) throw Error("Bad thumbnail (0x0117)");
exifData.add(Metadatum(*pos, tiffHeader.byteOrder()));
} // Thumbnail::updateTiffImage
long Thumbnail::copy(char* buf) const
{
long ret = 0;
switch (type_) {
case JPEG:
ret = copyJpegImage(buf);
break;
case TIFF:
ret = copyTiffImage(buf);
break;
}
return ret;
}
long Thumbnail::copyJpegImage(char* buf) const
{
memcpy(buf, image_.data(), image_.size());
return image_.size();
}
long Thumbnail::copyTiffImage(char* buf) const
{
// Read the TIFF header and IFD from the thumbnail image
TiffHeader tiffHeader;
tiffHeader.read(image_.data());
long offset = tiffHeader.offset();
Ifd thumbIfd(ifd1);
thumbIfd.read(image_.data() + offset, tiffHeader.byteOrder(), offset);
offset = thumbIfd.offset() + thumbIfd.size() + thumbIfd.dataSize();
long size = image_.size() - offset;
memcpy(buf, image_.data() + offset, size);
return size;
}
void Thumbnail::setOffsets(Ifd& ifd1, ByteOrder byteOrder) const
{
switch (type_) {
case JPEG:
setJpegImageOffsets(ifd1, byteOrder);
break;
case TIFF:
setTiffImageOffsets(ifd1, byteOrder);
break;
}
}
void Thumbnail::setJpegImageOffsets(Ifd& ifd1, ByteOrder byteOrder) const
{
Ifd::iterator pos = ifd1.findTag(0x0201);
if (pos == ifd1.end()) throw Error("Bad thumbnail (0x0201)");
pos->setOffset(ifd1.offset() + ifd1.size() + ifd1.dataSize(), byteOrder);
}
void Thumbnail::setTiffImageOffsets(Ifd& ifd1, ByteOrder byteOrder) const
{
// Read the TIFF header and IFD from the thumbnail image
TiffHeader tiffHeader;
tiffHeader.read(image_.data());
long offset = tiffHeader.offset();
Ifd thumbIfd(ifd1);
thumbIfd.read(image_.data() + offset, tiffHeader.byteOrder(), offset);
// Adjust the StripOffsets, assuming that the existing TIFF strips
// start immediately after the thumbnail IFD
long shift = ifd1.offset() + ifd1.size() + ifd1.dataSize()
- thumbIfd.offset() - thumbIfd.size() - thumbIfd.dataSize();
Ifd::const_iterator pos = thumbIfd.findTag(0x0111);
if (pos == thumbIfd.end()) throw Error("Bad thumbnail (0x0111)");
Metadatum offsets(*pos, tiffHeader.byteOrder());
std::ostringstream os;
for (long k = 0; k < offsets.count(); ++k) {
os << offsets.toLong(k) + shift << " ";
}
offsets.setValue(os.str());
// Write the offsets to IFD1, encoded in the corresponding byte order
ifd1.add(offsets, byteOrder);
} // Thumbnail::setTiffImageOffsets
ExifData::ExifData()
: ifd0_(ifd0, false), exifIfd_(exifIfd, false), iopIfd_(iopIfd, false),
gpsIfd_(gpsIfd, false), ifd1_(ifd1, false), valid_(false),
@ -852,7 +1020,7 @@ namespace Exif {
// Copy the data buffer
delete[] data_;
data_ = new char[len];
::memcpy(data_, buf, len);
memcpy(data_, buf, len);
size_ = len;
valid_ = true;
@ -908,30 +1076,82 @@ namespace Exif {
return ret;
} // ExifData::read
long ExifData::copy(char* buf) const
long ExifData::copy(char* buf)
{
// Todo: decide if we can just dump the buffer that we
// should have somewhere
// Todo: Check if the internal IFDs, i.e., the data buffer, are valid;
// if they are, update the IFDs and use the buffer
// Else do it the hard way...
// Copy the TIFF header
// Todo: What about the offset??
// len += tiffHeader_.copy(buf);
long ifd0Offset = tiffHeader_.copy(buf);
// // 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);
// Build IFD0
Ifd ifd0(ifd0, ifd0Offset);
ifd0.add(begin(), end(), byteOrder());
// Copy all IFDs
// Build Exif IFD from metadata
long exifIfdOffset = ifd0Offset + ifd0.size() + ifd0.dataSize();
Ifd exifIfd(exifIfd, exifIfdOffset);
exifIfd.add(begin(), end(), byteOrder());
// Set the offset to the Exif IFD in IFD0
ifd0.erase(0x8769);
if (exifIfd.size() > 0) {
ifd0.setOffset(0x8769, exifIfdOffset, byteOrder());
}
// Copy thumbnail data
// Build Interoperability IFD from metadata
long iopIfdOffset = exifIfdOffset + exifIfd.size() + exifIfd.dataSize();
Ifd iopIfd(iopIfd, iopIfdOffset);
iopIfd.add(begin(), end(), byteOrder());
return 0;
}
// Set the offset to the Interoperability IFD in Exif IFD
exifIfd.erase(0xa005);
if (iopIfd.size() > 0) {
exifIfd.setOffset(0xa005, iopIfdOffset, byteOrder());
}
// Build GPSInfo IFD from metadata
long gpsIfdOffset = iopIfdOffset + iopIfd.size() + iopIfd.dataSize();
Ifd gpsIfd(gpsIfd, gpsIfdOffset);
gpsIfd.add(begin(), end(), byteOrder());
// Set the offset to the GPSInfo IFD in IFD0
ifd0.erase(0x8825);
if (gpsIfd.size() > 0) {
ifd0.setOffset(0x8825, gpsIfdOffset, byteOrder());
}
// Update Exif data from thumbnail, build IFD1 from updated metadata
thumbnail_.update(*this);
long ifd1Offset = gpsIfdOffset + gpsIfd.size() + gpsIfd.dataSize();
Ifd ifd1(ifd1, ifd1Offset);
ifd1.add(begin(), end(), byteOrder());
thumbnail_.setOffsets(ifd1, byteOrder());
long thumbOffset = ifd1Offset + ifd1.size() + ifd1.dataSize();
// Set the offset to IFD1 in IFD0
if (ifd1.size() > 0) {
ifd0.setNext(ifd1Offset);
}
// Copy all IFDs and the thumbnail image to the data buffer
ifd0.sortByTag();
ifd0.copy(buf + ifd0Offset, byteOrder(), ifd0Offset);
exifIfd.sortByTag();
exifIfd.copy(buf + exifIfdOffset, byteOrder(), exifIfdOffset);
iopIfd.sortByTag();
iopIfd.copy(buf + iopIfdOffset, byteOrder(), iopIfdOffset);
gpsIfd.sortByTag();
gpsIfd.copy(buf + gpsIfdOffset, byteOrder(), gpsIfdOffset);
ifd1.sortByTag();
ifd1.copy(buf + ifd1Offset, byteOrder(), ifd1Offset);
long len = thumbnail_.copy(buf + thumbOffset);
return len + thumbOffset;
} // ExifData::copy
long ExifData::size() const
{
@ -945,11 +1165,7 @@ namespace Exif {
{
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(), i->ifdId(), i->ifdIdx(), value);
delete value;
add(md);
add(Metadatum(*i, byteOrder));
}
}
@ -1148,9 +1364,9 @@ namespace Exif {
os << (width > pos ? "" : align.substr(width)) << ss.str() << "\n";
}
os << std::dec << std::setfill(' ');
}
} // hexdump
bool cmpOffset(const Ifd::RawEntry& lhs, const Ifd::RawEntry& rhs)
bool cmpOffset(const RawEntry& lhs, const RawEntry& rhs)
{
// We need to ignore entries with size <= 4, so by definition,
// entries with size <= 4 are greater than those with size > 4
@ -1164,7 +1380,7 @@ namespace Exif {
return lhs.offset_ < rhs.offset_;
}
bool cmpTag(const Ifd::Entry& lhs, const Ifd::Entry& rhs)
bool cmpTag(const Entry& lhs, const Entry& rhs)
{
return lhs.tag() < rhs.tag();
}

View File

@ -21,7 +21,7 @@
/*!
@file exif.hpp
@brief Encoding and decoding of %Exif data
@version $Name: $ $Revision: 1.13 $
@version $Name: $ $Revision: 1.14 $
@author Andreas Huggel (ahu)
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
@date 09-Jan-04, ahu: created
@ -47,6 +47,7 @@ namespace Exif {
// *****************************************************************************
// class declarations
class ExifData;
class Entry;
// *****************************************************************************
// class definitions
@ -416,12 +417,9 @@ namespace Exif {
class Metadatum {
public:
/*!
@brief Constructor for tag data read from an IFD, when all
information is available. %Metadatum copies (clones) the value
if one is provided.
@brief Constructor to build a metadatum from an IFD entry.
*/
Metadatum(uint16 tag, uint16 type,
IfdId ifdId, int ifdIdx, Value* value =0);
Metadatum(const Entry& e, ByteOrder byteOrder);
/*!
@brief Constructor for new tags created by an application, which only
needs to provide a key / value pair. %Metadatum copies (clones)
@ -569,7 +567,153 @@ namespace Exif {
private:
std::string key_;
}; // class FindEntryByTag
}; // class FindMetadatumByTag
/*!
@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
allocation.
*/
class Entry {
public:
/*!
@brief The status of an Entry for entries without memory management.
<TABLE BORDER=0>
<TR><TD>valid:</TD>
<TD>The entry is valid, including in particular, the data
pointed to is valid and up to date.</TD></TR>
<TR><TD>invalid:</TD>
<TD>The entry is not valid, it has been invalidated by a
modification of the corresponding metadata, which did not
fit into the original data buffer.</TD></TR>
<TR><TD>erased:</TD>
<TD>The entry has been deleted.</TD></TR>
</TABLE>
*/
enum Status { valid, invalid, erased };
/*!
@brief Default constructor. The entry allocates memory for its
data if alloc is true (the default), otherwise it remembers
just the pointers into a read and writeable data buffer which
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);
//! @name Accessors
//@{
//! Return the status
Status status() const { return status_; }
//! 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 number of components in the value
uint32 count() const { return count_; }
//! 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;
//@}
//! Return the size in bytes of one element of this type
long typeSize() const
{ return ExifTags::typeSize(TypeId(type_)); }
//! Return the name of the type
const char* typeName() const
{ return ExifTags::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);
//! Set the status
void setStatus(Status status) { status_ = status; }
private:
/*!
@brief True: requires memory allocation and deallocation,<BR>
False: no memory management needed.
*/
bool alloc_;
Status status_; // Status of this entry
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
}; // class Entry
//! Container type to hold all IFD directory entries
typedef std::vector<Entry> Entries;
/*!
@brief Models an IFD (Image File Directory)
@ -597,143 +741,7 @@ namespace Exif {
@brief Constructor. Allows to set the IFD identifier and choose
whether or not memory management is required for the Entries.
*/
explicit Ifd(IfdId ifdId =ifdIdNotSet, bool alloc =true);
//! Simple structure for 'raw' IFD directory entries (without the data)
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 allocation.
*/
class Entry {
public:
/*!
@brief The status of an Entry for entries without memory management.
<TABLE BORDER=0>
<TR><TD>valid:</TD>
<TD>The entry is valid, including in particular, the data
pointed to is valid and up to date.</TD></TR>
<TR><TD>invalid:</TD>
<TD>The entry is not valid, it has been invalidated by a
modification of the corresponding metadata, which did not
fit into the original data buffer.</TD></TR>
<TR><TD>erased:</TD>
<TD>The entry has been deleted.</TD></TR>
</TABLE>
*/
enum Status { valid, invalid, erased };
/*!
@brief Default constructor. The entry allocates memory for its
data if alloc is true (the default), otherwise it remembers
just the pointers into a read and writeable data buffer which
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, char* buf, bool alloc =true);
//! Destructor
~Entry();
//! Copy constructor
Entry(const Entry& rhs);
//! Assignment operator
Entry& operator=(const Entry& rhs);
//! @name Accessors
//@{
//! Return the status
Status status() const { return status_; }
//! 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 number of components in the value
uint32 count() const { return count_; }
//! 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;
//@}
//! Set the status
void setStatus(Status status) { status_ = status; }
//! Return the size in bytes of one element of this type
long typeSize() const
{ return ExifTags::typeSize(TypeId(type_)); }
//! Return the name of the type
const char* typeName() const
{ return ExifTags::typeName(TypeId(type_)); }
private:
/*!
@brief True: requires memory allocation and deallocation,<BR>
False: no memory management needed.
*/
bool alloc_;
Status status_; // Status of this entry
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
}; // class Entry
//! Container type to hold all IFD directory entries
typedef std::vector<Entry> Entries;
explicit Ifd(IfdId ifdId =ifdIdNotSet, uint32 offset =0, bool alloc =true);
//! Entries const iterator type
typedef Entries::const_iterator const_iterator;
@ -757,6 +765,17 @@ 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 all metadata in the range from iterator position begin to
iterator position end, which have an IFD id matching that of
@ -784,7 +803,7 @@ namespace Exif {
@brief Returns true if the tag of the argument entry is equal
to that of the object.
*/
bool operator()(const Ifd::Entry& entry) const
bool operator()(const Entry& entry) const
{ return tag_ == entry.tag(); }
private:
uint16 tag_;
@ -795,7 +814,7 @@ 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 (unlike the readSubIfd() method).
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
header, if known. If not given, the offset will be guessed
@ -805,7 +824,7 @@ namespace Exif {
@return 0 if successful
*/
int read(char* buf, ByteOrder byteOrder, long offset =0);
int read(const char* buf, ByteOrder byteOrder, long offset =0);
/*!
@brief Read a sub-IFD from the location pointed to by the directory entry
with the given tag.
@ -822,18 +841,28 @@ namespace Exif {
Ifd& dest, char* buf, ByteOrder byteOrder, uint16 tag
) const;
/*!
@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.
@brief Copy the IFD to a data array, return the number of bytes
written.
@param buf Pointer to the data buffer.
First the number of IFD entries is written (2 bytes), followed
by all directory entries: tag (2), type (2), number of data
components (4) and offset to the data or the data, if it
occupies not more than four bytes (4). The directory entries
are followed by the offset of the next IFD (4). All these
fields are encoded according to the byte order argument. Data
that doesn't fit into the offset fields follows immediately
after the IFD entries. The offsets in the IFD are set to
correctly point to the data fields, using the offset parameter
or the offset of the IFD.
@param buf Pointer to the data buffer. The user must ensure that the
buffer has enough memory. Otherwise the call results in
undefined behaviour.
@param byteOrder Applicable byte order (little or big endian).
@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, i.e., the offset
of the IFD will be used.
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, 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;
@ -847,7 +876,7 @@ namespace Exif {
long next() const { return next_; }
//@}
//! Get the size of this IFD in bytes (IFD only, without data)
long size() const { return 2 + 12 * entries_.size() + 4; }
long size() const;
/*!
@brief Return the total size of the data of this IFD in bytes,
sums the size of all directory entries where size is greater
@ -897,14 +926,55 @@ namespace Exif {
ByteOrder byteOrder =littleEndian);
//! Write thumbnail to file path, return 0 if successful
int write(const std::string& path) const;
/*!
@brief Copy the thumbnail image data (without the IFD, if any) to the
data buffer buf. The user must ensure that the buffer has
enough memory. Otherwise the call results in undefined
behaviour. Return the number of characters written.
*/
long copy(char* buf) const;
/*!
@brief Update the %Exif data according to the actual thumbnail image.
If the type of the thumbnail image is JPEG, JPEGInterchangeFormat is
set to 0. If the type is TIFF, StripOffsets are set to the offsets of
the IFD of the thumbnail image itself.
*/
void update(ExifData& exifData) const;
/*!
@brief Update the thumbnail data offsets in IFD1 assuming the
thumbnail data follows immediately after IFD1.
If the type of the thumbnail image is JPEG, JPEGInterchangeFormat is
set to point directly behind the data area of IFD1. If the type is
TIFF, StripOffsets from the thumbnail image are adjusted to point to
the strips, which have to follow immediately after IFD1. Use copy() to
write the thumbnail image data. The offset of IFD1 must be set
correctly. Changing the size of IFD1 invalidates the thumbnail data
offsets set by this method.
*/
void setOffsets(Ifd& ifd1, ByteOrder byteOrder) const;
private:
//! 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);
//! Update the Exif data according to the actual JPEG thumbnail image
void updateJpegImage(ExifData& exifData) const;
//! Update the Exif data according to the actual TIFF thumbnail image
void updateTiffImage(ExifData& exifData) const;
//! Copy the JPEG thumbnail image data to the data buffer buf
long copyJpegImage(char* buf) const;
//! Copy the TIFF thumbnail image data to the data buffer buf
long copyTiffImage(char* buf) const;
//! Update the offsets to the JPEG thumbnail image in the IFD
void setJpegImageOffsets(Ifd& ifd1, ByteOrder byteOrder) const;
//! Update the offsets to the TIFF thumbnail image in the IFD
void setTiffImageOffsets(Ifd& ifd1, ByteOrder byteOrder) const;
std::string image_;
Type type_;
@ -948,15 +1018,19 @@ namespace Exif {
@return 0 if successful
*/
int read(const char* buf, long len);
//! Write %Exif data to a data buffer, return number of bytes written
long copy(char* buf) const;
/*!
@brief Write %Exif data to a data buffer, return number of bytes
written. Updates %Exif data with the metadata from the actual
thumbnail image (overriding existing metadata).
*/
long copy(char* buf);
//! Returns the size of all %Exif data (TIFF header plus metadata)
long size() const;
//! Returns the byte order as specified in the TIFF header
ByteOrder byteOrder() const { return tiffHeader_.byteOrder(); }
/*!
@brief Add all IFD entries in the range from iterator position begin
to iterator position end to the Exif metadata. Checks for
to iterator position end to the %Exif metadata. Checks for
duplicates: if a metadatum already exists, its value is
overwritten.
*/
@ -971,7 +1045,7 @@ namespace Exif {
*/
void add(const std::string& key, Value* value);
/*!
@brief Add a copy of the metadatum to the Exif metadata. If a
@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.
*/
@ -1080,12 +1154,12 @@ namespace Exif {
else false. By definition, entries without an offset are greater
than those with an offset.
*/
bool cmpOffset(const Ifd::RawEntry& lhs, const Ifd::RawEntry& rhs);
bool cmpOffset(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.
*/
bool cmpTag(const Ifd::Entry& lhs, const Ifd::Entry& rhs);
bool cmpTag(const Entry& lhs, const Entry& rhs);
// *****************************************************************************
// template and inline definitions

View File

@ -3,11 +3,11 @@
Abstract : This is playground code, do what you want with it.
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
Version : $Name: $ $Revision: 1.11 $
Version : $Name: $ $Revision: 1.12 $
*/
// *****************************************************************************
#include "rcsid.hpp"
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.11 $ $RCSfile: exiftest.cpp,v $")
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.12 $ $RCSfile: exiftest.cpp,v $")
// *****************************************************************************
// included header files
@ -64,9 +64,20 @@ try {
}
exifPrint(exifData);
exifData.writeThumbnail("thumb");
char* buf = new char[1024*128];
long siz = exifData.copy(buf);
std::cout << siz << " Bytes written.\n"
<< "=======================\n";
ExifData e2;
e2.read(buf, siz);
exifPrint(e2);
e2.writeThumbnail("t2");
return rc;
}
catch (Error& e) {