Completed CRW write support, feature #438. Only tested on Linux

v0.27.3
Andreas Huggel 20 years ago
parent 92b9339cd2
commit d545215ceb

@ -1,4 +1,4 @@
# Doxyfile 1.4.5 # Doxyfile 1.4.6
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project # doxygen (www.doxygen.org) for a project
@ -385,7 +385,7 @@ SHOW_USED_FILES = YES
# If the sources in your project are distributed over multiple directories # If the sources in your project are distributed over multiple directories
# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
# in the documentation. The default is YES. # in the documentation. The default is NO.
SHOW_DIRECTORIES = NO SHOW_DIRECTORIES = NO

@ -1,9 +1,9 @@
<!-- Main index from Doxygen 1.4.5 --> <!-- Main index from Doxygen 1.4.6 -->
<div class="tabs"> <div class="tabs">
<ul> <ul>
<li><a href="index.html"><span>Main&nbsp;Page</span></a></li> <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
<li><a href="namespaces.html"><span>Namespaces</span></a></li> <li><a href="namespaces.html"><span>Namespaces</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li> <li><a href="classes.html"><span>Classes</span></a></li>
<li><a href="files.html"><span>Files</span></a></li> <li><a href="files.html"><span>Files</span></a></li>
<li><a href="examples.html"><span>Examples</span></a></li> <li><a href="examples.html"><span>Examples</span></a></li>
</ul> </ul>

@ -60,7 +60,7 @@ CCSRC = basicio.cpp canonmn.cpp crwimage.cpp datasets.cpp error.cpp exif.cpp \
BINSRC = addmoddel.cpp dataarea-test.cpp exifcomment.cpp exifdata-test.cpp \ BINSRC = addmoddel.cpp dataarea-test.cpp exifcomment.cpp exifdata-test.cpp \
exifprint.cpp ifd-test.cpp iotest.cpp iptceasy.cpp iptcprint.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 \ iptctest.cpp key-test.cpp makernote-test.cpp taglist.cpp write-test.cpp \
write2-test.cpp crwparse.cpp write2-test.cpp crwparse.cpp crwedit.cpp
# Main source file of the Exiv2 application # Main source file of the Exiv2 application
EXIV2MAIN = exiv2.cpp EXIV2MAIN = exiv2.cpp

