diff --git a/src/exif.cpp b/src/exif.cpp index cec8bed6..c1a0c87d 100644 --- a/src/exif.cpp +++ b/src/exif.cpp @@ -25,7 +25,7 @@ RCS information $Name: $ - $Revision: 1.5 $ + $Revision: 1.6 $ */ // ***************************************************************************** // included header files @@ -47,7 +47,7 @@ namespace { // Compare two IFD entries by offset, taking care of special cases // where one or both of the entries don't have an offset. - bool cmpOffset(const Exif::Metadatum& lhs, const Exif::Metadatum& rhs); + bool cmpOffset(const Exif::Ifd::Entry& lhs, const Exif::Ifd::Entry& rhs); } @@ -295,21 +295,17 @@ namespace Exif { } Metadatum::Metadatum() - : tag_(0), type_(0), count_(0), offset_(0), - ifdId_(IfdIdNotSet), ifdIdx_(-1), value_(0), size_(0) + : tag_(0), type_(0), ifdId_(IfdIdNotSet), ifdIdx_(-1), value_(0) { } - Metadatum::Metadatum(uint16 tag, uint16 type, uint32 count, uint32 offset, + Metadatum::Metadatum(uint16 tag, uint16 type, IfdId ifdId, int ifdIdx, Value* value) - : tag_(tag), type_(type), count_(count), offset_(offset), - ifdId_(ifdId), ifdIdx_(ifdIdx), value_(value) + : tag_(tag), type_(type), ifdId_(ifdId), ifdIdx_(ifdIdx), value_(value) { key_ = std::string(ifdItem()) + "." + std::string(sectionName()) + "." + std::string(tagName()); - - size_ = count_ * typeSize(); } Metadatum::~Metadatum() @@ -321,40 +317,76 @@ namespace Exif { { tag_ = rhs.tag_; type_ = rhs.type_; - count_ = rhs.count_; - offset_ = rhs.offset_; - ifdId_ = rhs.ifdId_; ifdIdx_ = rhs.ifdIdx_; - value_ = 0; if (rhs.value_ != 0) value_ = rhs.value_->clone(); // deep copy - key_ = rhs.key_; - size_ = rhs.size_; } Metadatum& Metadatum::operator=(const Metadatum& rhs) { if (this == &rhs) return *this; - tag_ = rhs.tag_; type_ = rhs.type_; - count_ = rhs.count_; - offset_ = rhs.offset_; - ifdId_ = rhs.ifdId_; ifdIdx_ = rhs.ifdIdx_; - delete value_; value_ = 0; if (rhs.value_ != 0) value_ = rhs.value_->clone(); // deep copy - key_ = rhs.key_; + return *this; + } // Metadatum::operator= + + void Metadatum::setValue(Value* value) + { + delete value_; + value_ = value; + } + + Ifd::Entry::Entry() + : ifdIdx_(-1), tag_(0), type_(0), count_(0), offset_(0), + data_(0), size_(0) + { + } + + Ifd::Entry::~Entry() + { + delete[] data_; + } + + Ifd::Entry::Entry(const Entry& rhs) + { + ifdIdx_ = rhs.ifdIdx_; + tag_ = rhs.tag_; + type_ = rhs.type_; + count_ = rhs.count_; + offset_ = rhs.offset_; + data_ = 0; + if (rhs.data_) { + data_ = new char[rhs.size_]; + ::memcpy(data_, rhs.data_, rhs.size_); + } size_ = rhs.size_; + } + Ifd::Entry::Entry& Ifd::Entry::operator=(const Entry& rhs) + { + if (this == &rhs) return *this; + ifdIdx_ = rhs.ifdIdx_; + tag_ = rhs.tag_; + type_ = rhs.type_; + count_ = rhs.count_; + offset_ = rhs.offset_; + delete data_; + data_ = 0; + if (rhs.data_) { + data_ = new char[rhs.size_]; + ::memcpy(data_, rhs.data_, rhs.size_); + } + size_ = rhs.size_; return *this; - } // Metadatum::operator= + } Ifd::Ifd(IfdId ifdId) : ifdId_(ifdId), offset_(0), next_(0), size_(0) @@ -369,16 +401,15 @@ namespace Exif { entries_.clear(); for (int i=0; ivalue_; - i->value_ = Value::create(TypeId(i->type_)); + delete[] i->data_; if (i->size_ > 4) { i->offset_ = i->offset_ - offset_; - i->value_->read(buf + i->offset_, i->size_, byteOrder); + i->data_ = new char[i->size_]; + ::memcpy(i->data_, buf + i->offset_, i->size_); } else { - char tmpbuf[4]; - ul2Data(tmpbuf, i->offset_, byteOrder); - i->value_->read(tmpbuf, i->size_, byteOrder); + i->data_ = new char[4]; + ul2Data(i->data_, i->offset_, byteOrder); } } return 0; } // Ifd::read - Metadata::const_iterator Ifd::findTag(uint16 tag) const + Ifd::Entries::const_iterator Ifd::findTag(uint16 tag) const { return std::find_if(entries_.begin(), entries_.end(), - FindMetadatumByTag(tag)); + FindEntryByTag(tag)); } int Ifd::readSubIfd( @@ -431,7 +461,7 @@ namespace Exif { ) const { int rc = 0; - Metadata::const_iterator pos = findTag(tag); + Entries::const_iterator pos = findTag(tag); if (pos != entries_.end()) { rc = dest.read(buf + pos->offset_, byteOrder, pos->offset_); } @@ -448,9 +478,9 @@ namespace Exif { // Add all directory entries to the data buffer long dataSize = 0; - const Metadata::const_iterator b = entries_.begin(); - const Metadata::const_iterator e = entries_.end(); - Metadata::const_iterator i = b; + const Entries::const_iterator b = entries_.begin(); + const Entries::const_iterator e = entries_.end(); + Entries::const_iterator i = b; for (; i != e; ++i) { us2Data(buf+o, i->tag_, byteOrder); us2Data(buf+o+2, i->type_, byteOrder); @@ -460,10 +490,7 @@ namespace Exif { dataSize += i->size_; } else { - char tmpbuf[4]; - ::memset(tmpbuf, 0x0, 4); - i->value_->copy(tmpbuf, byteOrder); - ::memcpy(buf+o+8, tmpbuf, 4); + ::memcpy(buf+o+8, i->data_, 4); } o += 12; } @@ -481,10 +508,8 @@ namespace Exif { // Add the data of all IFD entries to the data buffer for (i = b; i != e; ++i) { if (i->size_ > 4) { - // Todo: Check this! There seems to be an inconsistency - // in the use of size_ and the return value of copy() here - // Todo: And can value_ be 0? - o += i->value_->copy(buf+o, byteOrder); + ::memcpy(buf + o, i->data_, i->size_); + o += i->size_; } } @@ -504,28 +529,20 @@ namespace Exif { << prefix << "Entry Tag Format (Bytes each) Number Offset\n" << prefix << "----- ------ --------------------- ------ -----------\n"; - const Metadata::const_iterator b = entries_.begin(); - const Metadata::const_iterator e = entries_.end(); - Metadata::const_iterator i = b; + const Entries::const_iterator b = entries_.begin(); + const Entries::const_iterator e = entries_.end(); + Entries::const_iterator i = b; for (; i != e; ++i) { std::ostringstream offset; - if (i->typeSize() * i->count_ <= 4) { - -// Todo: Fix me! This doesn't work with Value anymore because we do not know -// the byte order here. (Wait for Ifd to use a more special type) -// -// char tmpbuf[4]; -// i->value_->copy(tmpbuf, byteOrder); -// offset << std::setw(2) << std::setfill('0') << std::hex -// << (int)*(unsigned char*)tmpbuf << " " -// << std::setw(2) << std::setfill('0') << std::hex -// << (int)*(unsigned char*)(tmpbuf+1) << " " -// << std::setw(2) << std::setfill('0') << std::hex -// << (int)*(unsigned char*)(tmpbuf+2) << " " -// << std::setw(2) << std::setfill('0') << std::hex -// << (int)*(unsigned char*)(tmpbuf+3) << " "; - - offset << "n/a"; + if (i->size_ <= 4) { + offset << std::setw(2) << std::setfill('0') << std::hex + << (int)*(unsigned char*)i->data_ << " " + << std::setw(2) << std::setfill('0') << std::hex + << (int)*(unsigned char*)(i->data_+1) << " " + << std::setw(2) << std::setfill('0') << std::hex + << (int)*(unsigned char*)(i->data_+2) << " " + << std::setw(2) << std::setfill('0') << std::hex + << (int)*(unsigned char*)(i->data_+3) << " "; } else { offset << " 0x" << std::setw(8) << std::setfill('0') << std::hex @@ -548,42 +565,42 @@ namespace Exif { << std::setw(8) << std::setfill('0') << std::hex << std::right << next_ << "\n"; -// Todo: Fix me! This does not work with Value anymore -// for (i = b; i != e; ++i) { -// if (i->size_ > 4) { -// os << "Data of entry " << i-b << ":\n"; -// hexdump(os, i->data_, i->size_); -// } -// } + for (i = b; i != e; ++i) { + if (i->size_ > 4) { + os << "Data of entry " << i-b << ":\n"; + hexdump(os, i->data_, i->size_); + } + } } // Ifd::print // Todo: implement this properly.. // - Tag values 0x0201 and 0x0202 may be long OR short types... // - TIFF thumbnails + // Rewrite: it should use the higher level Metadata interface int Thumbnail::read(const char* buf, const Ifd& ifd1, ByteOrder byteOrder) { - Metadata::const_iterator pos = ifd1.findTag(0x0103); - if (pos == ifd1.entries().end()) return 1; - const UShortValue& compression = dynamic_cast(pos->value()); - if (compression.value() == 6) { - pos = ifd1.findTag(0x0201); - if (pos == ifd1.entries().end()) return 2; - const ULongValue& offset = dynamic_cast(pos->value()); - pos = ifd1.findTag(0x0202); - if (pos == ifd1.entries().end()) return 3; - const ULongValue& size = dynamic_cast(pos->value()); - - thumbnail_ = std::string(buf + offset.value(), size.value()); - } - else if (compression.value() == 1) { - // Todo: to be continued... - return 4; - } - else { - // invalid compression value - return 5; - } +// Ifd::Entries::const_iterator pos = ifd1.findTag(0x0103); +// if (pos == ifd1.entries().end()) return 1; +// const UShortValue& compression = dynamic_cast(pos->value()); +// if (compression.value() == 6) { +// pos = ifd1.findTag(0x0201); +// if (pos == ifd1.entries().end()) return 2; +// const ULongValue& offset = dynamic_cast(pos->value()); +// pos = ifd1.findTag(0x0202); +// if (pos == ifd1.entries().end()) return 3; +// const ULongValue& size = dynamic_cast(pos->value()); + +// thumbnail_ = std::string(buf + offset.value(), size.value()); +// } +// else if (compression.value() == 1) { +// // Todo: to be continued... +// return 4; +// } +// else { +// // invalid compression value +// return 5; +// } return 0; } @@ -654,16 +671,16 @@ namespace Exif { rc = ifd1.readSubIfd(ifd1GpsIfd, buf, byteOrder(), 0x8825); if (rc) return rc; - // Copy all metadata from the IFDs to the internal metadata + // Copy all entries from the IFDs to the internal metadata metadata_.clear(); - add(ifd0.entries()); - add(exifIfd.entries()); - add(iopIfd.entries()); - add(gpsIfd.entries()); - add(ifd1.entries()); - add(ifd1ExifIfd.entries()); - add(ifd1IopIfd.entries()); - add(ifd1GpsIfd.entries()); + add(ifd0, byteOrder()); + add(exifIfd, byteOrder()); + add(iopIfd, byteOrder()); + add(gpsIfd, byteOrder()); + add(ifd1, byteOrder()); + add(ifd1ExifIfd, byteOrder()); + add(ifd1IopIfd, byteOrder()); + add(ifd1GpsIfd, byteOrder()); // Read the thumbnail thumbnail_.read(buf, ifd1, byteOrder()); @@ -683,9 +700,16 @@ namespace Exif { return 0; } - void ExifData::add(const Metadata& src) + void ExifData::add(const Ifd& ifd, ByteOrder byteOrder) { - metadata_.insert(metadata_.end(), src.begin(), src.end()); + Ifd::const_iterator i = ifd.begin(); + Ifd::const_iterator end = ifd.end(); + for (; i != end; ++i) { + Value* value = Value::create(TypeId(i->type_)); + value->read(i->data_, i->size_, byteOrder); + Metadatum md(i->tag_, i->type_, ifd.ifdId(), i->ifdIdx_, value); + add(md); + } } void ExifData::add(const Metadatum& src) @@ -857,7 +881,7 @@ namespace Exif { // local definitions namespace { - bool cmpOffset(const Exif::Metadatum& lhs, const Exif::Metadatum& rhs) + bool cmpOffset(const Exif::Ifd::Entry& lhs, const Exif::Ifd::Entry& rhs) { // We need to ignore entries with size <= 4, so by definition, // entries with size <= 4 are greater than those with size > 4 diff --git a/src/exif.hpp b/src/exif.hpp index e4c34441..dd4959e6 100644 --- a/src/exif.hpp +++ b/src/exif.hpp @@ -21,7 +21,7 @@ /*! @file exif.hpp @brief Encoding and decoding of %Exif data - @version $Name: $ $Revision: 1.5 $ + @version $Name: $ $Revision: 1.6 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 09-Jan-03, ahu: created @@ -196,6 +196,8 @@ namespace Exif { @return Number of characters written. */ virtual long copy(char* buf, ByteOrder byteOrder) const =0; + //! Return the number of components of the value + virtual long count() const =0; //! Return the size of the value in bytes virtual long size() const =0; /*! @@ -259,6 +261,7 @@ namespace Exif { @return Number of characters written. */ virtual long copy(char* buf, ByteOrder byteOrder) const; + virtual long count() const { return size(); } virtual long size() const; virtual Value* clone() const; virtual std::ostream& write(std::ostream& os) const; @@ -296,6 +299,7 @@ namespace Exif { @return Number of characters written. */ virtual long copy(char* buf, ByteOrder byteOrder) const; + virtual long count() const { return size(); } virtual long size() const; virtual Value* clone() const; virtual std::ostream& write(std::ostream& os) const; @@ -323,6 +327,7 @@ namespace Exif { */ virtual void read(const std::string& buf); virtual long copy(char* buf, ByteOrder byteOrder) const; + virtual long count() const { return value_.size(); } virtual long size() const; virtual Value* clone() const; virtual std::ostream& write(std::ostream& os) const; @@ -371,7 +376,7 @@ namespace Exif { of the value pointer if one is provided, so the application must not delete it! */ - Metadatum(uint16 tag, uint16 type, uint32 count, uint32 offset, + Metadatum(uint16 tag, uint16 type, IfdId ifdId, int ifdIdx, Value* value =0); /*! @brief Constructor for new tags created by an application, @@ -387,7 +392,13 @@ namespace Exif { //! Assignment operator Metadatum& operator=(const Metadatum& rhs); - //! Return the name of the type + /*! + @brief Set the value. The Metadatum takes ownership of the value + pointer, so the application must not delete it! + */ + void setValue(Value* value); + + //! Return the name of the tag const char* tagName() const { return ExifTags::tagName(tag_, ifdId_); } //! Return the name of the type const char* typeName() const { return ExifTags::typeName(TypeId(type_)); } @@ -406,8 +417,10 @@ namespace Exif { uint16 tag() const { return tag_; } //! Return the type TypeId type() const { return TypeId(type_); } - //! Return the count - uint32 count() const { return count_; } + //! Return the number of components in the value + long count() const { return value_ == 0 ? 0 : value_->count(); } + //! Return the size of the value in bytes + long size() const { return value_ == 0 ? 0 : value_->size(); } //! Return the IFD id IfdId ifdId() const { return ifdId_; } //! Return the position in the IFD (-1: not set) @@ -422,53 +435,82 @@ namespace Exif { std::string key() const { return key_; } //@} - public: + private: uint16 tag_; //!< Tag value uint16 type_; //!< Type of the data - uint32 count_; //!< Number of components - uint32 offset_; //!< Offset of the data from start of IFD - IfdId ifdId_; //!< The IFD associated with this tag int ifdIdx_; //!< Position in the IFD (-1: not set) - Value* value_; //!< Pointer to the value - std::string key_; //!< Unique key - long size_; //!< Size of the data in bytes }; // class Metadatum //! Container type to hold all metadata typedef std::vector Metadata; - //! Unary predicate that matches a Metadatum with a given tag - class FindMetadatumByTag { - public: - //! Constructor, initializes the object with the tag to look for - FindMetadatumByTag(uint16 tag) : tag_(tag) {} - /*! - @brief Returns true if the tag of the argument metadatum is equal - to that of the object. - */ - bool operator()(const Metadatum& metadatum) const - { return tag_ == metadatum.tag_; } - - private: - uint16 tag_; - - }; // class FindMetadatumByTag - /*! @brief Models an IFD (Image File Directory) Todo: - make the data handling more intelligent - - should we return the size and do away with size() ? */ class Ifd { public: //! Constructor. Allows to set the IFD identifier. explicit Ifd(IfdId ifdId =IfdIdNotSet); + + //! Data structure for one IFD directory entry + struct Entry { + Entry(); //!< Default constructor + ~Entry(); //!< Destructor + Entry(const Entry& rhs); //!< Copy constructor + Entry& operator=(const Entry& rhs); //!< Assignment operator + + //! 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_)); } + + int ifdIdx_; //!< Position in the IFD + uint16 tag_; //!< Tag + uint16 type_; //!< Type + uint32 count_; //!< Number of components + //! Offset from the start of the IFD + uint32 offset_; + //! Pointer to the data buffer + char* data_; + //! Size of the data buffer in bytes + long size_; + }; // struct Entry + + //! Container type to hold all IFD directory entries + typedef std::vector Entries; + + //! Entries iterator type (const) + typedef Entries::const_iterator const_iterator; + //! The first entry + const_iterator begin() const { return entries_.begin(); } + //! End of the entries + const_iterator end() const { return entries_.end(); } + + //! Unary predicate that matches an Entry with a given tag + class FindEntryByTag { + public: + //! Constructor, initializes the object with the tag to look for + FindEntryByTag(uint16 tag) : tag_(tag) {} + /*! + @brief Returns true if the tag of the argument metadatum is equal + to that of the object. + */ + bool operator()(const Ifd::Entry& entry) const + { return tag_ == entry.tag_; } + private: + uint16 tag_; + + }; // class FindEntryByTag + /*! @brief Read a complete IFD and its data from a data buffer @@ -521,12 +563,12 @@ namespace Exif { void print(std::ostream& os, const std::string& prefix ="") const; //! @name Accessors //@{ + //! Ifd id of the IFD + IfdId ifdId() const { return ifdId_; } //! Offset of the IFD from SOI long offset() const { return offset_; } - //! Get the IFD entries - const Metadata& entries() const { return entries_; } //! Find an IFD entry by tag, return an iterator into the entries list - Metadata::const_iterator findTag(uint16 tag) const; + Entries::const_iterator findTag(uint16 tag) const; //! Get the offset to the next IFD from the start of the Tiff header long next() const { return next_; } //! Get the size of this IFD in bytes (IFD only, without data) @@ -534,10 +576,11 @@ namespace Exif { //@} private: + Entries entries_; // IFD entries + IfdId ifdId_; // IFD Id long offset_; // offset of the IFD from the start of // Tiff header - Metadata entries_; // IFD metadata entries long next_; // offset of next IFD from the start of // the Tiff header long size_; // size of the IFD in bytes @@ -599,8 +642,8 @@ namespace Exif { //! Returns the byte order as specified in the Tiff header ByteOrder byteOrder() const { return tiffHeader_.byteOrder(); } - //! Add all entries of src to the Exif metadata - void add(const Metadata& src); + //! Add all entries of an IFD to the Exif metadata + void add(const Ifd& ifd, ByteOrder byteOrder); //! Add Metadatum src to the Exif metadata void add(const Metadatum& src); diff --git a/src/exiftest.cpp b/src/exiftest.cpp index d2715576..7974b934 100644 --- a/src/exiftest.cpp +++ b/src/exiftest.cpp @@ -22,14 +22,14 @@ int main(int argc, char* const argv[]) for (; i != end; ++i) { std::cout << "0x" << std::hex << std::setw(4) << std::setfill('0') << std::right - << i->tag_ << " " + << i->tag() << " " << std::setw(27) << std::setfill(' ') << std::left << i->tagName() << " " << std::setw(17) << std::setfill(' ') << std::left << i->typeName() << " " << std::dec << std::setw(3) << std::setfill(' ') << std::right - << i->count_ << " " + << i->count() << " " << std::dec << i->value() << "\n"; } }