File indexing completed on 2024-04-21 15:22:25

0001 /*
0002     SPDX-FileCopyrightText: 2006-2015 Gilles Caulier <caulier dot gilles at gmail dot com>
0003     SPDX-FileCopyrightText: 2006-2012 Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #ifndef KEXIV2PRIVATE_H
0009 #define KEXIV2PRIVATE_H
0010 
0011 #include "kexiv2.h"
0012 
0013  // C++ includes
0014 
0015 #include <cstdlib>
0016 #include <cstdio>
0017 #include <cassert>
0018 #include <cmath>
0019 #include <cfloat>
0020 #include <iostream>
0021 #include <iomanip>
0022 #include <string>
0023 
0024 // Qt includes
0025 
0026 #include <QFile>
0027 #include <QSize>
0028 #include <QLatin1String>
0029 #include <QFileInfo>
0030 #include <QSharedData>
0031 
0032 // Exiv2 includes -------------------------------------------------------
0033 
0034 // NOTE: All Exiv2 header must be stay there to not expose external source code to Exiv2 API
0035 //       and reduce Exiv2 dependency to client code.
0036 
0037 // The pragmas are required to be able to catch exceptions thrown by libexiv2:
0038 // See https://gcc.gnu.org/wiki/Visibility, the section about c++ exceptions.
0039 // They are needed for all libexiv2 versions that do not care about visibility.
0040 #ifdef __GNUC__
0041 #pragma GCC visibility push(default)
0042 #endif
0043 
0044 #include <exiv2/exiv2.hpp>
0045 
0046 // Check if Exiv2 support XMP
0047 
0048 #ifdef EXV_HAVE_XMP_TOOLKIT
0049 #   define _XMP_SUPPORT_ 1
0050 #endif
0051 
0052 // With exiv2 > 0.20.0, all makernote header files have been removed to increase binary compatibility.
0053 // See Exiv2 bugzilla entry https://dev.exiv2.org/issues/719
0054 // and wiki topic           https://dev.exiv2.org/boards/3/topics/583
0055 
0056 #ifdef __GNUC__
0057 #pragma GCC visibility pop
0058 #endif
0059 
0060 // End of Exiv2 headers ------------------------------------------------------
0061 
0062 namespace KExiv2Iface
0063 {
0064 
0065 class KExiv2DataPrivate : public QSharedData
0066 {
0067 public:
0068 
0069     void clear();
0070 
0071 public:
0072 
0073     std::string     imageComments;
0074 
0075     Exiv2::ExifData exifMetadata;
0076 
0077     Exiv2::IptcData iptcMetadata;
0078 
0079 #ifdef _XMP_SUPPORT_
0080     Exiv2::XmpData  xmpMetadata;
0081 #endif
0082 };
0083 
0084 // --------------------------------------------------------------------------
0085 
0086 class KExiv2Private
0087 {
0088 public:
0089 
0090     KExiv2Private();
0091     ~KExiv2Private();
0092 
0093     void copyPrivateData(const KExiv2Private* const other);
0094 
0095     bool saveToXMPSidecar(const QFileInfo& finfo)                            const;
0096     bool saveToFile(const QFileInfo& finfo)                                  const;
0097 #if EXIV2_TEST_VERSION(0,28,0)
0098     bool saveOperations(const QFileInfo& finfo, Exiv2::Image::UniquePtr image) const;
0099 #else
0100     bool saveOperations(const QFileInfo& finfo, Exiv2::Image::AutoPtr image) const;
0101 #endif
0102 
0103     /** Wrapper method to convert a Comments content to a QString.
0104      */
0105     QString convertCommentValue(const Exiv2::Exifdatum& exifDatum) const;
0106 
0107     /** Charset autodetection to convert a string to a QString.
0108      */
0109     QString detectEncodingAndDecode(const std::string& value)      const;
0110 
0111     /** UTF8 autodetection from a string.
0112      */
0113     bool isUtf8(const char* const buffer)                          const;
0114 
0115     int getXMPTagsListFromPrefix(const QString& pf, KExiv2::TagsMap& tagsMap) const;
0116 
0117     const Exiv2::ExifData& exifMetadata()  const { return data.constData()->exifMetadata;  }
0118     const Exiv2::IptcData& iptcMetadata()  const { return data.constData()->iptcMetadata;  }
0119     const std::string&     imageComments() const { return data.constData()->imageComments; }
0120 
0121 #ifdef _XMP_SUPPORT_
0122     const Exiv2::XmpData&  xmpMetadata()   const { return data.constData()->xmpMetadata;   }
0123 #endif
0124 
0125     Exiv2::ExifData&       exifMetadata()        { return data.data()->exifMetadata;       }
0126     Exiv2::IptcData&       iptcMetadata()        { return data.data()->iptcMetadata;       }
0127     std::string&           imageComments()       { return data.data()->imageComments;      }
0128 
0129 #ifdef _XMP_SUPPORT_
0130     Exiv2::XmpData&        xmpMetadata()         { return data.data()->xmpMetadata;        }
0131 
0132 #if EXIV2_TEST_VERSION(0,28,0)
0133     void loadSidecarData(Exiv2::Image::UniquePtr xmpsidecar);
0134 #else
0135     void loadSidecarData(Exiv2::Image::AutoPtr xmpsidecar);
0136 #endif
0137 #endif
0138 
0139 public:
0140 
0141     /** Generic method to print the Exiv2 C++ Exception error message from 'e'.
0142      *  'msg' string is printed using kDebug rules..
0143      */
0144     static void printExiv2ExceptionError(const QString& msg, Exiv2::Error& e);
0145 
0146     /** Generic method to print debug message from Exiv2.
0147      *  'msg' string is printed using kDebug rules. 'lvl' is the debug level of Exiv2 message.
0148      */
0149     static void printExiv2MessageHandler(int lvl, const char* msg);
0150 
0151 public:
0152 
0153     bool                                           writeRawFiles;
0154     bool                                           updateFileTimeStamp;
0155 
0156     bool                                           useXMPSidecar4Reading;
0157 
0158     /// A mode from #MetadataWritingMode enum.
0159     int                                            metadataWritingMode;
0160 
0161     /// XMP, and parts of EXIF/IPTC, were loaded from an XMP sidecar file
0162     bool                                           loadedFromSidecar;
0163 
0164     QString                                        filePath;
0165     QSize                                          pixelSize;
0166     QString                                        mimeType;
0167 
0168     QSharedDataPointer<KExiv2DataPrivate> data;
0169 };
0170 
0171 // --------------------------------------------------------------------------------------------
0172 
0173 template <class Data, class Key, class KeyString, class KeyStringList = QList<KeyString> >
0174 
0175 class MergeHelper
0176 {
0177 public:
0178 
0179     KeyStringList keys;
0180 
0181     MergeHelper& operator<<(const KeyString& key)
0182     {
0183         keys << key;
0184         return *this;
0185     }
0186 
0187     /**
0188       * Merge two (Exif,IPTC,Xmp)Data packages, where the result is stored in dest
0189       * and fields from src take precedence over existing data from dest.
0190       */
0191     void mergeAll(const Data& src, Data& dest)
0192     {
0193         for (typename Data::const_iterator it = src.begin(); it != src.end(); ++it)
0194         {
0195             typename Data::iterator destIt = dest.findKey(Key(it->key()));
0196 
0197             if (destIt == dest.end())
0198             {
0199                 dest.add(*it);
0200             }
0201             else
0202             {
0203                 *destIt = *it;
0204             }
0205         }
0206     }
0207 
0208     /**
0209      * Merge two (Exif,IPTC,Xmp)Data packages, the result is stored in dest.
0210      * Only keys in keys are considered for merging.
0211      * Fields from src take precedence over existing data from dest.
0212      */
0213     void mergeFields(const Data& src, Data& dest) const
0214     {
0215         for (const KeyString& keyString : keys)
0216         {
0217             Key key(keyString.latin1());
0218             typename Data::const_iterator it = src.findKey(key);
0219 
0220             if (it == src.end())
0221             {
0222                 continue;
0223             }
0224 
0225             typename Data::iterator destIt = dest.findKey(key);
0226 
0227             if (destIt == dest.end())
0228             {
0229                 dest.add(*it);
0230             }
0231             else
0232             {
0233                 *destIt = *it;
0234             }
0235         }
0236     }
0237 
0238     /**
0239      * Merge two (Exif,IPTC,Xmp)Data packages, the result is stored in dest.
0240      * The following steps apply only to keys in "keys":
0241      * The result is determined by src.
0242      * Keys must exist in src to kept in dest.
0243      * Fields from src take precedence over existing data from dest.
0244      */
0245     void exclusiveMerge(const Data& src, Data& dest) const
0246     {
0247         for (const KeyString& keyString : keys)
0248         {
0249             Key key(keyString.latin1());
0250             typename Data::const_iterator it = src.findKey(key);
0251             typename Data::iterator destIt = dest.findKey(key);
0252 
0253             if (destIt == dest.end())
0254             {
0255                 if (it != src.end())
0256                 {
0257                     dest.add(*it);
0258                 }
0259             }
0260             else
0261             {
0262                 if (it == src.end())
0263                 {
0264                     dest.erase(destIt);
0265                 }
0266                 else
0267                 {
0268                     *destIt = *it;
0269                 }
0270             }
0271         }
0272     }
0273 };
0274 
0275 class ExifMergeHelper : public MergeHelper<Exiv2::ExifData, Exiv2::ExifKey, QLatin1String>
0276 {
0277 };
0278 
0279 class IptcMergeHelper : public MergeHelper<Exiv2::IptcData, Exiv2::IptcKey, QLatin1String>
0280 {
0281 };
0282 
0283 #ifdef _XMP_SUPPORT_
0284 class XmpMergeHelper : public MergeHelper<Exiv2::XmpData, Exiv2::XmpKey, QLatin1String>
0285 {
0286 };
0287 #endif
0288 
0289 }  // NameSpace KExiv2Iface
0290 
0291 #endif // KEXIV2PRIVATE_H