@ -184,7 +184,7 @@ namespace Exiv2 {
close(); close();
fileIo->close(); fileIo->close();
// MSVCRT rename that does not overwrite existing files // MSVCRT rename that does not overwrite existing files
if (std::remove(path_.c_str()) != 0) { if (fileExists(path_) && std::remove(path_.c_str()) != 0) {
throw Error(2, path_, strError(), "std::remove"); throw Error(2, path_, strError(), "std::remove");
} }
if (std::rename(fileIo->path_.c_str(), path_.c_str()) == -1) { if (std::rename(fileIo->path_.c_str(), path_.c_str()) == -1) {
@ -192,7 +192,7 @@ namespace Exiv2 {
} }
std::remove(fileIo->path_.c_str()); std::remove(fileIo->path_.c_str());
} }
else{ else {
// Generic handling, reopen both to reset to start // Generic handling, reopen both to reset to start
if (open("w+b") != 0) { if (open("w+b") != 0) {
throw Error(10, path_, "w+b", strError()); throw Error(10, path_, "w+b", strError());

@ -0,0 +1,131 @@
// ***************************************************************** -*- C++ -*-
// crwedit.cpp, $Rev$
// Print the CIFF structure of a CRW file
#include "crwimage.hpp"
#include "futils.hpp"
#include <iostream>
#include <string>
void remove(Exiv2::CiffHeader* pHead);
void add(Exiv2::CiffHeader* pHead);
void help();
void write(const std::string& filename, const Exiv2::CiffHeader* pHead);
int main(int argc, char* const argv[])
try {
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " file\n";
std::cout << "Edit the CIFF structure of a CRW file\n";
return 1;
}
std::string filename(argv[1]);
Exiv2::FileIo io(filename);
if(io.open() != 0) {
throw Exiv2::Error(9, io.path(), Exiv2::strError());
}
Exiv2::IoCloser closer(io);
// Ensure that this is a CRW image
if (!Exiv2::isCrwType(io, false)) {
if (io.error() || io.eof()) throw Exiv2::Error(14);
throw Exiv2::Error(33);
}
// Read the image into a memory buffer
long len = io.size();
Exiv2::DataBuf buf(len);
io.read(buf.pData_, len);
if (io.error() || io.eof()) throw Exiv2::Error(14);
// Parse the image, starting with a CIFF header component
Exiv2::CiffHeader::AutoPtr parseTree(new Exiv2::CiffHeader);
parseTree->read(buf.pData_, buf.size_);
// Allow user to make changes
bool go = true;
while (go) {
char cmd;
std::cout << "command> ";
std::cin >> cmd;
switch (cmd) {
case 'q': go = false; break;
case 'p': parseTree->print(std::cout); break;
case 'a': add(parseTree.get()); break;
case 'd': remove(parseTree.get()); break;
case 'w': write(filename, parseTree.get()); break;
case 'h': help(); break;
}
}
return 0;
}
catch (Exiv2::AnyError& e) {
std::cerr << e << "\n";
return -1;
}
void write(const std::string& filename, const Exiv2::CiffHeader* pHead)
{
Exiv2::Blob blob;
pHead->write(blob);
Exiv2::FileIo io(filename);
if(io.open("wb") != 0) {
throw Exiv2::Error(9, io.path(), Exiv2::strError());
}
Exiv2::IoCloser closer(io);
long ret = io.write(&blob[0], blob.size());
if (static_cast<size_t>(ret) != blob.size()) throw Exiv2::Error(21);
io.close();
}
void remove(Exiv2::CiffHeader* pHead)
{
uint16_t crwTag, crwDir;
std::cout << "crwTag> 0x";
std::cin >> std::hex >> crwTag;
std::cout << "crwDir> 0x";
std::cin >> std::hex >> crwDir;
std::cout << "Deleting tag 0x" << std::hex << crwTag
<< " in dir 0x" << crwDir << ", ok? ";
char cmd;
std::cin >> cmd;
if (cmd != 'n' && cmd != 'N') {
pHead->remove(crwTag, crwDir);
}
else {
std::cout << "Canceled.\n";
}
}
void add(Exiv2::CiffHeader* pHead)
{
uint16_t crwTag, crwDir;
uint32_t size;
std::cout << "crwTag> 0x";
std::cin >> std::hex >> crwTag;
std::cout << "crwDir> 0x";
std::cin >> std::hex >> crwDir;
std::cout << "size> ";
std::cin >> std::dec >> size;
std::cout << "Adding tag 0x" << std::hex << crwTag
<< " in dir 0x" << crwDir << ", " << size << " bytes, ok? ";
char cmd;
std::cin >> cmd;
if (cmd != 'n' && cmd != 'N') {
Exiv2::DataBuf buf(size);
memset(buf.pData_, 0xaa, size);
pHead->add(crwTag, crwDir, buf);
}
else {
std::cout << "Canceled.\n";
}
}
void help()
{
std::cout << "a: add tag, d: delete tag, p: print tags, w: write file, q: quit\n";
}

@ -38,6 +38,7 @@ EXIV2_RCSID("@(#) $Id$");
#ifdef _MSC_VER #ifdef _MSC_VER
# include "exv_msvc.h" # include "exv_msvc.h"
#else #else
# define _XOPEN_SOURCE /* glibc2 needs this for strptime */
# include "exv_conf.h" # include "exv_conf.h"
#endif #endif
@ -56,6 +57,9 @@ EXIV2_RCSID("@(#) $Id$");
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <cmath> #include <cmath>
#ifndef EXV_HAVE_TIMEGM
# include "timegm.h"
#endif
// ***************************************************************************** // *****************************************************************************
// class member definitions // class member definitions
@ -64,15 +68,15 @@ namespace Exiv2 {
const CrwMapping CrwMap::crwMapping_[] = { const CrwMapping CrwMap::crwMapping_[] = {
// CrwTag CrwDir Size ExifTag IfdId decodeFct encodeFct // CrwTag CrwDir Size ExifTag IfdId decodeFct encodeFct
// ------ ------ ---- ------- ----- --------- --------- // ------ ------ ---- ------- ----- --------- ---------
CrwMapping(0x0805, 0x300a, 0, 0x9286, exifIfdId, decode0x0805, encode0x0805), CrwMapping(0x0805, 0x300a, 0, 0, canonIfdId, decode0x0805, encode0x0805),
CrwMapping(0x080a, 0x2807, 0, 0x010f, ifd0Id, decode0x080a, encode0x080a), CrwMapping(0x080a, 0x2807, 0, 0, canonIfdId, decode0x080a, encode0x080a),
CrwMapping(0x080b, 0x3004, 0, 0x0007, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x080b, 0x3004, 0, 0x0007, canonIfdId, decodeBasic, encodeBasic),
CrwMapping(0x0810, 0x2807, 0, 0x0009, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x0810, 0x2807, 0, 0x0009, canonIfdId, decodeBasic, encodeBasic),
CrwMapping(0x0815, 0x2804, 0, 0x0006, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x0815, 0x2804, 0, 0x0006, canonIfdId, decodeBasic, encodeBasic),
CrwMapping(0x1029, 0x300b, 0, 0x0002, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x1029, 0x300b, 0, 0x0002, canonIfdId, decodeBasic, encodeBasic),
CrwMapping(0x102a, 0x300b, 0, 0x0004, canonIfdId, decode0x102a, 0), CrwMapping(0x102a, 0x300b, 0, 0x0004, canonIfdId, decodeArray, encodeArray),
CrwMapping(0x102d, 0x300b, 0, 0x0001, canonIfdId, decode0x102d, 0), CrwMapping(0x102d, 0x300b, 0, 0x0001, canonIfdId, decodeArray, encodeArray),
CrwMapping(0x1033, 0x300b, 0, 0x000f, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x1033, 0x300b, 0, 0x000f, canonIfdId, decodeArray, encodeArray),
CrwMapping(0x1038, 0x300b, 0, 0x0012, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x1038, 0x300b, 0, 0x0012, canonIfdId, decodeBasic, encodeBasic),
CrwMapping(0x10a9, 0x300b, 0, 0x00a9, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x10a9, 0x300b, 0, 0x00a9, canonIfdId, decodeBasic, encodeBasic),
// Mapped to Exif.Photo.ColorSpace instead (see below) // Mapped to Exif.Photo.ColorSpace instead (see below)
@ -83,15 +87,12 @@ namespace Exiv2 {
CrwMapping(0x10c1, 0x300b, 0, 0x00c1, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x10c1, 0x300b, 0, 0x00c1, canonIfdId, decodeBasic, encodeBasic),
CrwMapping(0x1807, 0x3002, 0, 0x9206, exifIfdId, decodeBasic, encodeBasic), CrwMapping(0x1807, 0x3002, 0, 0x9206, exifIfdId, decodeBasic, encodeBasic),
CrwMapping(0x180b, 0x2807, 0, 0x000c, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x180b, 0x2807, 0, 0x000c, canonIfdId, decodeBasic, encodeBasic),
CrwMapping(0x180e, 0x300a, 0, 0x9003, exifIfdId, decode0x180e, 0), CrwMapping(0x180e, 0x300a, 0, 0x9003, exifIfdId, decode0x180e, encode0x180e),
CrwMapping(0x1810, 0x300a, 0, 0xa002, exifIfdId, decode0x1810, 0), CrwMapping(0x1810, 0x300a, 0, 0xa002, exifIfdId, decode0x1810, encode0x1810),
CrwMapping(0x1810, 0x300a, 0, 0xa003, exifIfdId, 0, 0),
CrwMapping(0x1817, 0x300a, 4, 0x0008, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x1817, 0x300a, 4, 0x0008, canonIfdId, decodeBasic, encodeBasic),
//CrwMapping(0x1818, 0x3002, 0, 0x9204, exifIfdId, decodeBasic, encodeBasic), //CrwMapping(0x1818, 0x3002, 0, 0x9204, exifIfdId, decodeBasic, encodeBasic),
CrwMapping(0x183b, 0x300b, 0, 0x0015, canonIfdId, decodeBasic, encodeBasic), CrwMapping(0x183b, 0x300b, 0, 0x0015, canonIfdId, decodeBasic, encodeBasic),
CrwMapping(0x2008, 0x0000, 0, 0x0201, ifd1Id, decode0x2008, 0), CrwMapping(0x2008, 0x0000, 0, 0, ifd1Id, decode0x2008, encode0x2008),
CrwMapping(0x2008, 0x0000, 0, 0x0202, ifd1Id, 0, 0),
CrwMapping(0x2008, 0x0000, 0, 0x0103, ifd1Id, 0, 0),
// End of list marker // End of list marker
CrwMapping(0x0000, 0x0000, 0, 0x0000, ifdIdNotSet, 0, 0) CrwMapping(0x0000, 0x0000, 0, 0x0000, ifdIdNotSet, 0, 0)
}; // CrwMap::crwMapping_[] }; // CrwMap::crwMapping_[]
@ -126,30 +127,15 @@ namespace Exiv2 {
{ 0xffff, 0xffff } { 0xffff, 0xffff }
}; };
const byte CrwImage::blank_[] = {
0x00
};
CrwImage::CrwImage(BasicIo::AutoPtr io, bool create) CrwImage::CrwImage(BasicIo::AutoPtr io, bool create)
: io_(io) : io_(io)
{ {
if (create) { if (create) {
initImage(blank_, sizeof(blank_)); IoCloser closer(*io_);
io_->open();
} }
} // CrwImage::CrwImage } // CrwImage::CrwImage
int CrwImage::initImage(const byte initData[], long dataSize)
{
if (io_->open() != 0) {
return 4;
}
IoCloser closer(*io_);
if (io_->write(initData, dataSize) != dataSize) {
return 4;
}
return 0;
} // CrwImage::initImage
bool CrwImage::good() const bool CrwImage::good() const
{ {
if (io_->open() != 0) return false; if (io_->open() != 0) return false;
@ -352,29 +338,6 @@ namespace Exiv2 {
pRootDir_->readDirectory(pData + offset_, size - offset_, byteOrder_); pRootDir_->readDirectory(pData + offset_, size - offset_, byteOrder_);
} // CiffHeader::read } // CiffHeader::read
void CiffDirectory::readDirectory(const byte* pData,
uint32_t size,
ByteOrder byteOrder)
{
uint32_t o = getULong(pData + size - 4, byteOrder);
if (o + 2 > size) throw Error(33);
uint16_t count = getUShort(pData + o, byteOrder);
o += 2;
for (uint16_t i = 0; i < count; ++i) {
if (o + 10 > size) throw Error(33);
uint16_t tag = getUShort(pData + o, byteOrder);
AutoPtr m;
switch (CiffComponent::typeId(tag)) {
case directory: m = AutoPtr(new CiffDirectory); break;
default: m = AutoPtr(new CiffEntry); break;
}
m->setDir(this->tag());
m->read(pData, size, o, byteOrder);
add(m);
o += 10;
}
} // CiffDirectory::readDirectory
void CiffComponent::read(const byte* pData, void CiffComponent::read(const byte* pData,
uint32_t size, uint32_t size,
uint32_t start, uint32_t start,
@ -403,6 +366,13 @@ namespace Exiv2 {
offset_ = start + 2; offset_ = start + 2;
} }
pData_ = pData + offset_; pData_ = pData + offset_;
#ifdef DEBUG
std::cout << " Entry for tag 0x"
<< std::hex << tagId() << " (0x" << tag()
<< "), " << std::dec << size_
<< " Bytes, Offset is " << offset_ << "\n";
#endif
} // CiffComponent::doRead } // CiffComponent::doRead
void CiffDirectory::doRead(const byte* pData, void CiffDirectory::doRead(const byte* pData,
@ -411,9 +381,42 @@ namespace Exiv2 {
ByteOrder byteOrder) ByteOrder byteOrder)
{ {
CiffComponent::doRead(pData, size, start, byteOrder); CiffComponent::doRead(pData, size, start, byteOrder);
#ifdef DEBUG
std::cout << "Reading directory 0x" << std::hex << tag() << "\n";
#endif
readDirectory(pData + offset(), this->size(), byteOrder); readDirectory(pData + offset(), this->size(), byteOrder);
#ifdef DEBUG
std::cout << "<---- 0x" << std::hex << tag() << "\n";
#endif
} // CiffDirectory::doRead } // CiffDirectory::doRead
void CiffDirectory::readDirectory(const byte* pData,
uint32_t size,
ByteOrder byteOrder)
{
uint32_t o = getULong(pData + size - 4, byteOrder);
if (o + 2 > size) throw Error(33);
uint16_t count = getUShort(pData + o, byteOrder);
#ifdef DEBUG
std::cout << "Directory at offset " << std::dec << o
<<", " << count << " entries \n";
#endif
o += 2;
for (uint16_t i = 0; i < count; ++i) {
if (o + 10 > size) throw Error(33);
uint16_t tag = getUShort(pData + o, byteOrder);
AutoPtr m;
switch (CiffComponent::typeId(tag)) {
case directory: m = AutoPtr(new CiffDirectory); break;
default: m = AutoPtr(new CiffEntry); break;
}
m->setDir(this->tag());
m->read(pData, size, o, byteOrder);
add(m);
o += 10;
}
} // CiffDirectory::readDirectory
void CiffHeader::decode(Image& image) const void CiffHeader::decode(Image& image) const
{ {
// Nothing to decode from the header itself, just add correct byte order // Nothing to decode from the header itself, just add correct byte order
@ -472,7 +475,6 @@ namespace Exiv2 {
ByteOrder byteOrder, ByteOrder byteOrder,
uint32_t offset) uint32_t offset)
{ {
if (remove_) return offset;
return doWrite(blob, byteOrder, offset); return doWrite(blob, byteOrder, offset);
} }
@ -486,6 +488,10 @@ namespace Exiv2 {
uint32_t CiffComponent::writeValueData(Blob& blob, uint32_t offset) uint32_t CiffComponent::writeValueData(Blob& blob, uint32_t offset)
{ {
if (dataLocation() == valueData) { if (dataLocation() == valueData) {
#ifdef DEBUG
std::cout << " Data for tag 0x" << std::hex << tagId()
<< ", " << std::dec << size_ << " Bytes\n";
#endif
offset_ = offset; offset_ = offset;
append(blob, pData_, size_); append(blob, pData_, size_);
offset += size_; offset += size_;
@ -502,6 +508,9 @@ namespace Exiv2 {
ByteOrder byteOrder, ByteOrder byteOrder,
uint32_t offset) uint32_t offset)
{ {
#ifdef DEBUG
std::cout << "Writing directory 0x" << std::hex << tag() << "---->\n";
#endif
// Ciff offsets are relative to the start of the directory // Ciff offsets are relative to the start of the directory
uint32_t dirOffset = 0; uint32_t dirOffset = 0;
@ -515,7 +524,7 @@ namespace Exiv2 {
// Number of directory entries // Number of directory entries
byte buf[4]; byte buf[4];
us2Data(buf, components_.size(), byteOrder); us2Data(buf, static_cast<uint16_t>(components_.size()), byteOrder);
append(blob, buf, 2); append(blob, buf, 2);
dirOffset += 2; dirOffset += 2;
@ -534,11 +543,22 @@ namespace Exiv2 {
setOffset(offset); setOffset(offset);
setSize(dirOffset); setSize(dirOffset);
#ifdef DEBUG
std::cout << "Directory is at offset " << std::dec << dirStart
<< ", " << components_.size() << " entries\n"
<< "<---- 0x" << std::hex << tag() << "\n";
#endif
return offset + dirOffset; return offset + dirOffset;
} // CiffDirectory::doWrite } // CiffDirectory::doWrite
void CiffComponent::writeDirEntry(Blob& blob, ByteOrder byteOrder) const void CiffComponent::writeDirEntry(Blob& blob, ByteOrder byteOrder) const
{ {
#ifdef DEBUG
std::cout << " Directory entry for tag 0x"
<< std::hex << tagId() << " (0x" << tag()
<< "), " << std::dec << size_
<< " Bytes, Offset is " << offset_ << "\n";
#endif
byte buf[4]; byte buf[4];
DataLocId dl = dataLocation(); DataLocId dl = dataLocation();
@ -662,46 +682,46 @@ namespace Exiv2 {
} // CiffComponent::dataLocation } // CiffComponent::dataLocation
/*! /*!
@brief Finds \em crwTag in directory \em crwDir, returning a pointer to @brief Finds \em crwTagId in directory \em crwDir, returning a pointer to
the component or 0 if not found. the component or 0 if not found.
*/ */
CiffComponent* CiffHeader::findComponent(uint16_t crwTag, CiffComponent* CiffHeader::findComponent(uint16_t crwTagId,
uint16_t crwDir) const uint16_t crwDir) const
{ {
if (pRootDir_ == 0) return 0; if (pRootDir_ == 0) return 0;
return pRootDir_->findComponent(crwTag, crwDir); return pRootDir_->findComponent(crwTagId, crwDir);
} // CiffHeader::findComponent } // CiffHeader::findComponent
CiffComponent* CiffComponent::findComponent(uint16_t crwTag, CiffComponent* CiffComponent::findComponent(uint16_t crwTagId,
uint16_t crwDir) const uint16_t crwDir) const
{ {
return doFindComponent(crwTag, crwDir); return doFindComponent(crwTagId, crwDir);
} // CiffComponent::findComponent } // CiffComponent::findComponent
CiffComponent* CiffComponent::doFindComponent(uint16_t crwTag, CiffComponent* CiffComponent::doFindComponent(uint16_t crwTagId,
uint16_t crwDir) const uint16_t crwDir) const
{ {
if (tagId() == crwTag && dir() == crwDir) { if (tagId() == crwTagId && dir() == crwDir) {
return const_cast<CiffComponent*>(this); return const_cast<CiffComponent*>(this);
} }
return 0; return 0;
} // CiffComponent::doFindComponent } // CiffComponent::doFindComponent
CiffComponent* CiffDirectory::doFindComponent(uint16_t crwTag, CiffComponent* CiffDirectory::doFindComponent(uint16_t crwTagId,
uint16_t crwDir) const uint16_t crwDir) const
{ {
CiffComponent* cc = 0; CiffComponent* cc = 0;
const Components::const_iterator b = components_.begin(); const Components::const_iterator b = components_.begin();
const Components::const_iterator e = components_.end(); const Components::const_iterator e = components_.end();
for (Components::const_iterator i = b; i != e; ++i) { for (Components::const_iterator i = b; i != e; ++i) {
cc = (*i)->findComponent(crwTag, crwDir); cc = (*i)->findComponent(crwTagId, crwDir);
if (cc) return cc; if (cc) return cc;
} }
return 0; return 0;
} // CiffDirectory::doFindComponent } // CiffDirectory::doFindComponent
CiffComponent* CiffHeader::addTag(uint16_t crwTag, uint16_t crwDir) void CiffHeader::add(uint16_t crwTagId, uint16_t crwDir, DataBuf buf)
{ {
CrwDirs crwDirs; CrwDirs crwDirs;
CrwMap::loadStack(crwDirs, crwDir); CrwMap::loadStack(crwDirs, crwDir);
@ -709,28 +729,29 @@ namespace Exiv2 {
assert(rootDirectory == 0x0000); assert(rootDirectory == 0x0000);
crwDirs.pop(); crwDirs.pop();
if (!pRootDir_) pRootDir_ = new CiffDirectory; if (!pRootDir_) pRootDir_ = new CiffDirectory;
return pRootDir_->addTag(crwDirs, crwTag); CiffComponent* cc = pRootDir_->add(crwDirs, crwTagId);
} // CiffHeader::addTag cc->setValue(buf);
} // CiffHeader::add
CiffComponent* CiffComponent::addTag(CrwDirs& crwDirs, uint16_t crwTag) CiffComponent* CiffComponent::add(CrwDirs& crwDirs, uint16_t crwTagId)
{ {
return doAddTag(crwDirs, crwTag); return doAdd(crwDirs, crwTagId);
} // CiffComponent::addTag } // CiffComponent::add
CiffComponent* CiffComponent::doAddTag(CrwDirs& crwDirs, uint16_t crwTag) CiffComponent* CiffComponent::doAdd(CrwDirs& crwDirs, uint16_t crwTagId)
{ {
return 0; return 0;
} // CiffComponent::doAddTag } // CiffComponent::doAdd
CiffComponent* CiffDirectory::doAddTag(CrwDirs& crwDirs, uint16_t crwTag) CiffComponent* CiffDirectory::doAdd(CrwDirs& crwDirs, uint16_t crwTagId)
{ {
/* /*
addTag() add()
if stack not empty if stack not empty
pop from stack pop from stack
find dir among components find dir among components
if not found, create it if not found, create it
addTag() add()
else else
find tag among components find tag among components
if not found, create it if not found, create it
@ -758,25 +779,93 @@ namespace Exiv2 {
add(m); add(m);
} }
// Recursive call to next lower level directory // Recursive call to next lower level directory
cc = cc->addTag(crwDirs, crwTag); cc = cc->add(crwDirs, crwTagId);
} }
else { else {
// Find the tag // Find the tag
for (Components::iterator i = b; i != e; ++i) { for (Components::iterator i = b; i != e; ++i) {
if ((*i)->tag() == crwTag) { if ((*i)->tagId() == crwTagId) {
cc = *i; cc = *i;
break; break;
} }
} }
if (cc == 0) { if (cc == 0) {
// Tag doesn't exist yet, add it // Tag doesn't exist yet, add it
AutoPtr m(new CiffEntry(crwTag, tag())); AutoPtr m(new CiffEntry(crwTagId, tag()));
cc = m.get(); cc = m.get();
add(m); add(m);
} }
} }
return cc; return cc;
} // CiffDirectory::doAddTag } // CiffDirectory::doAdd
void CiffHeader::remove(uint16_t crwTagId, uint16_t crwDir)
{
if (pRootDir_) {
CrwDirs crwDirs;
CrwMap::loadStack(crwDirs, crwDir);
uint16_t rootDirectory = crwDirs.top().crwDir_;
assert(rootDirectory == 0x0000);
crwDirs.pop();
pRootDir_->remove(crwDirs, crwTagId);
}
} // CiffHeader::remove
void CiffComponent::remove(CrwDirs& crwDirs, uint16_t crwTagId)
{
return doRemove(crwDirs, crwTagId);
} // CiffComponent::remove
void CiffComponent::doRemove(CrwDirs& crwDirs, uint16_t crwTagId)
{
// do nothing
} // CiffComponent::doRemove
void CiffDirectory::doRemove(CrwDirs& crwDirs, uint16_t crwTagId)
{
const Components::iterator b = components_.begin();
const Components::iterator e = components_.end();
Components::iterator i;
if (!crwDirs.empty()) {
CrwSubDir csd = crwDirs.top();
crwDirs.pop();
// Find the directory
for (i = b; i != e; ++i) {
if ((*i)->tag() == csd.crwDir_) {
// Recursive call to next lower level directory
(*i)->remove(crwDirs, crwTagId);
if ((*i)->empty()) components_.erase(i);
break;
}
}
}
else {
// Find the tag
for (i = b; i != e; ++i) {
if ((*i)->tagId() == crwTagId) {
// Remove the entry and abort the loop
components_.erase(i);
break;
}
}
}
} // CiffDirectory::doRemove
bool CiffComponent::empty() const
{
return doEmpty();
}
bool CiffComponent::doEmpty() const
{
return size_ == 0;
}
bool CiffDirectory::doEmpty() const
{
return components_.empty();
}
void CrwMap::decode(const CiffComponent& ciffComponent, void CrwMap::decode(const CiffComponent& ciffComponent,
Image& image, Image& image,
@ -789,11 +878,11 @@ namespace Exiv2 {
} }
} // CrwMap::decode } // CrwMap::decode
const CrwMapping* CrwMap::crwMapping(uint16_t dir, uint16_t tagId) const CrwMapping* CrwMap::crwMapping(uint16_t crwDir, uint16_t crwTagId)
{ {
for (int i = 0; crwMapping_[i].ifdId_ != ifdIdNotSet; ++i) { for (int i = 0; crwMapping_[i].ifdId_ != ifdIdNotSet; ++i) {
if ( crwMapping_[i].crwDir_ == dir if ( crwMapping_[i].crwDir_ == crwDir
&& crwMapping_[i].crwTagId_ == tagId) { && crwMapping_[i].crwTagId_ == crwTagId) {
return &(crwMapping_[i]); return &(crwMapping_[i]);
} }
} }
@ -814,9 +903,7 @@ namespace Exiv2 {
Image& image, Image& image,
ByteOrder byteOrder) ByteOrder byteOrder)
{ {
if (ciffComponent.typeId() != asciiString) { if (ciffComponent.typeId() != asciiString) return;
return decodeBasic(ciffComponent, pCrwMapping, image, byteOrder);
}
// Make // Make
ExifKey key1("Exif.Image.Make"); ExifKey key1("Exif.Image.Make");
@ -841,10 +928,10 @@ namespace Exiv2 {
image.exifData().add(key2, value2.get()); image.exifData().add(key2, value2.get());
} // CrwMap::decode0x080a } // CrwMap::decode0x080a
void CrwMap::decode0x102a(const CiffComponent& ciffComponent, void CrwMap::decodeArray(const CiffComponent& ciffComponent,
const CrwMapping* pCrwMapping, const CrwMapping* pCrwMapping,
Image& image, Image& image,
ByteOrder byteOrder) ByteOrder byteOrder)
{ {
if (ciffComponent.typeId() != unsignedShort) { if (ciffComponent.typeId() != unsignedShort) {
return decodeBasic(ciffComponent, pCrwMapping, image, byteOrder); return decodeBasic(ciffComponent, pCrwMapping, image, byteOrder);
@ -853,59 +940,47 @@ namespace Exiv2 {
long aperture = 0; long aperture = 0;
long shutterSpeed = 0; long shutterSpeed = 0;
std::string ifdItem(ExifTags::ifdItem(canonCs2IfdId)); IfdId ifdId = ifdIdNotSet;
uint16_t c = 1; switch (pCrwMapping->tag_) {
while (uint32_t(c)*2 < ciffComponent.size()) { case 0x0001: ifdId = canonCs1IfdId; break;
uint16_t n = 1; case 0x0004: ifdId = canonCs2IfdId; break;
ExifKey key(c, ifdItem); case 0x000f: ifdId = canonCfIfdId; break;
UShortValue value;
value.read(ciffComponent.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;
} }
assert(ifdId != ifdIdNotSet);
// Exif.Photo.FNumber std::string ifdItem(ExifTags::ifdItem(ifdId));
float f = fnumber(canonEv(aperture));
// Beware: primitive conversion algorithm
uint32_t den = 1000000;
uint32_t nom = static_cast<uint32_t>(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::decode0x102a
void CrwMap::decode0x102d(const CiffComponent& ciffComponent,
const CrwMapping* pCrwMapping,
Image& image,
ByteOrder byteOrder)
{
if (ciffComponent.typeId() != unsignedShort) {
return decodeBasic(ciffComponent, pCrwMapping, image, byteOrder);
}
std::string ifdItem(ExifTags::ifdItem(canonCs1IfdId));
uint16_t c = 1; uint16_t c = 1;
while (uint32_t(c)*2 < ciffComponent.size()) { while (uint32_t(c)*2 < ciffComponent.size()) {
uint16_t n = 1; uint16_t n = 1;
ExifKey key(c, ifdItem); ExifKey key(c, ifdItem);
UShortValue value; UShortValue value;
if (c == 23 && ciffComponent.size() > 50) n = 3; if (ifdId == canonCs1IfdId && c == 23 && ciffComponent.size() > 50) n = 3;
value.read(ciffComponent.pData() + c*2, n*2, byteOrder); value.read(ciffComponent.pData() + c*2, n*2, byteOrder);
image.exifData().add(key, &value); image.exifData().add(key, &value);
if (ifdId == canonCs2IfdId && c == 21) aperture = value.toLong();
if (ifdId == canonCs2IfdId && c == 22) shutterSpeed = value.toLong();
c += n; c += n;
} }
} // CrwMap::decode0x102d
if (ifdId == canonCs2IfdId) {
// Exif.Photo.FNumber
float f = fnumber(canonEv(aperture));
// Beware: primitive conversion algorithm
uint32_t den = 1000000;
uint32_t nom = static_cast<uint32_t>(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::decodeArray
void CrwMap::decode0x180e(const CiffComponent& ciffComponent, void CrwMap::decode0x180e(const CiffComponent& ciffComponent,
const CrwMapping* pCrwMapping, const CrwMapping* pCrwMapping,
@ -1034,56 +1109,51 @@ namespace Exiv2 {
ExifKey ek(pCrwMapping->tag_, ExifTags::ifdItem(pCrwMapping->ifdId_)); ExifKey ek(pCrwMapping->tag_, ExifTags::ifdItem(pCrwMapping->ifdId_));
ExifData::const_iterator ed = image.exifData().findKey(ek); ExifData::const_iterator ed = image.exifData().findKey(ek);
// Find the target metadatum in the Ciff parse tree // Set the new value or remove the entry
CiffComponent* cc = pHead->findComponent(pCrwMapping->crwTagId_,
pCrwMapping->crwDir_);
if (ed != image.exifData().end()) { if (ed != image.exifData().end()) {
// Create the directory and component as needed
if (cc == 0) cc = pHead->addTag(pCrwMapping->crwTagId_,
pCrwMapping->crwDir_);
// Set the new value
DataBuf buf(ed->size()); DataBuf buf(ed->size());
ed->copy(buf.pData_, pHead->byteOrder()); ed->copy(buf.pData_, pHead->byteOrder());
cc->setValue(buf); pHead->add(pCrwMapping->crwTagId_, pCrwMapping->crwDir_, buf);
} }
else { else {
if (cc) cc->remove(); pHead->remove(pCrwMapping->crwTagId_, pCrwMapping->crwDir_);
} }
} // CrwMap::encodeBasic } // CrwMap::encodeBasic
void CrwMap::encode0x0805(const Image& image, void CrwMap::encode0x0805(const Image& image,
const CrwMapping* /*pCrwMapping*/, const CrwMapping* pCrwMapping,
CiffHeader* pHead) CiffHeader* pHead)
{ {
assert(pCrwMapping != 0);
assert(pHead != 0); assert(pHead != 0);
std::string comment = image.comment(); std::string comment = image.comment();
const uint16_t crwTag = 0x0805; CiffComponent* cc = pHead->findComponent(pCrwMapping->crwTagId_,
const uint16_t crwDir = 0x300a; pCrwMapping->crwDir_);
CiffComponent* cc = pHead->findComponent(crwTag, crwDir);
if (!comment.empty()) { if (!comment.empty()) {
if (cc == 0) cc = pHead->addTag(crwTag, crwDir); uint32_t size = comment.size();
DataBuf buf(std::max(cc->size(), comment.size())); if (cc && cc->size() > size) size = cc->size();
DataBuf buf(size);
memset(buf.pData_, 0x0, buf.size_); memset(buf.pData_, 0x0, buf.size_);
memcpy(buf.pData_, comment.data(), comment.size()); memcpy(buf.pData_, comment.data(), comment.size());
cc->setValue(buf); pHead->add(pCrwMapping->crwTagId_, pCrwMapping->crwDir_, buf);
} }
else { else {
if (cc) { if (cc) {
// Just delete the value, do not remove the tag // Just delete the value, do not remove the tag
DataBuf buf(cc->size()); DataBuf buf(cc->size());
memset(buf.pData_, 0x0, buf.size_); memset(buf.pData_, 0x0, buf.size_);
cc->setValue(buf);
} }
} }
} // CrwMap::encode0x0805 } // CrwMap::encode0x0805
void CrwMap::encode0x080a(const Image& image, void CrwMap::encode0x080a(const Image& image,
const CrwMapping* /*pCrwMapping*/, const CrwMapping* pCrwMapping,
CiffHeader* pHead) CiffHeader* pHead)
{ {
assert(pCrwMapping != 0);
assert(pHead != 0); assert(pHead != 0);
const ExifKey k1("Exif.Image.Make"); const ExifKey k1("Exif.Image.Make");
@ -1092,24 +1162,124 @@ namespace Exiv2 {
const ExifData::const_iterator ed2 = image.exifData().findKey(k2); const ExifData::const_iterator ed2 = image.exifData().findKey(k2);
const ExifData::const_iterator edEnd = image.exifData().end(); const ExifData::const_iterator edEnd = image.exifData().end();
const uint16_t crwTag = 0x080a;
const uint16_t crwDir = 0x2807;
CiffComponent* cc = pHead->findComponent(crwTag, crwDir);
long size = 0; long size = 0;
if (ed1 != edEnd) size += ed1->size(); if (ed1 != edEnd) size += ed1->size();
if (ed2 != edEnd) size += ed2->size(); if (ed2 != edEnd) size += ed2->size();
if (size != 0) { if (size != 0) {
if (cc == 0) cc = pHead->addTag(crwTag, crwDir);
DataBuf buf(size); DataBuf buf(size);
if (ed1 != edEnd) ed1->copy(buf.pData_, pHead->byteOrder()); if (ed1 != edEnd) ed1->copy(buf.pData_, pHead->byteOrder());
if (ed2 != edEnd) ed2->copy(buf.pData_ + ed1->size(), pHead->byteOrder()); if (ed2 != edEnd) ed2->copy(buf.pData_ + ed1->size(), pHead->byteOrder());
cc->setValue(buf); pHead->add(pCrwMapping->crwTagId_, pCrwMapping->crwDir_, buf);
}
else {
pHead->remove(pCrwMapping->crwTagId_, pCrwMapping->crwDir_);
}
} // CrwMap::encode0x080a
void CrwMap::encodeArray(const Image& image,
const CrwMapping* pCrwMapping,
CiffHeader* pHead)
{
assert(pCrwMapping != 0);
assert(pHead != 0);
IfdId ifdId = ifdIdNotSet;
switch (pCrwMapping->tag_) {
case 0x0001: ifdId = canonCs1IfdId; break;
case 0x0004: ifdId = canonCs2IfdId; break;
case 0x000f: ifdId = canonCfIfdId; break;
}
assert(ifdId != ifdIdNotSet);
DataBuf buf = packIfdId(image.exifData(), ifdId, pHead->byteOrder());
if (buf.size_ == 0) {
// Try the undecoded tag
encodeBasic(image, pCrwMapping, pHead);
}
if (buf.size_ > 0) {
// Write the number of shorts to the beginning of buf
us2Data(buf.pData_, buf.size_, pHead->byteOrder());
pHead->add(pCrwMapping->crwTagId_, pCrwMapping->crwDir_, buf);
}
else {
pHead->remove(pCrwMapping->crwTagId_, pCrwMapping->crwDir_);
}
} // CrwMap::encodeArray
void CrwMap::encode0x180e(const Image& image,
const CrwMapping* pCrwMapping,
CiffHeader* pHead)
{
assert(pCrwMapping != 0);
assert(pHead != 0);
time_t t = 0;
const ExifKey key(pCrwMapping->tag_, ExifTags::ifdItem(pCrwMapping->ifdId_));
const ExifData::const_iterator ed = image.exifData().findKey(key);
if (ed != image.exifData().end()) {
struct tm tm;
char* p = strptime(ed->toString().c_str(), "%Y:%m:%d %T", &tm);
if (p != 0) t = timegm(&tm);
}
if (t != 0) {
DataBuf buf(12);
memset(buf.pData_, 0x0, 12);
ul2Data(buf.pData_, static_cast<uint32_t>(t), pHead->byteOrder());
pHead->add(pCrwMapping->crwTagId_, pCrwMapping->crwDir_, buf);
} }
else { else {
if (cc) cc->remove(); pHead->remove(pCrwMapping->crwTagId_, pCrwMapping->crwDir_);
} }
} // encode0x080a } // CrwMap::encode0x180e
void CrwMap::encode0x1810(const Image& image,
const CrwMapping* pCrwMapping,
CiffHeader* pHead)
{
assert(pCrwMapping != 0);
assert(pHead != 0);
const ExifKey kX("Exif.Photo.PixelXDimension");
const ExifKey kY("Exif.Photo.PixelYDimension");
const ExifData::const_iterator edX = image.exifData().findKey(kX);
const ExifData::const_iterator edY = image.exifData().findKey(kY);
const ExifData::const_iterator edEnd = image.exifData().end();
CiffComponent* cc = pHead->findComponent(pCrwMapping->crwTagId_,
pCrwMapping->crwDir_);
if (edX != edEnd || edY != edEnd) {
uint32_t size = 28;
if (cc && cc->size() > size) size = cc->size();
DataBuf buf(size);
memset(buf.pData_, 0x0, buf.size_);
if (cc) memcpy(buf.pData_ + 8, cc->pData() + 8, cc->size() - 8);
if (edX != edEnd && edX->size() == 4) {
edX->copy(buf.pData_, pHead->byteOrder());
}
if (edY != edEnd && edY->size() == 4) {
edY->copy(buf.pData_ + 4, pHead->byteOrder());
}
pHead->add(pCrwMapping->crwTagId_, pCrwMapping->crwDir_, buf);
}
else {
pHead->remove(pCrwMapping->crwTagId_, pCrwMapping->crwDir_);
}
} // CrwMap::encode0x1810
void CrwMap::encode0x2008(const Image& image,
const CrwMapping* pCrwMapping,
CiffHeader* pHead)
{
assert(pCrwMapping != 0);
assert(pHead != 0);
DataBuf buf = image.exifData().copyThumbnail();
if (buf.size_ != 0) {
pHead->add(pCrwMapping->crwTagId_, pCrwMapping->crwDir_, buf);
}
else {
pHead->remove(pCrwMapping->crwTagId_, pCrwMapping->crwDir_);
}
} // CrwMap::encode0x1810
// ************************************************************************* // *************************************************************************
// free functions // free functions
@ -1143,4 +1313,27 @@ namespace Exiv2 {
return result; return result;
} }
DataBuf packIfdId(const ExifData& exifData,
IfdId ifdId,
ByteOrder byteOrder)
{
const uint16_t size = 1024;
DataBuf buf(size);
memset(buf.pData_, 0x0, buf.size_);
uint16_t len = 0;
const ExifData::const_iterator b = exifData.begin();
const ExifData::const_iterator e = exifData.end();
for (ExifData::const_iterator i = b; i != e; ++i) {
if (i->ifdId() != ifdId) continue;
const uint16_t s = i->tag()*2 + static_cast<uint16_t>(i->size());
assert(s <= size);
if (len < s) len = s;
i->copy(buf.pData_ + i->tag()*2, byteOrder);
}
// Round the size to make it even.
buf.size_ = len + len%2;
return buf;
}
} // namespace Exiv2 } // namespace Exiv2

@ -157,12 +157,6 @@ namespace Exiv2 {
//@} //@}
private: private:
//! @name Manipulators
//@{
int initImage(const byte initData[], long dataSize);
//@}
//! @name Accessors //! @name Accessors
//@{ //@{
/*! /*!
@ -191,8 +185,6 @@ namespace Exiv2 {
//@} //@}
// DATA // DATA
static const byte blank_[]; //!< Minimal Crw image
BasicIo::AutoPtr io_; //!< Image data io pointer BasicIo::AutoPtr io_; //!< Image data io pointer
ExifData exifData_; //!< Exif data container ExifData exifData_; //!< Exif data container
IptcData iptcData_; //!< Iptc data container IptcData iptcData_; //!< Iptc data container
@ -254,11 +246,11 @@ namespace Exiv2 {
//! Default constructor //! Default constructor
CiffComponent() CiffComponent()
: dir_(0), tag_(0), size_(0), offset_(0), pData_(0), : dir_(0), tag_(0), size_(0), offset_(0), pData_(0),
remove_(false), isAllocated_(false) {} isAllocated_(false) {}
//! Constructor taking a tag and directory //! Constructor taking a tag and directory
CiffComponent(uint16_t tag, uint16_t dir) CiffComponent(uint16_t tag, uint16_t dir)
: dir_(dir), tag_(tag), size_(0), offset_(0), pData_(0), : dir_(dir), tag_(tag), size_(0), offset_(0), pData_(0),
remove_(false), isAllocated_(false) {} isAllocated_(false) {}
//! Virtual destructor. //! Virtual destructor.
virtual ~CiffComponent(); virtual ~CiffComponent();
//@} //@}
@ -269,6 +261,30 @@ namespace Exiv2 {
//! Add a component to the composition //! Add a component to the composition
void add(AutoPtr component); void add(AutoPtr component);
/*!
@brief Add \em crwTagId to the parse tree, if it doesn't exist
yet. \em crwDirs contains the path of subdirectories, starting
with the root directory, leading to \em crwTagId. Directories
that don't exist yet are added along the way. Returns a pointer
to the newly added component.
@param crwDirs Subdirectory path from root to the subdirectory
containing the tag to be added.
@param crwTagId Tag to be added.
@return A pointer to the newly added component.
*/
CiffComponent* add(CrwDirs& crwDirs, uint16_t crwTagId);
/*!
@brief Remove \em crwTagId from the parse tree, if it exists yet. \em
crwDirs contains the path of subdirectories, starting with the
root directory, leading to \em crwTagId.
@param crwDirs Subdirectory path from root to the subdirectory
containing the tag to be removed.
@param crwTagId Tag to be removed.
*/
void remove(CrwDirs& crwDirs, uint16_t crwTagId);
/*! /*!
@brief Read a component from a data buffer @brief Read a component from a data buffer
@ -294,18 +310,6 @@ namespace Exiv2 {
@return New offset @return New offset
*/ */
uint32_t write(Blob& blob, ByteOrder byteOrder, uint32_t offset); uint32_t write(Blob& blob, ByteOrder byteOrder, uint32_t offset);
/*!
@brief Add \em crwTag to the parse tree, if it doesn' exist yet. \em
crwDirs contains the path of subdirectories, starting with the
root directory, leading to \em crwTag. Directories that don't
exist yet are added along the way. Returns a pointer to the
newly added component.
@param crwDirs Subdirectory path from root to the subdirectory containing
the tag to be added.
@param crwTag Tag to be added.
*/
CiffComponent* addTag(CrwDirs& crwDirs, uint16_t crwTag);
/*! /*!
@brief Writes the entry's value if size is larger than eight bytes. If @brief Writes the entry's value if size is larger than eight bytes. If
needed, the value is padded with one 0 byte to make the number needed, the value is padded with one 0 byte to make the number
@ -319,8 +323,6 @@ namespace Exiv2 {
uint32_t writeValueData(Blob& blob, uint32_t offset); uint32_t writeValueData(Blob& blob, uint32_t offset);
//! Set the directory tag for this component. //! Set the directory tag for this component.
void setDir(uint16_t dir) { dir_ = dir; } void setDir(uint16_t dir) { dir_ = dir; }
//! Mark the component as deleted, so that it won't be written.
void remove() { remove_ = true; }
//! Set the data value of the entry. //! Set the data value of the entry.
void setValue(DataBuf buf); void setValue(DataBuf buf);
//@} //@}
@ -362,6 +364,9 @@ namespace Exiv2 {
//! Return the tag of this component //! Return the tag of this component
uint16_t tag() const { return tag_; } uint16_t tag() const { return tag_; }
//! Return true if the component is empty, else false
bool empty() const;
/*! /*!
@brief Return the data size of this component @brief Return the data size of this component
@ -388,10 +393,10 @@ namespace Exiv2 {
DataLocId dataLocation() const { return dataLocation(tag_); } DataLocId dataLocation() const { return dataLocation(tag_); }
/*! /*!
@brief Finds \em crwTag in directory \em crwDir, returning a pointer to @brief Finds \em crwTagId in directory \em crwDir, returning a pointer to
the component or 0 if not found. the component or 0 if not found.
*/ */
CiffComponent* findComponent(uint16_t crwTag, uint16_t crwDir) const; CiffComponent* findComponent(uint16_t crwTagId, uint16_t crwDir) const;
//@} //@}
protected: protected:
@ -399,6 +404,10 @@ namespace Exiv2 {
//@{ //@{
//! Implements add() //! Implements add()
virtual void doAdd(AutoPtr component) =0; virtual void doAdd(AutoPtr component) =0;
//! Implements add(). The default implementation does nothing.
virtual CiffComponent* doAdd(CrwDirs& crwDirs, uint16_t crwTagId);
//! Implements remove(). The default implementation does nothing.
virtual void doRemove(CrwDirs& crwDirs, uint16_t crwTagId);
//! Implements read(). The default implementation reads a directory entry. //! Implements read(). The default implementation reads a directory entry.
virtual void doRead(const byte* pData, virtual void doRead(const byte* pData,
uint32_t size, uint32_t size,
@ -408,8 +417,6 @@ namespace Exiv2 {
virtual uint32_t doWrite(Blob& blob, virtual uint32_t doWrite(Blob& blob,
ByteOrder byteOrder, ByteOrder byteOrder,
uint32_t offset) =0; uint32_t offset) =0;
//! Implements addTag(). The default implementation does nothing.
virtual CiffComponent* doAddTag(CrwDirs& crwDirs, uint16_t crwTag);
//! Set the size of the data area. //! Set the size of the data area.
void setSize(uint32_t size) { size_ = size; } void setSize(uint32_t size) { size_ = size; }
//! Set the offset for this component. //! Set the offset for this component.
@ -425,8 +432,10 @@ namespace Exiv2 {
virtual void doPrint(std::ostream& os, virtual void doPrint(std::ostream& os,
ByteOrder byteOrder, ByteOrder byteOrder,
const std::string& prefix) const; const std::string& prefix) const;
//! Implements empty(). Default implementation returns true if size is 0.
virtual bool doEmpty() const;
//! Implements findComponent(). The default implementation checks the entry. //! Implements findComponent(). The default implementation checks the entry.
virtual CiffComponent* doFindComponent(uint16_t crwTag, virtual CiffComponent* doFindComponent(uint16_t crwTagId,
uint16_t crwDir) const; uint16_t crwDir) const;
//@} //@}
@ -437,7 +446,6 @@ namespace Exiv2 {
uint32_t size_; //!< Size of the data area uint32_t size_; //!< Size of the data area
uint32_t offset_; //!< Offset to the data area from start of dir uint32_t offset_; //!< Offset to the data area from start of dir
const byte* pData_; //!< Pointer to the data area const byte* pData_; //!< Pointer to the data area
bool remove_; //!< If true, the entry should not be written
bool isAllocated_; //!< True if this entry owns the value data bool isAllocated_; //!< True if this entry owns the value data
}; // class CiffComponent }; // class CiffComponent
@ -518,6 +526,10 @@ namespace Exiv2 {
//@{ //@{
// See base class comment // See base class comment
virtual void doAdd(AutoPtr component); virtual void doAdd(AutoPtr component);
// See base class comment
virtual CiffComponent* doAdd(CrwDirs& crwDirs, uint16_t crwTagId);
// See base class comment
virtual void doRemove(CrwDirs& crwDirs, uint16_t crwTagId);
/*! /*!
@brief Implements write(). Writes the complete Ciff directory to @brief Implements write(). Writes the complete Ciff directory to
the blob. the blob.
@ -530,8 +542,6 @@ namespace Exiv2 {
uint32_t size, uint32_t size,
uint32_t start, uint32_t start,
ByteOrder byteOrder); ByteOrder byteOrder);
// See base class comment
virtual CiffComponent* doAddTag(CrwDirs& crwDirs, uint16_t crwTag);
//@} //@}
//! @name Accessors //! @name Accessors
@ -544,8 +554,12 @@ namespace Exiv2 {
virtual void doPrint(std::ostream& os, virtual void doPrint(std::ostream& os,
ByteOrder byteOrder, ByteOrder byteOrder,
const std::string& prefix) const; const std::string& prefix) const;
//! See base class comment. A directory is empty if it has no components.
virtual bool doEmpty() const;
// See base class comment // See base class comment
virtual CiffComponent* doFindComponent(uint16_t crwTag, virtual CiffComponent* doFindComponent(uint16_t crwTagId,
uint16_t crwDir) const; uint16_t crwDir) const;
//@} //@}
@ -555,7 +569,12 @@ namespace Exiv2 {
}; // class CiffDirectory }; // class CiffDirectory
//! This class models the header of a Crw (Canon Raw data) image. /*!
@brief This class models the header of a Crw (Canon Raw data) image. It
is the head of a CIFF parse tree, consisting of CiffDirectory and
CiffEntry objects. Most of its methods will walk the parse tree to
perform the requested action.
*/
class CiffHeader { class CiffHeader {
public: public:
//! CiffHeader auto_ptr type //! CiffHeader auto_ptr type
@ -586,18 +605,24 @@ namespace Exiv2 {
*/ */
void read(const byte* pData, uint32_t size); void read(const byte* pData, uint32_t size);
/*! /*!
@brief Adds an entry for \em crwTag in directory \em crwDir to the @brief Set the value of entry \em crwTagId in directory \em crwDir to
parse tree if it doesn't exist yet. Directories that don't \em buf. If this tag doesn't exist, it is added along with all
exist yet are added along the way. Nothing is added if the tag directories needed.
already exists in the correct directory. Returns the newly
added component.
@param crwTag Tag to be added. @param crwTagId Tag to be added.
@param crwDir Parent directory of the tag. @param crwDir Parent directory of the tag.
@param buf Value to be set.
*/
void add(uint16_t crwTagId, uint16_t crwDir, DataBuf buf);
/*!
@brief Remove entry \em crwTagId in directory \em crwDir from the parse
tree. If it's the last entry in the directory, the directory is
removed as well, etc.
@return The newly added component. @param crwTagId Tag id to be removed.
@param crwDir Parent directory of the tag.
*/ */
CiffComponent* addTag(uint16_t crwTag, uint16_t crwDir); void remove(uint16_t crwTagId, uint16_t crwDir);
//@} //@}
//! Return a pointer to the Canon Crw signature. //! Return a pointer to the Canon Crw signature.
@ -630,10 +655,10 @@ namespace Exiv2 {
//! Return the byte order (little or big endian). //! Return the byte order (little or big endian).
ByteOrder byteOrder() const { return byteOrder_; } ByteOrder byteOrder() const { return byteOrder_; }
/*! /*!
@brief Finds \em crwTag in directory \em crwDir in the parse tree, @brief Finds \em crwTagId in directory \em crwDir in the parse tree,
returning a pointer to the component or 0 if not found. returning a pointer to the component or 0 if not found.
*/ */
CiffComponent* findComponent(uint16_t crwTag, uint16_t crwDir) const; CiffComponent* findComponent(uint16_t crwTagId, uint16_t crwDir) const;
//@} //@}
private: private:
@ -734,8 +759,8 @@ namespace Exiv2 {
static void loadStack(CrwDirs& crwDirs, uint16_t crwDir); static void loadStack(CrwDirs& crwDirs, uint16_t crwDir);
private: private:
//! Return conversion information for one Crw \em dir and \em tagId //! Return conversion information for one \em crwDir and \em crwTagId
static const CrwMapping* crwMapping(uint16_t dir, uint16_t tagId); static const CrwMapping* crwMapping(uint16_t crwDir, uint16_t crwTagId);
/*! /*!
@brief Standard decode function to convert Crw entries to @brief Standard decode function to convert Crw entries to
@ -763,17 +788,11 @@ namespace Exiv2 {
Image& image, Image& image,
ByteOrder byteOrder); ByteOrder byteOrder);
//! Decode Canon Camera Settings 2 //! Decode Canon Camera Settings 1, 2 and Custom Function arrays
static void decode0x102a(const CiffComponent& ciffComponent, static void decodeArray(const CiffComponent& ciffComponent,
const CrwMapping* pCrwMapping, const CrwMapping* pCrwMapping,
Image& image, Image& image,
ByteOrder byteOrder); ByteOrder byteOrder);
//! Decode Canon Camera Settings 1
static void decode0x102d(const CiffComponent& ciffComponent,
const CrwMapping* pCrwMapping,
Image& image,
ByteOrder byteOrder);
//! Decode the date when the picture was taken //! Decode the date when the picture was taken
static void decode0x180e(const CiffComponent& ciffComponent, static void decode0x180e(const CiffComponent& ciffComponent,
@ -821,6 +840,25 @@ namespace Exiv2 {
const CrwMapping* pCrwMapping, const CrwMapping* pCrwMapping,
CiffHeader* pHead); CiffHeader* pHead);
//! Encode Canon Camera Settings 1, 2 and Custom Function arrays
static void encodeArray(const Image& image,
const CrwMapping* pCrwMapping,
CiffHeader* pHead);
//! Encode the date when the picture was taken
static void encode0x180e(const Image& image,
const CrwMapping* pCrwMapping,
CiffHeader* pHead);
//! Encode image width and height
static void encode0x1810(const Image& image,
const CrwMapping* pCrwMapping,
CiffHeader* pHead);
//! Encode the thumbnail image
static void encode0x2008(const Image& image,
const CrwMapping* pCrwMapping,
CiffHeader* pHead);
private: private:
// DATA // DATA
static const CrwMapping crwMapping_[]; //!< Metadata conversion table static const CrwMapping crwMapping_[]; //!< Metadata conversion table
@ -843,6 +881,15 @@ namespace Exiv2 {
//! Check if the file iIo is a Crw image. //! Check if the file iIo is a Crw image.
bool isCrwType(BasicIo& iIo, bool advance); bool isCrwType(BasicIo& iIo, bool advance);
/*!
@brief Pack the tag values of all \em ifdId tags in \em exifData into a
data buffer. This function is used to pack Canon Camera Settings1,2
and Custom Function tags.
*/
DataBuf packIfdId(const ExifData& exifData,
IfdId ifdId,
ByteOrder byteOrder);
} // namespace Exiv2 } // namespace Exiv2
#endif // #ifndef CRWIMAGE_HPP_ #endif // #ifndef CRWIMAGE_HPP_

@ -15,9 +15,10 @@ SHELL = /bin/sh
.PHONY: all test clean distclean maintainer-clean .PHONY: all test clean distclean maintainer-clean
# Add test drivers to this list # Add test drivers to this list
TESTS = addmoddel.sh bugfixes-test.sh exifdata-test.sh exiv2-test.sh ifd-test.sh \ TESTS = addmoddel.sh bugfixes-test.sh crw-test.sh exifdata-test.sh \
imagetest.sh iotest.sh iptctest.sh makernote-test.sh modify-test.sh \ exiv2-test.sh ifd-test.sh imagetest.sh iotest.sh iptctest.sh \
path-test.sh write-test.sh write2-test.sh makernote-test.sh modify-test.sh path-test.sh write-test.sh \
write2-test.sh
test: test:
@list='$(TESTS)'; for p in $$list; do \ @list='$(TESTS)'; for p in $$list; do \

@ -20,34 +20,12 @@ LD_LIBRARY_PATH=../../src:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH export LD_LIBRARY_PATH
binpath="$VALGRIND ../../src" binpath="$VALGRIND ../../src"
cmdfile=cmdfile cmdfile=cmdfile
crwfile=CanonRaw.crw crwfile=exiv2-canon-powershot-s40.crw
cd ./tmp cd ./tmp
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Testcases: Add tags # Testcases: Add and modify tags
# Create an image from scratch with just one tag
# Add one tag to an existing image
# Add a non-CIFF tag
# Add a second tag with the same tag id
# Are new tags created as directory data if possible?
# ----------------------------------------------------------------------
# Testcases: Modify tags
# Modify tag value only
# Change number of components (from directory data to value data)
# Change value type
# Exif.Canon.FirmwareVersion Ascii
# Exif.Canon.OwnerName Ascii
# Exif.Canon.ImageType Ascii
# Exif.Canon.0x0002 Short
# Exif.Canon.CustomFunctions Short
# Exif.Canon.PictureInfo Short
# Exif.Canon.SerialNumber Short
# Exif.Canon.ImageNumber Long
cat > $cmdfile <<EOF cat > $cmdfile <<EOF
set Exif.Photo.ColorSpace 65535 set Exif.Photo.ColorSpace 65535
@ -55,6 +33,8 @@ set Exif.Canon.OwnerName Somebody else's Camera
set Exif.Canon.FirmwareVersion Whatever version set Exif.Canon.FirmwareVersion Whatever version
set Exif.Canon.SerialNumber 1 set Exif.Canon.SerialNumber 1
add Exif.Canon.SerialNumber 2 add Exif.Canon.SerialNumber 2
set Exif.Photo.ISOSpeedRatings 155
set Exif.Photo.DateTimeOriginal 2007:11:11 09:10:11
EOF EOF
cp -f ../data/$crwfile . cp -f ../data/$crwfile .
@ -68,10 +48,17 @@ $binpath/exiv2 -v -pt $crwfile
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Testcases: Delete tags # Testcases: Delete tags
# Delete one tag cat > $cmdfile <<EOF
# Delete one directory completely del Exif.Canon.OwnerName
# Delete all EOF
cp -f ../data/$crwfile .
$binpath/exiv2 -v -pt $crwfile
$binpath/exiv2 -v -m $cmdfile $crwfile
$binpath/crwparse $crwfile
$binpath/exiv2 -v -pt $crwfile
) > $results 2>&1 ) > $results 2>&1

Binary file not shown.
Loading…
Cancel
Save