makernote:

Added MakerNote::offset()
Fixed IfdMakerNote::copy() for IfdMakerNote with a prefix
Various doc fixes

ifd:
Fixed Ifd::erase(pos) to return an iterator
Added Ifd::dataOffset()
Various doc fixes

exif:
Added Thumbnail::offset()
Fixed ExifData::eraseThumbnail()
Fixed ExifData::erase(pos) to return an iterator
v0.27.3
Andreas Huggel 21 years ago
parent 1f943f45d2
commit 0b24a0ad29

@ -20,14 +20,14 @@
*/ */
/* /*
File: exif.cpp File: exif.cpp
Version: $Name: $ $Revision: 1.39 $ Version: $Name: $ $Revision: 1.40 $
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net> Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
History: 26-Jan-04, ahu: created History: 26-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component 11-Feb-04, ahu: isolated as a component
*/ */
// ***************************************************************************** // *****************************************************************************
#include "rcsid.hpp" #include "rcsid.hpp"
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.39 $ $RCSfile: exif.cpp,v $") EXIV2_RCSID("@(#) $Name: $ $Revision: 1.40 $ $RCSfile: exif.cpp,v $")
// Define DEBUG_MAKERNOTE to output debug information to std::cerr // Define DEBUG_MAKERNOTE to output debug information to std::cerr
#undef DEBUG_MAKERNOTE #undef DEBUG_MAKERNOTE
@ -159,7 +159,7 @@ namespace Exif {
} }
TiffThumbnail::TiffThumbnail() TiffThumbnail::TiffThumbnail()
: size_(0), pImage_(0), ifd_(ifd1, 0, false) : offset_(0), size_(0), pImage_(0), ifd_(ifd1, 0, false)
{ {
} }
@ -169,7 +169,8 @@ namespace Exif {
} }
TiffThumbnail::TiffThumbnail(const TiffThumbnail& rhs) TiffThumbnail::TiffThumbnail(const TiffThumbnail& rhs)
: size_(rhs.size_), pImage_(0), ifd_(ifd1, 0, false) : offset_(rhs.offset_), size_(rhs.size_), pImage_(0),
ifd_(ifd1, 0, false)
{ {
if (rhs.pImage_ && rhs.size_ > 0) { if (rhs.pImage_ && rhs.size_ > 0) {
pImage_ = new char[rhs.size_]; pImage_ = new char[rhs.size_];
@ -190,6 +191,7 @@ namespace Exif {
ifd_.read(pNewImage + tiffHeader_.offset(), ifd_.read(pNewImage + tiffHeader_.offset(),
tiffHeader_.byteOrder(), tiffHeader_.offset()); tiffHeader_.byteOrder(), tiffHeader_.offset());
} }
offset_ = rhs.offset_;
size_ = rhs.size_; size_ = rhs.size_;
delete[] pImage_; delete[] pImage_;
pImage_ = pNewImage; pImage_ = pNewImage;
@ -212,10 +214,8 @@ namespace Exif {
// Create IFD (without Exif and GPS tags) from metadata // Create IFD (without Exif and GPS tags) from metadata
Ifd ifd1(ifd1); Ifd ifd1(ifd1);
addToIfd(ifd1, exifData.begin(), exifData.end(), tiffHeader.byteOrder()); addToIfd(ifd1, exifData.begin(), exifData.end(), tiffHeader.byteOrder());
Ifd::iterator i = ifd1.findTag(0x8769); ifd1.erase(0x8769);
if (i != ifd1.end()) ifd1.erase(i); ifd1.erase(0x8825);
i = ifd1.findTag(0x8825);
if (i != ifd1.end()) ifd1.erase(i);
// Do not copy the IFD yet, remember the location and leave a gap // Do not copy the IFD yet, remember the location and leave a gap
long ifdOffset = len; long ifdOffset = len;
@ -229,19 +229,23 @@ namespace Exif {
ExifData::const_iterator sizes = exifData.findKey(key); ExifData::const_iterator sizes = exifData.findKey(key);
if (sizes == exifData.end()) return 2; if (sizes == exifData.end()) return 2;
std::ostringstream os; // for the new strip offsets std::ostringstream os; // for the new strip offsets
long minOffset = 0;
for (long k = 0; k < offsets->count(); ++k) { for (long k = 0; k < offsets->count(); ++k) {
long offset = offsets->toLong(k); long offset = offsets->toLong(k);
long size = sizes->toLong(k); long size = sizes->toLong(k);
memcpy(img.pData_ + len, buf + offset, size); memcpy(img.pData_ + len, buf + offset, size);
os << len << " "; os << len << " ";
len += size; len += size;
}
minOffset = offset; // just to initialize minOffset
}
for (long k = 0; k < offsets->count(); ++k) {
minOffset = std::min(minOffset, offsets->toLong(k));
}
// Update the IFD with the actual strip offsets (replace existing entry) // Update the IFD with the actual strip offsets (replace existing entry)
Metadatum newOffsets(*offsets); Metadatum newOffsets(*offsets);
newOffsets.setValue(os.str()); newOffsets.setValue(os.str());
i = ifd1.findTag(0x0111); ifd1.erase(0x0111);
if (i != ifd1.end()) ifd1.erase(i);
addToIfd(ifd1, newOffsets, tiffHeader.byteOrder()); addToIfd(ifd1, newOffsets, tiffHeader.byteOrder());
// Finally, sort and copy the IFD // Finally, sort and copy the IFD
@ -252,6 +256,7 @@ namespace Exif {
pImage_ = new char[len]; pImage_ = new char[len];
memcpy(pImage_, img.pData_, len); memcpy(pImage_, img.pData_, len);
size_ = len; size_ = len;
offset_ = minOffset;
tiffHeader_.read(pImage_); tiffHeader_.read(pImage_);
ifd_.read(pImage_ + tiffHeader_.offset(), ifd_.read(pImage_ + tiffHeader_.offset(),
tiffHeader_.byteOrder(), tiffHeader_.offset()); tiffHeader_.byteOrder(), tiffHeader_.offset());
@ -326,7 +331,12 @@ namespace Exif {
return size_; return size_;
} }
void TiffThumbnail::setOffsets(Ifd& ifd1, ByteOrder byteOrder) const long TiffThumbnail::offset() const
{
return offset_;
}
void TiffThumbnail::setOffsets(Ifd& ifd1, ByteOrder byteOrder)
{ {
// Adjust the StripOffsets, assuming that the existing TIFF strips // Adjust the StripOffsets, assuming that the existing TIFF strips
// start immediately after the thumbnail IFD // start immediately after the thumbnail IFD
@ -336,20 +346,25 @@ namespace Exif {
if (pos == ifd_.end()) throw Error("Bad thumbnail (0x0111)"); if (pos == ifd_.end()) throw Error("Bad thumbnail (0x0111)");
Metadatum offsets(*pos, tiffHeader_.byteOrder()); Metadatum offsets(*pos, tiffHeader_.byteOrder());
std::ostringstream os; std::ostringstream os;
long minOffset = 0;
for (long k = 0; k < offsets.count(); ++k) { for (long k = 0; k < offsets.count(); ++k) {
os << offsets.toLong(k) + shift << " "; os << offsets.toLong(k) + shift << " ";
minOffset = offsets.toLong(k) + shift; // initialize minOffset
} }
offsets.setValue(os.str()); offsets.setValue(os.str());
for (long k = 0; k < offsets.count(); ++k) {
minOffset = std::min(minOffset, offsets.toLong(k));
}
offset_ = minOffset;
// Update the IFD with the re-calculated strip offsets // Update the IFD with the re-calculated strip offsets
// (replace existing entry) // (replace existing entry)
Ifd::iterator i = ifd1.findTag(0x0111); ifd1.erase(0x0111);
if (i != ifd1.end()) ifd1.erase(i);
addToIfd(ifd1, offsets, byteOrder); addToIfd(ifd1, offsets, byteOrder);
} // TiffThumbnail::setOffsets } // TiffThumbnail::setOffsets
JpegThumbnail::JpegThumbnail() JpegThumbnail::JpegThumbnail()
: size_(0), pImage_(0) : offset_(0), size_(0), pImage_(0)
{ {
} }
@ -359,7 +374,7 @@ namespace Exif {
} }
JpegThumbnail::JpegThumbnail(const JpegThumbnail& rhs) JpegThumbnail::JpegThumbnail(const JpegThumbnail& rhs)
: size_(rhs.size_), pImage_(0) : offset_(rhs.offset_), size_(rhs.size_), pImage_(0)
{ {
if (rhs.pImage_ && rhs.size_ > 0) { if (rhs.pImage_ && rhs.size_ > 0) {
pImage_ = new char[rhs.size_]; pImage_ = new char[rhs.size_];
@ -374,6 +389,7 @@ namespace Exif {
pNewImage = new char[rhs.size_]; pNewImage = new char[rhs.size_];
memcpy(pNewImage, rhs.pImage_, rhs.size_); memcpy(pNewImage, rhs.pImage_, rhs.size_);
} }
offset_ = rhs.offset_;
size_ = rhs.size_; size_ = rhs.size_;
delete[] pImage_; delete[] pImage_;
pImage_ = pNewImage; pImage_ = pNewImage;
@ -396,6 +412,7 @@ namespace Exif {
pImage_ = new char[size]; pImage_ = new char[size];
memcpy(pImage_, buf + offset, size); memcpy(pImage_, buf + offset, size);
size_ = size; size_ = size;
offset_ = offset;
return 0; return 0;
} // JpegThumbnail::read } // JpegThumbnail::read
@ -429,7 +446,7 @@ namespace Exif {
delete value; delete value;
pos = exifData.findKey(key); pos = exifData.findKey(key);
} }
pos->setValue("0"); pos->setValue(toString(offset_));
key = "Thumbnail.RecordingOffset.JPEGInterchangeFormatLength"; key = "Thumbnail.RecordingOffset.JPEGInterchangeFormatLength";
pos = exifData.findKey(key); pos = exifData.findKey(key);
@ -459,18 +476,24 @@ namespace Exif {
return size_; return size_;
} }
void JpegThumbnail::setOffsets(Ifd& ifd1, ByteOrder byteOrder) const long JpegThumbnail::offset() const
{
return offset_;
}
void JpegThumbnail::setOffsets(Ifd& ifd1, ByteOrder byteOrder)
{ {
Ifd::iterator pos = ifd1.findTag(0x0201); Ifd::iterator pos = ifd1.findTag(0x0201);
if (pos == ifd1.end()) throw Error("Bad thumbnail (0x0201)"); if (pos == ifd1.end()) throw Error("Bad thumbnail (0x0201)");
pos->setValue(ifd1.offset() + ifd1.size() + ifd1.dataSize(), byteOrder); offset_ = ifd1.offset() + ifd1.size() + ifd1.dataSize();
pos->setValue(offset_, byteOrder);
} }
ExifData::ExifData() ExifData::ExifData()
: pThumbnail_(0), pMakerNote_(0), ifd0_(ifd0, 0, false), : pThumbnail_(0), pMakerNote_(0), ifd0_(ifd0, 0, false),
exifIfd_(exifIfd, 0, false), iopIfd_(iopIfd, 0, false), exifIfd_(exifIfd, 0, false), iopIfd_(iopIfd, 0, false),
gpsIfd_(gpsIfd, 0, false), ifd1_(ifd1, 0, false), gpsIfd_(gpsIfd, 0, false), ifd1_(ifd1, 0, false),
size_(0), pData_(0) size_(0), pData_(0), compatible_(true)
{ {
} }
@ -571,7 +594,6 @@ namespace Exif {
ifd1_.erase(pos); ifd1_.erase(pos);
ret = -99; ret = -99;
} }
// Copy all entries from the IFDs and the MakerNote to the metadata // Copy all entries from the IFDs and the MakerNote to the metadata
metadata_.clear(); metadata_.clear();
add(ifd0_.begin(), ifd0_.end(), byteOrder()); add(ifd0_.begin(), ifd0_.end(), byteOrder());
@ -582,7 +604,6 @@ namespace Exif {
add(iopIfd_.begin(), iopIfd_.end(), byteOrder()); add(iopIfd_.begin(), iopIfd_.end(), byteOrder());
add(gpsIfd_.begin(), gpsIfd_.end(), byteOrder()); add(gpsIfd_.begin(), gpsIfd_.end(), byteOrder());
add(ifd1_.begin(), ifd1_.end(), byteOrder()); add(ifd1_.begin(), ifd1_.end(), byteOrder());
// Read the thumbnail // Read the thumbnail
readThumbnail(); readThumbnail();
@ -626,7 +647,7 @@ namespace Exif {
// If we can update the internal IFDs and the underlying data buffer // If we can update the internal IFDs and the underlying data buffer
// from the metadata without changing the data size, then it is enough // from the metadata without changing the data size, then it is enough
// to copy the data buffer. // to copy the data buffer.
if (updateEntries()) { if (compatible_ && updateEntries()) {
#ifdef DEBUG_MAKERNOTE #ifdef DEBUG_MAKERNOTE
std::cerr << "->>>>>> using non-intrusive writing <<<<<<-\n"; std::cerr << "->>>>>> using non-intrusive writing <<<<<<-\n";
#endif #endif
@ -845,28 +866,79 @@ namespace Exif {
std::sort(metadata_.begin(), metadata_.end(), cmpMetadataByTag); std::sort(metadata_.begin(), metadata_.end(), cmpMetadataByTag);
} }
void ExifData::erase(ExifData::iterator pos) ExifData::iterator ExifData::erase(ExifData::iterator pos)
{ {
metadata_.erase(pos); return metadata_.erase(pos);
} }
long ExifData::eraseThumbnail() long ExifData::eraseThumbnail()
{ {
// Delete all Thumbnail.*.* (IFD1) metadata // Delete all Thumbnail.*.* (IFD1) metadata
for (Metadata::iterator i = begin(); i != end(); ++i) { Metadata::iterator i = begin();
if (i->ifdId() == ifd1) erase(i); while (i != end()) {
if (i->ifdId() == ifd1) {
i = erase(i);
}
else {
++i;
}
}
long delta = 0;
if (stdThumbPosition()) {
delta = size_;
if (size_ > 0 && ifd0_.next() > 0) {
// Truncate IFD1 and thumbnail data from the data buffer
size_ = ifd0_.next();
ifd0_.setNext(0, byteOrder());
}
delta -= size_;
}
else {
// We will have to write the hard way and re-arrange the data
compatible_ = false;
delta = ifd1_.size() + ifd1_.dataSize()
+ pThumbnail_ ? pThumbnail_->size() : 0;
} }
// Truncate IFD1 and thumbnail data from the data buffer
long delta = size_;
if (size_ > 0) size_ = ifd0_.next();
delta -= size_;
ifd0_.setNext(0, byteOrder());
// Delete the thumbnail itself // Delete the thumbnail itself
delete pThumbnail_; if (pThumbnail_) {
pThumbnail_ = 0; delete pThumbnail_;
pThumbnail_ = 0;
}
return delta; return delta;
} }
bool ExifData::stdThumbPosition() const
{
// Todo: There is still an invalid assumption here: The data of an IFD
// can be stored in multiple non-contiguous blocks. In this case,
// dataOffset + dataSize does not point to the end of the IFD data.
// in particular, this is potentially the case for the remaining Exif
// data in the presence of a known Makernote.
bool rc = true;
if (pThumbnail_) {
long maxOffset;
maxOffset = std::max(ifd0_.offset(), ifd0_.dataOffset());
maxOffset = std::max(maxOffset, exifIfd_.offset());
maxOffset = std::max(maxOffset, exifIfd_.dataOffset()
+ exifIfd_.dataSize());
if (pMakerNote_) {
maxOffset = std::max(maxOffset, pMakerNote_->offset()
+ pMakerNote_->size());
}
maxOffset = std::max(maxOffset, iopIfd_.offset());
maxOffset = std::max(maxOffset, iopIfd_.dataOffset()
+ iopIfd_.dataSize());
maxOffset = std::max(maxOffset, gpsIfd_.offset());
maxOffset = std::max(maxOffset, gpsIfd_.dataOffset()
+ gpsIfd_.dataSize());
if ( maxOffset > ifd1_.offset()
|| maxOffset > ifd1_.dataOffset() && ifd1_.dataOffset() > 0
|| maxOffset > pThumbnail_->offset()) rc = false;
}
return rc;
}
int ExifData::readThumbnail() int ExifData::readThumbnail()
{ {
delete pThumbnail_; delete pThumbnail_;

@ -21,7 +21,7 @@
/*! /*!
@file exif.hpp @file exif.hpp
@brief Encoding and decoding of %Exif data @brief Encoding and decoding of %Exif data
@version $Name: $ $Revision: 1.37 $ @version $Name: $ $Revision: 1.38 $
@author Andreas Huggel (ahu) @author Andreas Huggel (ahu)
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a> <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
@date 09-Jan-04, ahu: created @date 09-Jan-04, ahu: created
@ -271,6 +271,20 @@ namespace Exif {
virtual int read(const char* buf, virtual int read(const char* buf,
const ExifData& exifData, const ExifData& exifData,
ByteOrder byteOrder =littleEndian) =0; ByteOrder byteOrder =littleEndian) =0;
/*!
@brief Update the internal offset and 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 or data size of IFD1 invalidates the
thumbnail data offsets set by this method.
*/
virtual void setOffsets(Ifd& ifd1, ByteOrder byteOrder) =0;
//@} //@}
//! @name Accessors //! @name Accessors
@ -308,18 +322,10 @@ namespace Exif {
*/ */
virtual void update(ExifData& exifData) const =0; virtual void update(ExifData& exifData) const =0;
/*! /*!
@brief Update the thumbnail data offsets in IFD1 assuming the @brief Return the position of the thumbnail image data from the
thumbnail data follows immediately after IFD1. start of the TIFF header in the original %Exif data.
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.
*/ */
virtual void setOffsets(Ifd& ifd1, ByteOrder byteOrder) const =0; virtual long offset() const =0;
/*! /*!
@brief Return the size of the thumbnail image (the size it @brief Return the size of the thumbnail image (the size it
would occupy when extracted from the %Exif data) would occupy when extracted from the %Exif data)
@ -364,6 +370,7 @@ namespace Exif {
int read(const char* buf, int read(const char* buf,
const ExifData& exifData, const ExifData& exifData,
ByteOrder byteOrder =littleEndian); ByteOrder byteOrder =littleEndian);
void setOffsets(Ifd& ifd1, ByteOrder byteOrder);
//@} //@}
//! @name Accessors //! @name Accessors
@ -373,13 +380,15 @@ namespace Exif {
const char* extension() const; const char* extension() const;
long copy(char* buf) const; long copy(char* buf) const;
void update(ExifData& exifData) const; void update(ExifData& exifData) const;
void setOffsets(Ifd& ifd1, ByteOrder byteOrder) const; long offset() const;
long size() const; long size() const;
long dataSize() const; long dataSize() const;
//@} //@}
private: private:
// DATA // DATA
long offset_; // Original offset of the thumbnail data
// from the start of the TIFF header
long size_; //!< Size of the image data long size_; //!< Size of the image data
char* pImage_; //!< Thumbnail image data char* pImage_; //!< Thumbnail image data
TiffHeader tiffHeader_; //!< Thumbnail TIFF Header TiffHeader tiffHeader_; //!< Thumbnail TIFF Header
@ -407,6 +416,7 @@ namespace Exif {
int read(const char* buf, int read(const char* buf,
const ExifData& exifData, const ExifData& exifData,
ByteOrder byteOrder =littleEndian); ByteOrder byteOrder =littleEndian);
void setOffsets(Ifd& ifd1, ByteOrder byteOrder);
//@} //@}
//! @name Accessors //! @name Accessors
@ -416,13 +426,15 @@ namespace Exif {
const char* extension() const; const char* extension() const;
long copy(char* buf) const; long copy(char* buf) const;
void update(ExifData& exifData) const; void update(ExifData& exifData) const;
void setOffsets(Ifd& ifd1, ByteOrder byteOrder) const; long offset() const;
long size() const; long size() const;
long dataSize() const; long dataSize() const;
//@} //@}
private: private:
// DATA // DATA
long offset_; // Original offset of the thumbnail data
// from the start of the TIFF header
long size_; // Size of the image data long size_; // Size of the image data
char* pImage_; // Thumbnail image data char* pImage_; // Thumbnail image data
@ -562,7 +574,7 @@ namespace Exif {
buffer has enough memory. Otherwise the call results in buffer has enough memory. Otherwise the call results in
undefined behaviour. undefined behaviour.
@return Number of characters written to the buffer. @return Number of characters written to the buffer.
*/ */
long copy(char* buf); long copy(char* buf);
/*! /*!
@brief Add all (IFD) entries in the range from iterator position begin @brief Add all (IFD) entries in the range from iterator position begin
@ -586,8 +598,13 @@ namespace Exif {
multiple metadata with the same key. multiple metadata with the same key.
*/ */
void add(const Metadatum& metadatum); void add(const Metadatum& metadatum);
//! Delete the metadatum at iterator position pos /*!
void erase(iterator pos); @brief Delete the metadatum at iterator position pos, return the
position of the next metadatum. Note that iterators into
the metadata, including pos, are potentially invalidated
by this call.
*/
iterator erase(iterator pos);
//! Sort metadata by key //! Sort metadata by key
void sortByKey(); void sortByKey();
//! Sort metadata by tag //! Sort metadata by tag
@ -617,8 +634,8 @@ namespace Exif {
iterator findIfdIdIdx(IfdId ifdId, int idx); iterator findIfdIdIdx(IfdId ifdId, int idx);
/*! /*!
@brief Delete the thumbnail from the %Exif data. Removes all related @brief Delete the thumbnail from the %Exif data. Removes all related
(Thumbnail.*.*, i.e., IFD1) metadata as well. (%Thumbnail.*.*, i.e., IFD1) metadata as well.
@return The number of bytes truncated from the original %Exif data. @return The number of bytes erased from the original %Exif data.
*/ */
long eraseThumbnail(); long eraseThumbnail();
//@} //@}
@ -666,18 +683,21 @@ namespace Exif {
//! Returns the byte order as specified in the TIFF header //! Returns the byte order as specified in the TIFF header
ByteOrder byteOrder() const { return tiffHeader_.byteOrder(); } ByteOrder byteOrder() const { return tiffHeader_.byteOrder(); }
/*! /*!
@brief Write the thumbnail image to a file. The filename extension @brief Write the thumbnail image to a file. A filename extension
will be set according to the image type of the thumbnail, so is appended to path according to the image type of the
the path should not include an extension. thumbnail, so the path should not include an extension.
*/ */
int writeThumbnail(const std::string& path) const int writeThumbnail(const std::string& path) const
{ return pThumbnail_ ? pThumbnail_->write(path) : 0; } { return pThumbnail_ ? pThumbnail_->write(path) : 0; }
//! Return the file extension of the thumbnail image file /*!
@brief Return a short string describing the format of the %Exif
thumbnail ("TIFF", "JPEG").
*/
const char* thumbnailFormat() const const char* thumbnailFormat() const
{ return pThumbnail_ ? pThumbnail_->format() : ""; } { return pThumbnail_ ? pThumbnail_->format() : ""; }
/*! /*!
@brief Return the file extension for the format of the thumbnail @brief Return the file extension for the %Exif thumbnail depending
(".tif", ".jpg"). on the format (".tif", ".jpg").
*/ */
const char* thumbnailExtension() const const char* thumbnailExtension() const
{ return pThumbnail_ ? pThumbnail_->extension() : ""; } { return pThumbnail_ ? pThumbnail_->extension() : ""; }
@ -751,6 +771,12 @@ namespace Exif {
findEntry(IfdId ifdId, int idx) const; findEntry(IfdId ifdId, int idx) const;
//! Return a pointer to the internal IFD identified by its IFD id //! Return a pointer to the internal IFD identified by its IFD id
const Ifd* getIfd(IfdId ifdId) const; const Ifd* getIfd(IfdId ifdId) const;
/*!
@brief Check if IFD1, the IFD1 data and thumbnail data are located at
the end of the Exif data. Return true, if they are or if there
is no thumbnail at all, else return false.
*/
bool stdThumbPosition() const;
//@} //@}
// DATA // DATA
@ -770,6 +796,13 @@ namespace Exif {
long size_; //!< Size of the Exif raw data in bytes long size_; //!< Size of the Exif raw data in bytes
char* pData_; //!< Exif raw data buffer char* pData_; //!< Exif raw data buffer
/*!
Can be set to false to indicate that non-intrusive writing is not
possible. If it is true (the default), then the compatibility checks
will be performed to determine which writing method to use.
*/
bool compatible_;
}; // class ExifData }; // class ExifData
// ***************************************************************************** // *****************************************************************************

@ -20,14 +20,14 @@
*/ */
/* /*
File: ifd.cpp File: ifd.cpp
Version: $Name: $ $Revision: 1.15 $ Version: $Name: $ $Revision: 1.16 $
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net> Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
History: 26-Jan-04, ahu: created History: 26-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component 11-Feb-04, ahu: isolated as a component
*/ */
// ***************************************************************************** // *****************************************************************************
#include "rcsid.hpp" #include "rcsid.hpp"
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.15 $ $RCSfile: ifd.cpp,v $") EXIV2_RCSID("@(#) $Name: $ $Revision: 1.16 $ $RCSfile: ifd.cpp,v $")
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
@ -156,21 +156,24 @@ namespace Exif {
} // Entry::component } // Entry::component
Ifd::Ifd(IfdId ifdId) Ifd::Ifd(IfdId ifdId)
: alloc_(true), ifdId_(ifdId), offset_(0), pNext_(0), next_(0) : alloc_(true), ifdId_(ifdId), offset_(0), dataOffset_(0),
pNext_(0), next_(0)
{ {
pNext_ = new char[4]; pNext_ = new char[4];
memset(pNext_, 0x0, 4); memset(pNext_, 0x0, 4);
} }
Ifd::Ifd(IfdId ifdId, uint32 offset) Ifd::Ifd(IfdId ifdId, uint32 offset)
: alloc_(true), ifdId_(ifdId), offset_(offset), pNext_(0), next_(0) : alloc_(true), ifdId_(ifdId), offset_(offset), dataOffset_(0),
pNext_(0), next_(0)
{ {
pNext_ = new char[4]; pNext_ = new char[4];
memset(pNext_, 0x0, 4); memset(pNext_, 0x0, 4);
} }
Ifd::Ifd(IfdId ifdId, uint32 offset, bool alloc) Ifd::Ifd(IfdId ifdId, uint32 offset, bool alloc)
: alloc_(alloc), ifdId_(ifdId), offset_(offset), pNext_(0), next_(0) : alloc_(alloc), ifdId_(ifdId), offset_(offset), dataOffset_(0),
pNext_(0), next_(0)
{ {
if (alloc_) { if (alloc_) {
pNext_ = new char[4]; pNext_ = new char[4];
@ -185,7 +188,8 @@ namespace Exif {
Ifd::Ifd(const Ifd& rhs) Ifd::Ifd(const Ifd& rhs)
: alloc_(rhs.alloc_), entries_(rhs.entries_), ifdId_(rhs.ifdId_), : alloc_(rhs.alloc_), entries_(rhs.entries_), ifdId_(rhs.ifdId_),
offset_(rhs.offset_), pNext_(rhs.pNext_), next_(rhs.next_) offset_(rhs.offset_), dataOffset_(rhs.dataOffset_),
pNext_(rhs.pNext_), next_(rhs.next_)
{ {
if (alloc_ && rhs.pNext_) { if (alloc_ && rhs.pNext_) {
pNext_ = new char[4]; pNext_ = new char[4];
@ -221,18 +225,24 @@ namespace Exif {
} }
next_ = getULong(buf+o, byteOrder); next_ = getULong(buf+o, byteOrder);
// Guess the offset of the IFD, if it was not given. The guess is based // Set the offset of the first data entry outside of the IFD.
// on the assumption that the smallest offset points to a data buffer // At the same time we guess the offset of the IFD, if it was not
// directly following the IFD. Subsequently all offsets of IFD entries // given. The guess is based on the assumption that the smallest offset
// will need to be recalculated. // points to a data buffer directly following the IFD. Subsequently all
if (offset_ == 0 && preEntries.size() > 0) { // offsets of IFD entries will need to be recalculated.
if (preEntries.size() > 0) {
// Find the entry with the smallest offset // Find the entry with the smallest offset
Ifd::PreEntries::const_iterator i = std::min_element( Ifd::PreEntries::const_iterator i = std::min_element(
preEntries.begin(), preEntries.end(), cmpPreEntriesByOffset); preEntries.begin(), preEntries.end(), cmpPreEntriesByOffset);
// Set the 'guessed' IFD offset, the test is needed for the case when // Only do something if there is at least one entry with data
// all entries have data sizes not exceeding 4. // outside the IFD directory itself.
if (i->size_ > 4) { if (i->size_ > 4) {
offset_ = i->offset_ - size(); if (offset_ == 0) {
// Set the 'guessed' IFD offset
offset_ = i->offset_ - size();
}
// Set the offset of the first data entry outside of the IFD
dataOffset_ = i->offset_;
} }
} }
@ -362,6 +372,7 @@ namespace Exif {
pNext_ = 0; pNext_ = 0;
} }
offset_ = 0; offset_ = 0;
dataOffset_ = 0;
} // Ifd::clear } // Ifd::clear
void Ifd::setNext(uint32 next, ByteOrder byteOrder) void Ifd::setNext(uint32 next, ByteOrder byteOrder)
@ -390,9 +401,9 @@ namespace Exif {
return idx; return idx;
} }
void Ifd::erase(iterator pos) Ifd::iterator Ifd::erase(iterator pos)
{ {
entries_.erase(pos); return entries_.erase(pos);
} }
long Ifd::size() const long Ifd::size() const

@ -21,7 +21,7 @@
/*! /*!
@file ifd.hpp @file ifd.hpp
@brief Encoding and decoding of IFD (Image File Directory) data @brief Encoding and decoding of IFD (Image File Directory) data
@version $Name: $ $Revision: 1.13 $ @version $Name: $ $Revision: 1.14 $
@author Andreas Huggel (ahu) @author Andreas Huggel (ahu)
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a> <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
@date 09-Jan-04, ahu: created @date 09-Jan-04, ahu: created
@ -385,8 +385,13 @@ namespace Exif {
of the deleted entry or 0 if no entry with tag was found. of the deleted entry or 0 if no entry with tag was found.
*/ */
int erase(uint16 tag); int erase(uint16 tag);
//! Delete the directory entry at iterator position pos /*!
void erase(iterator pos); @brief Delete the directory entry at iterator position pos, return the
position of the next entry. Note that iterators into the
directory, including pos, are potentially invalidated by this
call.
*/
iterator erase(iterator pos);
//! Sort the IFD entries by tag //! Sort the IFD entries by tag
void sortByTag(); void sortByTag();
//! The first entry //! The first entry
@ -415,6 +420,12 @@ namespace Exif {
IfdId ifdId() const { return ifdId_; } IfdId ifdId() const { return ifdId_; }
//! Get the offset of the IFD from the start of the TIFF header //! Get the offset of the IFD from the start of the TIFF header
long offset() const { return offset_; } long offset() const { return offset_; }
/*!
@brief Get the offset of the first data entry outside of the IFD,
return 0 if there is none. The data offset is determined when
the IFD is read.
*/
long dataOffset() const { return dataOffset_; }
//! 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
uint32 next() const { return next_; } uint32 next() const { return next_; }
//! Get the number of directory entries in the IFD //! Get the number of directory entries in the IFD
@ -462,11 +473,13 @@ namespace Exif {
Entries entries_; Entries entries_;
//! IFD Id //! IFD Id
IfdId ifdId_; IfdId ifdId_;
//! offset of the IFD from the start of TIFF header //! Offset of the IFD from the start of TIFF header
long offset_; long offset_;
// Pointer to the offset of next IFD from the start of the TIFF header //! Offset of the first data entry outside of the IFD directory
long dataOffset_;
//! Pointer to the offset of next IFD from the start of the TIFF header
char* pNext_; char* pNext_;
// The offset of the next IFD as data value (always in sync with *pNext_) //! The offset of the next IFD as data value (always in sync with *pNext_)
uint32 next_; uint32 next_;
}; // class Ifd }; // class Ifd

@ -20,13 +20,13 @@
*/ */
/* /*
File: makernote.cpp File: makernote.cpp
Version: $Name: $ $Revision: 1.15 $ Version: $Name: $ $Revision: 1.16 $
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net> Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
History: 18-Feb-04, ahu: created History: 18-Feb-04, ahu: created
*/ */
// ***************************************************************************** // *****************************************************************************
#include "rcsid.hpp" #include "rcsid.hpp"
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.15 $ $RCSfile: makernote.cpp,v $") EXIV2_RCSID("@(#) $Name: $ $Revision: 1.16 $ $RCSfile: makernote.cpp,v $")
// Define DEBUG_MAKERNOTE to output debug information to std::cerr // Define DEBUG_MAKERNOTE to output debug information to std::cerr
#undef DEBUG_MAKERNOTE #undef DEBUG_MAKERNOTE
@ -155,6 +155,8 @@ namespace Exif {
ByteOrder byteOrder, ByteOrder byteOrder,
long offset) long offset)
{ {
// Remember the offset
offset_ = offset;
// Set byte order if none is set yet // Set byte order if none is set yet
if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder; if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder;
int rc = 0; int rc = 0;
@ -182,7 +184,6 @@ namespace Exif {
i->setMakerNote(this); i->setMakerNote(this);
} }
} }
#ifdef DEBUG_MAKERNOTE #ifdef DEBUG_MAKERNOTE
hexdump(std::cerr, buf, len, offset); hexdump(std::cerr, buf, len, offset);
if (rc == 0) ifd_.print(std::cerr); if (rc == 0) ifd_.print(std::cerr);
@ -193,10 +194,23 @@ namespace Exif {
long IfdMakerNote::copy(char* buf, ByteOrder byteOrder, long offset) long IfdMakerNote::copy(char* buf, ByteOrder byteOrder, long offset)
{ {
// Remember the new offset
offset_ = offset;
// Set byte order if none is set yet // Set byte order if none is set yet
if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder; if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder;
return ifd_.copy(buf, byteOrder_, offset); long len = 0;
} if (!prefix_.empty()) {
// Write the prefix string to the Makernote buffer
memcpy(buf, prefix_.data(), prefix_.size());
len += prefix_.size();
}
if (!absOffset_) {
// Use offsets relative to the start of the Makernote field
offset = 0;
}
len += ifd_.copy(buf + len, byteOrder_, offset + len);
return len;
} // IfdMakerNote::copy
Entries::const_iterator IfdMakerNote::findIdx(int idx) const Entries::const_iterator IfdMakerNote::findIdx(int idx) const
{ {
@ -205,7 +219,7 @@ namespace Exif {
long IfdMakerNote::size() const long IfdMakerNote::size() const
{ {
return ifd_.size() + ifd_.dataSize(); return prefix_.size() + ifd_.size() + ifd_.dataSize();
} }
MakerNoteFactory* MakerNoteFactory::pInstance_ = 0; MakerNoteFactory* MakerNoteFactory::pInstance_ = 0;
@ -229,7 +243,7 @@ namespace Exif {
// Todo: use case insensitive make and model comparisons // Todo: use case insensitive make and model comparisons
// find or create a registry entry for make // Find or create a registry entry for make
ModelRegistry* modelRegistry = 0; ModelRegistry* modelRegistry = 0;
Registry::const_iterator end1 = registry_.end(); Registry::const_iterator end1 = registry_.end();
Registry::const_iterator pos1; Registry::const_iterator pos1;
@ -243,7 +257,7 @@ namespace Exif {
modelRegistry = new ModelRegistry; modelRegistry = new ModelRegistry;
registry_.push_back(std::make_pair(make, modelRegistry)); registry_.push_back(std::make_pair(make, modelRegistry));
} }
// find or create a registry entry for model // Find or create a registry entry for model
ModelRegistry::iterator end2 = modelRegistry->end(); ModelRegistry::iterator end2 = modelRegistry->end();
ModelRegistry::iterator pos2; ModelRegistry::iterator pos2;
for (pos2 = modelRegistry->begin(); pos2 != end2; ++pos2) { for (pos2 = modelRegistry->begin(); pos2 != end2; ++pos2) {

@ -22,7 +22,7 @@
@file makernote.hpp @file makernote.hpp
@brief Contains the %Exif %MakerNote interface, IFD %MakerNote and a @brief Contains the %Exif %MakerNote interface, IFD %MakerNote and a
MakerNote factory MakerNote factory
@version $Name: $ $Revision: 1.14 $ @version $Name: $ $Revision: 1.15 $
@author Andreas Huggel (ahu) @author Andreas Huggel (ahu)
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a> <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
@date 18-Feb-04, ahu: created @date 18-Feb-04, ahu: created
@ -113,7 +113,8 @@ namespace Exif {
for the Entries. for the Entries.
*/ */
MakerNote(const MnTagInfo* pMnTagInfo =0, bool alloc =true) MakerNote(const MnTagInfo* pMnTagInfo =0, bool alloc =true)
: pMnTagInfo_(pMnTagInfo), alloc_(alloc), byteOrder_(invalidByteOrder) {} : pMnTagInfo_(pMnTagInfo), alloc_(alloc),
byteOrder_(invalidByteOrder), offset_(0) {}
//! Virtual destructor. //! Virtual destructor.
virtual ~MakerNote() {} virtual ~MakerNote() {}
//@} //@}
@ -160,6 +161,8 @@ namespace Exif {
uint16 decomposeKey(const std::string& key) const; uint16 decomposeKey(const std::string& key) const;
//! Return the byte order (little or big endian). //! Return the byte order (little or big endian).
ByteOrder byteOrder() const { return byteOrder_; } ByteOrder byteOrder() const { return byteOrder_; }
//! Return the offset of the makernote from the start of the TIFF header
long offset() const { return offset_; }
/*! /*!
@brief Return the name of a makernote tag. The default implementation @brief Return the name of a makernote tag. The default implementation
looks up the makernote info tag array if one is set, else looks up the makernote info tag array if one is set, else
@ -208,7 +211,7 @@ namespace Exif {
virtual Entries::const_iterator end() const =0; virtual Entries::const_iterator end() const =0;
//! Find an entry by idx, return a const iterator to the record //! Find an entry by idx, return a const iterator to the record
virtual Entries::const_iterator findIdx(int idx) const =0; virtual Entries::const_iterator findIdx(int idx) const =0;
//! Return the size of the makernote in bytes. //! Return the size of the makernote in bytes
virtual long size() const =0; virtual long size() const =0;
//! Return the name of the makernote section //! Return the name of the makernote section
virtual std::string sectionName(uint16 tag) const =0; virtual std::string sectionName(uint16 tag) const =0;
@ -219,19 +222,23 @@ namespace Exif {
//@} //@}
protected: protected:
// DATA
//! Pointer to an array of makernote tag infos //! Pointer to an array of makernote tag infos
const MnTagInfo* pMnTagInfo_; const MnTagInfo* pMnTagInfo_;
/*! /*!
Memory management @brief Flag to control the memory management: <BR>
True: requires memory allocation and deallocation, True: requires memory allocation and deallocation, <BR>
False: no memory management needed. False: no memory management needed.
*/ */
const bool alloc_; const bool alloc_;
/*! /*!
Alternative byte order to use, invalid if the byte order of the @brief Alternative byte order to use, invalid if the byte order of the
%Exif block can be used %Exif block can be used
*/ */
ByteOrder byteOrder_; ByteOrder byteOrder_;
//! Offset of the makernote from the start of the TIFF header
long offset_;
}; // class MakerNote }; // class MakerNote
/*! /*!
@ -279,11 +286,12 @@ namespace Exif {
//@} //@}
protected: protected:
//! Prefix before the start of the IFD // DATA
//! String prefix at the beginning of the makernote, before the IFD
std::string prefix_; std::string prefix_;
/*! /*!
True: Offsets are from start of the TIFF header @brief True: Offsets are from start of the TIFF header,
False: Offsets are from start of the makernote False: Offsets are from start of the makernote
*/ */
bool absOffset_; bool absOffset_;
//! MakerNote IFD //! MakerNote IFD

Loading…
Cancel
Save