"Fossies" - the Fresh Open Source Software Archive

Member "rawtherapee-5.7/rtexif/rtexif.h" (10 Sep 2019, 24398 Bytes) of package /linux/misc/rawtherapee-5.7.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "rtexif.h" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.6_vs_5.7.

    1 /*
    2  *  This file is part of RawTherapee.
    3  *
    4  *  Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
    5  *
    6  *  RawTherapee is free software: you can redistribute it and/or modify
    7  *  it under the terms of the GNU General Public License as published by
    8  *  the Free Software Foundation, either version 3 of the License, or
    9  *  (at your option) any later version.
   10  *
   11  *  RawTherapee is distributed in the hope that it will be useful,
   12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  *  GNU General Public License for more details.
   15  *
   16  *  You should have received a copy of the GNU General Public License
   17  *  along with RawTherapee.  If not, see <https://www.gnu.org/licenses/>.
   18  */
   19 #ifndef _MEXIF3_
   20 #define _MEXIF3_
   21 
   22 #include <cmath>
   23 #include <cstdint>
   24 #include <cstdio>
   25 #include <cstdlib>
   26 #include <iomanip>
   27 #include <map>
   28 #include <memory>
   29 #include <sstream>
   30 #include <string>
   31 #include <vector>
   32 
   33 #include <glibmm.h>
   34 
   35 #include "../rtengine/noncopyable.h"
   36 #include "../rtengine/rawmetadatalocation.h"
   37 
   38 namespace rtengine
   39 {
   40 
   41 namespace procparams
   42 {
   43     class ExifPairs;
   44 }
   45 
   46 }
   47 
   48 class CacheImageData;
   49 
   50 namespace rtexif
   51 {
   52 
   53 enum TagType {INVALID = 0, BYTE = 1, ASCII = 2, SHORT = 3, LONG = 4, RATIONAL = 5, SBYTE = 6, UNDEFINED = 7, SSHORT = 8, SLONG = 9, SRATIONAL = 10, FLOAT = 11, DOUBLE = 12, OLYUNDEF = 13, AUTO = 98, SUBDIR = 99};
   54 enum ActionCode {
   55     AC_DONTWRITE,  // don't write it to the output
   56     AC_WRITE,      // write it to the output
   57     AC_SYSTEM,     // changed by RT (not editable/deletable) - don't write, don't show
   58     AC_NEW,        // new addition - write, don't show
   59 
   60     AC_INVALID = 100,  // invalid state
   61 };
   62 enum ByteOrder {UNKNOWN = 0, INTEL = 0x4949, MOTOROLA = 0x4D4D};
   63 #if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
   64 const ByteOrder HOSTORDER = INTEL;
   65 #else
   66 const enum ByteOrder HOSTORDER = MOTOROLA;
   67 #endif
   68 enum MNKind {NOMK, IFD, HEADERIFD, NIKON3, OLYMPUS2, FUJI, TABLESUBDIR};
   69 
   70 bool extractLensInfo (const std::string &fullname, double &minFocal, double &maxFocal, double &maxApertureAtMinFocal, double &maxApertureAtMaxFocal);
   71 
   72 unsigned short sget2 (unsigned char *s, ByteOrder order);
   73 int sget4 (unsigned char *s, ByteOrder order);
   74 unsigned short get2 (FILE* f, ByteOrder order);
   75 int get4 (FILE* f, ByteOrder order);
   76 void sset2 (unsigned short v, unsigned char *s, ByteOrder order);
   77 void sset4 (int v, unsigned char *s, ByteOrder order);
   78 float int_to_float (int i);
   79 short int int2_to_signed (short unsigned int i);
   80 
   81 struct TIFFHeader {
   82 
   83     unsigned short byteOrder;
   84     unsigned short fixed;
   85     unsigned int   ifdOffset;
   86 };
   87 
   88 class Tag;
   89 class Interpreter;
   90 
   91 /// Structure of information describing an Exif tag
   92 struct TagAttrib {
   93     int                 ignore;   // =0: never ignore, =1: always ignore, =2: ignore if the subdir type is reduced image, =-1: end of table
   94     ActionCode          action;
   95     int                 editable;
   96     const  TagAttrib*   subdirAttribs;  // !NULL if this tag points to a subdir
   97     /**  Numeric identifier of tag (or index inside DirectoryTable)
   98          To avoid rewriting all the tables, and to address the problem of TagDirectoryTable with heterogeneous tag's type,
   99          this parameter is now an unsigned int, where the leftmost 2 bytes represent the tag's type, which by default will be aqual
  100          to 0 (INVALID). Only non null tag type will be used. See nikon attrib for an example
  101     */
  102     unsigned short      ID;
  103     TagType             type;
  104     const char*         name;
  105     Interpreter*        interpreter; // Call back hook
  106 };
  107 
  108 const TagAttrib* lookupAttrib (const TagAttrib* dir, const char* field);
  109 
  110 /// A directory of tags
  111 class TagDirectory
  112 {
  113 
  114 protected:
  115     std::vector<Tag*> tags;         // tags in the directory
  116     const TagAttrib*  attribs;      // descriptor table to decode the tags
  117     ByteOrder         order;        // byte order
  118     TagDirectory*     parent;       // parent directory (NULL if root)
  119     static Glib::ustring getDumpKey (int tagID, const Glib::ustring &tagName);
  120 
  121 public:
  122     TagDirectory ();
  123     TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border, bool skipIgnored = true);
  124     TagDirectory (TagDirectory* p, const TagAttrib* ta, ByteOrder border);
  125     virtual ~TagDirectory ();
  126 
  127     inline ByteOrder getOrder      () const
  128     {
  129         return order;
  130     }
  131     TagDirectory*    getParent     ()
  132     {
  133         return parent;
  134     }
  135     TagDirectory*    getRoot       ();
  136     inline int       getCount      () const
  137     {
  138         return tags.size ();
  139     }
  140     const TagAttrib* getAttrib     (int id);
  141     // Find a Tag by scanning the whole tag tree and stopping at the first occurrence
  142     const TagAttrib* getAttrib     (const char* name);
  143     // Try to get the Tag at a given location. 'name' is a path relative to this directory (e.g. "LensInfo/FocalLength")
  144     const TagAttrib* getAttribP    (const char* name);
  145     const TagAttrib* getAttribTable()
  146     {
  147         return attribs;
  148     }
  149     // Find a Tag by scanning the whole tag tree and stopping at the first occurrence
  150     Tag*             getTag        (const char* name) const;
  151     // Try to get the Tag at a given location. 'name' is a path relative to this directory (e.g. "LensInfo/FocalLength")
  152     Tag*             getTagP       (const char* name) const;
  153     Tag*             getTag        (int ID) const;
  154 
  155     // Try to get the Tag in the current directory and in subdirectories
  156     // if lookUpward = true, it will scan the parents TagDirectory up to the root one,
  157     // but w/o looking into their subdirs
  158     virtual Tag*     findTag       (const char* name, bool lookUpward = false) const;
  159     // Find a all Tags with the given name by scanning the whole tag tree
  160     std::vector<const Tag*> findTags (const char* name);
  161     // Find a all Tags with the given ID by scanning the whole tag tree
  162     std::vector<const Tag*> findTags (int ID);
  163     // Try to get the Tag in the current directory and in parent directories
  164     // (won't look into subdirs)
  165     virtual Tag*     findTagUpward (const char* name) const;
  166     bool             getXMPTagValue (const char* name, char* value) const;
  167 
  168     void        keepTag       (int ID);
  169     void        addTag        (Tag* &a);
  170     void        addTagFront   (Tag* &a);
  171     void        replaceTag    (Tag* a);
  172     inline Tag* getTagByIndex (int ix)
  173     {
  174         return tags[ix];
  175     }
  176     inline void      setOrder      (ByteOrder bo)
  177     {
  178         order = bo;
  179     }
  180 
  181     virtual int      calculateSize ();
  182     virtual int      write         (int start, unsigned char* buffer);
  183     virtual TagDirectory* clone    (TagDirectory* parent) const;
  184     void     applyChange   (const std::string &field, const Glib::ustring &value);
  185 
  186     virtual void     printAll      (unsigned  int level = 0) const; // reentrant debug function, keep level=0 on first call !
  187     virtual bool     CPBDump       (const Glib::ustring &commFName, const Glib::ustring &imageFName, const Glib::ustring &profileFName, const Glib::ustring &defaultPParams,
  188                                     const CacheImageData* cfs, const bool flagMode, Glib::KeyFile *keyFile = nullptr, Glib::ustring tagDirName = "") const;
  189     virtual void     sort     ();
  190 };
  191 
  192 // a table of tags: id are offset from beginning and not identifiers
  193 class TagDirectoryTable: public TagDirectory, public rtengine::NonCopyable
  194 {
  195 protected:
  196     unsigned char *values; // Tags values are saved internally here
  197     long           zeroOffset; // Offset 0 (index 0) could be at an offset from values
  198     long           valuesSize; // Size of allocated memory
  199     TagType        defaultType; // Default type of all tags in this directory
  200 public:
  201     TagDirectoryTable();
  202     TagDirectoryTable (TagDirectory* p, unsigned char *v, int memsize, int offs, TagType type, const TagAttrib* ta, ByteOrder border);
  203     TagDirectoryTable (TagDirectory* p, FILE* f, int memsize, int offset, TagType type, const TagAttrib* ta, ByteOrder border);
  204     ~TagDirectoryTable() override;
  205     int calculateSize () override;
  206     int write (int start, unsigned char* buffer) override;
  207     TagDirectory* clone (TagDirectory* parent) const override;
  208 };
  209 
  210 // a class representing a single tag
  211 class Tag :
  212     public rtengine::NonCopyable
  213 {
  214 
  215 protected:
  216     unsigned short tag;
  217     TagType        type;
  218     unsigned int   count;
  219     unsigned char* value;
  220     int            valuesize;
  221     bool           keep;
  222     bool           allocOwnMemory;
  223 
  224     const TagAttrib* attrib;
  225     TagDirectory*    parent;
  226     TagDirectory**   directory;
  227     MNKind           makerNoteKind;
  228     bool             parseMakerNote (FILE* f, int base, ByteOrder bom );
  229 
  230 public:
  231     Tag (TagDirectory* parent, FILE* f, int base);                          // parse next tag from the file
  232     Tag (TagDirectory* parent, const TagAttrib* attr);
  233     Tag (TagDirectory* parent, const TagAttrib* attr, unsigned char *data, TagType t);
  234     Tag (TagDirectory* parent, const TagAttrib* attr, int data, TagType t);  // create a new tag from array (used
  235     Tag (TagDirectory* parent, const TagAttrib* attr, const char* data);  // create a new tag from array (used
  236 
  237     ~Tag ();
  238     void initType        (unsigned char *data, TagType type);
  239     void initInt         (int data, TagType t, int count = 1);
  240     void initUserComment (const Glib::ustring &text);
  241     void initString      (const char* text);
  242     void initSubDir      ();
  243     void initSubDir      (TagDirectory* dir);
  244     void initMakerNote   (MNKind mnk, const TagAttrib* ta);
  245     void initUndefArray  (const char* data, int len);
  246     void initLongArray   (const char* data, int len);
  247     void initRational    (int num, int den);
  248 
  249     static void swapByteOrder2 (unsigned char *buffer, int count);
  250 
  251     // get basic tag properties
  252     int                  getID          () const
  253     {
  254         return tag;
  255     }
  256     int                  getCount       () const
  257     {
  258         return count;
  259     }
  260     TagType              getType        () const
  261     {
  262         return (attrib && attrib->type > INVALID && attrib->type < AUTO) ? attrib->type : type;
  263     }
  264     unsigned char*       getValue       () const
  265     {
  266         return value;
  267     }
  268     signed char*         getSignedValue () const
  269     {
  270         return reinterpret_cast<signed char*> (value);
  271     }
  272     const TagAttrib*     getAttrib      () const
  273     {
  274         return attrib;
  275     }
  276     inline ByteOrder     getOrder       () const
  277     {
  278         return parent ? parent->getOrder() : HOSTORDER;
  279     }
  280     inline TagDirectory* getParent      () const
  281     {
  282         return parent;
  283     }
  284     int                  getValueSize   () const
  285     {
  286         return valuesize;
  287     }
  288     bool                 getOwnMemory   () const
  289     {
  290         return allocOwnMemory;
  291     }
  292 
  293     // read/write value
  294     int     toInt           (int ofs = 0, TagType astype = INVALID) const;
  295     void    fromInt         (int v);
  296     double  toDouble        (int ofs = 0) const;
  297     double* toDoubleArray   (int ofs = 0) const;
  298     void    toRational      (int& num, int& denom, int ofs = 0) const;
  299     void    toString        (char* buffer, int ofs = 0) const;
  300     void    fromString      (const char* v, int size = -1);
  301     void    setInt          (int v, int ofs = 0, TagType astype = LONG);
  302     int     getDistanceFrom (const TagDirectory *root);
  303 
  304     // additional getter/setter for more comfortable use
  305     std::string valueToString         () const;
  306     std::string nameToString          (int i = 0);
  307     void        valueFromString       (const std::string& value);
  308     void        userCommentFromString (const Glib::ustring& text);
  309 
  310     // functions for writing
  311     int  calculateSize ();
  312     int  write         (int offs, int dataOffs, unsigned char* buffer);
  313     Tag* clone         (TagDirectory* parent) const;
  314 
  315     // to control if the tag shall be written
  316     bool getKeep ()
  317     {
  318         return keep;
  319     }
  320     void setKeep (bool k)
  321     {
  322         keep = k;
  323     }
  324 
  325     // get subdirectory (there can be several, the last is NULL)
  326     bool           isDirectory  ()
  327     {
  328         return directory != nullptr;
  329     }
  330     TagDirectory*  getDirectory (int i = 0)
  331     {
  332         return (directory) ? directory[i] : nullptr;
  333     }
  334 
  335     MNKind getMakerNoteFormat ()
  336     {
  337         return makerNoteKind;
  338     }
  339 };
  340 
  341 class ExifManager
  342 {
  343 
  344     Tag* saveCIFFMNTag (TagDirectory* root, int len, const char* name);
  345     void parseCIFF (int length, TagDirectory* root);
  346     void parse (bool isRaw, bool skipIgnored = true);
  347 
  348 public:
  349     FILE* f;
  350     std::unique_ptr<rtengine::RawMetaDataLocation> rml;
  351     ByteOrder order;
  352     bool onlyFirst;  // Only first IFD
  353     unsigned int IFDOffset;
  354     std::vector<TagDirectory*> roots;
  355     std::vector<TagDirectory*> frames;
  356 
  357     ExifManager (FILE* fHandle, std::unique_ptr<rtengine::RawMetaDataLocation> _rml, bool onlyFirstIFD)
  358         : f(fHandle), rml(std::move(_rml)), order(UNKNOWN), onlyFirst(onlyFirstIFD),
  359           IFDOffset(0) {}
  360 
  361     void setIFDOffset(unsigned int offset);
  362 
  363 
  364     void parseRaw (bool skipIgnored = true);
  365     void parseStd (bool skipIgnored = true);
  366     void parseJPEG (int offset = 0); // offset: to extract exif data from a embedded preview/thumbnail
  367     void parseTIFF (bool skipIgnored = true);
  368     void parseCIFF ();
  369 
  370     /// @brief Get default tag for TIFF
  371     /// @param forthis The byte order will be taken from the given directory.
  372     /// @return The ownership of the return tags is passed to the caller.
  373     static std::vector<Tag*> getDefaultTIFFTags (TagDirectory* forthis);
  374     static int    createJPEGMarker (const TagDirectory* root, const rtengine::procparams::ExifPairs& changeList, int W, int H, unsigned char* buffer);
  375     static int    createTIFFHeader (const TagDirectory* root, const rtengine::procparams::ExifPairs& changeList, int W, int H, int bps, const char* profiledata, int profilelen, const char* iptcdata, int iptclen, unsigned char *&buffer, unsigned &bufferSize);
  376     static int createPNGMarker(const TagDirectory *root, const rtengine::procparams::ExifPairs &changeList, int W, int H, int bps, const char *iptcdata, int iptclen, unsigned char *&buffer, unsigned &bufferSize);
  377 };
  378 
  379 class Interpreter
  380 {
  381 public:
  382     Interpreter () {}
  383     virtual ~Interpreter() {};
  384     virtual std::string toString (const Tag* t) const
  385     {
  386         char buffer[1024];
  387         t->toString (buffer);
  388         std::string s (buffer);
  389         std::string::size_type p1 = s.find_first_not_of (' ');
  390 
  391         if ( p1 == std::string::npos ) {
  392             return s;
  393         } else {
  394             return s.substr (p1, s.find_last_not_of (' ') - p1 + 1);
  395         }
  396     }
  397     virtual void fromString (Tag* t, const std::string& value)
  398     {
  399         if (t->getType() == SHORT || t->getType() == LONG) {
  400             t->fromInt (atoi (value.c_str()));
  401         } else {
  402             t->fromString (value.c_str());
  403         }
  404     }
  405     // Get the value as a double
  406     virtual double toDouble (const Tag* t, int ofs = 0)
  407     {
  408 
  409         switch (t->getType()) {
  410             case SBYTE:
  411                 return double (int (t->getSignedValue()[ofs]));
  412 
  413             case BYTE:
  414                 return (double) ((int)t->getValue()[ofs]);
  415 
  416             case ASCII:
  417                 return 0.0;
  418 
  419             case SSHORT:
  420                 return (double)int2_to_signed (sget2 (t->getValue() + ofs, t->getOrder()));
  421 
  422             case SHORT:
  423                 return (double) ((int)sget2 (t->getValue() + ofs, t->getOrder()));
  424 
  425             case SLONG:
  426             case LONG:
  427                 return (double) ((int)sget4 (t->getValue() + ofs, t->getOrder()));
  428 
  429             case SRATIONAL:
  430             case RATIONAL: {
  431                 const double dividend = (int)sget4 (t->getValue() + ofs, t->getOrder());
  432                 const double divisor = (int)sget4 (t->getValue() + ofs + 4, t->getOrder());
  433                 return divisor == 0. ? 0. : dividend / divisor;
  434             }
  435 
  436             case FLOAT:
  437                 return double (sget4 (t->getValue() + ofs, t->getOrder()));
  438 
  439             case UNDEFINED:
  440                 return 0.;
  441 
  442             default:
  443                 return 0.; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR)
  444         }
  445     }
  446     // Get the value as an int
  447     virtual int toInt (const Tag* t, int ofs = 0, TagType astype = INVALID)
  448     {
  449         int a;
  450 
  451         if (astype == INVALID || astype == AUTO) {
  452             astype = t->getType();
  453         }
  454 
  455         switch (astype) {
  456             case SBYTE:
  457                 return int (t->getSignedValue()[ofs]);
  458 
  459             case BYTE:
  460                 return t->getValue()[ofs];
  461 
  462             case ASCII:
  463                 return 0;
  464 
  465             case SSHORT:
  466                 return (int)int2_to_signed (sget2 (t->getValue() + ofs, t->getOrder()));
  467 
  468             case SHORT:
  469                 return (int)sget2 (t->getValue() + ofs, t->getOrder());
  470 
  471             case SLONG:
  472             case LONG:
  473                 return (int)sget4 (t->getValue() + ofs, t->getOrder());
  474 
  475             case SRATIONAL:
  476             case RATIONAL:
  477                 a = (int)sget4 (t->getValue() + ofs + 4, t->getOrder());
  478                 return a == 0 ? 0 : (int)sget4 (t->getValue() + ofs, t->getOrder()) / a;
  479 
  480             case FLOAT:
  481                 return (int)toDouble (t, ofs);
  482 
  483             case UNDEFINED:
  484                 return 0;
  485 
  486             default:
  487                 return 0; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR)
  488         }
  489 
  490         return 0;
  491     }
  492 };
  493 
  494 extern Interpreter stdInterpreter;
  495 
  496 template<typename T = std::uint32_t>
  497 class ChoiceInterpreter : public Interpreter
  498 {
  499 protected:
  500     using Choices = std::map<T, std::string>;
  501     using ChoicesIterator = typename Choices::const_iterator;
  502     Choices choices;
  503 public:
  504     ChoiceInterpreter () {};
  505     std::string toString (const Tag* t) const override
  506     {
  507         const typename std::map<T, std::string>::const_iterator r = choices.find(t->toInt());
  508 
  509         if (r != choices.end()) {
  510             return r->second;
  511         } else {
  512             char buffer[1024];
  513             t->toString(buffer);
  514             return buffer;
  515         }
  516     }
  517 };
  518 
  519 template< class T >
  520 class IntLensInterpreter : public Interpreter
  521 {
  522 protected:
  523     typedef std::multimap< T, std::string> container_t;
  524     typedef typename std::multimap< T, std::string>::const_iterator it_t;
  525     typedef std::pair< T, std::string> p_t;
  526     container_t choices;
  527 
  528     virtual std::string guess (const T lensID, double focalLength, double maxApertureAtFocal, double *lensInfoArray) const
  529     {
  530         it_t r;
  531         size_t nFound = choices.count ( lensID );
  532 
  533         switch ( nFound ) {
  534             case 0: { // lens Unknown
  535                 std::ostringstream s;
  536                 s << lensID;
  537                 return s.str();
  538             }
  539 
  540             case 1: // lens found
  541                 r = choices.find ( lensID );
  542                 return r->second;
  543 
  544             default:
  545                 // More than one hit: we must guess
  546                 break;
  547         }
  548 
  549         std::string bestMatch ("Unknown");
  550         double a1, a2, f1, f2;
  551 
  552         /* FIRST TRY
  553         *
  554         * Get the lens info (min/man focal, min/max aperture) and compare them to the possible choice
  555         */
  556         if (lensInfoArray) {
  557             for ( r = choices.lower_bound ( lensID ); r != choices.upper_bound (lensID); ++r  ) {
  558                 if ( !extractLensInfo ( r->second, f1, f2, a1, a2) ) {
  559                     continue;
  560                 }
  561 
  562                 if (f1 == lensInfoArray[0] && f2 == lensInfoArray[1] && a1 == lensInfoArray[2] && a2 == lensInfoArray[3])
  563                     // can't match better! we take this entry as being the one
  564                 {
  565                     return r->second;
  566                 }
  567             }
  568 
  569             // No lens found, we update the "unknown" string with the lens info values
  570             if (lensInfoArray[0] == lensInfoArray[1]) {
  571                 bestMatch += Glib::ustring::compose (" (%1mm", int (lensInfoArray[0]));
  572             } else {
  573                 bestMatch += Glib::ustring::compose (" (%1-%2mm", int (lensInfoArray[0]), int (lensInfoArray[1]));
  574             }
  575 
  576             if (lensInfoArray[2] == lensInfoArray[3]) {
  577                 bestMatch += Glib::ustring::compose (" f/%1)", Glib::ustring::format (std::fixed, std::setprecision (1), lensInfoArray[2]));
  578             } else
  579                 bestMatch += Glib::ustring::compose (" f/%1-%2)",
  580                                                      Glib::ustring::format (std::fixed, std::setprecision (1), lensInfoArray[2]),
  581                                                      Glib::ustring::format (std::fixed, std::setprecision (1), lensInfoArray[3]));
  582         }
  583 
  584         /* SECOND TRY
  585         *
  586         * Choose the best match: thanks to exiftool by Phil Harvey
  587         * first throws for "out of focal range" and lower or upper aperture of the lens compared to MaxApertureAtFocal
  588         * if the lens is not constant aperture, calculate aprox. aperture of the lens at focalLength
  589         * and compare with actual aperture.
  590         */
  591         std::ostringstream candidates;
  592         double deltaMin = 1000.;
  593 
  594         for ( r = choices.lower_bound ( lensID ); r != choices.upper_bound (lensID); ++r  ) {
  595             double dif;
  596 
  597             if ( !extractLensInfo ( r->second, f1, f2, a1, a2) ) {
  598                 continue;
  599             }
  600 
  601             if ( f1 == 0. || a1 == 0.) {
  602                 continue;
  603             }
  604 
  605             if ( focalLength < f1 - .5 || focalLength > f2 + 0.5 ) {
  606                 continue;
  607             }
  608 
  609             if ( maxApertureAtFocal > 0.1) {
  610                 double lensAperture;
  611 
  612                 if ( maxApertureAtFocal < a1 - 0.15 || maxApertureAtFocal > a2 + 0.15) {
  613                     continue;
  614                 }
  615 
  616                 if ( a1 == a2 || f1 == f2) {
  617                     lensAperture = a1;
  618                 } else {
  619                     lensAperture = exp ( log (a1) + (log (a2) - log (a1)) / (log (f2) - log (f1)) * (log (focalLength) - log (f1)) );
  620                 }
  621 
  622                 dif = std::abs (lensAperture - maxApertureAtFocal);
  623             } else {
  624                 dif = 0;
  625             }
  626 
  627             if ( dif < deltaMin ) {
  628                 deltaMin = dif;
  629                 bestMatch = r->second;
  630             }
  631 
  632             if ( dif < 0.15) {
  633                 if ( candidates.tellp() ) {
  634                     candidates << "\n or " <<  r->second;
  635                 } else {
  636                     candidates <<  r->second;
  637                 }
  638             }
  639         }
  640 
  641         if ( !candidates.tellp() ) {
  642             return bestMatch;
  643         } else {
  644             return candidates.str();
  645         }
  646     }
  647 };
  648 
  649 inline static int getTypeSize ( TagType type )
  650 {
  651     return ("11124811248484"[type < 14 ? type : 0] - '0');
  652 }
  653 
  654 extern const TagAttrib exifAttribs[];
  655 extern const TagAttrib gpsAttribs[];
  656 extern const TagAttrib iopAttribs[];
  657 extern const TagAttrib ifdAttribs[];
  658 extern const TagAttrib nikon2Attribs[];
  659 extern const TagAttrib nikon3Attribs[];
  660 extern const TagAttrib canonAttribs[];
  661 extern const TagAttrib pentaxAttribs[];
  662 extern const TagAttrib pentaxLensDataAttribs[];
  663 extern const TagAttrib pentaxLensInfoQAttribs[];
  664 extern const TagAttrib pentaxLensCorrAttribs[];
  665 extern const TagAttrib pentaxAEInfoAttribs[];
  666 extern const TagAttrib pentaxAEInfo2Attribs[];
  667 extern const TagAttrib pentaxAEInfo3Attribs[];
  668 extern const TagAttrib pentaxCameraSettingsAttribs[];
  669 extern const TagAttrib pentaxFlashInfoAttribs[];
  670 extern const TagAttrib pentaxSRInfoAttribs[];
  671 extern const TagAttrib pentaxSRInfo2Attribs[];
  672 extern const TagAttrib pentaxBatteryInfoAttribs[];
  673 extern const TagAttrib pentaxCameraInfoAttribs[];
  674 extern const TagAttrib fujiAttribs[];
  675 extern const TagAttrib minoltaAttribs[];
  676 extern const TagAttrib sonyAttribs[];
  677 extern const TagAttrib sonyTag9405Attribs[];
  678 extern const TagAttrib sonyCameraInfoAttribs[];
  679 extern const TagAttrib sonyCameraInfo2Attribs[];
  680 extern const TagAttrib sonyCameraSettingsAttribs[];
  681 extern const TagAttrib sonyCameraSettingsAttribs2[];
  682 extern const TagAttrib sonyCameraSettingsAttribs3[];
  683 //extern const TagAttrib sonyDNGMakerNote[];
  684 extern const TagAttrib olympusAttribs[];
  685 extern const TagAttrib kodakIfdAttribs[];
  686 void parseKodakIfdTextualInfo (Tag *textualInfo, Tag* exif);
  687 extern const TagAttrib panasonicAttribs[];
  688 extern const TagAttrib panasonicRawAttribs[];
  689 }
  690 #endif