* Replaced too simple prefix with a more general concept of a makernote header.

* Fixed match() to prefer an exact match over a wildcard match with the same
  number of matching characters (Key "Nikon" now prefers registry entry "Nikon"
  over "Nikon*"), simplified the return value of match() to an int score value.
* Added DEBUG_REGISTRY debug output.
* Code and documentation cleanup.
v0.27.3
Andreas Huggel 21 years ago
parent 0c14d79ad1
commit 29f2e78ce3

@ -20,16 +20,17 @@
*/ */
/* /*
File: makernote.cpp File: makernote.cpp
Version: $Name: $ $Revision: 1.18 $ Version: $Name: $ $Revision: 1.19 $
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net> Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
History: 18-Feb-04, ahu: created History: 18-Feb-04, ahu: created
*/ */
// ***************************************************************************** // *****************************************************************************
#include "rcsid.hpp" #include "rcsid.hpp"
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.18 $ $RCSfile: makernote.cpp,v $") EXIV2_RCSID("@(#) $Name: $ $Revision: 1.19 $ $RCSfile: makernote.cpp,v $")
// Define DEBUG_MAKERNOTE to output debug information to std::cerr // Define DEBUG_* to output debug information to std::cerr
#undef DEBUG_MAKERNOTE #undef DEBUG_MAKERNOTE
#define DEBUG_REGISTRY
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
@ -42,7 +43,7 @@ EXIV2_RCSID("@(#) $Name: $ $Revision: 1.18 $ $RCSfile: makernote.cpp,v $")
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#ifdef DEBUG_MAKERNOTE #if defined DEBUG_MAKERNOTE || defined DEBUG_REGISTRY
# include <iostream> # include <iostream>
#endif #endif
@ -50,6 +51,12 @@ EXIV2_RCSID("@(#) $Name: $ $Revision: 1.18 $ $RCSfile: makernote.cpp,v $")
// class member definitions // class member definitions
namespace Exiv2 { namespace Exiv2 {
MakerNote::MakerNote(const MnTagInfo* pMnTagInfo, bool alloc)
: pMnTagInfo_(pMnTagInfo), alloc_(alloc),
offset_(0), byteOrder_(invalidByteOrder)
{
}
std::string MakerNote::makeKey(uint16 tag) const std::string MakerNote::makeKey(uint16 tag) const
{ {
return std::string(ExifTags::ifdItem(makerIfd)) return std::string(ExifTags::ifdItem(makerIfd))
@ -150,6 +157,14 @@ namespace Exiv2 {
<< tagDesc(tag); << tagDesc(tag);
} // MakerNote::writeMnTagInfo } // MakerNote::writeMnTagInfo
IfdMakerNote::IfdMakerNote(const MakerNote::MnTagInfo* pMnTagInfo,
bool alloc)
: MakerNote(pMnTagInfo, alloc),
absOffset_(true), adjOffset_(0), ifd_(makerIfd, 0, alloc)
{
}
int IfdMakerNote::read(const char* buf, int IfdMakerNote::read(const char* buf,
long len, long len,
ByteOrder byteOrder, ByteOrder byteOrder,
@ -159,24 +174,22 @@ namespace Exiv2 {
offset_ = offset; offset_ = offset;
// Set byte order if none is set yet // Set byte order if none is set yet
if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder; if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder;
int rc = 0; // Read and check the header (and set offset adjustment)
if (!prefix_.empty()) { int rc = readHeader(buf, len, byteOrder);
// Check if makernote is long enough and starts with prefix if (rc == 0) {
if ( len < static_cast<long>(prefix_.size()) rc = checkHeader();
|| prefix_ != std::string(buf, prefix_.size())) rc = 2;
}
if (!absOffset_) {
// Use offsets relative to the start of the Makernote field
offset = 0;
} }
// Adjust the offset
offset = absOffset_ ? offset + adjOffset_ : adjOffset_;
// Read the makernote IFD
if (rc == 0) { if (rc == 0) {
rc = ifd_.read(buf + prefix_.size(), rc = ifd_.read(buf + headerSize(),
len - prefix_.size(), len - headerSize(),
byteOrder_, byteOrder_,
offset + prefix_.size()); offset);
} }
if (rc == 0) { if (rc == 0) {
// IfdMakerNote does not support multiple IFDs // IfdMakerNote currently does not support multiple IFDs
if (ifd_.next() != 0) rc = 3; if (ifd_.next() != 0) rc = 3;
} }
if (rc == 0) { if (rc == 0) {
@ -188,8 +201,8 @@ namespace Exiv2 {
#ifdef DEBUG_MAKERNOTE #ifdef DEBUG_MAKERNOTE
hexdump(std::cerr, buf, len, offset); hexdump(std::cerr, buf, len, offset);
if (rc == 0) ifd_.print(std::cerr); if (rc == 0) ifd_.print(std::cerr);
else std::cerr << "IfdMakerNote::read() failed, rc = " << rc << "\n";
#endif #endif
return rc; return rc;
} // IfdMakerNote::read } // IfdMakerNote::read
@ -199,20 +212,39 @@ namespace Exiv2 {
offset_ = offset; offset_ = offset;
// Set byte order if none is set yet // Set byte order if none is set yet
if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder; if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder;
long len = 0; long len = 0;
if (!prefix_.empty()) { len += copyHeader(buf);
// Write the prefix string to the Makernote buffer len += ifd_.copy(buf + len, byteOrder_, offset_);
memcpy(buf, prefix_.data(), prefix_.size());
len += prefix_.size();
}
if (!absOffset_) {
// Use offsets relative to the start of the Makernote field
offset = 0;
}
len += ifd_.copy(buf + len, byteOrder_, offset + len);
return len; return len;
} // IfdMakerNote::copy } // IfdMakerNote::copy
int IfdMakerNote::readHeader(const char* buf,
long len,
ByteOrder byteOrder)
{
// Default implementation does nothing, assuming there is no header
return 0;
}
int IfdMakerNote::checkHeader() const
{
// Default implementation does nothing, assuming there is no header
return 0;
}
long IfdMakerNote::copyHeader(char* buf) const
{
if (header_.size_ != 0) memcpy(buf, header_.pData_, header_.size_);
return header_.size_;
}
long IfdMakerNote::headerSize() const
{
return header_.size_;
}
Entries::const_iterator IfdMakerNote::findIdx(int idx) const Entries::const_iterator IfdMakerNote::findIdx(int idx) const
{ {
return ifd_.findIdx(idx); return ifd_.findIdx(idx);
@ -220,7 +252,7 @@ namespace Exiv2 {
long IfdMakerNote::size() const long IfdMakerNote::size() const
{ {
return prefix_.size() + ifd_.size() + ifd_.dataSize(); return headerSize() + ifd_.size() + ifd_.dataSize();
} }
MakerNoteFactory* MakerNoteFactory::pInstance_ = 0; MakerNoteFactory* MakerNoteFactory::pInstance_ = 0;
@ -237,7 +269,7 @@ namespace Exiv2 {
const std::string& model, const std::string& model,
CreateFct createMakerNote) CreateFct createMakerNote)
{ {
#ifdef DEBUG_MAKERNOTE #ifdef DEBUG_REGISTRY
std::cerr << "Registering MakerNote create function for \"" std::cerr << "Registering MakerNote create function for \""
<< make << "\" and \"" << model << "\".\n"; << make << "\" and \"" << model << "\".\n";
#endif #endif
@ -276,42 +308,80 @@ namespace Exiv2 {
const std::string& model, const std::string& model,
bool alloc) const bool alloc) const
{ {
#ifdef DEBUG_REGISTRY
std::cerr << "Entering MakerNoteFactory::create(\""
<< make << "\", \"" << model << "\", "
<< (alloc == true ? "true" : "false") << ")\n";
#endif
// loop through each make of the registry to find the best matching make // loop through each make of the registry to find the best matching make
int matchCount = -1; int score = 0;
ModelRegistry* modelRegistry = 0; ModelRegistry* modelRegistry = 0;
#ifdef DEBUG_REGISTRY
std::string makeMatch;
std::cerr << "Searching make registry...\n";
#endif
Registry::const_iterator end1 = registry_.end(); Registry::const_iterator end1 = registry_.end();
Registry::const_iterator pos1; Registry::const_iterator pos1;
for (pos1 = registry_.begin(); pos1 != end1; ++pos1) { for (pos1 = registry_.begin(); pos1 != end1; ++pos1) {
std::pair<bool, int> rc = match(pos1->first, make); int rc = match(pos1->first, make);
if (rc.first && rc.second > matchCount) { if (rc > score) {
matchCount = rc.second; score = rc;
#ifdef DEBUG_REGISTRY
makeMatch = pos1->first;
#endif
modelRegistry = pos1->second; modelRegistry = pos1->second;
} }
} }
if (modelRegistry == 0) return 0; if (modelRegistry == 0) return 0;
#ifdef DEBUG_REGISTRY
std::cerr << "Best match is \"" << makeMatch << "\".\n";
#endif
// loop through each model of the model registry to find the best match // loop through each model of the model registry to find the best match
matchCount = -1; score = 0;
CreateFct createMakerNote = 0; CreateFct createMakerNote = 0;
#ifdef DEBUG_REGISTRY
std::string modelMatch;
std::cerr << "Searching model registry...\n";
#endif
ModelRegistry::const_iterator end2 = modelRegistry->end(); ModelRegistry::const_iterator end2 = modelRegistry->end();
ModelRegistry::const_iterator pos2; ModelRegistry::const_iterator pos2;
for (pos2 = modelRegistry->begin(); pos2 != end2; ++pos2) { for (pos2 = modelRegistry->begin(); pos2 != end2; ++pos2) {
std::pair<bool, int> rc = match(pos2->first, model); int rc = match(pos2->first, model);
if (rc.first && rc.second > matchCount) { if (rc > score) {
matchCount = rc.second; score = rc;
#ifdef DEBUG_REGISTRY
modelMatch = pos2->first;
#endif
createMakerNote = pos2->second; createMakerNote = pos2->second;
} }
} }
if (createMakerNote == 0) return 0; if (createMakerNote == 0) return 0;
#ifdef DEBUG_REGISTRY
std::cerr << "Best match is \"" << modelMatch << "\".\n";
#endif
return createMakerNote(alloc); return createMakerNote(alloc);
} // MakerNoteFactory::create } // MakerNoteFactory::create
std::pair<bool, int> MakerNoteFactory::match(const std::string& regEntry, int MakerNoteFactory::match(const std::string& regEntry,
const std::string& key) const std::string& key)
{ {
#ifdef DEBUG_REGISTRY
std::cerr << " Matching registry entry \"" << regEntry << "\" ("
<< regEntry.size() << ") with key \"" << key << "\" ("
<< key.size() << "): ";
#endif
// Todo: make the comparisons case insensitive // Todo: make the comparisons case insensitive
// Handle exact match (this is only necessary because of the different
// return value - the following algorithm also finds exact matches)
if (regEntry == key) {
#ifdef DEBUG_REGISTRY
std::cerr << "Exact match (score: " << key.size() + 2 << ")\n";
#endif
return key.size() + 2;
}
std::string uKey = key; std::string uKey = key;
std::string uReg = regEntry; std::string uReg = regEntry;
@ -327,7 +397,10 @@ namespace Exiv2 {
uReg.substr(ei) : uReg.substr(ei, pos - ei); uReg.substr(ei) : uReg.substr(ei, pos - ei);
if (ki == std::string::npos) { if (ki == std::string::npos) {
return std::make_pair(false, 0); #ifdef DEBUG_REGISTRY
std::cerr << "Not a match.\n";
#endif
return 0;
} }
bool found = false; bool found = false;
@ -372,15 +445,21 @@ namespace Exiv2 {
count += ss.size(); count += ss.size();
} }
else { else {
return std::make_pair(false, 0); #ifdef DEBUG_REGISTRY
std::cerr << "Not a match.\n";
#endif
return 0;
} }
} // if the substr is not empty } // if the substr is not empty
ei = pos == std::string::npos ? std::string::npos : pos + 1; ei = pos == std::string::npos ? std::string::npos : pos + 1;
} // while ei doesn't point to the end of the registry entry } // while ei doesn't point to the end of the registry entry
return std::make_pair(true, count); #ifdef DEBUG_REGISTRY
std::cerr << "Match (score: " << count + 1 << ")\n";
#endif
return count + 1;
} // MakerNoteFactory::match } // MakerNoteFactory::match

@ -22,7 +22,7 @@
@file makernote.hpp @file makernote.hpp
@brief Contains the Exif %MakerNote interface, IFD %MakerNote and a @brief Contains the Exif %MakerNote interface, IFD %MakerNote and a
MakerNote factory MakerNote factory
@version $Name: $ $Revision: 1.17 $ @version $Name: $ $Revision: 1.18 $
@author Andreas Huggel (ahu) @author Andreas Huggel (ahu)
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a> <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
@date 18-Feb-04, ahu: created @date 18-Feb-04, ahu: created
@ -85,11 +85,17 @@ namespace Exiv2 {
To implement a new IFD makernote, all that you need to do is To implement a new IFD makernote, all that you need to do is
- subclass %IfdMakerNote, - subclass %IfdMakerNote,
- implement the create function, - implement methods to read and check the header (if any) as well as
clone and create functions,
- add a list of tag descriptions and appropriate print functions and - add a list of tag descriptions and appropriate print functions and
- register the camera make/model and create function in the makernote factory. - register the camera make/model and create function in the makernote factory.
. .
See CanonMakerNote for an example. See existing makernote implementations for examples, e.g., CanonMakerNote
or FujiMakerNote.
Finally, the implementation files should be named *mn.[ch]pp, so that the
magic, which ensures that the makernote is automatically registered
in the factory, will pick it up (see mn.sh for details).
*/ */
class MakerNote { class MakerNote {
public: public:
@ -112,21 +118,18 @@ namespace Exiv2 {
allows to choose whether or not memory management is required allows to choose whether or not memory management is required
for the Entries. for the Entries.
*/ */
MakerNote(const MnTagInfo* pMnTagInfo =0, bool alloc =true) explicit MakerNote(const MnTagInfo* pMnTagInfo =0, bool alloc =true);
: pMnTagInfo_(pMnTagInfo), alloc_(alloc),
byteOrder_(invalidByteOrder), offset_(0) {}
//! Virtual destructor. //! Virtual destructor.
virtual ~MakerNote() {} virtual ~MakerNote() {}
//@} //@}
//! @name Manipulators //! @name Manipulators
//@{ //@{
//! Set the byte order (little or big endian).
void setByteOrder(ByteOrder byteOrder) { byteOrder_ = byteOrder; }
/*! /*!
@brief Read the makernote from character buffer buf of length len at @brief Read the makernote, including the makernote header, from
position offset (from the start of the TIFF header) and encoded character buffer buf of length len at position offset (from the
in byte order byteOrder. start of the TIFF header) and encoded in byte order byteOrder.
Return 0 if successful.
*/ */
virtual int read(const char* buf, virtual int read(const char* buf,
long len, long len,
@ -231,13 +234,16 @@ namespace Exiv2 {
False: no memory management needed. False: no memory management needed.
*/ */
const bool alloc_; const bool alloc_;
/*! /*!
@brief Offset of the makernote from the start of the TIFF header
(for offset()).
*/
long offset_;
/*!
@brief Alternative byte order to use, invalid if the byte order of the @brief Alternative byte order to use, invalid if the byte order of the
Exif block can be used Exif block can be used
*/ */
ByteOrder byteOrder_; ByteOrder byteOrder_;
//! Offset of the makernote from the start of the TIFF header
long offset_;
}; // class MakerNote }; // class MakerNote
@ -253,9 +259,8 @@ namespace Exiv2 {
allows to choose whether or not memory management is required allows to choose whether or not memory management is required
for the Entries. for the Entries.
*/ */
IfdMakerNote(const MakerNote::MnTagInfo* pMnTagInfo =0, bool alloc =true) explicit IfdMakerNote(const MakerNote::MnTagInfo* pMnTagInfo =0,
: MakerNote(pMnTagInfo, alloc), bool alloc =true);
absOffset_(true), ifd_(makerIfd, 0, alloc) {}
//! Virtual destructor //! Virtual destructor
virtual ~IfdMakerNote() {} virtual ~IfdMakerNote() {}
//@} //@}
@ -266,6 +271,17 @@ namespace Exiv2 {
long len, long len,
ByteOrder byteOrder, ByteOrder byteOrder,
long offset); long offset);
/*!
@brief Read the makernote header from the makernote databuffer. This
method must set the offset adjustment (adjOffset_), if needed
(assuming that the required information is in the header).
Return 0 if successful.
@note The default implementation does nothing, assuming there is no
header
*/
virtual int readHeader(const char* buf,
long len,
ByteOrder byteOrder);
virtual long copy(char* buf, ByteOrder byteOrder, long offset); virtual long copy(char* buf, ByteOrder byteOrder, long offset);
void add(const Entry& entry) { ifd_.add(entry); } void add(const Entry& entry) { ifd_.add(entry); }
Entries::iterator begin() { return ifd_.begin(); } Entries::iterator begin() { return ifd_.begin(); }
@ -278,7 +294,27 @@ namespace Exiv2 {
Entries::const_iterator end() const { return ifd_.end(); } Entries::const_iterator end() const { return ifd_.end(); }
Entries::const_iterator findIdx(int idx) const; Entries::const_iterator findIdx(int idx) const;
long size() const; long size() const;
virtual MakerNote* clone(bool alloc =true) const =0; /*!
@brief Check the makernote header. This will typically check if a
required prefix string is present in the header. Return 0 if
successful.
@note The default implementation does nothing, assuming there is no
header
*/
virtual int checkHeader() const;
/*!
@brief Write the makernote header to a character buffer, return the
number of characters written.
@note The default implementation copies the header_ buffer.
*/
virtual long copyHeader(char* buf) const;
/*!
@brief Return the size of the makernote header in bytes.
@note The default implementation returns the size of the header_
buffer.
*/
virtual long headerSize() const;
virtual IfdMakerNote* clone(bool alloc =true) const =0;
virtual std::string sectionName(uint16 tag) const =0; virtual std::string sectionName(uint16 tag) const =0;
virtual std::ostream& printTag(std::ostream& os, virtual std::ostream& printTag(std::ostream& os,
uint16 tag, uint16 tag,
@ -287,14 +323,23 @@ namespace Exiv2 {
protected: protected:
// DATA // DATA
//! String prefix at the beginning of the makernote, before the IFD
std::string prefix_;
/*! /*!
@brief True: Offsets are from start of the TIFF header, @brief True: Adjustment of the IFD offsets is to be added to the
False: Offsets are from start of the makernote offset from the start of the TIFF header,
False: Adjustment of the IFD offsets is a suitable absolute
value. Ignore the offset from the start of the TIFF
header.
*/
bool absOffset_;
/*!
@brief Adjustment of the IFD offsets relative to the start of the
TIFF header or to the start of the makernote, depending on
the setting of absOffset_.
*/ */
bool absOffset_; long adjOffset_;
//! MakerNote IFD //! Data buffer for the makernote header
DataBuf header_;
//! The makernote IFD
Ifd ifd_; Ifd ifd_;
}; // class IfdMakerNote }; // class IfdMakerNote
@ -383,15 +428,20 @@ namespace Exiv2 {
@brief Match a registry entry with a key (used for make and model). @brief Match a registry entry with a key (used for make and model).
The matching algorithm is case insensitive and wildcards ('*') in the The matching algorithm is case insensitive and wildcards ('*') in the
registry entry are supported. The best match is the match with the registry entry are supported. The best match is an exact match, then
most matching characters. a match is rated according to the number of matching characters.
@return A pair of which the first component indicates whether or not @return A score value indicating how good the key and registry entry
the key matches and the second component contains the number match. 0 means no match, values greater than 0 indicate a
of matching characters. match, larger values are better matches:<BR>
0: key and registry entry do not match<BR>
1: a pure wildcard match, i.e., the registry entry is just
a wildcard.<BR>
Score values greater than 1 are computed by adding 1 to the
number of matching characters, except for an exact match,
which scores 2 plus the number of matching characters.
*/ */
static std::pair<bool, int> match(const std::string& regEntry, static int match(const std::string& regEntry, const std::string& key);
const std::string& key);
private: private:
//! @name Creators //! @name Creators

Loading…
Cancel
Save