From 489fff191d6149179d048ec085f25ac34d5f72f2 Mon Sep 17 00:00:00 2001 From: Andreas Huggel Date: Sun, 11 Sep 2005 08:41:54 +0000 Subject: [PATCH] Canon CRW read support, first try. Introduces a new approach to parsing metadata. Cluttered by some related additions to the Canon makernote note and a bit of cleanup. --- src/Makefile | 10 +- src/actions.cpp | 22 +- src/basicio.hpp | 2 - src/canonmn.cpp | 64 ++-- src/canonmn.hpp | 25 ++ src/crwimage.cpp | 686 ++++++++++++++++++++++++++++++++++++ src/crwimage.hpp | 608 ++++++++++++++++++++++++++++++++ src/error.cpp | 4 + src/exif.cpp | 2 +- src/image.cpp | 1 + src/image.hpp | 2 +- src/nikonmn.cpp | 11 +- src/olympusmn.cpp | 6 +- src/panasonicmn.cpp | 10 +- src/tags.cpp | 55 ++- src/tags.hpp | 21 +- src/types.cpp | 31 +- src/types.hpp | 55 ++- test/data/exifdata-test.out | 32 +- test/data/exiv2-test.out | 42 +-- test/data/modify-test.out | 4 +- test/data/write-test.out | 64 ++-- test/data/write2-test.out | 8 +- 23 files changed, 1578 insertions(+), 187 deletions(-) create mode 100644 src/crwimage.cpp create mode 100644 src/crwimage.hpp diff --git a/src/Makefile b/src/Makefile index 6f752137..c08624f7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -51,16 +51,16 @@ include $(top_srcdir)/config/config.mk CCHDR = exv_conf.h exv_msvc.h mn.hpp rcsid.hpp # Add library C++ source files to this list -CCSRC = basicio.cpp canonmn.cpp datasets.cpp error.cpp exif.cpp futils.cpp \ - fujimn.cpp ifd.cpp image.cpp iptc.cpp jpgimage.cpp makernote.cpp \ - metadatum.cpp nikonmn.cpp olympusmn.cpp panasonicmn.cpp sigmamn.cpp \ - sonymn.cpp tags.cpp types.cpp value.cpp +CCSRC = basicio.cpp canonmn.cpp crwimage.cpp datasets.cpp error.cpp exif.cpp \ + futils.cpp fujimn.cpp ifd.cpp image.cpp iptc.cpp jpgimage.cpp \ + makernote.cpp metadatum.cpp nikonmn.cpp olympusmn.cpp panasonicmn.cpp \ + sigmamn.cpp sonymn.cpp tags.cpp types.cpp value.cpp # Add source files of simple applications to this list BINSRC = addmoddel.cpp dataarea-test.cpp exifcomment.cpp exifdata-test.cpp \ exifprint.cpp ifd-test.cpp iotest.cpp iptceasy.cpp iptcprint.cpp \ iptctest.cpp key-test.cpp makernote-test.cpp taglist.cpp write-test.cpp \ - write2-test.cpp + write2-test.cpp # Main source file of the Exiv2 application EXIV2MAIN = exiv2.cpp diff --git a/src/actions.cpp b/src/actions.cpp index f2eb9d11..1b031eb9 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -229,28 +229,28 @@ namespace Action { md = exifData.findKey( Exiv2::ExifKey("Exif.Photo.ShutterSpeedValue")); if (md != exifData.end()) { - double tmp = exp(log(2.0) * md->toFloat()) + 0.5; + double tmp = exp(log(2.0) * md->toFloat()); if (tmp > 1) { - std::cout << "1/" << static_cast(tmp) << " s"; + std::cout << "1/" << static_cast(tmp + 0.5); } else { - std::cout << static_cast(1/tmp) << " s"; + std::cout << static_cast(1/tmp + 0.5); } + std::cout << " s"; } } std::cout << std::endl; // Aperture // Get if from FNumber and, failing that, try ApertureValue + bool done = false; std::cout << std::setw(align_) << std::setfill(' ') << std::left << "Aperture" << ": "; - if (0 == printTag(exifData, "Exif.Photo.FNumber")) { - md = exifData.findKey( - Exiv2::ExifKey("Exif.Photo.ApertureValue")); - if (md != exifData.end()) { - std::cout << std::fixed << std::setprecision(1) - << "F" << exp(log(2.0) * md->toFloat() / 2); - } + if (!done) { + done = 0 != printTag(exifData, "Exif.Photo.FNumber"); + } + if (!done) { + done = 0 != printTag(exifData, "Exif.Photo.ApertureValue"); } std::cout << std::endl; @@ -262,7 +262,7 @@ namespace Action { // Todo: Flash bias, flash energy // Todo: Implement this for other cameras - bool done = false; + done = false; std::cout << std::setw(align_) << std::setfill(' ') << std::left << "Flash bias" << ": "; if (!done) { diff --git a/src/basicio.hpp b/src/basicio.hpp index a651fb47..291c0487 100644 --- a/src/basicio.hpp +++ b/src/basicio.hpp @@ -406,8 +406,6 @@ namespace Exiv2 { /*! @brief Flush any buffered writes and get the current file size in bytes. - @note On Win32 systems the file must be closed prior to calling this - function. @return Size of the file in bytes;
-1 if failure; */ diff --git a/src/canonmn.cpp b/src/canonmn.cpp index 3ff4871b..f60d0cf1 100644 --- a/src/canonmn.cpp +++ b/src/canonmn.cpp @@ -49,27 +49,6 @@ EXIV2_RCSID("@(#) $Id$"); #include #include -// ***************************************************************************** -// local declarations -namespace { - /* - @brief Convert Canon hex-based EV (modulo 0x20) to real number - Ported from Phil Harvey's Image::ExifTool::Canon::CanonEv - by Will Stokes - - 0x00 -> 0 - 0x0c -> 0.33333 - 0x10 -> 0.5 - 0x14 -> 0.66666 - 0x20 -> 1 - .. - 160 -> 5 - 128 -> 4 - 143 -> 4.46875 - */ - float canonEv(long val); -} - // ***************************************************************************** // class member definitions namespace Exiv2 { @@ -163,8 +142,8 @@ namespace Exiv2 { TagInfo(0x0001, "0x0001", "0x0001", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), TagInfo(0x0002, "ISOSpeed", "ISOSpeed", "ISO speed used", canonCs2IfdId, makerTags, unsignedShort, printCs20x0002), TagInfo(0x0003, "0x0003", "0x0003", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), - TagInfo(0x0004, "0x0004", "0x0004", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), - TagInfo(0x0005, "0x0005", "0x0005", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), + TagInfo(0x0004, "TargetAperture", "TargetAperture", "Target Aperture", canonCs2IfdId, makerTags, unsignedShort, printCs20x0015), + TagInfo(0x0005, "TargetShutterSpeed", "TargetShutterSpeed", "Target shutter speed", canonCs2IfdId, makerTags, unsignedShort, printCs20x0016), TagInfo(0x0006, "0x0006", "0x0006", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), TagInfo(0x0007, "WhiteBalance", "WhiteBalance", "White balance setting", canonCs2IfdId, makerTags, unsignedShort, printCs20x0007), TagInfo(0x0008, "0x0008", "0x0008", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), @@ -180,8 +159,8 @@ namespace Exiv2 { TagInfo(0x0012, "0x0012", "0x0012", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), TagInfo(0x0013, "SubjectDistance", "SubjectDistance", "Subject distance (units are not clear)", canonCs2IfdId, makerTags, unsignedShort, printCs20x0013), TagInfo(0x0014, "0x0014", "0x0014", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), - TagInfo(0x0015, "0x0015", "0x0015", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), - TagInfo(0x0016, "0x0016", "0x0016", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), + TagInfo(0x0015, "ApertureValue", "ApertureValue", "Aperture", canonCs2IfdId, makerTags, unsignedShort, printCs20x0015), + TagInfo(0x0016, "ShutterSpeedValue", "ShutterSpeedValue", "Shutter speed", canonCs2IfdId, makerTags, unsignedShort, printCs20x0016), TagInfo(0x0017, "0x0017", "0x0017", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), TagInfo(0x0018, "0x0018", "0x0018", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), TagInfo(0x0019, "0x0019", "0x0019", "Unknown", canonCs2IfdId, makerTags, unsignedShort, printValue), @@ -893,6 +872,33 @@ namespace Exiv2 { return os; } + std::ostream& CanonMakerNote::printCs20x0015(std::ostream& os, + const Value& value) + { + if (value.typeId() != unsignedShort) return os << value; + + std::ostringstream oss; + oss.copyfmt(os); + os << std::setprecision(2) + << "F" << fnumber(canonEv(value.toLong())); + os.copyfmt(oss); + + return os; + } + + std::ostream& CanonMakerNote::printCs20x0016(std::ostream& os, + const Value& value) + { + if (value.typeId() != unsignedShort) return os << value; + + URational ur = exposureTime(canonEv(value.toLong())); + os << ur.first; + if (ur.second > 1) { + os << "/" << ur.second; + } + return os << " s"; + } + // ***************************************************************************** // free functions @@ -905,12 +911,6 @@ namespace Exiv2 { return MakerNote::AutoPtr(new CanonMakerNote(alloc)); } -} // namespace Exiv2 - -// ***************************************************************************** -// local definitions -namespace { - float canonEv(long val) { // temporarily remove sign @@ -932,4 +932,4 @@ namespace { return sign * (val + frac) / 32.0f; } -} +} // namespace Exiv2 diff --git a/src/canonmn.hpp b/src/canonmn.hpp index 87dcba0b..cb64c5e7 100644 --- a/src/canonmn.hpp +++ b/src/canonmn.hpp @@ -188,6 +188,10 @@ namespace Exiv2 { static std::ostream& printCs20x000f(std::ostream& os, const Value& value); //! Subject distance static std::ostream& printCs20x0013(std::ostream& os, const Value& value); + //! Aperture + static std::ostream& printCs20x0015(std::ostream& os, const Value& value); + //! Shutter speed + static std::ostream& printCs20x0016(std::ostream& os, const Value& value); //@} //! @cond IGNORE @@ -234,6 +238,27 @@ namespace Exiv2 { }; // class CanonMakerNote static CanonMakerNote::RegisterMn registerCanonMakerNote; + +// ***************************************************************************** +// template, inline and free functions + + /*! + @brief Convert Canon hex-based EV (modulo 0x20) to real number + Ported from Phil Harvey's Image::ExifTool::Canon::CanonEv + by Will Stokes + + 0x00 -> 0 + 0x0c -> 0.33333 + 0x10 -> 0.5 + 0x14 -> 0.66666 + 0x20 -> 1 + .. + 160 -> 5 + 128 -> 4 + 143 -> 4.46875 + */ + float canonEv(long val); + } // namespace Exiv2 #endif // #ifndef CANONMN_HPP_ diff --git a/src/crwimage.cpp b/src/crwimage.cpp new file mode 100644 index 00000000..a5c7b677 --- /dev/null +++ b/src/crwimage.cpp @@ -0,0 +1,686 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2005 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + File: crwimage.cpp + Version: $Rev$ + Author(s): Andreas Huggel (ahu) + History: 28-Aug-05, ahu: created + + */ +// ***************************************************************************** +#include "rcsid.hpp" +EXIV2_RCSID("@(#) $Id$"); + +// Define DEBUG to output debug information to std::cerr, e.g, by calling make +// like this: make DEFS=-DDEBUG crwimage.o +//#define DEBUG + +// ***************************************************************************** +// included header files +#ifdef _MSC_VER +# include "exv_msvc.h" +#else +# include "exv_conf.h" +#endif + +#include "crwimage.hpp" +#include "error.hpp" +#include "futils.hpp" +#include "value.hpp" +#include "tags.hpp" +#include "canonmn.hpp" + +// + standard includes +#include +#include +#include +#include +#include +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + + // Local functions. These could be static private functions on Image + // subclasses but then ImageFactory needs to be made a friend. + /*! + @brief Create a new CrwImage instance and return an auto-pointer to it. + Caller owns the returned object and the auto-pointer ensures that + it will be deleted. + */ + Image::AutoPtr newCrwInstance(BasicIo::AutoPtr io, bool create); + + //! Check if the file iIo is a CRW image. + bool isCrwType(BasicIo& iIo, bool advance); + + const byte CrwImage::blank_[] = { + 0x00 + }; + + CrwImage::CrwImage(BasicIo::AutoPtr io, bool create) + : io_(io) + { + if (create) { + initImage(blank_, sizeof(blank_)); + } + } // CrwImage::CrwImage + + int CrwImage::initImage(const byte initData[], size_t dataSize) + { + if (io_->open() != 0) { + return 4; + } + IoCloser closer(*io_); + if (static_cast(io_->write(initData, dataSize)) != dataSize) { + return 4; + } + return 0; + } // CrwImage::initImage + + bool CrwImage::good() const + { + if (io_->open() != 0) return false; + IoCloser closer(*io_); + return isThisType(*io_, false); + } + + void CrwImage::clearMetadata() + { + clearExifData(); + clearComment(); + } + + void CrwImage::setMetadata(const Image& image) + { + setExifData(image.exifData()); + setComment(image.comment()); + } + + void CrwImage::clearExifData() + { + exifData_.clear(); + } + + void CrwImage::setExifData(const ExifData& exifData) + { + exifData_ = exifData; + } + + void CrwImage::clearIptcData() + { + throw Error(31, "CrwImage::clearIptcData"); + } + + void CrwImage::setIptcData(const IptcData& iptcData) + { + throw Error(31, "CrwImage::setIptcData"); + } + + void CrwImage::clearComment() + { + comment_.erase(); + } + + void CrwImage::setComment(const std::string& comment) + { + comment_ = comment; + } + + void CrwImage::readMetadata() + { +#ifdef DEBUG + std::cerr << "Reading CRW file " << io_->path() << "\n"; +#endif + if (io_->open() != 0) { + throw Error(9, io_->path(), strError()); + } + IoCloser closer(*io_); + // Ensure that this is the correct image type + if (!isThisType(*io_, false)) { + if (io_->error() || io_->eof()) throw Error(14); + throw Error(33); + } + clearMetadata(); + + // Read the image into a memory buffer + size_t imageSize = io_->size(); + DataBuf image(imageSize); + io_->read(image.pData_, imageSize); + if (io_->error() || io_->eof()) throw Error(14); + + // Parse the image + RawMetadata::AutoPtr parseTree(new CrwHeader); + parseTree->read(image.pData_, image.size_, 0, invalidByteOrder); +#ifdef DEBUG + parseTree->print(std::cerr, invalidByteOrder); +#endif + parseTree->extract(*this, invalidByteOrder); + + } // CrwImage::readMetadata + + void CrwImage::writeMetadata() + { + // Todo: implement me! + } // CrwImage::writeMetadata + + //! @cond IGNORE + CrwImage::CrwRegister::CrwRegister() + { + ImageFactory::registerImage( + Image::crw, newCrwInstance, isCrwType); + } + //! @endcond + + bool CrwImage::isThisType(BasicIo& iIo, bool advance) const + { + return isCrwType(iIo, advance); + } + + TypeId CrwEntry::typeId(uint16_t tag) + { + TypeId ti = invalidTypeId; + switch (tag & 0x3800) { + case 0x0000: ti = unsignedByte; break; + case 0x0800: ti = asciiString; break; + case 0x1000: ti = unsignedShort; break; + case 0x1800: ti = unsignedLong; break; + case 0x2000: ti = undefined; break; + case 0x2800: // fallthrough + case 0x3000: ti = directory; break; + } + return ti; + } // CrwEntry::typeId + + DataLocId CrwEntry::dataLocation(uint16_t tag) + { + DataLocId di = invalidDataLocId; + switch (tag & 0xc000) { + case 0x0000: di = valueData; break; + case 0x4000: di = directoryData; break; + } + return di; + } // CrwEntry::dataLocation + + void CrwEntry::add(RawMetadata::AutoPtr component) + { + throw Error(34, "CrwEntry::add"); + } // CrwEntry::add + + void CrwEntry::read(const byte* buf, + size_t len, + size_t start, + ByteOrder byteOrder, + long /*shift*/) + { + if (len < 10) throw Error(33); + tag_ = getUShort(buf + start, byteOrder); + switch (dataLocation()) { + case valueData: + size_ = getULong(buf + start + 2, byteOrder); + offset_ = getULong(buf + start + 6, byteOrder); + break; + case directoryData: + size_ = 8; + offset_ = start + 2; + break; + case invalidDataLocId: + case lastDataLocId: + // empty + break; + } + pData_ = buf + offset_; + } // CrwEntry::read + + void CrwEntry::extract(Image& image, ByteOrder byteOrder) const + { + CrwMap::extract(*this, image, byteOrder); + } // CrwEntry::extract + + void CrwEntry::print(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix) const + { + os << prefix + << "tag = 0x" << std::setw(4) << std::setfill('0') + << std::hex << std::right << tagId() + << ", dir = 0x" << std::setw(4) << std::setfill('0') + << std::hex << std::right << dir() + << ", type = " << TypeInfo::typeName(typeId()) + << ", size = " << std::dec << size_ + << ", offset = " << offset_ << "\n"; + + Value::AutoPtr value; + if (typeId() != directory) { + value = Value::create(typeId()); + value->read(pData_, size_, byteOrder); + if (value->size() < 100) { + os << prefix << *value << "\n"; + } + } + + } // CrwEntry::print + + CrwDirectory::~CrwDirectory() + { + RawMetadata::Components::iterator b = components_.begin(); + RawMetadata::Components::iterator e = components_.end(); + for (RawMetadata::Components::iterator i = b; i != e; ++i) { + delete *i; + } + } + + void CrwDirectory::add(RawMetadata::AutoPtr component) + { + components_.push_back(component.release()); + } // CrwEntry::add + + void CrwDirectory::read(const byte* buf, + size_t len, + size_t start, + ByteOrder byteOrder, + long /*shift*/) + { + CrwEntry::read(buf, len, start, byteOrder); + readDirectory(buf + offset(), size(), 0, byteOrder, 0); + } // CrwDirectory::read + + void CrwDirectory::extract(Image& image, ByteOrder byteOrder) const + { + RawMetadata::Components::const_iterator b = components_.begin(); + RawMetadata::Components::const_iterator e = components_.end(); + for (RawMetadata::Components::const_iterator i = b; i != e; ++i) { + (*i)->extract(image, byteOrder); + } + } // CrwDirectory::extract + + void CrwDirectory::print(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix) const + { + CrwEntry::print(os, byteOrder, prefix); + RawMetadata::Components::const_iterator b = components_.begin(); + RawMetadata::Components::const_iterator e = components_.end(); + for (RawMetadata::Components::const_iterator i = b; i != e; ++i) { + (*i)->print(os, byteOrder, prefix + " "); + } + } // CrwDirectory::print + + void CrwDirectory::readDirectory(const byte* buf, + size_t len, + size_t start, + ByteOrder byteOrder, + long /*shift*/) + { + uint32_t dataSize = getULong(buf + len - 4, byteOrder); + uint32_t o = start + dataSize; + if (o + 2 > len) throw Error(33); + uint16_t count = getUShort(buf + o, byteOrder); + o += 2; + for (uint16_t i = 0; i < count; ++i) { + if (o + 10 > len) throw Error(33); + uint16_t tag = getUShort(buf + o, byteOrder); + CrwEntry* p = 0; + switch (CrwEntry::typeId(tag)) { + case directory: p = new CrwDirectory; break; + default: p = new CrwEntry; break; + } + p->setDir(this->tag()); + RawMetadata::AutoPtr m(p); + m->read(buf, len, o, byteOrder); + add(m); + o += 10; + } + } // CrwDirectory::readDirectory + + const char CrwHeader::signature_[] = "HEAPCCDR"; + + CrwHeader::~CrwHeader() + { + delete rootDirectory_; + } + + void CrwHeader::add(RawMetadata::AutoPtr component) + { + throw Error(34, "CrwHeader::add"); + } // CrwEntry::add + + void CrwHeader::read(const byte* buf, + size_t len, + size_t start, + ByteOrder byteOrder, + long /*shift*/) + { + if (len < 14) throw Error(33); + + if (buf[0] == 0x49 && buf[1] == 0x49) { + byteOrder_ = littleEndian; + } + else if (buf[0] == 0x4d && buf[1] == 0x4d) { + byteOrder_ = bigEndian; + } + else { + throw Error(33); + } + offset_ = getULong(buf + 2, byteOrder_); + if (std::memcmp(buf + 6, signature_, 8) != 0) { + throw Error(33); + } + + rootDirectory_ = new CrwDirectory; + rootDirectory_->readDirectory(buf + offset_, len - offset_, 0, byteOrder_); + } // CrwHeader::read + + void CrwHeader::extract(Image& image, ByteOrder byteOrder) const + { + // Nothing to extract from the header itself, just add correct byte order + if (rootDirectory_) rootDirectory_->extract(image, byteOrder_); + } // CrwHeader::extract + + void CrwHeader::print(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix) const + { + os << prefix + << "Header, offset = 0x" << std::setw(8) << std::setfill('0') + << std::hex << std::right << offset_ << "\n"; + if (rootDirectory_) rootDirectory_->print(os, byteOrder_, prefix); + } // CrwHeader::print + + const CrwMapInfo CrwMap::crwMapInfos_[] = { + CrwMapInfo(0x0805, 0x300a, 0, 0x9286, exifIfdId, extract0x0805, 0), + CrwMapInfo(0x080a, 0x2807, 0, 0x010f, ifd0Id, extract0x080a, 0), + CrwMapInfo(0x080a, 0x2807, 0, 0x0110, ifd0Id, 0, 0), + CrwMapInfo(0x080b, 0x3004, 0, 0x0007, canonIfdId, extractBasic, 0), + CrwMapInfo(0x0810, 0x2807, 0, 0x0009, canonIfdId, extractBasic, 0), + CrwMapInfo(0x0815, 0x2804, 0, 0x0006, canonIfdId, extractBasic, 0), + CrwMapInfo(0x1029, 0x300b, 0, 0x0002, canonIfdId, extractBasic, 0), + CrwMapInfo(0x102a, 0x300b, 0, 0x0004, canonIfdId, extract0x102a, 0), + CrwMapInfo(0x102d, 0x300b, 0, 0x0001, canonIfdId, extract0x102d, 0), + CrwMapInfo(0x1033, 0x300b, 0, 0x000f, canonIfdId, extractBasic, 0), + CrwMapInfo(0x1038, 0x300b, 0, 0x0012, canonIfdId, extractBasic, 0), + CrwMapInfo(0x10a9, 0x300b, 0, 0x00a9, canonIfdId, extractBasic, 0), +// CrwMapInfo(0x10b4, 0x300b, 0, 0x00b4, canonIfdId, extractBasic, 0), + CrwMapInfo(0x10b4, 0x300b, 0, 0xa001, exifIfdId, extractBasic, 0), + CrwMapInfo(0x10b5, 0x300b, 0, 0x00b5, canonIfdId, extractBasic, 0), + CrwMapInfo(0x10c0, 0x300b, 0, 0x00c0, canonIfdId, extractBasic, 0), + CrwMapInfo(0x10c1, 0x300b, 0, 0x00c1, canonIfdId, extractBasic, 0), + CrwMapInfo(0x1807, 0x3002, 0, 0x9206, exifIfdId, extractBasic, 0), + CrwMapInfo(0x180b, 0x2807, 0, 0x000c, canonIfdId, extractBasic, 0), + CrwMapInfo(0x180e, 0x300a, 0, 0x9003, exifIfdId, extract0x180e, 0), + CrwMapInfo(0x1810, 0x300a, 0, 0xa002, exifIfdId, extract0x1810, 0), + CrwMapInfo(0x1810, 0x300a, 0, 0xa003, exifIfdId, extract0x1810, 0), + CrwMapInfo(0x1817, 0x300a, 4, 0x0008, canonIfdId, extractBasic, 0), +// CrwMapInfo(0x1818, 0x3002, 0, 0x9204, exifIfdId, extractBasic, 0), + CrwMapInfo(0x183b, 0x300b, 0, 0x0015, canonIfdId, extractBasic, 0), + CrwMapInfo(0x2008, 0x0000, 0, 0x0201, ifd1Id, extract0x2008, 0), + CrwMapInfo(0x2008, 0x0000, 0, 0x0202, ifd1Id, 0, 0), + CrwMapInfo(0x2008, 0x0000, 0, 0x0103, ifd1Id, 0, 0), + CrwMapInfo(0x0000, 0x0000, 0, 0x0000, ifdIdNotSet, extractBasic, 0) + }; // CrwMap::crwMapInfos_[] + + void CrwMap::extract(const CrwEntry& crwEntry, + Image& image, + ByteOrder byteOrder) + { + const CrwMapInfo* cmi = crwMapInfo(crwEntry.dir(), crwEntry.tagId()); + if (cmi && cmi->toExif_) { + cmi->toExif_(crwEntry, cmi, image, byteOrder); + } + } // CrwMap::extract + + const CrwMapInfo* CrwMap::crwMapInfo(uint16_t dir, uint16_t tagId) + { + for (int i = 0; crwMapInfos_[i].ifdId_ != ifdIdNotSet; ++i) { + if ( crwMapInfos_[i].crwDir_ == dir + && crwMapInfos_[i].crwTagId_ == tagId) { + return &(crwMapInfos_[i]); + } + } + return 0; + } // CrwMap::crwMapInfo + + void CrwMap::extract0x0805(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder /*byteOrder*/) + { + std::string s(reinterpret_cast(crwEntry.pData())); + image.setComment(s); + } // CrwMap::extract0x0805 + + void CrwMap::extract0x080a(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder) + { + if (crwEntry.typeId() != asciiString) { + return extractBasic(crwEntry, crwMapInfo, image, byteOrder); + } + + // Make + ExifKey key1("Exif.Image.Make"); + Value::AutoPtr value1 = Value::create(crwEntry.typeId()); + uint32_t i = 0; + for (; i < crwEntry.size() && crwEntry.pData()[i] != '\0'; ++i) { + // empty + } + value1->read(crwEntry.pData(), ++i, byteOrder); + image.exifData().add(key1, value1.get()); + + // Model + ExifKey key2("Exif.Image.Model"); + Value::AutoPtr value2 = Value::create(crwEntry.typeId()); + uint32_t j = i; + for (; i < crwEntry.size() && crwEntry.pData()[i] != '\0'; ++i) { + // empty + } + value2->read(crwEntry.pData() + j, i - j + 1, byteOrder); + image.exifData().add(key2, value2.get()); + } // CrwMap::extract0x080a + + void CrwMap::extract0x102a(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder) + { + if (crwEntry.typeId() != unsignedShort) { + return extractBasic(crwEntry, crwMapInfo, image, byteOrder); + } + + long aperture = 0; + long shutterSpeed = 0; + + std::string ifdItem(ExifTags::ifdItem(canonCs2IfdId)); + uint16_t c = 1; + while (uint32_t(c)*2 < crwEntry.size()) { + uint16_t n = 1; + ExifKey key(c, ifdItem); + UShortValue value; + value.read(crwEntry.pData() + c*2, n*2, byteOrder); + image.exifData().add(key, &value); + if (c == 21) aperture = value.toLong(); + if (c == 22) shutterSpeed = value.toLong(); + c += n; + } + + // Exif.Photo.FNumber + float f = fnumber(canonEv(aperture)); + // Beware: primitive conversion algorithm + uint32_t den = 1000000; + uint32_t nom = static_cast(f * den); + uint32_t g = gcd(nom, den); + URational ur(nom/g, den/g); + URationalValue fn; + fn.value_.push_back(ur); + image.exifData().add(ExifKey("Exif.Photo.FNumber"), &fn); + + // Exif.Photo.ExposureTime + ur = exposureTime(canonEv(shutterSpeed)); + URationalValue et; + et.value_.push_back(ur); + image.exifData().add(ExifKey("Exif.Photo.ExposureTime"), &et); + + } // CrwMap::extract0x102a + + void CrwMap::extract0x102d(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder) + { + if (crwEntry.typeId() != unsignedShort) { + return extractBasic(crwEntry, crwMapInfo, image, byteOrder); + } + + std::string ifdItem(ExifTags::ifdItem(canonCs1IfdId)); + uint16_t c = 1; + while (uint32_t(c)*2 < crwEntry.size()) { + uint16_t n = 1; + ExifKey key(c, ifdItem); + UShortValue value; + if (c == 23 && crwEntry.size() > 50) n = 3; + value.read(crwEntry.pData() + c*2, n*2, byteOrder); + image.exifData().add(key, &value); + c += n; + } + } // CrwMap::extract0x102d + + void CrwMap::extract0x180e(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder) + { + if (crwEntry.size() < 8 || crwEntry.typeId() != unsignedLong) { + return extractBasic(crwEntry, crwMapInfo, image, byteOrder); + } + + ULongValue v; + v.read(crwEntry.pData(), 8, byteOrder); + time_t t = v.value_[0]; + + // Todo: use _r version + struct tm* tm = std::gmtime(&t); + size_t m = 20; + char s[m]; + std::strftime(s, m, "%Y:%m:%d %T", tm); + + ExifKey key(crwMapInfo->tag_, ExifTags::ifdItem(crwMapInfo->ifdId_)); + AsciiValue value; + value.read(std::string(s)); + image.exifData().add(key, &value); + } // CrwMap::extract0x180e + + void CrwMap::extract0x1810(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder) + { + if (crwEntry.typeId() != unsignedLong || crwEntry.size() < 28) { + return extractBasic(crwEntry, crwMapInfo, image, byteOrder); + } + + ExifKey key1("Exif.Photo.PixelXDimension"); + ULongValue value1; + value1.read(crwEntry.pData(), 4, byteOrder); + image.exifData().add(key1, &value1); + + ExifKey key2("Exif.Photo.PixelYDimension"); + ULongValue value2; + value2.read(crwEntry.pData() + 4, 4, byteOrder); + image.exifData().add(key2, &value2); + + } // CrwMap::extract0x1810 + + void CrwMap::extract0x2008(const CrwEntry& crwEntry, + const CrwMapInfo* /*crwMapInfo*/, + Image& image, + ByteOrder /*byteOrder*/) + { + image.exifData().setJpegThumbnail(crwEntry.pData(), crwEntry.size()); + } // CrwMap::extract0x2008 + + void CrwMap::extractBasic(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder) + { + // create a key and value pair + ExifKey key(crwMapInfo->tag_, ExifTags::ifdItem(crwMapInfo->ifdId_)); + Value::AutoPtr value; + if (crwEntry.typeId() != directory) { + value = Value::create(crwEntry.typeId()); + uint32_t size = 0; + if (crwMapInfo->size_ != 0) { + // size in the mapping table overrides all + size = crwMapInfo->size_; + } + else if (crwEntry.typeId() == asciiString) { + // determine size from the data, by looking for the first 0 + uint32_t i = 0; + for (; i < crwEntry.size() && crwEntry.pData()[i] != '\0'; ++i) { + // empty + } + size = ++i; + } + else { + // by default, use the size from the directory entry + size = crwEntry.size(); + } + value->read(crwEntry.pData(), size, byteOrder); + } + // Add metadatum to exif data + image.exifData().add(key, value.get()); + } // CrwMap::extractBasic + + + // ************************************************************************* + // free functions + + Image::AutoPtr newCrwInstance(BasicIo::AutoPtr io, bool create) + { + Image::AutoPtr image = Image::AutoPtr(new CrwImage(io, create)); + if (!image->good()) { + image.reset(); + } + return image; + } + + bool isCrwType(BasicIo& iIo, bool advance) + { + bool result = true; + byte tmpBuf[14]; + iIo.read(tmpBuf, 14); + if (iIo.error() || iIo.eof()) { + return false; + } + if (!( ('I' == tmpBuf[0] && 'I' == tmpBuf[1]) + || ('M' == tmpBuf[0] && 'M' == tmpBuf[1]))) { + result = false; + } + if ( true == result + && std::memcmp(tmpBuf + 6, CrwHeader::signature_, 8) != 0) { + result = false; + } + if (!advance || !result) iIo.seek(-14, BasicIo::cur); + return result; + } + +} // namespace Exiv2 diff --git a/src/crwimage.hpp b/src/crwimage.hpp new file mode 100644 index 00000000..12714d00 --- /dev/null +++ b/src/crwimage.hpp @@ -0,0 +1,608 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2005 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/*! + @file crwimage.hpp + @brief Class CrwImage to access Canon CRW images.
+ References:
+ The Canon RAW (CRW) File Format by Phil Harvey + @version $Rev$ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @date 28-Aug-05, ahu: created + */ +#ifndef CRWIMAGE_HPP_ +#define CRWIMAGE_HPP_ + +// ***************************************************************************** +// included header files +#include "types.hpp" +#include "image.hpp" +#include "basicio.hpp" +#include "exif.hpp" +#include "iptc.hpp" + +// + standard includes +#include +#include +#include + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class declarations + class CrwEntry; + struct CrwMapInfo; + +// ***************************************************************************** +// type definitions + + //! Function pointer for functions to extract Exif tags from a CRW entry + typedef void (*CrwExtractFct)(const CrwEntry&, + const CrwMapInfo*, + Image&, + ByteOrder); + + //! Function pointer for functions to insert CRW entries from an Exif tag + typedef void (*CrwInsertFct)(); + +// ***************************************************************************** +// class definitions + + /*! + @brief Class to access Canon CRW images. + */ + class CrwImage : public Image { + friend bool isCrwType(BasicIo& iIo, bool advance); + + // NOT Implemented + //! Copy constructor + CrwImage(const CrwImage& rhs); + //! Assignment operator + CrwImage& operator=(const CrwImage& rhs); + public: + //! @name Creators + //@{ + /*! + @brief Constructor that can either open an existing Crw image or create + a new image from scratch. If a new image is to be created, any + existing data is overwritten. Since the constructor can not return + a result, callers should check the good() method after object + construction to determine success or failure. + @param io An auto-pointer that owns a BasicIo instance used for + reading and writing image metadata. \b Important: The constructor + takes ownership of the passed in BasicIo instance through the + auto-pointer. Callers should not continue to use the BasicIo + instance after it is passed to this method. Use the Image::io() + method to get a temporary reference. + @param create Specifies if an existing image should be read (false) + or if a new file should be created (true). + */ + CrwImage(BasicIo::AutoPtr io, bool create); + //! Destructor + ~CrwImage() {} + //@} + + //! @name Manipulators + //@{ + /*! + @brief Read all metadata from the image. Before this method + is called, the various metadata types (Iptc, Exif) will be empty. + + This method returns success even when no metadata is found in + the image. Callers must therefore check the size of individual + metadata types before accessing the data. + + @throw Error if opening or reading of the file fails or the image + data is not valid (does not look like CRW data). + */ + void readMetadata(); + /*! + @brief Write metadata back to the image. + + All existing metadata sections in the image are either created, + replaced, or erased. If values for a given metadata type have been + assigned, a section for that metadata type will either be created or + replaced. If no values have been assigned to a given metadata type, + any exists section for that metadata type will be removed from the + image. + + @throw Error if the operation fails + */ + void writeMetadata(); + /*! + @brief Assign new exif data. The new exif data is not written + to the image until the writeMetadata() method is called. + @param exifData An ExifData instance holding exif data to be copied + */ + void setExifData(const ExifData& exifData); + void clearExifData(); + void setIptcData(const IptcData& iptcData); + void clearIptcData(); + void setComment(const std::string& comment); + void clearComment(); + void setMetadata(const Image& image); + void clearMetadata(); + ExifData& exifData() { return exifData_; } + IptcData& iptcData() { return iptcData_; } + //@} + + //! @name Accessors + //@{ + bool good() const; + const ExifData& exifData() const { return exifData_; } + const IptcData& iptcData() const { return iptcData_; } + std::string comment() const { return comment_; } + BasicIo& io() const { return *io_; } + //@} + + //! @cond IGNORE + // Public only so that we can create a static instance + struct CrwRegister{ + CrwRegister(); + }; + //! @endcond + + private: + + //! @name Manipulators + //@{ + + int initImage(const byte initData[], size_t dataSize); + + //@} + + //! @name Accessors + //@{ + /*! + @brief Determine if the content of the BasicIo instance is a Crw image. + + The advance flag determines if the read position in the stream is + moved (see below). This applies only if the type matches and the + function returns true. If the type does not match, the stream + position is not changed. However, if reading from the stream fails, + the stream position is undefined. Consult the stream state to obtain + more information in this case. + + @param iIo BasicIo instance to read from. + @param advance Flag indicating whether the position of the io + should be advanced by the number of characters read to + analyse the data (true) or left at its original + position (false). This applies only if the type matches. + @return true if the data matches the type of this class;
+ false if the data does not match + */ + bool isThisType(BasicIo& iIo, bool advance) const; + /*! + @brief Writes a Crw header (aka signature) to the BasicIo instance. + @param oIo BasicIo instance that the header is written to. + @return 0 if successful;
+ 2 if the input image is invalid or can not be read;
+ 4 if the temporary image can not be written to;
+ -3 other temporary errors;
+ */ + int writeHeader(BasicIo& oIo) const; + //@} + + // DATA + static const byte blank_[]; //!< Minimal CRW image + + BasicIo::AutoPtr io_; //!< Image data io pointer + ExifData exifData_; //!< Exif data container + IptcData iptcData_; //!< Iptc data container + std::string comment_; //!< User comment + + }; // class CrwImage + + static CrwImage::CrwRegister crwReg; + + /*! + Base class for all objects in a raw metadata parse tree. + Defines the interface for raw metadata composites. + */ + class RawMetadata { + public: + //! RawMetadata auto_ptr type + typedef std::auto_ptr AutoPtr; + //! Container type to hold all metadata + typedef std::vector Components; + + //! @name Creators + //@{ + //! Virtual Destructor + virtual ~RawMetadata() {} + //@} + + //! @name Manipulators + //@{ + //! Add a component to the composition + virtual void add(AutoPtr component) =0; + /*! + @brief Read a component from a data buffer + + @param buf Pointer to the data buffer. + @param len Number of bytes in the data buffer. + @param start Component starts at \em buf + \em start. + @param byteOrder Applicable byte order (little or big endian). + @param shift offsets in component are relative to \em buf + \em shift. + + @throw Error If the component cannot be parsed. + */ + virtual void read(const byte* buf, + size_t len, + size_t start, + ByteOrder byteOrder, + long shift =0) =0; + //@} + + //! @name Accessors + //@{ + /*! + @brief Extract metadata from the component and add it to + \em image. + + @param image Image to add metadata to + @param byteOrder Byte order + */ + virtual void extract(Image& image, + ByteOrder byteOrder) const =0; + + /*! + @brief Print debug info about a component to \em os. + + @param os Output stream to write to + @param byteOrder Byte order + @param prefix Prefix to be written before each line of output + */ + virtual void print(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix ="") const =0; + //@} + + }; // class RawMetadata + + /*! + @brief This class models one directory entry of a CIFF directory of + a CRW (Canon Raw data) image. + */ + class CrwEntry : public RawMetadata { + public: + //! @name Creators + //@{ + // Default and copy constructors are fine + + //! Virtual destructor. + virtual ~CrwEntry() {} + //@} + + //! @name Manipulators + //@{ + // Default assignment operator is fine + + // See base class comment + virtual void add(RawMetadata::AutoPtr component); + + // See base class comment + virtual void read(const byte* buf, + size_t len, + size_t start, + ByteOrder byteOrder, + long shift =0); + + //! Set the directory tag for this component. + void setDir(uint16_t dir) { dir_ = dir; } + //@} + + //! Return the type id for a tag + static TypeId typeId(uint16_t tag); + //! Return the data location id for a tag + static DataLocId dataLocation(uint16_t tag); + + //! @name Accessors + //@{ + // See base class comment + virtual void extract(Image& image, + ByteOrder byteOrder) const; + + // See base class comment + virtual void print(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix ="") const; + + //! Return the tag of the directory containing this component + uint16_t dir() const { return dir_; } + + //! Return the tag of this component + uint16_t tag() const { return tag_; } + + /*! + @brief Return the data size of this component + + @note If the data is contained in the directory entry itself, + this method returns 8, which is the maximum number + of data bytes this component can have. The actual size, + i.e., used data bytes, may be less than 8. + */ + uint32_t size() const { return size_; } + + //! Return the offset to the data from the start of the directory + uint32_t offset() const { return offset_; } + + //! Return a pointer to the data area of this component + const byte* pData() const { return pData_; } + + //! Return the tag id of this component + uint16_t tagId() const { return tag_ & 0x3fff; } + + //! Return the type id of thi component + TypeId typeId() const { return typeId(tag_); } + + //! Return the data location for this component + DataLocId dataLocation() const { return dataLocation(tag_); } + //@} + + private: + // DATA + uint16_t dir_; //!< Tag of the directory containing this component + uint16_t tag_; //!< Tag of the entry + uint32_t size_; //!< Size of the data area + uint32_t offset_; //!< Offset to the data area from the start of the dir + const byte* pData_; //!< Pointer to the data area + + }; // class CrwEntry + + //! This class models a CIFF directory of a CRW (Canon Raw data) image. + class CrwDirectory : public CrwEntry { + public: + //! @name Creators + //@{ + // Default and copy constructors are fine + + //! Virtual destructor + virtual ~CrwDirectory(); + //@} + + //! @name Manipulators + //@{ + // Default assignment operator is fine + + // See base class comment + virtual void add(RawMetadata::AutoPtr component); + + // See base class comment + virtual void read(const byte* buf, + size_t len, + size_t start, + ByteOrder byteOrder, + long shift =0); + + /*! + @brief Parse a CIFF directory from a memory buffer + + @param buf Pointer to the memory buffer containing the directory + @param len Size of the memory buffer + @param start Directory starts at buf + start + @param byteOrder Applicable byte order (little or big endian) + @param shift Not used + */ + void readDirectory(const byte* buf, + size_t len, + size_t start, + ByteOrder byteOrder, + long shift =0); + //@} + + //! @name Accessors + //@{ + // See base class comment + virtual void extract(Image& image, + ByteOrder byteOrder) const; + + // See base class comment + virtual void print(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix ="") const; + //@} + + private: + // DATA + RawMetadata::Components components_; //!< List of components in this dir + + }; // class CrwDirectory + + //! This class models the header of a CRW (Canon Raw data) image. + class CrwHeader : public RawMetadata { + public: + //! @name Creators + //@{ + //! Default constructor + CrwHeader() + : rootDirectory_ (0), + byteOrder_ (littleEndian), + offset_ (0x0000001a) + {} + //! Virtual destructor + virtual ~CrwHeader(); + //@} + + //! @name Manipulators + //@{ + // See base class comment + virtual void add(RawMetadata::AutoPtr component); + + // See base class comment + virtual void read(const byte* buf, + size_t len, + size_t start, + ByteOrder byteOrder, + long shift =0); + //@} + + //! @name Accessors + //@{ + // See base class comment + virtual void extract(Image& image, + ByteOrder byteOrder) const; + + // See base class comment + virtual void print(std::ostream& os, + ByteOrder byteOrder, + const std::string& prefix ="") const; + //@} + + // DATA + static const char signature_[]; //!< Canon CRW signature "HEAPCCDR" + + private: + // DATA + CrwDirectory* rootDirectory_; //!< Pointer to the root directory + ByteOrder byteOrder_; //!< Applicable byte order + uint32_t offset_; //!< Offset to the start of the root dir + + }; // class CrwHeader + + //! Structure for conversion info for CIFF entries + struct CrwMapInfo { + //! @name Creators + //@{ + //! Default constructor + CrwMapInfo( + uint16_t crwTagId, + uint16_t crwDir, + uint32_t size, + uint16_t tag, + IfdId ifdId, + CrwExtractFct toExif, + CrwInsertFct fromExif) + : crwTagId_ (crwTagId), + crwDir_ (crwDir), + size_ (size), + tag_ (tag), + ifdId_ (ifdId), + toExif_ (toExif), + fromExif_ (fromExif) + {} + //@} + + // DATA + uint16_t crwTagId_; //!< CRW tag id + uint16_t crwDir_; //!< CRW directory tag + uint32_t size_; //!< Data size (overwrites the size from the entry) + uint16_t tag_; //!< Exif tag to map to + IfdId ifdId_; //!< Exif Ifd id to map to + CrwExtractFct toExif_; //!< Conversion function + CrwInsertFct fromExif_; //!< Reverse conversion function + + }; // struct CrwMapInfo + + /*! + @brief Static class providing mapping functionality from CRW entries + to image metadata and vice versa + */ + class CrwMap { + //! @name Not implemented + //@{ + //! Default constructor + CrwMap(); + //@} + + public: + /*! + @brief Extract image metadata from a CRW entry convert and add it + to the image metadata. + + @param crwEntry Source CRW entry + @param image Destination image for the metadata + @param byteOrder Byte order in which the data of the entry is encoded + */ + static void extract(const CrwEntry& crwEntry, + Image& image, + ByteOrder byteOrder); + + private: + //! Return conversion information for one CRW \em dir and \em tagId + static const CrwMapInfo* crwMapInfo(uint16_t dir, uint16_t tagId); + + /*! + @brief Standard extraction function to convert CRW entries to + Exif metadata. + + Uses the mapping defined in the conversion structure \em crwMapInfo + to convert the data. If the \em size field in the conversion structure + is not 0, then it is used instead of the \em size provided by the + entry itself. + */ + static void extractBasic(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder); + + //! Extract the user comment + static void extract0x0805(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder); + + //! Extract camera Make and Model information + static void extract0x080a(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder); + + //! Extract Canon Camera Settings 2 + static void extract0x102a(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder); + + //! Extract Canon Camera Settings 1 + static void extract0x102d(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder); + + //! Extract the date when the picture was taken + static void extract0x180e(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder); + + //! Extract image width and height + static void extract0x1810(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder); + + //! Extract the thumbnail image + static void extract0x2008(const CrwEntry& crwEntry, + const CrwMapInfo* crwMapInfo, + Image& image, + ByteOrder byteOrder); + + // DATA + static const CrwMapInfo crwMapInfos_[]; //!< Metadata conversion table + + }; // class CrwMap + +} // namespace Exiv2 + +#endif // #ifndef CRWIMAGE_HPP_ diff --git a/src/error.cpp b/src/error.cpp index 60111dd6..ecb80f87 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -74,6 +74,10 @@ namespace Exiv2 { ErrMsg( 28, "Invalid charset: `%1'"), // %1=charset name ErrMsg( 29, "Unsupported date format"), ErrMsg( 30, "Unsupported time format"), + ErrMsg( 31, "%1: CRW images don't support IPTC metadata"), // %1=function + ErrMsg( 32, "%1: CRW images don't support JPEG comments"), // %1=function + ErrMsg( 33, "This does not look like a CRW image"), + ErrMsg( 34, "%1: Not supported"), // %1=function // Last error message (message is not used) ErrMsg( -2, "(Unknown Error)") diff --git a/src/exif.cpp b/src/exif.cpp index 512274fc..c44860ba 100644 --- a/src/exif.cpp +++ b/src/exif.cpp @@ -48,7 +48,7 @@ EXIV2_RCSID("@(#) $Id$"); #include "value.hpp" #include "ifd.hpp" #include "tags.hpp" -#include "jpgimage.hpp" +#include "image.hpp" #include "makernote.hpp" #include "futils.hpp" diff --git a/src/image.cpp b/src/image.cpp index 3934700d..8b0a66aa 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -46,6 +46,7 @@ EXIV2_RCSID("@(#) $Id$"); // Ensure registration with factory #include "jpgimage.hpp" +#include "crwimage.hpp" // + standard includes #include diff --git a/src/image.hpp b/src/image.hpp index f6343e57..5c32178a 100644 --- a/src/image.hpp +++ b/src/image.hpp @@ -65,7 +65,7 @@ namespace Exiv2 { class Image { public: //! Supported image formats - enum Type { none, jpeg, exv }; + enum Type { none, jpeg, exv, crw }; //! Image auto_ptr type typedef std::auto_ptr AutoPtr; diff --git a/src/nikonmn.cpp b/src/nikonmn.cpp index ea1ec4cf..269d06d7 100644 --- a/src/nikonmn.cpp +++ b/src/nikonmn.cpp @@ -579,11 +579,16 @@ namespace Exiv2 { if (len2 != len1) { os << "-" << len2; } - os << "mm " - << "F" << (float)fno1.first / fno1.second; + os << "mm "; + std::ostringstream oss; + oss.copyfmt(os); + os << "F" << std::setprecision(2) + << static_cast(fno1.first) / fno1.second; if (fno2 != fno1) { - os << "-" << (float)fno2.first / fno2.second; + os << "-" << std::setprecision(2) + << static_cast(fno2.first) / fno2.second; } + os.copyfmt(oss); } else { os << "(" << value << ")"; diff --git a/src/olympusmn.cpp b/src/olympusmn.cpp index e4f32915..7b0b4f14 100644 --- a/src/olympusmn.cpp +++ b/src/olympusmn.cpp @@ -265,7 +265,11 @@ namespace Exiv2 { { float f = value.toFloat(); if (f == 0.0 || f == 1.0) return os << "None"; - return os << std::fixed << std::setprecision(1) << f << "x"; + std::ostringstream oss; + oss.copyfmt(os); + os << std::fixed << std::setprecision(1) << f << "x"; + os.copyfmt(oss); + return os; } // OlympusMakerNote::print0x0204 //! OneTouchWB diff --git a/src/panasonicmn.cpp b/src/panasonicmn.cpp index 9535f787..850920ae 100644 --- a/src/panasonicmn.cpp +++ b/src/panasonicmn.cpp @@ -289,8 +289,14 @@ namespace Exiv2 { std::ostream& PanasonicMakerNote::print0x0023(std::ostream& os, const Value& value) { - return os << std::fixed << std::setprecision(1) - << value.toLong() / 3 << " EV"; + std::ostringstream oss; + oss.copyfmt(os); + os << std::fixed << std::setprecision(1) + << value.toLong() / 3 << " EV"; + os.copyfmt(oss); + + return os; + } // PanasonicMakerNote::print0x0023 //! ColorEffect diff --git a/src/tags.cpp b/src/tags.cpp index 120b1e20..b1fcc5d2 100644 --- a/src/tags.cpp +++ b/src/tags.cpp @@ -45,6 +45,7 @@ EXIV2_RCSID("@(#) $Id$"); #include #include #include +#include // ***************************************************************************** // class member definitions @@ -187,8 +188,8 @@ namespace Exiv2 { TagInfo(0x9004, "DateTimeDigitized", "Date and Time (digitized)", "Date and time image was made digital data", exifIfdId, dateTime, asciiString, printValue), TagInfo(0x9101, "ComponentsConfiguration", "ComponentsConfiguration", "Meaning of each component", exifIfdId, imgConfig, undefined, print0x9101), TagInfo(0x9102, "CompressedBitsPerPixel", "Compressed Bits per Pixel", "Image compression mode", exifIfdId, imgConfig, unsignedRational, printFloat), - TagInfo(0x9201, "ShutterSpeedValue", "Shutter speed", "Shutter speed", exifIfdId, captureCond, signedRational, printFloat), - TagInfo(0x9202, "ApertureValue", "Aperture", "Aperture", exifIfdId, captureCond, unsignedRational, printFloat), + TagInfo(0x9201, "ShutterSpeedValue", "Shutter speed", "Shutter speed", exifIfdId, captureCond, signedRational, print0x9201), + TagInfo(0x9202, "ApertureValue", "Aperture", "Aperture", exifIfdId, captureCond, unsignedRational, print0x9202), TagInfo(0x9203, "BrightnessValue", "Brightness", "Brightness", exifIfdId, captureCond, signedRational, printFloat), TagInfo(0x9204, "ExposureBiasValue", "Exposure Bias", "Exposure bias", exifIfdId, captureCond, signedRational, print0x9204), TagInfo(0x9205, "MaxApertureValue", "MaxApertureValue", "Maximum lens aperture", exifIfdId, captureCond, unsignedRational, printFloat), @@ -883,7 +884,11 @@ namespace Exiv2 { { Rational fnumber = value.toRational(); if (fnumber.second != 0) { - os << "F" << (float)fnumber.first / fnumber.second; + std::ostringstream oss; + oss.copyfmt(os); + os << "F" << std::setprecision(2) + << static_cast(fnumber.first) / fnumber.second; + os.copyfmt(oss); } else { os << "(" << value << ")"; @@ -932,6 +937,26 @@ namespace Exiv2 { return os; } + std::ostream& print0x9201(std::ostream& os, const Value& value) + { + std::ostringstream oss; + oss.copyfmt(os); + os << "F" << std::setprecision(2) << fnumber(value.toFloat()); + os.copyfmt(oss); + + return os; + } + + std::ostream& print0x9202(std::ostream& os, const Value& value) + { + URational ur = exposureTime(value.toFloat()); + os << ur.first; + if (ur.second > 1) { + os << "/" << ur.second; + } + return os << " s"; + } + std::ostream& print0x9204(std::ostream& os, const Value& value) { Rational bias = value.toRational(); @@ -942,9 +967,9 @@ namespace Exiv2 { os << "0"; } else { - long d = lgcd(labs(bias.first), bias.second); - long num = labs(bias.first) / d; - long den = bias.second / d; + int32_t d = gcd(bias.first, bias.second); + int32_t num = std::abs(bias.first) / d; + int32_t den = bias.second / d; os << (bias.first < 0 ? "-" : "+") << num; if (den != 1) { os << "/" << den; @@ -1259,4 +1284,22 @@ namespace Exiv2 { return os; } + float fnumber(float apertureValue) + { + return std::exp(std::log(2.0) * apertureValue / 2); + } + + URational exposureTime(float shutterSpeedValue) + { + URational ur(1, 1); + double tmp = std::exp(std::log(2.0) * shutterSpeedValue); + if (tmp > 1) { + ur.second = static_cast(tmp + 0.5); + } + else { + ur.first = static_cast(1/tmp + 0.5); + } + return ur; + } + } // namespace Exiv2 diff --git a/src/tags.hpp b/src/tags.hpp index 5f9c555a..45834af1 100644 --- a/src/tags.hpp +++ b/src/tags.hpp @@ -146,7 +146,7 @@ namespace Exiv2 { //! Container for Exif tag information. Implemented as a static class. class ExifTags { //! Prevent construction: not implemented. - ExifTags() {} + ExifTags(); //! Prevent copy-construction: not implemented. ExifTags(const ExifTags& rhs); //! Prevent assignment: not implemented. @@ -394,18 +394,22 @@ namespace Exiv2 { std::ostream& print0x0112(std::ostream& os, const Value& value); //! Print the YCbCrPositioning std::ostream& print0x0213(std::ostream& os, const Value& value); - //! Print the Copyright + //! Print the copyright std::ostream& print0x8298(std::ostream& os, const Value& value); - //! Print the Exposure time + //! Print the exposure time std::ostream& print0x829a(std::ostream& os, const Value& value); - //! Print the F number + //! Print the f-number std::ostream& print0x829d(std::ostream& os, const Value& value); - //! Print the Exposure mode + //! Print the exposure mode std::ostream& print0x8822(std::ostream& os, const Value& value); //! Print ISO speed ratings std::ostream& print0x8827(std::ostream& os, const Value& value); //! Print components configuration specific to compressed data std::ostream& print0x9101(std::ostream& os, const Value& value); + //! Print exposure time converted from APEX shutter speed value + std::ostream& print0x9201(std::ostream& os, const Value& value); + //! Print f-number converted from APEX aperture value + std::ostream& print0x9202(std::ostream& os, const Value& value); //! Print the exposure bias value std::ostream& print0x9204(std::ostream& os, const Value& value); //! Print the subject distance @@ -449,6 +453,13 @@ namespace Exiv2 { //! Print subject distance range std::ostream& print0xa40c(std::ostream& os, const Value& value); //@} + + //! Calculate F number from an APEX aperture value + float fnumber(float apertureValue); + + //! Calculate the exposure time from an APEX shutter speed value + URational exposureTime(float shutterSpeedValue); + } // namespace Exiv2 #endif // #ifndef TAGS_HPP_ diff --git a/src/types.cpp b/src/types.cpp index 45c135d5..be9eb9cc 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -67,6 +67,7 @@ namespace Exiv2 { TypeInfoTable(date, "Date", 8), TypeInfoTable(time, "Time", 11), TypeInfoTable(comment, "Comment", 1), + TypeInfoTable(directory, "Directory", 1), // End of list marker TypeInfoTable(lastTypeId, "(Unknown)", 0) }; @@ -297,36 +298,6 @@ namespace Exiv2 { os << std::dec << std::setfill(' '); } // hexdump - int gcd(int a, int b) - { - int temp; - if (a < b) { - temp = a; - a = b; - b = temp; - } - while ((temp = a % b) != 0) { - a = b; - b = temp; - } - return b; - } // gcd - - long lgcd(long a, long b) - { - long temp; - if (a < b) { - temp = a; - a = b; - b = temp; - } - while ((temp = a % b) != 0) { - a = b; - b = temp; - } - return b; - } // lgcd - bool isHex(const std::string& str, size_t size, const std::string& prefix) { if ( str.size() <= prefix.size() diff --git a/src/types.hpp b/src/types.hpp index aa78eefe..27a85922 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -81,7 +81,8 @@ namespace Exiv2 { unsignedLong, unsignedRational, invalid6, undefined, signedShort, signedLong, signedRational, string, date, time, - comment, + comment, + directory, lastTypeId }; // Todo: decentralize IfdId, so that new ids can be defined elsewhere @@ -93,6 +94,11 @@ namespace Exiv2 { olympusIfdId, panasonicIfdId, sigmaIfdId, sonyIfdId, lastIfdId }; + //! Type to identify where the data is stored in a directory + enum DataLocId { invalidDataLocId, + valueData, directoryData, + lastDataLocId }; + // ***************************************************************************** // class definitions @@ -269,18 +275,6 @@ namespace Exiv2 { */ void hexdump(std::ostream& os, const byte* buf, long len, long offset =0); - /*! - @brief Return the greatest common denominator of integers a and b. - Both parameters must be greater than 0. - */ - int gcd(int a, int b); - - /*! - @brief Return the greatest common denominator of long values a and b. - Both parameters must be greater than 0. - */ - long lgcd(long a, long b); - /*! @brief Return true if str is a hex number starting with prefix followed by size hex digits, false otherwise. If size is 0, any number of @@ -302,6 +296,41 @@ namespace Exiv2 { return os.str(); } + /*! + @brief Return the greatest common denominator of n and m. + (implementation from Boost rational.hpp) + + @note We use n and m as temporaries in this function, so there is no + value in using const IntType& as we would only need to make a copy + anyway... + */ + template + IntType gcd(IntType n, IntType m) + { + // Avoid repeated construction + IntType zero(0); + + // This is abs() - given the existence of broken compilers with Koenig + // lookup issues and other problems, I code this explicitly. (Remember, + // IntType may be a user-defined type). + if (n < zero) + n = -n; + if (m < zero) + m = -m; + + // As n and m are now positive, we can be sure that %= returns a + // positive value (the standard guarantees this for built-in types, + // and we require it of user-defined types). + for(;;) { + if(m == zero) + return n; + n %= m; + if(n == zero) + return m; + m %= n; + } + } + } // namespace Exiv2 #endif // #ifndef TYPES_HPP_ diff --git a/test/data/exifdata-test.out b/test/data/exifdata-test.out index 855629fb..0775e5cd 100644 --- a/test/data/exifdata-test.out +++ b/test/data/exifdata-test.out @@ -255,8 +255,8 @@ Exif.CanonCs1.0x0027 0x0027 Makernote Short 1 Exif.CanonCs2.0x0001 0x0001 Makernote Short 1 0 Exif.CanonCs2.ISOSpeed 0x0002 Makernote Short 1 160 Exif.CanonCs2.0x0003 0x0003 Makernote Short 1 276 -Exif.CanonCs2.0x0004 0x0004 Makernote Short 1 149 -Exif.CanonCs2.0x0005 0x0005 Makernote Short 1 287 +Exif.CanonCs2.TargetAperture 0x0004 Makernote Short 1 149 +Exif.CanonCs2.TargetShutterSpeed 0x0005 Makernote Short 1 287 Exif.CanonCs2.0x0006 0x0006 Makernote Short 1 0 Exif.CanonCs2.WhiteBalance 0x0007 Makernote Short 1 0 Exif.CanonCs2.0x0008 0x0008 Makernote Short 1 0 @@ -272,8 +272,8 @@ Exif.CanonCs2.0x0011 0x0011 Makernote Short 1 Exif.CanonCs2.0x0012 0x0012 Makernote Short 1 1 Exif.CanonCs2.SubjectDistance 0x0013 Makernote Short 1 782 Exif.CanonCs2.0x0014 0x0014 Makernote Short 1 0 -Exif.CanonCs2.0x0015 0x0015 Makernote Short 1 149 -Exif.CanonCs2.0x0016 0x0016 Makernote Short 1 289 +Exif.CanonCs2.ApertureValue 0x0015 Makernote Short 1 149 +Exif.CanonCs2.ShutterSpeedValue 0x0016 Makernote Short 1 289 Exif.CanonCs2.0x0017 0x0017 Makernote Short 1 0 Exif.CanonCs2.0x0018 0x0018 Makernote Short 1 0 Exif.CanonCs2.0x0019 0x0019 Makernote Short 1 0 @@ -379,8 +379,8 @@ Exif.CanonCs1.0x0027 0x0027 Makernote Short 1 Exif.CanonCs2.0x0001 0x0001 Makernote Short 1 0 Exif.CanonCs2.ISOSpeed 0x0002 Makernote Short 1 160 Exif.CanonCs2.0x0003 0x0003 Makernote Short 1 276 -Exif.CanonCs2.0x0004 0x0004 Makernote Short 1 149 -Exif.CanonCs2.0x0005 0x0005 Makernote Short 1 287 +Exif.CanonCs2.TargetAperture 0x0004 Makernote Short 1 149 +Exif.CanonCs2.TargetShutterSpeed 0x0005 Makernote Short 1 287 Exif.CanonCs2.0x0006 0x0006 Makernote Short 1 0 Exif.CanonCs2.WhiteBalance 0x0007 Makernote Short 1 0 Exif.CanonCs2.0x0008 0x0008 Makernote Short 1 0 @@ -396,8 +396,8 @@ Exif.CanonCs2.0x0011 0x0011 Makernote Short 1 Exif.CanonCs2.0x0012 0x0012 Makernote Short 1 1 Exif.CanonCs2.SubjectDistance 0x0013 Makernote Short 1 782 Exif.CanonCs2.0x0014 0x0014 Makernote Short 1 0 -Exif.CanonCs2.0x0015 0x0015 Makernote Short 1 149 -Exif.CanonCs2.0x0016 0x0016 Makernote Short 1 289 +Exif.CanonCs2.ApertureValue 0x0015 Makernote Short 1 149 +Exif.CanonCs2.ShutterSpeedValue 0x0016 Makernote Short 1 289 Exif.CanonCs2.0x0017 0x0017 Makernote Short 1 0 Exif.CanonCs2.0x0018 0x0018 Makernote Short 1 0 Exif.CanonCs2.0x0019 0x0019 Makernote Short 1 0 @@ -504,8 +504,8 @@ Exif.CanonCs1.0x0027 0x0027 Makernote Short 1 Exif.CanonCs2.0x0001 0x0001 Makernote Short 1 0 Exif.CanonCs2.ISOSpeed 0x0002 Makernote Short 1 160 Exif.CanonCs2.0x0003 0x0003 Makernote Short 1 276 -Exif.CanonCs2.0x0004 0x0004 Makernote Short 1 149 -Exif.CanonCs2.0x0005 0x0005 Makernote Short 1 287 +Exif.CanonCs2.TargetAperture 0x0004 Makernote Short 1 149 +Exif.CanonCs2.TargetShutterSpeed 0x0005 Makernote Short 1 287 Exif.CanonCs2.0x0006 0x0006 Makernote Short 1 0 Exif.CanonCs2.WhiteBalance 0x0007 Makernote Short 1 0 Exif.CanonCs2.0x0008 0x0008 Makernote Short 1 0 @@ -521,8 +521,8 @@ Exif.CanonCs2.0x0011 0x0011 Makernote Short 1 Exif.CanonCs2.0x0012 0x0012 Makernote Short 1 1 Exif.CanonCs2.SubjectDistance 0x0013 Makernote Short 1 782 Exif.CanonCs2.0x0014 0x0014 Makernote Short 1 0 -Exif.CanonCs2.0x0015 0x0015 Makernote Short 1 149 -Exif.CanonCs2.0x0016 0x0016 Makernote Short 1 289 +Exif.CanonCs2.ApertureValue 0x0015 Makernote Short 1 149 +Exif.CanonCs2.ShutterSpeedValue 0x0016 Makernote Short 1 289 Exif.CanonCs2.0x0017 0x0017 Makernote Short 1 0 Exif.CanonCs2.0x0018 0x0018 Makernote Short 1 0 Exif.CanonCs2.0x0019 0x0019 Makernote Short 1 0 @@ -628,8 +628,8 @@ Exif.CanonCs1.0x0027 0x0027 Makernote Short 1 Exif.CanonCs2.0x0001 0x0001 Makernote Short 1 0 Exif.CanonCs2.ISOSpeed 0x0002 Makernote Short 1 160 Exif.CanonCs2.0x0003 0x0003 Makernote Short 1 276 -Exif.CanonCs2.0x0004 0x0004 Makernote Short 1 149 -Exif.CanonCs2.0x0005 0x0005 Makernote Short 1 287 +Exif.CanonCs2.TargetAperture 0x0004 Makernote Short 1 149 +Exif.CanonCs2.TargetShutterSpeed 0x0005 Makernote Short 1 287 Exif.CanonCs2.0x0006 0x0006 Makernote Short 1 0 Exif.CanonCs2.WhiteBalance 0x0007 Makernote Short 1 0 Exif.CanonCs2.0x0008 0x0008 Makernote Short 1 0 @@ -645,8 +645,8 @@ Exif.CanonCs2.0x0011 0x0011 Makernote Short 1 Exif.CanonCs2.0x0012 0x0012 Makernote Short 1 1 Exif.CanonCs2.SubjectDistance 0x0013 Makernote Short 1 782 Exif.CanonCs2.0x0014 0x0014 Makernote Short 1 0 -Exif.CanonCs2.0x0015 0x0015 Makernote Short 1 149 -Exif.CanonCs2.0x0016 0x0016 Makernote Short 1 289 +Exif.CanonCs2.ApertureValue 0x0015 Makernote Short 1 149 +Exif.CanonCs2.ShutterSpeedValue 0x0016 Makernote Short 1 289 Exif.CanonCs2.0x0017 0x0017 Makernote Short 1 0 Exif.CanonCs2.0x0018 0x0018 Makernote Short 1 0 Exif.CanonCs2.0x0019 0x0019 Makernote Short 1 0 diff --git a/test/data/exiv2-test.out b/test/data/exiv2-test.out index fb6ee37e..73df936e 100644 --- a/test/data/exiv2-test.out +++ b/test/data/exiv2-test.out @@ -9,7 +9,7 @@ This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Exiv2 help --------------------------------------------------------------- -Usage: exiv2 [ options ] [ action ] file ... +Usage: lt-exiv2 [ options ] [ action ] file ... Manipulate the Exif metadata of images. @@ -429,8 +429,8 @@ Exif.Photo.DateTimeOriginal Ascii 20 2003:12:14 00:00:43 Exif.Photo.DateTimeDigitized Ascii 20 2003:12:14 00:00:43 Exif.Photo.ComponentsConfiguration Undefined 4 YCbCr Exif.Photo.CompressedBitsPerPixel Rational 1 5 -Exif.Photo.ShutterSpeedValue SRational 1 8.96875 -Exif.Photo.ApertureValue Rational 1 4.65625 +Exif.Photo.ShutterSpeedValue SRational 1 F22 +Exif.Photo.ApertureValue Rational 1 1/25 s Exif.Photo.ExposureBiasValue SRational 1 0 Exif.Photo.MaxApertureValue Rational 1 2.97086 Exif.Photo.MeteringMode Short 1 Center weighted @@ -502,8 +502,8 @@ Exif.CanonCs1.0x0027 Short 1 1 Exif.CanonCs2.0x0001 Short 1 0 Exif.CanonCs2.ISOSpeed Short 1 100 Exif.CanonCs2.0x0003 Short 1 276 -Exif.CanonCs2.0x0004 Short 1 149 -Exif.CanonCs2.0x0005 Short 1 287 +Exif.CanonCs2.TargetAperture Short 1 F5 +Exif.CanonCs2.TargetShutterSpeed Short 1 1/501 s Exif.CanonCs2.0x0006 Short 1 0 Exif.CanonCs2.WhiteBalance Short 1 Auto Exif.CanonCs2.0x0008 Short 1 0 @@ -519,8 +519,8 @@ Exif.CanonCs2.0x0011 Short 1 0 Exif.CanonCs2.0x0012 Short 1 1 Exif.CanonCs2.SubjectDistance Short 1 782 Exif.CanonCs2.0x0014 Short 1 0 -Exif.CanonCs2.0x0015 Short 1 149 -Exif.CanonCs2.0x0016 Short 1 289 +Exif.CanonCs2.ApertureValue Short 1 F5 +Exif.CanonCs2.ShutterSpeedValue Short 1 1/523 s Exif.CanonCs2.0x0017 Short 1 0 Exif.CanonCs2.0x0018 Short 1 0 Exif.CanonCs2.0x0019 Short 1 0 @@ -770,8 +770,8 @@ Exif.Photo.DateTimeOriginal Ascii 20 2003:09:25 20:18:50 Exif.Photo.DateTimeDigitized Ascii 20 2003:09:25 20:18:50 Exif.Photo.ComponentsConfiguration Undefined 4 YCbCr Exif.Photo.CompressedBitsPerPixel Rational 1 3 -Exif.Photo.ShutterSpeedValue SRational 1 6.32193 -Exif.Photo.ApertureValue Rational 1 4.97086 +Exif.Photo.ShutterSpeedValue SRational 1 F8.9 +Exif.Photo.ApertureValue Rational 1 1/31 s Exif.Photo.ExposureBiasValue SRational 1 0 Exif.Photo.MaxApertureValue Rational 1 3.61472 Exif.Photo.MeteringMode Short 1 Matrix @@ -862,8 +862,8 @@ Exif.CanonCs1.0x002d Short 1 65535 Exif.CanonCs2.0x0001 Short 1 0 Exif.CanonCs2.ISOSpeed Short 1 100 Exif.CanonCs2.0x0003 Short 1 200 -Exif.CanonCs2.0x0004 Short 1 159 -Exif.CanonCs2.0x0005 Short 1 202 +Exif.CanonCs2.TargetAperture Short 1 F5.6 +Exif.CanonCs2.TargetShutterSpeed Short 1 1/79 s Exif.CanonCs2.0x0006 Short 1 0 Exif.CanonCs2.WhiteBalance Short 1 Auto Exif.CanonCs2.0x0008 Short 1 3 @@ -879,8 +879,8 @@ Exif.CanonCs2.0x0011 Short 1 0 Exif.CanonCs2.0x0012 Short 1 1 Exif.CanonCs2.SubjectDistance Short 1 Infinite Exif.CanonCs2.0x0014 Short 1 546 -Exif.CanonCs2.0x0015 Short 1 156 -Exif.CanonCs2.0x0016 Short 1 204 +Exif.CanonCs2.ApertureValue Short 1 F5.4 +Exif.CanonCs2.ShutterSpeedValue Short 1 1/81 s Exif.CanonCs2.0x0017 Short 1 140 Exif.CanonCs2.0x0018 Short 1 0 Exif.CanonCs2.0x0019 Short 1 0 @@ -918,8 +918,8 @@ Exif.Photo.ExifVersion Undefined 4 48 49 49 48 Exif.Photo.DateTimeOriginal Ascii 20 2000:10:26 04:45:50 Exif.Photo.ComponentsConfiguration Undefined 4 YCbCr Exif.Photo.CompressedBitsPerPixel Rational 1 (0/0) -Exif.Photo.ShutterSpeedValue SRational 1 5 -Exif.Photo.ApertureValue Rational 1 4 +Exif.Photo.ShutterSpeedValue SRational 1 F5.7 +Exif.Photo.ApertureValue Rational 1 1/16 s Exif.Photo.BrightnessValue SRational 1 1.5 Exif.Photo.ExposureBiasValue SRational 1 0 Exif.Photo.MaxApertureValue Rational 1 4 @@ -962,8 +962,8 @@ Exif.Photo.DateTimeOriginal Ascii 20 2003:09:26 11:15:35 Exif.Photo.DateTimeDigitized Ascii 20 2003:09:26 11:15:35 Exif.Photo.ComponentsConfiguration Undefined 4 YCbCr Exif.Photo.CompressedBitsPerPixel Rational 1 1.5 -Exif.Photo.ShutterSpeedValue SRational 1 6 -Exif.Photo.ApertureValue Rational 1 4 +Exif.Photo.ShutterSpeedValue SRational 1 F8 +Exif.Photo.ApertureValue Rational 1 1/16 s Exif.Photo.BrightnessValue SRational 1 -1.83 Exif.Photo.ExposureBiasValue SRational 1 +1/2 Exif.Photo.MeteringMode Short 1 Center weighted @@ -1171,9 +1171,9 @@ Exif.Photo.ExifVersion Undefined 4 48 50 50 48 Exif.Photo.DateTimeOriginal Ascii 20 2005:02:18 21:20:16 Exif.Photo.DateTimeDigitized Ascii 20 2005:02:18 21:20:16 Exif.Photo.ComponentsConfiguration Undefined 4 YCbCr -Exif.Photo.CompressedBitsPerPixel Rational 1 4.0 +Exif.Photo.CompressedBitsPerPixel Rational 1 4 Exif.Photo.ExposureBiasValue SRational 1 0 -Exif.Photo.MaxApertureValue Rational 1 3.0 +Exif.Photo.MaxApertureValue Rational 1 3 Exif.Photo.MeteringMode Short 1 Matrix Exif.Photo.LightSource Short 1 Unknown Exif.Photo.Flash Short 1 No, compulsory @@ -1253,9 +1253,9 @@ Exif.Photo.ExifVersion Undefined 4 48 50 50 48 Exif.Photo.DateTimeOriginal Ascii 20 2005:05:27 05:18:33 Exif.Photo.DateTimeDigitized Ascii 20 2005:05:27 05:18:33 Exif.Photo.ComponentsConfiguration Undefined 4 YCbCr -Exif.Photo.CompressedBitsPerPixel Rational 1 8.0 +Exif.Photo.CompressedBitsPerPixel Rational 1 8 Exif.Photo.ExposureBiasValue SRational 1 0 -Exif.Photo.MaxApertureValue Rational 1 3.0 +Exif.Photo.MaxApertureValue Rational 1 3 Exif.Photo.MeteringMode Short 1 Matrix Exif.Photo.LightSource Short 1 Unknown Exif.Photo.Flash Short 1 Yes, auto, return light detected diff --git a/test/data/modify-test.out b/test/data/modify-test.out index adc64ef4..67f78827 100644 --- a/test/data/modify-test.out +++ b/test/data/modify-test.out @@ -39,8 +39,8 @@ Exif.Photo.ExifVersion Undefined 4 48 50 49 48 Exif.Photo.DateTimeOriginal Ascii 20 2004:06:08 16:04:50 Exif.Photo.DateTimeDigitized Ascii 20 2004:06:08 16:04:50 Exif.Photo.ComponentsConfiguration Undefined 4 YCbCr -Exif.Photo.ShutterSpeedValue SRational 1 6.6 -Exif.Photo.ApertureValue Rational 1 6.4 +Exif.Photo.ShutterSpeedValue SRational 1 F9.8 +Exif.Photo.ApertureValue Rational 1 1/84 s Exif.Photo.ExposureBiasValue SRational 1 0 Exif.Photo.MaxApertureValue Rational 1 3.1 Exif.Photo.MeteringMode Short 1 Matrix diff --git a/test/data/write-test.out b/test/data/write-test.out index 30e59bd6..df8bd7e3 100644 --- a/test/data/write-test.out +++ b/test/data/write-test.out @@ -95,8 +95,8 @@ Case 1: Non-intrusive change to the standard Exif metadata < Exif.CanonCs2.0x0001 0x0001 Short 1 0 < Exif.CanonCs2.ISOSpeed 0x0002 Short 1 160 < Exif.CanonCs2.0x0003 0x0003 Short 1 276 -< Exif.CanonCs2.0x0004 0x0004 Short 1 149 -< Exif.CanonCs2.0x0005 0x0005 Short 1 287 +< Exif.CanonCs2.TargetAperture 0x0004 Short 1 149 +< Exif.CanonCs2.TargetShutterSpeed 0x0005 Short 1 287 < Exif.CanonCs2.0x0006 0x0006 Short 1 0 < Exif.CanonCs2.WhiteBalance 0x0007 Short 1 0 < Exif.CanonCs2.0x0008 0x0008 Short 1 0 @@ -112,8 +112,8 @@ Case 1: Non-intrusive change to the standard Exif metadata < Exif.CanonCs2.0x0012 0x0012 Short 1 1 < Exif.CanonCs2.SubjectDistance 0x0013 Short 1 782 < Exif.CanonCs2.0x0014 0x0014 Short 1 0 -< Exif.CanonCs2.0x0015 0x0015 Short 1 149 -< Exif.CanonCs2.0x0016 0x0016 Short 1 289 +< Exif.CanonCs2.ApertureValue 0x0015 Short 1 149 +< Exif.CanonCs2.ShutterSpeedValue 0x0016 Short 1 289 < Exif.CanonCs2.0x0017 0x0017 Short 1 0 < Exif.CanonCs2.0x0018 0x0018 Short 1 0 < Exif.CanonCs2.0x0019 0x0019 Short 1 0 @@ -218,8 +218,8 @@ Case 1: Non-intrusive change to the standard Exif metadata > Exif.CanonCs2.0x0001 0x0001 0 > Exif.CanonCs2.ISOSpeed 0x0002 160 > Exif.CanonCs2.0x0003 0x0003 276 -> Exif.CanonCs2.0x0004 0x0004 149 -> Exif.CanonCs2.0x0005 0x0005 287 +> Exif.CanonCs2.TargetAperture 0x0004 149 +> Exif.CanonCs2.TargetShutterSpeed 0x0005 287 > Exif.CanonCs2.0x0006 0x0006 0 > Exif.CanonCs2.WhiteBalance 0x0007 0 > Exif.CanonCs2.0x0008 0x0008 0 @@ -235,8 +235,8 @@ Case 1: Non-intrusive change to the standard Exif metadata > Exif.CanonCs2.0x0012 0x0012 1 > Exif.CanonCs2.SubjectDistance 0x0013 782 > Exif.CanonCs2.0x0014 0x0014 0 -> Exif.CanonCs2.0x0015 0x0015 149 -> Exif.CanonCs2.0x0016 0x0016 289 +> Exif.CanonCs2.ApertureValue 0x0015 149 +> Exif.CanonCs2.ShutterSpeedValue 0x0016 289 > Exif.CanonCs2.0x0017 0x0017 0 > Exif.CanonCs2.0x0018 0x0018 0 > Exif.CanonCs2.0x0019 0x0019 0 @@ -348,8 +348,8 @@ Case 2: Non-intrusive change to the makernote metadata < Exif.CanonCs2.0x0001 0x0001 Short 1 0 < Exif.CanonCs2.ISOSpeed 0x0002 Short 1 160 < Exif.CanonCs2.0x0003 0x0003 Short 1 276 -< Exif.CanonCs2.0x0004 0x0004 Short 1 149 -< Exif.CanonCs2.0x0005 0x0005 Short 1 287 +< Exif.CanonCs2.TargetAperture 0x0004 Short 1 149 +< Exif.CanonCs2.TargetShutterSpeed 0x0005 Short 1 287 < Exif.CanonCs2.0x0006 0x0006 Short 1 0 < Exif.CanonCs2.WhiteBalance 0x0007 Short 1 0 < Exif.CanonCs2.0x0008 0x0008 Short 1 0 @@ -365,8 +365,8 @@ Case 2: Non-intrusive change to the makernote metadata < Exif.CanonCs2.0x0012 0x0012 Short 1 1 < Exif.CanonCs2.SubjectDistance 0x0013 Short 1 782 < Exif.CanonCs2.0x0014 0x0014 Short 1 0 -< Exif.CanonCs2.0x0015 0x0015 Short 1 149 -< Exif.CanonCs2.0x0016 0x0016 Short 1 289 +< Exif.CanonCs2.ApertureValue 0x0015 Short 1 149 +< Exif.CanonCs2.ShutterSpeedValue 0x0016 Short 1 289 < Exif.CanonCs2.0x0017 0x0017 Short 1 0 < Exif.CanonCs2.0x0018 0x0018 Short 1 0 < Exif.CanonCs2.0x0019 0x0019 Short 1 0 @@ -471,8 +471,8 @@ Case 2: Non-intrusive change to the makernote metadata > Exif.CanonCs2.0x0001 0x0001 0 > Exif.CanonCs2.ISOSpeed 0x0002 160 > Exif.CanonCs2.0x0003 0x0003 276 -> Exif.CanonCs2.0x0004 0x0004 149 -> Exif.CanonCs2.0x0005 0x0005 287 +> Exif.CanonCs2.TargetAperture 0x0004 149 +> Exif.CanonCs2.TargetShutterSpeed 0x0005 287 > Exif.CanonCs2.0x0006 0x0006 0 > Exif.CanonCs2.WhiteBalance 0x0007 0 > Exif.CanonCs2.0x0008 0x0008 0 @@ -488,8 +488,8 @@ Case 2: Non-intrusive change to the makernote metadata > Exif.CanonCs2.0x0012 0x0012 1 > Exif.CanonCs2.SubjectDistance 0x0013 782 > Exif.CanonCs2.0x0014 0x0014 0 -> Exif.CanonCs2.0x0015 0x0015 149 -> Exif.CanonCs2.0x0016 0x0016 289 +> Exif.CanonCs2.ApertureValue 0x0015 149 +> Exif.CanonCs2.ShutterSpeedValue 0x0016 289 > Exif.CanonCs2.0x0017 0x0017 0 > Exif.CanonCs2.0x0018 0x0018 0 > Exif.CanonCs2.0x0019 0x0019 0 @@ -688,8 +688,8 @@ Case 4: Intrusive change to the standard Exif metadata < Exif.CanonCs2.0x0001 0x0001 Short 1 0 < Exif.CanonCs2.ISOSpeed 0x0002 Short 1 160 < Exif.CanonCs2.0x0003 0x0003 Short 1 276 -< Exif.CanonCs2.0x0004 0x0004 Short 1 149 -< Exif.CanonCs2.0x0005 0x0005 Short 1 287 +< Exif.CanonCs2.TargetAperture 0x0004 Short 1 149 +< Exif.CanonCs2.TargetShutterSpeed 0x0005 Short 1 287 < Exif.CanonCs2.0x0006 0x0006 Short 1 0 < Exif.CanonCs2.WhiteBalance 0x0007 Short 1 0 < Exif.CanonCs2.0x0008 0x0008 Short 1 0 @@ -705,8 +705,8 @@ Case 4: Intrusive change to the standard Exif metadata < Exif.CanonCs2.0x0012 0x0012 Short 1 1 < Exif.CanonCs2.SubjectDistance 0x0013 Short 1 782 < Exif.CanonCs2.0x0014 0x0014 Short 1 0 -< Exif.CanonCs2.0x0015 0x0015 Short 1 149 -< Exif.CanonCs2.0x0016 0x0016 Short 1 289 +< Exif.CanonCs2.ApertureValue 0x0015 Short 1 149 +< Exif.CanonCs2.ShutterSpeedValue 0x0016 Short 1 289 < Exif.CanonCs2.0x0017 0x0017 Short 1 0 < Exif.CanonCs2.0x0018 0x0018 Short 1 0 < Exif.CanonCs2.0x0019 0x0019 Short 1 0 @@ -811,8 +811,8 @@ Case 4: Intrusive change to the standard Exif metadata > Exif.CanonCs2.0x0001 0x0001 0 > Exif.CanonCs2.ISOSpeed 0x0002 160 > Exif.CanonCs2.0x0003 0x0003 276 -> Exif.CanonCs2.0x0004 0x0004 149 -> Exif.CanonCs2.0x0005 0x0005 287 +> Exif.CanonCs2.TargetAperture 0x0004 149 +> Exif.CanonCs2.TargetShutterSpeed 0x0005 287 > Exif.CanonCs2.0x0006 0x0006 0 > Exif.CanonCs2.WhiteBalance 0x0007 0 > Exif.CanonCs2.0x0008 0x0008 0 @@ -828,8 +828,8 @@ Case 4: Intrusive change to the standard Exif metadata > Exif.CanonCs2.0x0012 0x0012 1 > Exif.CanonCs2.SubjectDistance 0x0013 782 > Exif.CanonCs2.0x0014 0x0014 0 -> Exif.CanonCs2.0x0015 0x0015 149 -> Exif.CanonCs2.0x0016 0x0016 289 +> Exif.CanonCs2.ApertureValue 0x0015 149 +> Exif.CanonCs2.ShutterSpeedValue 0x0016 289 > Exif.CanonCs2.0x0017 0x0017 0 > Exif.CanonCs2.0x0018 0x0018 0 > Exif.CanonCs2.0x0019 0x0019 0 @@ -941,8 +941,8 @@ Case 5: Intrusive change to the Canon makernote metadata < Exif.CanonCs2.0x0001 0x0001 Short 1 0 < Exif.CanonCs2.ISOSpeed 0x0002 Short 1 160 < Exif.CanonCs2.0x0003 0x0003 Short 1 276 -< Exif.CanonCs2.0x0004 0x0004 Short 1 149 -< Exif.CanonCs2.0x0005 0x0005 Short 1 287 +< Exif.CanonCs2.TargetAperture 0x0004 Short 1 149 +< Exif.CanonCs2.TargetShutterSpeed 0x0005 Short 1 287 < Exif.CanonCs2.0x0006 0x0006 Short 1 0 < Exif.CanonCs2.WhiteBalance 0x0007 Short 1 0 < Exif.CanonCs2.0x0008 0x0008 Short 1 0 @@ -958,8 +958,8 @@ Case 5: Intrusive change to the Canon makernote metadata < Exif.CanonCs2.0x0012 0x0012 Short 1 1 < Exif.CanonCs2.SubjectDistance 0x0013 Short 1 782 < Exif.CanonCs2.0x0014 0x0014 Short 1 0 -< Exif.CanonCs2.0x0015 0x0015 Short 1 149 -< Exif.CanonCs2.0x0016 0x0016 Short 1 289 +< Exif.CanonCs2.ApertureValue 0x0015 Short 1 149 +< Exif.CanonCs2.ShutterSpeedValue 0x0016 Short 1 289 < Exif.CanonCs2.0x0017 0x0017 Short 1 0 < Exif.CanonCs2.0x0018 0x0018 Short 1 0 < Exif.CanonCs2.0x0019 0x0019 Short 1 0 @@ -1064,8 +1064,8 @@ Case 5: Intrusive change to the Canon makernote metadata > Exif.CanonCs2.0x0001 0x0001 0 > Exif.CanonCs2.ISOSpeed 0x0002 160 > Exif.CanonCs2.0x0003 0x0003 276 -> Exif.CanonCs2.0x0004 0x0004 149 -> Exif.CanonCs2.0x0005 0x0005 287 +> Exif.CanonCs2.TargetAperture 0x0004 149 +> Exif.CanonCs2.TargetShutterSpeed 0x0005 287 > Exif.CanonCs2.0x0006 0x0006 0 > Exif.CanonCs2.WhiteBalance 0x0007 0 > Exif.CanonCs2.0x0008 0x0008 0 @@ -1081,8 +1081,8 @@ Case 5: Intrusive change to the Canon makernote metadata > Exif.CanonCs2.0x0012 0x0012 1 > Exif.CanonCs2.SubjectDistance 0x0013 782 > Exif.CanonCs2.0x0014 0x0014 0 -> Exif.CanonCs2.0x0015 0x0015 149 -> Exif.CanonCs2.0x0016 0x0016 289 +> Exif.CanonCs2.ApertureValue 0x0015 149 +> Exif.CanonCs2.ShutterSpeedValue 0x0016 289 > Exif.CanonCs2.0x0017 0x0017 0 > Exif.CanonCs2.0x0018 0x0018 0 > Exif.CanonCs2.0x0019 0x0019 0 diff --git a/test/data/write2-test.out b/test/data/write2-test.out index 6500bff1..02bd97ed 100644 --- a/test/data/write2-test.out +++ b/test/data/write2-test.out @@ -19,8 +19,8 @@ Exif.CanonCs1.Selftimer 0x0002 Makernote Short 1 Exif.CanonCs2.0x0001 0x0001 Makernote Short 1 0 Exif.CanonCs2.ISOSpeed 0x0002 Makernote Short 1 0 Exif.CanonCs2.0x0003 0x0003 Makernote Short 1 0 -Exif.CanonCs2.0x0004 0x0004 Makernote Short 1 0 -Exif.CanonCs2.0x0005 0x0005 Makernote Short 1 42 +Exif.CanonCs2.TargetAperture 0x0004 Makernote Short 1 0 +Exif.CanonCs2.TargetShutterSpeed 0x0005 Makernote Short 1 42 Exif.CanonCf.NoiseReduction 0x0001 Makernote Short 1 43 ----- Non-intrusive writing of special Canon MakerNote tags @@ -33,8 +33,8 @@ Exif.CanonCs1.Selftimer 0x0002 Makernote Short 1 Exif.CanonCs2.0x0001 0x0001 Makernote Short 1 0 Exif.CanonCs2.ISOSpeed 0x0002 Makernote Short 1 0 Exif.CanonCs2.0x0003 0x0003 Makernote Short 1 0 -Exif.CanonCs2.0x0004 0x0004 Makernote Short 1 99 -Exif.CanonCs2.0x0005 0x0005 Makernote Short 1 42 +Exif.CanonCs2.TargetAperture 0x0004 Makernote Short 1 99 +Exif.CanonCs2.TargetShutterSpeed 0x0005 Makernote Short 1 42 Exif.CanonCf.NoiseReduction 0x0001 Makernote Short 1 43 ----- One Fujifilm MakerNote tag