File indexing completed on 2025-01-05 03:56:26
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2006-09-15 0007 * Description : Exiv2 library interface 0008 * Exiv2: www.exiv2.org 0009 * Exif : www.exif.org/Exif2-2.PDF 0010 * Iptc : www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf 0011 * Xmp : www.adobe.com/devnet/xmp/pdfs/xmp_specification.pdf 0012 * www.iptc.org/std/Iptc4xmpCore/1.0/specification/Iptc4xmpCore_1.0-spec-XMPSchema_8.pdf 0013 * Paper: www.metadataworkinggroup.com/pdf/mwg_guidance.pdf 0014 * 0015 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0016 * SPDX-FileCopyrightText: 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0017 * 0018 * SPDX-License-Identifier: GPL-2.0-or-later 0019 * 0020 * ============================================================ */ 0021 0022 #include "metaengine_p.h" 0023 #include "metaengine_data_p.h" 0024 0025 // Local includes 0026 0027 #include "digikam_debug.h" 0028 #include "digikam_version.h" 0029 0030 #if defined(Q_CC_CLANG) 0031 # pragma clang diagnostic push 0032 # pragma clang diagnostic ignored "-Wdeprecated-declarations" 0033 #endif 0034 0035 namespace Digikam 0036 { 0037 0038 MetaEngine::MetaEngine() 0039 : d(new Private(this)) 0040 { 0041 } 0042 0043 MetaEngine::MetaEngine(const MetaEngine& metadata) 0044 : d(new Private(this)) 0045 { 0046 d->copyPrivateData(metadata.d); 0047 } 0048 0049 MetaEngine::MetaEngine(const MetaEngineData& data) 0050 : d(new Private(this)) 0051 { 0052 setData(data); 0053 } 0054 0055 MetaEngine::MetaEngine(const QString& filePath) 0056 : d(new Private(this)) 0057 { 0058 // NOTE: use dynamic binding as this virtual method can be re-implemented in derived classes. 0059 0060 this->load(filePath); 0061 } 0062 0063 MetaEngine::~MetaEngine() 0064 { 0065 delete d; 0066 } 0067 0068 MetaEngine& MetaEngine::operator=(const MetaEngine& metadata) 0069 { 0070 d->copyPrivateData(metadata.d); 0071 0072 return *this; 0073 } 0074 0075 //-- Statics methods ---------------------------------------------- 0076 0077 bool MetaEngine::initializeExiv2() 0078 { 0079 0080 #ifdef _XMP_SUPPORT_ 0081 0082 if (!Exiv2::XmpParser::initialize()) 0083 { 0084 return false; 0085 } 0086 0087 registerXmpNameSpace(QLatin1String("http://ns.google.com/photos/1.0/container/item_1_/"), 0088 QLatin1String("Item_1_")); // krazy:exclude=insecurenet 0089 registerXmpNameSpace(QLatin1String("http://ns.google.com/photos/1.0/container/item/"), 0090 QLatin1String("Item")); // krazy:exclude=insecurenet 0091 registerXmpNameSpace(QLatin1String("http://ns.apple.com/faceinfo/1.0/"), 0092 QLatin1String("apple-fi")); // krazy:exclude=insecurenet 0093 registerXmpNameSpace(QLatin1String("http://ns.excire.com/foto/1.0/"), 0094 QLatin1String("excire")); // krazy:exclude=insecurenet 0095 0096 /** 0097 * It cleans up memory used by Adobe XMP SDK automatically at application exit. 0098 * See Bug #166424 for details. 0099 */ 0100 ::atexit(Exiv2::XmpParser::terminate); 0101 0102 #endif // _XMP_SUPPORT_ 0103 0104 #if EXIV2_TEST_VERSION(0,27,4) 0105 0106 // For Base Media File Format (aka CR3, HEIF, HEIC, and AVIF) 0107 0108 s_metaEngineSupportBmff = Exiv2::enableBMFF(true); 0109 0110 #endif 0111 0112 return true; 0113 } 0114 0115 bool MetaEngine::supportBmff() 0116 { 0117 return s_metaEngineSupportBmff; 0118 } 0119 0120 bool MetaEngine::supportXmp() 0121 { 0122 0123 #ifdef _XMP_SUPPORT_ 0124 0125 return true; 0126 0127 #else 0128 0129 return false; 0130 0131 #endif // _XMP_SUPPORT_ 0132 0133 } 0134 0135 bool MetaEngine::supportJpegXL() 0136 { 0137 0138 #ifdef EXV_HAVE_BROTLI 0139 0140 return true; 0141 0142 #else 0143 0144 return false; 0145 0146 #endif // EXV_HAVE_BROTLI 0147 0148 } 0149 0150 bool MetaEngine::supportMetadataWriting(const QString& /*typeMime*/) 0151 { 0152 /* FIXME : use Exiv2 API to return right writings support 0153 if (typeMime == QLatin1String("image/jpeg")) 0154 { 0155 return true; 0156 } 0157 else if (typeMime == QLatin1String("image/tiff")) 0158 { 0159 return true; 0160 } 0161 else if (typeMime == QLatin1String("image/png")) 0162 { 0163 return true; 0164 } 0165 else if (typeMime == QLatin1String("image/jp2")) 0166 { 0167 return true; 0168 } 0169 else if (typeMime == QLatin1String("image/x-raw")) 0170 { 0171 return true; 0172 } 0173 else if (typeMime == QLatin1String("image/pgf")) 0174 { 0175 return true; 0176 } 0177 */ 0178 return true; 0179 } 0180 0181 QString MetaEngine::Exiv2Version() 0182 { 0183 0184 #if EXIV2_TEST_VERSION(0,27,0) 0185 0186 return QString::fromStdString(Exiv2::versionString()); 0187 0188 #else 0189 0190 return QLatin1String(Exiv2::version()); 0191 0192 #endif 0193 0194 } 0195 0196 //-- General methods ---------------------------------------------- 0197 0198 MetaEngineData MetaEngine::data() const 0199 { 0200 MetaEngineData data; 0201 data.d = d->data; 0202 0203 return data; 0204 } 0205 0206 void MetaEngine::setData(const MetaEngineData& data) 0207 { 0208 if (data.d) 0209 { 0210 d->data = data.d; 0211 } 0212 else 0213 { 0214 // MetaEngineData can have a null pointer, 0215 // but we never want a null pointer in Private. 0216 d->data->clear(); 0217 } 0218 } 0219 0220 bool MetaEngine::loadFromData(const QByteArray& imgData) 0221 { 0222 if (imgData.isEmpty()) 0223 { 0224 return false; 0225 } 0226 0227 QMutexLocker lock(&s_metaEngineMutex); 0228 0229 try 0230 { 0231 Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((Exiv2::byte*)imgData.data(), imgData.size()); 0232 0233 d->filePath.clear(); 0234 image->readMetadata(); 0235 0236 // Size and mimetype --------------------------------- 0237 0238 d->pixelSize = QSize(image->pixelWidth(), image->pixelHeight()); 0239 d->mimeType = QString::fromStdString(image->mimeType()); 0240 0241 // Image comments --------------------------------- 0242 0243 d->itemComments() = image->comment(); 0244 0245 // Exif metadata ---------------------------------- 0246 0247 d->exifMetadata() = image->exifData(); 0248 0249 // Iptc metadata ---------------------------------- 0250 0251 d->iptcMetadata() = image->iptcData(); 0252 0253 #ifdef _XMP_SUPPORT_ 0254 0255 // Xmp metadata ----------------------------------- 0256 0257 d->xmpMetadata() = image->xmpData(); 0258 0259 #endif // _XMP_SUPPORT_ 0260 0261 return true; 0262 } 0263 catch (Exiv2::AnyError& e) 0264 { 0265 d->printExiv2ExceptionError(QLatin1String("Cannot load metadata with Exiv2:"), e); 0266 } 0267 catch (...) 0268 { 0269 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 0270 } 0271 0272 return false; 0273 } 0274 0275 bool MetaEngine::loadFromDataAndMerge(const QByteArray& imgData, const QStringList& exclude) 0276 { 0277 if (imgData.isEmpty()) 0278 { 0279 return false; 0280 } 0281 0282 QMutexLocker lock(&s_metaEngineMutex); 0283 0284 try 0285 { 0286 Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((Exiv2::byte*)imgData.data(), imgData.size()); 0287 0288 d->filePath.clear(); 0289 image->readMetadata(); 0290 0291 // Size and mimetype --------------------------------- 0292 0293 d->pixelSize = QSize(image->pixelWidth(), image->pixelHeight()); 0294 d->mimeType = QString::fromStdString(image->mimeType()); 0295 0296 // Exif metadata ---------------------------------- 0297 0298 Q_FOREACH (const QString& exTag, exclude) 0299 { 0300 Exiv2::ExifKey exifKey(exTag.toLatin1().constData()); 0301 Exiv2::ExifData::iterator it = image->exifData().findKey(exifKey); 0302 0303 if (it != image->exifData().end()) 0304 { 0305 image->exifData().erase(it); 0306 } 0307 } 0308 0309 ExifMetaEngineMergeHelper exifHelper; 0310 exifHelper.mergeAll(image->exifData(), d->exifMetadata()); 0311 0312 // Iptc metadata ---------------------------------- 0313 0314 IptcMetaEngineMergeHelper iptcHelper; 0315 iptcHelper.mergeAll(image->iptcData(), d->iptcMetadata()); 0316 0317 #ifdef _XMP_SUPPORT_ 0318 0319 // Xmp metadata ----------------------------------- 0320 0321 XmpMetaEngineMergeHelper xmpHelper; 0322 xmpHelper.mergeAll(image->xmpData(), d->xmpMetadata()); 0323 0324 #endif // _XMP_SUPPORT_ 0325 0326 return true; 0327 } 0328 catch (Exiv2::AnyError& e) 0329 { 0330 d->printExiv2ExceptionError(QLatin1String("Cannot load and merge metadata with Exiv2:"), e); 0331 } 0332 catch (...) 0333 { 0334 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 0335 } 0336 0337 return false; 0338 } 0339 0340 bool MetaEngine::isEmpty() const 0341 { 0342 if (!hasComments() && !hasExif() && !hasIptc() && !hasXmp()) 0343 { 0344 return true; 0345 } 0346 0347 return false; 0348 } 0349 0350 QSize MetaEngine::getPixelSize() const 0351 { 0352 return d->pixelSize; 0353 } 0354 0355 QString MetaEngine::getMimeType() const 0356 { 0357 return d->mimeType; 0358 } 0359 0360 void MetaEngine::setWriteWithExifTool(const bool on) 0361 { 0362 d->writeWithExifTool = on; 0363 } 0364 0365 bool MetaEngine::writeWithExifTool() const 0366 { 0367 return d->writeWithExifTool; 0368 } 0369 0370 void MetaEngine::setWriteRawFiles(const bool on) 0371 { 0372 d->writeRawFiles = on; 0373 } 0374 0375 bool MetaEngine::writeRawFiles() const 0376 { 0377 return d->writeRawFiles; 0378 } 0379 0380 void MetaEngine::setWriteDngFiles(const bool on) 0381 { 0382 d->writeDngFiles = on; 0383 } 0384 0385 bool MetaEngine::writeDngFiles() const 0386 { 0387 return d->writeDngFiles; 0388 } 0389 0390 void MetaEngine::setUseXMPSidecar4Reading(const bool on) 0391 { 0392 d->useXMPSidecar4Reading = on; 0393 } 0394 0395 bool MetaEngine::useXMPSidecar4Reading() const 0396 { 0397 return d->useXMPSidecar4Reading; 0398 } 0399 0400 void MetaEngine::setUseCompatibleFileName(const bool on) 0401 { 0402 d->useCompatibleFileName = on; 0403 } 0404 0405 bool MetaEngine::useCompatibleFileName() const 0406 { 0407 return d->useCompatibleFileName; 0408 } 0409 0410 void MetaEngine::setMetadataWritingMode(const int mode) 0411 { 0412 d->metadataWritingMode = mode; 0413 } 0414 0415 int MetaEngine::metadataWritingMode() const 0416 { 0417 return d->metadataWritingMode; 0418 } 0419 0420 void MetaEngine::setUpdateFileTimeStamp(bool on) 0421 { 0422 d->updateFileTimeStamp = on; 0423 } 0424 0425 bool MetaEngine::updateFileTimeStamp() const 0426 { 0427 return d->updateFileTimeStamp; 0428 } 0429 0430 bool MetaEngine::setProgramId() const 0431 { 0432 QString version(digiKamVersion()); 0433 QLatin1String software("digiKam"); 0434 0435 return setItemProgramId(software, version); 0436 } 0437 0438 } // namespace Digikam 0439 0440 #if defined(Q_CC_CLANG) 0441 # pragma clang diagnostic pop 0442 #endif