From 440571b5447dde7eba1c735091344f1fa7f99b9c Mon Sep 17 00:00:00 2001 From: Andreas Huggel Date: Sun, 18 Jan 2004 16:53:12 +0000 Subject: [PATCH] Values implemented --- src/exif.cpp | 226 ++++++++++++++++++++++++++----------- src/exif.hpp | 286 +++++++++++++++++++++++++++++++++++++++++------ src/exiftest.cpp | 48 ++++---- src/tags.cpp | 52 +++++++-- src/tags.hpp | 45 ++++++-- 5 files changed, 524 insertions(+), 133 deletions(-) diff --git a/src/exif.cpp b/src/exif.cpp index 70cfd0af..4183ccc6 100644 --- a/src/exif.cpp +++ b/src/exif.cpp @@ -12,7 +12,7 @@ RCS information $Name: $ - $Revision: 1.3 $ + $Revision: 1.4 $ */ // ***************************************************************************** // included header files @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -101,15 +102,15 @@ namespace Exif { if (getUShort(marker, bigEndian) != app1_) return 3; // Read the length of the APP1 field and the Exif identifier - char buf[8]; - ::memset(buf, 0x0, 8); - is.read(buf, 8); + char tmpbuf[8]; + ::memset(tmpbuf, 0x0, 8); + is.read(tmpbuf, 8); if (!is.good()) return 1; // Get the length of the APP1 field and do a plausibility check - long app1Length = getUShort(buf, bigEndian); + long app1Length = getUShort(tmpbuf, bigEndian); if (app1Length < 8) return 4; // Check the Exif identifier - if (::memcmp(buf+2, exifId_, 6) != 0) return 4; + if (::memcmp(tmpbuf+2, exifId_, 6) != 0) return 4; // Read the rest of the APP1 field (Exif data) long sizeExifData = app1Length - 8; @@ -166,95 +167,112 @@ namespace Exif { return size(); } - Value* Value::create(TypeId typeId, ByteOrder byteOrder) + Value* Value::create(TypeId typeId) { Value* value = 0; switch (typeId) { case invalid: + value = new DataValue(invalid); break; case unsignedByte: - value = new AsciiValue; + value = new DataValue(unsignedByte); break; case asciiString: value = new AsciiValue; break; case unsignedShort: - value = new UShortValue(byteOrder); + value = new ValueType; break; case unsignedLong: + value = new ValueType; + break; case unsignedRational: - case signedByte: + value = new ValueType; + break; + case invalid6: + value = new DataValue(invalid6); + break; case undefined: + value = new DataValue; + break; case signedShort: + value = new ValueType; + break; case signedLong: + value = new ValueType; + break; case signedRational: - case singleFloat: - case doubleFloat: - value = new AsciiValue; + value = new ValueType; break; } return value; } // Value::create - void AsciiValue::read(const char* buf, long len) + void DataValue::read(const char* buf, long len, ByteOrder byteOrder) { + // byteOrder not needed value_ = std::string(buf, len); } - void AsciiValue::read(const std::string& buf) + void DataValue::read(const std::string& buf) { + // Todo: read from a string of bytes?? value_ = buf; } - long AsciiValue::copy(char* buf) const + long DataValue::copy(char* buf, ByteOrder byteOrder) const { + // byteOrder not needed return value_.copy(buf, value_.size()); } - long AsciiValue::size() const + long DataValue::size() const { return value_.size(); } - Value* AsciiValue::clone() const + Value* DataValue::clone() const { - return new AsciiValue(*this); + return new DataValue(*this); } - std::ostream& AsciiValue::write(std::ostream& os) const + std::ostream& DataValue::write(std::ostream& os) const { - return os << value_; + std::string::size_type end = value_.size(); + for (std::string::size_type i = 0; i != end; ++i) { + os << (int)(unsigned char)value_[i] << " "; + } + return os; } - void UShortValue::read(const char* buf, long len) + void AsciiValue::read(const char* buf, long len, ByteOrder byteOrder) { - // Todo: Should we check to make sure that len is 2 - value_ = getUShort(buf, byteOrder_); + // byteOrder not needed + value_ = std::string(buf, len); } - void UShortValue::read(const std::string& buf) + void AsciiValue::read(const std::string& buf) { - std::istringstream is(buf); - is >> value_; + value_ = buf; } - long UShortValue::copy(char* buf) const + long AsciiValue::copy(char* buf, ByteOrder byteOrder) const { - us2Data(buf, value_, byteOrder_); - return size(); + // byteOrder not needed + return value_.copy(buf, value_.size()); } - long UShortValue::size() const + long AsciiValue::size() const { - return 2; + return value_.size(); } - Value* UShortValue::clone() const + Value* AsciiValue::clone() const { - return new UShortValue(*this); + return new AsciiValue(*this); } - std::ostream& UShortValue::write(std::ostream& os) const + std::ostream& AsciiValue::write(std::ostream& os) const { return os << value_; } @@ -287,6 +305,8 @@ namespace Exif { Metadatum& Metadatum::operator=(const Metadatum& rhs) { + if (this == &rhs) return *this; + tag_ = rhs.tag_; type_ = rhs.type_; count_ = rhs.count_; @@ -361,15 +381,15 @@ namespace Exif { for (i = eb; i != ee; ++i) { delete i->value_; //! Todo: Create the correct type here, once we have them - i->value_ = Value::create(TypeId(i->type_), byteOrder); + i->value_ = Value::create(TypeId(i->type_)); if (i->size_ > 4) { i->offset_ = i->offset_ - offset_; - i->value_->read(buf + i->offset_, i->size_); + i->value_->read(buf + i->offset_, i->size_, byteOrder); } else { - char value[4]; - ul2Data(value, i->offset_, byteOrder); - i->value_->read(value, 4); + char tmpbuf[4]; + ul2Data(tmpbuf, i->offset_, byteOrder); + i->value_->read(tmpbuf, i->size_, byteOrder); } } return 0; @@ -411,7 +431,10 @@ namespace Exif { dataSize += i->size_; } else { - ul2Data(buf+o+8, i->offset_, byteOrder); + char tmpbuf[4]; + ::memset(tmpbuf, 0x0, 4); + i->value_->copy(tmpbuf, byteOrder); + ::memcpy(buf+o+8, tmpbuf, 4); } o += 12; } @@ -432,7 +455,7 @@ namespace Exif { // 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); + o += i->value_->copy(buf+o, byteOrder); } } @@ -449,7 +472,7 @@ namespace Exif { << ", IFD Entries: " << std::setfill(' ') << std::dec << std::right << entries_.size() << "\n" - << prefix << "Entry Tag Format (Bytes each) Number Offset/Data\n" + << prefix << "Entry Tag Format (Bytes each) Number Offset\n" << prefix << "----- ------ --------------------- ------ -----------\n"; const Metadata::const_iterator b = entries_.begin(); @@ -458,19 +481,22 @@ namespace Exif { for (; i != e; ++i) { std::ostringstream offset; if (i->typeSize() * i->count_ <= 4) { - // Minor cheat here: we use value_ instead of offset_ to avoid - // having to invoke ul2Data() which would require byte order. - // Todo: can value_ be 0 here? - char tmpbuf[4]; - i->value_->copy(tmpbuf); - 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) << " "; + +// 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"; } else { offset << " 0x" << std::setw(8) << std::setfill('0') << std::hex @@ -622,13 +648,80 @@ namespace Exif { } } - std::string getString(const char* buf, long len) + URational getURational(const char* buf, ByteOrder byteOrder) + { + uint32 nominator = getULong(buf, byteOrder); + uint32 denominator = getULong(buf + 4, byteOrder); + return std::make_pair(nominator, denominator); + } + + int16 getShort(const char* buf, ByteOrder byteOrder) + { + if (byteOrder == littleEndian) { + return (unsigned char)buf[1] << 8 | (unsigned char)buf[0]; + } + else { + return (unsigned char)buf[0] << 8 | (unsigned char)buf[1]; + } + } + + int32 getLong(const char* buf, ByteOrder byteOrder) + { + if (byteOrder == littleEndian) { + return (unsigned char)buf[3] << 24 | (unsigned char)buf[2] << 16 + | (unsigned char)buf[1] << 8 | (unsigned char)buf[0]; + } + else { + return (unsigned char)buf[0] << 24 | (unsigned char)buf[1] << 16 + | (unsigned char)buf[2] << 8 | (unsigned char)buf[3]; + } + } + + Rational getRational(const char* buf, ByteOrder byteOrder) + { + int32 nominator = getLong(buf, byteOrder); + int32 denominator = getLong(buf + 4, byteOrder); + return std::make_pair(nominator, denominator); + } + + long us2Data(char* buf, uint16 s, ByteOrder byteOrder) + { + if (byteOrder == littleEndian) { + buf[0] = s & 0x00ff; + buf[1] = (s & 0xff00) >> 8; + } + else { + buf[0] = (s & 0xff00) >> 8; + buf[1] = s & 0x00ff; + } + return 2; + } + + long ul2Data(char* buf, uint32 l, ByteOrder byteOrder) + { + if (byteOrder == littleEndian) { + buf[0] = l & 0x000000ff; + buf[1] = (l & 0x0000ff00) >> 8; + buf[2] = (l & 0x00ff0000) >> 16; + buf[3] = (l & 0xff000000) >> 24; + } + else { + buf[0] = (l & 0xff000000) >> 24; + buf[1] = (l & 0x00ff0000) >> 16; + buf[2] = (l & 0x0000ff00) >> 8; + buf[3] = l & 0x000000ff; + } + return 4; + } + + long ur2Data(char* buf, URational l, ByteOrder byteOrder) { - std::string txt(buf, len); - return txt; + long o = ul2Data(buf, l.first, byteOrder); + o += ul2Data(buf+o, l.second, byteOrder); + return o; } - char* us2Data(char* buf, uint16 s, ByteOrder byteOrder) + long s2Data(char* buf, int16 s, ByteOrder byteOrder) { if (byteOrder == littleEndian) { buf[0] = s & 0x00ff; @@ -638,10 +731,10 @@ namespace Exif { buf[0] = (s & 0xff00) >> 8; buf[1] = s & 0x00ff; } - return buf; + return 2; } - char* ul2Data(char* buf, uint32 l, ByteOrder byteOrder) + long l2Data(char* buf, int32 l, ByteOrder byteOrder) { if (byteOrder == littleEndian) { buf[0] = l & 0x000000ff; @@ -655,7 +748,14 @@ namespace Exif { buf[2] = (l & 0x0000ff00) >> 8; buf[3] = l & 0x000000ff; } - return buf; + return 4; + } + + long r2Data(char* buf, Rational l, ByteOrder byteOrder) + { + long o = l2Data(buf, l.first, byteOrder); + o += l2Data(buf+o, l.second, byteOrder); + return o; } void hexdump(std::ostream& os, const char* buf, long len) diff --git a/src/exif.hpp b/src/exif.hpp index a5f2b11e..5150842c 100644 --- a/src/exif.hpp +++ b/src/exif.hpp @@ -8,7 +8,7 @@ /*! @file exif.hpp @brief Encoding and decoding of %Exif data - @version $Name: $ $Revision: 1.3 $ + @version $Name: $ $Revision: 1.4 $ @author Andreas Huggel (ahu) @date 09-Jan-03, ahu: created */ @@ -22,7 +22,8 @@ // + standard includes #include #include -#include +#include +#include // ***************************************************************************** // namespace extensions @@ -145,7 +146,14 @@ namespace Exif { uint32 offset_; }; // class TiffHeader - //! Common interface for all values + /*! + @brief Common interface for all values. The interface provides a uniform + way to access values independent from their actual C++ type for + simple tasks like reading the values. For other tasks, like modifying + values you need to downcast it to the actual subclass of Value so + that you can access the subclass specific interface (e.g., assignment + operator for a vector of unsigned longs). + */ class Value { public: //! Constructor, taking a type id to initialize the base class with @@ -157,8 +165,9 @@ namespace Exif { @param buf Pointer to the data buffer to read from @param len Number of bytes in the data buffer + @param byteOrder Applicable byte order (little or big endian). */ - virtual void read(const char* buf, long len) =0; + virtual void read(const char* buf, long len, ByteOrder byteOrder) =0; //! Set the value from a string buffer virtual void read(const std::string& buf) =0; /*! @@ -168,13 +177,14 @@ namespace Exif { the call results in undefined behaviour. @param buf Data buffer to write to. + @param byteOrder Applicable byte order (little or big endian). @return Number of characters written. */ - virtual long copy(char* buf) const =0; - //! Returns the size of the value in bytes + virtual long copy(char* buf, ByteOrder byteOrder) const =0; + //! Return the size of the value in bytes virtual long size() const =0; /*! - @brief Returns a pointer to a copy of itself (deep copy). + @brief Return a pointer to a copy of itself (deep copy). The caller owns this copy and is responsible to delete it! */ virtual Value* clone() const =0; @@ -185,12 +195,10 @@ namespace Exif { @brief A (simple) factory to create a Value type. @param typeId Type of the value. - @param byteOrder Applicable byte order (little or big endian). - @return Pointer to the newly created Value. The caller owns this copy and is responsible to delete it! */ - static Value* create(TypeId typeId, ByteOrder byteOrder); + static Value* create(TypeId typeId); protected: const TypeId typeId_; //!< Format type identifier @@ -203,14 +211,31 @@ namespace Exif { return value.write(os); } - //! %Value representing an Ascii string type. + //! %Value for an undefined data type. + class DataValue : public Value { + public: + //! Default constructor. + DataValue(TypeId typeId =undefined) : Value(typeId) {} + virtual void read(const char* buf, long len, ByteOrder byteOrder); + virtual void read(const std::string& buf); + virtual long copy(char* buf, ByteOrder byteOrder) const; + virtual long size() const; + virtual Value* clone() const; + virtual std::ostream& write(std::ostream& os) const; + + private: + std::string value_; + + }; + + //! %Value for an Ascii string type. class AsciiValue : public Value { public: //! Default constructor. AsciiValue() : Value(asciiString) {} - virtual void read(const char* buf, long len); + virtual void read(const char* buf, long len, ByteOrder byteOrder); virtual void read(const std::string& buf); - virtual long copy(char* buf) const; + virtual long copy(char* buf, ByteOrder byteOrder) const; virtual long size() const; virtual Value* clone() const; virtual std::ostream& write(std::ostream& os) const; @@ -220,22 +245,25 @@ namespace Exif { }; - //! %Value representing one unsigned short type. - class UShortValue : public Value { + /*! + @brief Template for a %Value for a basic Type. This is used for unsigned + and signed short, long and rational. + */ + template + class ValueType : public Value { public: - //! Constructor, taking the byte order (endianness) as argument - UShortValue(ByteOrder byteOrder) - : Value(unsignedShort), byteOrder_(byteOrder) {} - virtual void read(const char* buf, long len); + //! Default constructor. + ValueType() : Value(getType()) {} + virtual void read(const char* buf, long len, ByteOrder byteOrder); virtual void read(const std::string& buf); - virtual long copy(char* buf) const; + virtual long copy(char* buf, ByteOrder byteOrder) const; virtual long size() const; virtual Value* clone() const; virtual std::ostream& write(std::ostream& os) const; private: - ByteOrder byteOrder_; - uint16 value_; + typedef std::vector ValueList; + ValueList value_; }; @@ -252,9 +280,9 @@ namespace Exif { //! Return the name of the type const char* tagName() const { return ExifTags::tagName(tag_, ifdId_); } //! Return the name of the type - const char* typeName() const { return ExifTags::typeName(type_); } + const char* typeName() const { return ExifTags::typeName(TypeId(type_)); } //! Return the size in bytes of one element of this type - long typeSize() const { return ExifTags::typeSize(type_); } + long typeSize() const { return ExifTags::typeSize(TypeId(type_)); } //! Return the name of the IFD const char* ifdName() const { return ExifTags::ifdName(ifdId_); } //! Return the related image item (image or thumbnail) @@ -273,7 +301,7 @@ namespace Exif { public: uint16 tag_; //!< Tag value - uint16 type_; //!< Type of the data + uint16 type_; //!< Type of the data Todo: change to TypeId? uint32 count_; //!< Number of components uint32 offset_; //!< Offset of the data from start of IFD long size_; //!< Size of the data in bytes @@ -445,28 +473,220 @@ namespace Exif { TiffHeader tiffHeader_; Metadata metadata_; }; // class ExifData - + // ***************************************************************************** // free functions //! Read a 2 byte unsigned short value from the data buffer uint16 getUShort(const char* buf, ByteOrder byteOrder); - //! Read a 4 byte unsigned long value from the data buffer uint32 getULong(const char* buf, ByteOrder byteOrder); + //! Read an 8 byte unsigned rational value from the data buffer + URational getURational(const char* buf, ByteOrder byteOrder); + //! Read a 2 byte signed short value from the data buffer + int16 getShort(const char* buf, ByteOrder byteOrder); + //! Read a 4 byte signed long value from the data buffer + int32 getLong(const char* buf, ByteOrder byteOrder); + //! Read an 8 byte signed rational value from the data buffer + Rational getRational(const char* buf, ByteOrder byteOrder); + + /*! + @brief Read a value of type T from the data buffer. + + We need this template function for the ValueType template classes. + There are only specializations of this function available; no default + implementation is provided. + + @param buf Pointer to the data buffer to read from. + @param byteOrder Applicable byte order (little or big endian). + @return A value of type T. + */ + template T getValue(const char* buf, ByteOrder byteOrder); + // Specialization for a 2 byte unsigned short value. + template<> inline uint16 getValue(const char* buf, ByteOrder byteOrder) + { + return getUShort(buf, byteOrder); + } + // Specialization for a 4 byte unsigned long value. + template<> inline uint32 getValue(const char* buf, ByteOrder byteOrder) + { + return getULong(buf, byteOrder); + } + // Specialization for an 8 byte unsigned rational value. + template<> inline URational getValue(const char* buf, ByteOrder byteOrder) + { + return getURational(buf, byteOrder); + } + // Specialization for a 2 byte signed short value. + template<> inline int16 getValue(const char* buf, ByteOrder byteOrder) + { + return getShort(buf, byteOrder); + } + // Specialization for a 4 byte signed long value. + template<> inline int32 getValue(const char* buf, ByteOrder byteOrder) + { + return getLong(buf, byteOrder); + } + // Specialization for an 8 byte signed rational value. + template<> inline Rational getValue(const char* buf, ByteOrder byteOrder) + { + return getRational(buf, byteOrder); + } + + /*! + @brief Convert an unsigned short to data, write the data to the buffer, + return number of bytes written. + */ + long us2Data(char* buf, uint16 s, ByteOrder byteOrder); + /*! + @brief Convert an unsigned long to data, write the data to the buffer, + return number of bytes written. + */ + long ul2Data(char* buf, uint32 l, ByteOrder byteOrder); + /*! + @brief Convert an unsigned rational to data, write the data to the buffer, + return number of bytes written. + */ + long ur2Data(char* buf, URational l, ByteOrder byteOrder); + /*! + @brief Convert a signed short to data, write the data to the buffer, + return number of bytes written. + */ + long s2Data(char* buf, int16 s, ByteOrder byteOrder); + /*! + @brief Convert a signed long to data, write the data to the buffer, + return number of bytes written. + */ + long l2Data(char* buf, int32 l, ByteOrder byteOrder); + /*! + @brief Convert a signed rational to data, write the data to the buffer, + return number of bytes written. + */ + long r2Data(char* buf, Rational l, ByteOrder byteOrder); - //! Convert len bytes from the data buffer into a string - std::string getString(const char* buf, long len); + /*! + @brief Convert a value of type T to data, write the data to the data buffer. - //! Write an unsigned short to the data buffer - char* us2Data(char* buf, uint16 s, ByteOrder byteOrder); + We need this template function for the ValueType template classes. + There are only specializations of this function available; no default + implementation is provided. - //! Convert an unsigned long to data, write the data to the buffer - char* ul2Data(char* buf, uint32 l, ByteOrder byteOrder); + @param buf Pointer to the data buffer to write to. + @param t Value to be converted. + @param byteOrder Applicable byte order (little or big endian). + @return The number of bytes written to the buffer. + */ + template long toData(char* buf, T t, ByteOrder byteOrder); + /*! + @brief Specialization to write an unsigned short to the data buffer. + Return the number of bytes written. + */ + template<> inline long toData(char* buf, uint16 t, ByteOrder byteOrder) + { + return us2Data(buf, t, byteOrder); + } + /*! + @brief Specialization to write an unsigned long to the data buffer. + Return the number of bytes written. + */ + template<> inline long toData(char* buf, uint32 t, ByteOrder byteOrder) + { + return ul2Data(buf, t, byteOrder); + } + /*! + @brief Specialization to write an unsigned rational to the data buffer. + Return the number of bytes written. + */ + template<> inline long toData(char* buf, URational t, ByteOrder byteOrder) + { + return ur2Data(buf, t, byteOrder); + } + /*! + @brief Specialization to write a signed short to the data buffer. + Return the number of bytes written. + */ + template<> inline long toData(char* buf, int16 t, ByteOrder byteOrder) + { + return s2Data(buf, t, byteOrder); + } + /*! + @brief Specialization to write a signed long to the data buffer. + Return the number of bytes written. + */ + template<> inline long toData(char* buf, int32 t, ByteOrder byteOrder) + { + return l2Data(buf, t, byteOrder); + } + /*! + @brief Specialization to write a signed rational to the data buffer. + Return the number of bytes written. + */ + template<> inline long toData(char* buf, Rational t, ByteOrder byteOrder) + { + return r2Data(buf, t, byteOrder); + } //! Print len bytes from buf in hex and ASCII format to the given stream void hexdump(std::ostream& os, const char* buf, long len); +// ***************************************************************************** +// template definitions + + template + void ValueType::read(const char* buf, long len, ByteOrder byteOrder) + { + value_.clear(); + for (long i = 0; i < len; i += ExifTags::typeSize(typeId_)) { + value_.push_back(getValue(buf + i, byteOrder)); + } + } + + template + void ValueType::read(const std::string& buf) + { + std::istringstream is(buf); + T tmp; + value_.clear(); + while (is >> tmp) { + value_.push_back(tmp); + } + } + + template + long ValueType::copy(char* buf, ByteOrder byteOrder) const + { + long offset = 0; + typename ValueList::const_iterator end = value_.end(); + for (typename ValueList::const_iterator i = value_.begin(); i != end; ++i) { + offset += toData(buf + offset, *i, byteOrder); + } + return offset; + } + + template + long ValueType::size() const + { + return ExifTags::typeSize(typeId_) * value_.size(); + } + + template + Value* ValueType::clone() const + { + return new ValueType(*this); + } + + template + std::ostream& ValueType::write(std::ostream& os) const + { + typename ValueList::const_iterator end = value_.end(); + typename ValueList::const_iterator i = value_.begin(); + while (i != end) { + os << *i; + if (++i != end) os << " "; + } + return os; + } + } // namespace Exif #endif // #ifndef _EXIF_HPP_ diff --git a/src/exiftest.cpp b/src/exiftest.cpp index 4c7dd81f..2bcb1efb 100644 --- a/src/exiftest.cpp +++ b/src/exiftest.cpp @@ -1,3 +1,4 @@ +#include "tags.hpp" #include "exif.hpp" #include #include @@ -19,34 +20,39 @@ int main(int argc, char* const argv[]) ExifData::const_iterator end = exifData.end(); ExifData::const_iterator i = beg; for (; i != end; ++i) { - std::cout << "0x" << std::hex << std::setw(4) << std::setfill('0') << std::right << i->tag_ << " " - << std::setw(50) << std::setfill(' ') << std::left - << i->key() << " "; - - if (i->type_ == 2 || i->type_ == 3) { - std::cout << std::dec << i->value() << "\n"; - } - else { - std::cout << std::setw(17) << std::setfill(' ') << std::left - << i->typeName() << " " - << std::dec << std::setw(3) - << std::setfill(' ') << std::right - << i->count_ << " " - << std::dec << std::setw(3) - << std::setfill(' ') << std::right - << i->typeSize() * i->count_ << "\n"; - } - + << 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_ << " " + << std::dec << i->value() << "\n"; } } - std::string tmp = "12"; - Value* val = Value::create(unsignedShort, littleEndian); +// std::string tmp = "12 2ddd4. xd35"; + std::string tmp = " 1 2 3"; + Value* val = Value::create(unsignedShort); + std::cout << "Reading test string \"" << tmp << "\"\n"; val->read(tmp); - std::cout << "And the answer is: " << *val << "\n"; + std::cout << "And the answer is: " << *val << ", size is " << val->size() << "\n"; + Rational r = std::make_pair(1,72); + URational ur = std::make_pair(2,3); + + std::cout << "Rational r = " << r << "\n"; + std::cout << "URational ur = " << ur << "\n"; + + ValueType vr; + ValueType vur; + + std::string str(" 4 / 5 x2 5/3"); + vr.read(str); + std::cout << "ValueType vr = " << vr + << ", size is " << vr.size() << "\n"; return rc; diff --git a/src/tags.cpp b/src/tags.cpp index beed3df5..31441d34 100644 --- a/src/tags.cpp +++ b/src/tags.cpp @@ -12,12 +12,14 @@ RCS information $Name: $ - $Revision: 1.3 $ + $Revision: 1.4 $ */ // ***************************************************************************** // included header files #include "tags.hpp" +#include + // ***************************************************************************** // class member definitions namespace Exif { @@ -80,13 +82,11 @@ namespace Exif { TagFormat(unsignedShort, "unsigned short", 2), TagFormat(unsignedLong, "unsigned long", 4), TagFormat(unsignedRational, "unsigned rational", 8), - TagFormat(signedByte, "signed byte", 1), + TagFormat(invalid6, "invalid (6)", 1), TagFormat(undefined, "undefined", 1), TagFormat(signedShort, "signed short", 2), TagFormat(signedLong, "signed long", 4), - TagFormat(signedRational, "signed rational", 8), - TagFormat(singleFloat, "single float", 4), - TagFormat(doubleFloat, "double float", 8) + TagFormat(signedRational, "signed rational", 8) }; TagInfo::TagInfo( @@ -281,14 +281,14 @@ namespace Exif { return sectionInfo_[tagInfo[tagInfoIdx(tag, ifdId)].sectionId_].name_; } - const char* ExifTags::typeName(uint16 type) + const char* ExifTags::typeName(TypeId typeId) { - return tagFormat_[type].name_; + return tagFormat_[typeId].name_; } - long ExifTags::typeSize(uint16 type) + long ExifTags::typeSize(TypeId typeId) { - return tagFormat_[type].size_; + return tagFormat_[typeId].size_; } const char* ExifTags::ifdName(IfdId ifdId) @@ -306,4 +306,38 @@ namespace Exif { return sectionInfo_[sectionId].name_; } + + // ************************************************************************* + // free functions + + std::ostream& operator<<(std::ostream& os, const Rational& r) + { + return os << r.first << "/" << r.second; + } + + std::istream& operator>>(std::istream& is, Rational& r) + { + int32 nominator; + int32 denominator; + char c; + is >> nominator >> c >> denominator; + if (is && c == '/') r = std::make_pair(nominator, denominator); + return is; + } + + std::ostream& operator<<(std::ostream& os, const URational& r) + { + return os << r.first << "/" << r.second; + } + + std::istream& operator>>(std::istream& is, URational& r) + { + uint32 nominator; + uint32 denominator; + char c; + is >> nominator >> c >> denominator; + if (is && c == '/') r = std::make_pair(nominator, denominator); + return is; + } + } // namespace Exif diff --git a/src/tags.hpp b/src/tags.hpp index fedb2889..0eff1dfb 100644 --- a/src/tags.hpp +++ b/src/tags.hpp @@ -8,7 +8,7 @@ /*! @file tags.hpp @brief %Exif tag and type information - @version $Name: $ $Revision: 1.3 $ + @version $Name: $ $Revision: 1.4 $ @author Andreas Huggel (ahu) @date 15-Jan-03, ahu: created */ @@ -20,6 +20,7 @@ // + standard includes #include // for std::pair +#include // ***************************************************************************** // namespace extensions @@ -44,16 +45,18 @@ namespace Exif { //! Type identifiers for IFD format types enum TypeId { invalid, unsignedByte, asciiString, unsignedShort, - unsignedLong, unsignedRational, signedByte, undefined, - signedShort, signedLong, signedRational, singleFloat, - doubleFloat }; + unsignedLong, unsignedRational, invalid6, undefined, + signedShort, signedLong, signedRational }; //! Type to specify the IFD to which a metadata belongs enum IfdId { IfdIdNotSet, ifd0, exifIfd, gpsIfd, makerIfd, iopIfd, ifd1, ifd1ExifIfd, ifd1GpsIfd, ifd1MakerIfd, ifd1IopIfd }; - //! Section identifiers to logically group tags + /*! + @brief Section identifiers to logically group tags. A section consists + of nothing more than a name, based on the Exif standard. + */ enum SectionId { SectionIdNotSet, imgStruct, recOffset, imgCharacter, otherTags, exifFormat, exifVersion, imgConfig, userInfo, relatedFile, dateTime, @@ -119,9 +122,9 @@ namespace Exif { //! Returns the name of the tag static const char* tagName(uint16 tag, IfdId ifdId); //! Returns the name of the type - static const char* typeName(uint16 type); + static const char* typeName(TypeId typeId); //! Returns the size in bytes of one element of this type - static long typeSize(uint16 type); + static long typeSize(TypeId typeId); //! Returns the name of the IFD static const char* ifdName(IfdId ifdId); //! Returns the related image item (image or thumbnail) @@ -145,6 +148,34 @@ namespace Exif { // ***************************************************************************** // free functions + //! Output operator for our fake rational + std::ostream& operator<<(std::ostream& os, const Rational& r); + //! Input operator for our fake rational + std::istream& operator>>(std::istream& is, Rational& r); + //! Output operator for our fake unsigned rational + std::ostream& operator<<(std::ostream& os, const URational& r); + //! Input operator for our fake unsigned rational + std::istream& operator>>(std::istream& is, URational& r); + + //! Template to determine the TypeId for a type T + template TypeId getType(); + + //! Specialization for an unsigned short + template<> inline TypeId getType() { return unsignedShort; } + //! Specialization for an unsigned long + template<> inline TypeId getType() { return unsignedLong; } + //! Specialization for an unsigned rational + template<> inline TypeId getType() { return unsignedRational; } + //! Specialization for a signed short + template<> inline TypeId getType() { return signedShort; } + //! Specialization for a signed long + template<> inline TypeId getType() { return signedLong; } + //! Specialization for a signed rational + template<> inline TypeId getType() { return signedRational; } + + // No default implementation: let the compiler/linker complain +// template inline TypeId getType() { return invalid; } + } // namespace Exif #endif // #ifndef _TAGS_HPP_