File indexing completed on 2024-05-05 12:57:36
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