Implemented ExifData::copy() and related Thumbnail stuff
This commit is contained in:
parent
c2f61538d8
commit
39172050a8
330
src/exif.cpp
330
src/exif.cpp
@ -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();
|
||||
}
|
||||
|
||||
400
src/exif.hpp
400
src/exif.hpp
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user