File indexing completed on 2025-01-19 03:56:01
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 * Common metadata image information manipulation methods 0009 * 0010 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "metaengine_p.h" 0018 0019 // Qt includes 0020 0021 #include <QBuffer> 0022 0023 // Local includes 0024 0025 #include "metaengine_rotation.h" 0026 #include "digikam_debug.h" 0027 0028 #if defined(Q_CC_CLANG) 0029 # pragma clang diagnostic push 0030 # pragma clang diagnostic ignored "-Wdeprecated-declarations" 0031 #endif 0032 0033 namespace Digikam 0034 { 0035 0036 bool MetaEngine::setItemProgramId(const QString& program, const QString& version) const 0037 { 0038 QMutexLocker lock(&s_metaEngineMutex); 0039 0040 try 0041 { 0042 QString software(program); 0043 software.append(QLatin1Char('-')); 0044 software.append(version); 0045 0046 // Set program info into Exif.Image.ProcessingSoftware tag (only available with Exiv2 >= 0.14.0). 0047 0048 d->exifMetadata()["Exif.Image.ProcessingSoftware"] = std::string(software.toLatin1().constData()); 0049 0050 // See B.K.O #142564: Check if Exif.Image.Software already exist. If yes, do not touch this tag. 0051 0052 if (!d->exifMetadata().empty()) 0053 { 0054 Exiv2::ExifData exifData(d->exifMetadata()); 0055 Exiv2::ExifKey key("Exif.Image.Software"); 0056 Exiv2::ExifData::const_iterator it = exifData.findKey(key); 0057 0058 if (it == exifData.end()) 0059 { 0060 d->exifMetadata()["Exif.Image.Software"] = std::string(software.toLatin1().constData()); 0061 } 0062 } 0063 0064 // set program info into XMP tags. 0065 0066 #ifdef _XMP_SUPPORT_ 0067 0068 if (!d->xmpMetadata().empty()) 0069 { 0070 // Only create Xmp.xmp.CreatorTool if it do not exist. 0071 Exiv2::XmpData xmpData(d->xmpMetadata()); 0072 Exiv2::XmpKey key("Xmp.xmp.CreatorTool"); 0073 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0074 0075 if (it == xmpData.end()) 0076 { 0077 setXmpTagString("Xmp.xmp.CreatorTool", software); 0078 } 0079 } 0080 0081 setXmpTagString("Xmp.tiff.Software", software); 0082 0083 #endif // _XMP_SUPPORT_ 0084 0085 // Set program info into IPTC tags. 0086 0087 d->iptcMetadata()["Iptc.Application2.Program"] = std::string(program.toLatin1().constData()); 0088 d->iptcMetadata()["Iptc.Application2.ProgramVersion"] = std::string(version.toLatin1().constData()); 0089 0090 return true; 0091 } 0092 catch (Exiv2::AnyError& e) 0093 { 0094 d->printExiv2ExceptionError(QLatin1String("Cannot set Program identity into image with Exiv2:"), e); 0095 } 0096 catch (...) 0097 { 0098 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 0099 } 0100 0101 return false; 0102 } 0103 0104 QSize MetaEngine::getItemDimensions() const 0105 { 0106 QMutexLocker lock(&s_metaEngineMutex); 0107 0108 try 0109 { 0110 long width = -1; 0111 long height = -1; 0112 0113 // Try to get Exif.Photo tags 0114 0115 Exiv2::ExifData exifData(d->exifMetadata()); 0116 Exiv2::ExifKey key("Exif.Photo.PixelXDimension"); 0117 Exiv2::ExifData::const_iterator it = exifData.findKey(key); 0118 0119 if ((it != exifData.end()) && it->count()) 0120 { 0121 #if EXIV2_TEST_VERSION(0,27,99) 0122 width = it->toInt64(); 0123 #else 0124 width = it->toLong(); 0125 #endif 0126 } 0127 0128 Exiv2::ExifKey key2("Exif.Photo.PixelYDimension"); 0129 Exiv2::ExifData::const_iterator it2 = exifData.findKey(key2); 0130 0131 if ((it2 != exifData.end()) && it2->count()) 0132 { 0133 #if EXIV2_TEST_VERSION(0,27,99) 0134 height = it2->toInt64(); 0135 #else 0136 height = it2->toLong(); 0137 #endif 0138 } 0139 0140 if ((width != -1) && (height != -1)) 0141 { 0142 return QSize(width, height); 0143 } 0144 0145 // Try to get Exif.Image tags 0146 0147 width = -1; 0148 height = -1; 0149 0150 Exiv2::ExifKey key3("Exif.Image.ImageWidth"); 0151 Exiv2::ExifData::const_iterator it3 = exifData.findKey(key3); 0152 0153 if ((it3 != exifData.end()) && it3->count()) 0154 { 0155 #if EXIV2_TEST_VERSION(0,27,99) 0156 width = it3->toInt64(); 0157 #else 0158 width = it3->toLong(); 0159 #endif 0160 } 0161 0162 Exiv2::ExifKey key4("Exif.Image.ImageLength"); 0163 Exiv2::ExifData::const_iterator it4 = exifData.findKey(key4); 0164 0165 if ((it4 != exifData.end()) && it4->count()) 0166 { 0167 #if EXIV2_TEST_VERSION(0,27,99) 0168 height = it4->toInt64(); 0169 #else 0170 height = it4->toLong(); 0171 #endif 0172 } 0173 0174 if ((width != -1) && (height != -1)) 0175 { 0176 return QSize(width, height); 0177 } 0178 0179 #ifdef _XMP_SUPPORT_ 0180 0181 // Try to get Xmp.tiff tags 0182 0183 width = -1; 0184 height = -1; 0185 bool wOk = false; 0186 bool hOk = false; 0187 0188 QString str = getXmpTagString("Xmp.tiff.ImageWidth"); 0189 0190 if (!str.isEmpty()) 0191 { 0192 width = str.toInt(&wOk); 0193 } 0194 0195 str = getXmpTagString("Xmp.tiff.ImageLength"); 0196 0197 if (!str.isEmpty()) 0198 { 0199 height = str.toInt(&hOk); 0200 } 0201 0202 if (wOk && hOk) 0203 { 0204 return QSize(width, height); 0205 } 0206 0207 // Try to get Xmp.exif tags 0208 0209 width = -1; 0210 height = -1; 0211 wOk = false; 0212 hOk = false; 0213 0214 str = getXmpTagString("Xmp.exif.PixelXDimension"); 0215 0216 if (!str.isEmpty()) 0217 { 0218 width = str.toInt(&wOk); 0219 } 0220 0221 str = getXmpTagString("Xmp.exif.PixelYDimension"); 0222 0223 if (!str.isEmpty()) 0224 { 0225 height = str.toInt(&hOk); 0226 } 0227 0228 if (wOk && hOk) 0229 { 0230 return QSize(width, height); 0231 } 0232 0233 #endif // _XMP_SUPPORT_ 0234 0235 } 0236 catch (Exiv2::AnyError& e) 0237 { 0238 d->printExiv2ExceptionError(QLatin1String("Cannot parse image dimensions tag with Exiv2:"), e); 0239 } 0240 catch (...) 0241 { 0242 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 0243 } 0244 0245 return QSize(); 0246 } 0247 0248 bool MetaEngine::setItemDimensions(const QSize& size) const 0249 { 0250 QMutexLocker lock(&s_metaEngineMutex); 0251 0252 try 0253 { 0254 // Set Exif values. 0255 0256 // NOTE: see B.K.O #144604: need to cast to record an unsigned integer value. 0257 0258 d->exifMetadata()["Exif.Image.ImageWidth"] = static_cast<uint32_t>(size.width()); 0259 d->exifMetadata()["Exif.Image.ImageLength"] = static_cast<uint32_t>(size.height()); 0260 d->exifMetadata()["Exif.Photo.PixelXDimension"] = static_cast<uint32_t>(size.width()); 0261 d->exifMetadata()["Exif.Photo.PixelYDimension"] = static_cast<uint32_t>(size.height()); 0262 0263 // Set Xmp values. 0264 0265 #ifdef _XMP_SUPPORT_ 0266 0267 setXmpTagString("Xmp.tiff.ImageWidth", QString::number(size.width())); 0268 setXmpTagString("Xmp.tiff.ImageLength", QString::number(size.height())); 0269 setXmpTagString("Xmp.exif.PixelXDimension", QString::number(size.width())); 0270 setXmpTagString("Xmp.exif.PixelYDimension", QString::number(size.height())); 0271 0272 #endif // _XMP_SUPPORT_ 0273 0274 return true; 0275 } 0276 catch (Exiv2::AnyError& e) 0277 { 0278 d->printExiv2ExceptionError(QLatin1String("Cannot set image dimensions with Exiv2:"), e); 0279 } 0280 catch (...) 0281 { 0282 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 0283 } 0284 0285 return false; 0286 } 0287 0288 MetaEngine::ImageOrientation MetaEngine::getItemOrientation() const 0289 { 0290 QMutexLocker lock(&s_metaEngineMutex); 0291 0292 try 0293 { 0294 Exiv2::ExifData exifData(d->exifMetadata()); 0295 Exiv2::ExifData::iterator it; 0296 long orientation; 0297 ImageOrientation imageOrient = ORIENTATION_NORMAL; 0298 0299 // -- Standard Xmp tag -------------------------------- 0300 0301 #ifdef _XMP_SUPPORT_ 0302 0303 bool ok = false; 0304 QString str = getXmpTagString("Xmp.tiff.Orientation"); 0305 0306 if (!str.isEmpty()) 0307 { 0308 orientation = str.toLong(&ok); 0309 0310 if (ok) 0311 { 0312 //qCDebug(DIGIKAM_METAENGINE_LOG) << "Orientation => Xmp.tiff.Orientation =>" << (int)orientation; 0313 0314 return (ImageOrientation)orientation; 0315 } 0316 } 0317 0318 #endif // _XMP_SUPPORT_ 0319 0320 // Because some camera set a wrong standard exif orientation tag, 0321 // We need to check makernote tags in first! 0322 0323 // -- Minolta Cameras ---------------------------------- 0324 0325 Exiv2::ExifKey minoltaKey1("Exif.MinoltaCs7D.Rotation"); 0326 it = exifData.findKey(minoltaKey1); 0327 0328 if ((it != exifData.end()) && it->count()) 0329 { 0330 #if EXIV2_TEST_VERSION(0,27,99) 0331 orientation = it->toInt64(); 0332 #else 0333 orientation = it->toLong(); 0334 #endif 0335 0336 //qCDebug(DIGIKAM_METAENGINE_LOG) << "Orientation => Exif.MinoltaCs7D.Rotation =>" << (int)orientation; 0337 0338 switch (orientation) 0339 { 0340 case 76: 0341 { 0342 imageOrient = ORIENTATION_ROT_90; 0343 break; 0344 } 0345 0346 case 82: 0347 { 0348 imageOrient = ORIENTATION_ROT_270; 0349 break; 0350 } 0351 } 0352 0353 return imageOrient; 0354 } 0355 0356 Exiv2::ExifKey minoltaKey2("Exif.MinoltaCs5D.Rotation"); 0357 it = exifData.findKey(minoltaKey2); 0358 0359 if ((it != exifData.end()) && it->count()) 0360 { 0361 #if EXIV2_TEST_VERSION(0,27,99) 0362 orientation = it->toInt64(); 0363 #else 0364 orientation = it->toLong(); 0365 #endif 0366 0367 //qCDebug(DIGIKAM_METAENGINE_LOG) << "Orientation => Exif.MinoltaCs5D.Rotation =>" << (int)orientation; 0368 0369 switch (orientation) 0370 { 0371 case 76: 0372 { 0373 imageOrient = ORIENTATION_ROT_90; 0374 break; 0375 } 0376 0377 case 82: 0378 { 0379 imageOrient = ORIENTATION_ROT_270; 0380 break; 0381 } 0382 } 0383 0384 return imageOrient; 0385 } 0386 0387 // -- Standard Exif tag -------------------------------- 0388 0389 Exiv2::ExifKey keyStd("Exif.Image.Orientation"); 0390 it = exifData.findKey(keyStd); 0391 0392 if ((it != exifData.end()) && it->count()) 0393 { 0394 #if EXIV2_TEST_VERSION(0,27,99) 0395 orientation = it->toInt64(); 0396 #else 0397 orientation = it->toLong(); 0398 #endif 0399 0400 //qCDebug(DIGIKAM_METAENGINE_LOG) << "Orientation => Exif.Image.Orientation =>" << (int)orientation; 0401 0402 return (ImageOrientation)orientation; 0403 } 0404 0405 } 0406 catch (Exiv2::AnyError& e) 0407 { 0408 d->printExiv2ExceptionError(QLatin1String("Cannot parse Exif Orientation tag with Exiv2:"), e); 0409 } 0410 catch (...) 0411 { 0412 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 0413 } 0414 0415 return ORIENTATION_UNSPECIFIED; 0416 } 0417 0418 bool MetaEngine::setItemOrientation(ImageOrientation orientation) const 0419 { 0420 QMutexLocker lock(&s_metaEngineMutex); 0421 0422 try 0423 { 0424 if (orientation < ORIENTATION_UNSPECIFIED || orientation > ORIENTATION_ROT_270) 0425 { 0426 qCDebug(DIGIKAM_METAENGINE_LOG) << "Image orientation value is not correct!"; 0427 return false; 0428 } 0429 0430 // Set Exif values. 0431 0432 d->exifMetadata()["Exif.Image.Orientation"] = static_cast<uint16_t>(orientation); 0433 qCDebug(DIGIKAM_METAENGINE_LOG) << "Exif.Image.Orientation tag set to:" << (int)orientation; 0434 0435 // Set Xmp values. 0436 0437 #ifdef _XMP_SUPPORT_ 0438 0439 setXmpTagString("Xmp.tiff.Orientation", QString::number((int)orientation)); 0440 0441 #endif // _XMP_SUPPORT_ 0442 0443 // -- Minolta/Sony Cameras ---------------------------------- 0444 0445 // Minolta and Sony camera store image rotation in Makernote. 0446 // We remove these information to prevent duplicate values. 0447 0448 Exiv2::ExifData::iterator it; 0449 0450 Exiv2::ExifKey minoltaKey1("Exif.MinoltaCs7D.Rotation"); 0451 it = d->exifMetadata().findKey(minoltaKey1); 0452 0453 if (it != d->exifMetadata().end()) 0454 { 0455 d->exifMetadata().erase(it); 0456 qCDebug(DIGIKAM_METAENGINE_LOG) << "Removing Exif.MinoltaCs7D.Rotation tag"; 0457 } 0458 0459 Exiv2::ExifKey minoltaKey2("Exif.MinoltaCs5D.Rotation"); 0460 it = d->exifMetadata().findKey(minoltaKey2); 0461 0462 if (it != d->exifMetadata().end()) 0463 { 0464 d->exifMetadata().erase(it); 0465 qCDebug(DIGIKAM_METAENGINE_LOG) << "Removing Exif.MinoltaCs5D.Rotation tag"; 0466 } 0467 0468 // -- Exif embedded thumbnail ---------------------------------- 0469 0470 Exiv2::ExifKey thumbKey("Exif.Thumbnail.Orientation"); 0471 it = d->exifMetadata().findKey(thumbKey); 0472 0473 if ((it != d->exifMetadata().end()) && it->count()) 0474 { 0475 (*it) = static_cast<uint16_t>(orientation); 0476 } 0477 0478 return true; 0479 } 0480 catch (Exiv2::AnyError& e) 0481 { 0482 d->printExiv2ExceptionError(QLatin1String("Cannot set Exif Orientation tag with Exiv2:"), e); 0483 } 0484 catch (...) 0485 { 0486 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 0487 } 0488 0489 return false; 0490 } 0491 0492 MetaEngine::ImageColorWorkSpace MetaEngine::getItemColorWorkSpace() const 0493 { 0494 // Check Exif values. 0495 0496 long exifColorSpace = -1; 0497 0498 if (!getExifTagLong("Exif.Photo.ColorSpace", exifColorSpace)) 0499 { 0500 #ifdef _XMP_SUPPORT_ 0501 0502 QVariant var = getXmpTagVariant("Xmp.exif.ColorSpace"); 0503 0504 if (!var.isNull()) 0505 { 0506 exifColorSpace = var.toInt(); 0507 } 0508 0509 #endif // _XMP_SUPPORT_ 0510 } 0511 0512 if (exifColorSpace == 1) 0513 { 0514 return WORKSPACE_SRGB; // as specified by standard 0515 } 0516 else if (exifColorSpace == 2) 0517 { 0518 return WORKSPACE_ADOBERGB; // not in the standard! 0519 } 0520 else 0521 { 0522 if (exifColorSpace == 65535) 0523 { 0524 // A lot of cameras set the Exif.Iop.InteroperabilityIndex, 0525 // as documented for ExifTool 0526 0527 QString interopIndex = getExifTagString("Exif.Iop.InteroperabilityIndex"); 0528 0529 if (!interopIndex.isNull()) 0530 { 0531 if (interopIndex == QLatin1String("R03")) 0532 { 0533 return WORKSPACE_ADOBERGB; 0534 } 0535 else if (interopIndex == QLatin1String("R98")) 0536 { 0537 return WORKSPACE_SRGB; 0538 } 0539 } 0540 } 0541 0542 // Note: Text EXIF ColorSpace tag may just not be present (NEF files) 0543 0544 // Nikon camera set Exif.Photo.ColorSpace to uncalibrated or just skip this field, 0545 // then add additional information into the makernotes. 0546 // Exif.Nikon3.ColorSpace: 1 => sRGB, 2 => AdobeRGB 0547 0548 long nikonColorSpace; 0549 0550 if (getExifTagLong("Exif.Nikon3.ColorSpace", nikonColorSpace)) 0551 { 0552 if (nikonColorSpace == 1) 0553 { 0554 return WORKSPACE_SRGB; 0555 } 0556 else if (nikonColorSpace == 2) 0557 { 0558 return WORKSPACE_ADOBERGB; 0559 } 0560 } 0561 // Exif.Nikon3.ColorMode is set to "MODE2" for AdobeRGB, but there are sometimes two ColorMode fields 0562 // in a NEF, with the first one "COLOR" and the second one "MODE2"; but in this case, ColorSpace (above) was set. 0563 0564 if (getExifTagString("Exif.Nikon3.ColorMode").contains(QLatin1String("MODE2"))) 0565 { 0566 return WORKSPACE_ADOBERGB; 0567 } 0568 0569 // TODO: This makernote tag (0x00b4) must be added to libexiv2 0570 0571 /* 0572 long canonColorSpace; 0573 0574 if (getExifTagLong("Exif.Canon.ColorSpace", canonColorSpace)) 0575 { 0576 if (canonColorSpace == 1) 0577 { 0578 return WORKSPACE_SRGB; 0579 } 0580 else if (canonColorSpace == 2) 0581 { 0582 return WORKSPACE_ADOBERGB; 0583 } 0584 } 0585 */ 0586 0587 // TODO : add more Makernote parsing here ... 0588 0589 if (exifColorSpace == 65535) 0590 { 0591 return WORKSPACE_UNCALIBRATED; 0592 } 0593 } 0594 0595 return WORKSPACE_UNSPECIFIED; 0596 } 0597 0598 bool MetaEngine::setItemColorWorkSpace(ImageColorWorkSpace workspace) const 0599 { 0600 QMutexLocker lock(&s_metaEngineMutex); 0601 0602 try 0603 { 0604 // Set Exif value. 0605 0606 d->exifMetadata()["Exif.Photo.ColorSpace"] = static_cast<uint16_t>(workspace); 0607 0608 // Set Xmp value. 0609 0610 #ifdef _XMP_SUPPORT_ 0611 0612 setXmpTagString("Xmp.exif.ColorSpace", QString::number((int)workspace)); 0613 0614 #endif // _XMP_SUPPORT_ 0615 0616 return true; 0617 } 0618 catch (Exiv2::AnyError& e) 0619 { 0620 d->printExiv2ExceptionError(QLatin1String("Cannot set Exif color workspace tag with Exiv2:"), e); 0621 } 0622 catch (...) 0623 { 0624 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 0625 } 0626 0627 return false; 0628 } 0629 0630 QDateTime MetaEngine::getItemDateTime() const 0631 { 0632 QMutexLocker lock(&s_metaEngineMutex); 0633 0634 try 0635 { 0636 // In first, trying to get Date & time from Exif tags. 0637 0638 QMap<QDateTime, int> dateMap; 0639 0640 if (!d->exifMetadata().empty()) 0641 { 0642 Exiv2::ExifData exifData(d->exifMetadata()); 0643 { 0644 Exiv2::ExifKey key("Exif.Photo.DateTimeOriginal"); 0645 Exiv2::ExifData::const_iterator it = exifData.findKey(key); 0646 0647 if (it != exifData.end()) 0648 { 0649 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0650 dateTime.setTimeSpec(Qt::UTC); 0651 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 4); 0652 0653 if (dateTime.isValid()) 0654 { 0655 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Exif.Photo.DateTimeOriginal =>" << dateTime; 0656 } 0657 } 0658 } 0659 { 0660 Exiv2::ExifKey key("Exif.Photo.DateTimeDigitized"); 0661 Exiv2::ExifData::const_iterator it = exifData.findKey(key); 0662 0663 if (it != exifData.end()) 0664 { 0665 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0666 dateTime.setTimeSpec(Qt::UTC); 0667 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0668 0669 if (dateTime.isValid() && dateMap.value(dateTime) > 4) 0670 { 0671 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Exif.Photo.DateTimeDigitized =>" << dateTime; 0672 0673 return dateTime; 0674 } 0675 } 0676 } 0677 { 0678 Exiv2::ExifKey key("Exif.Image.DateTime"); 0679 Exiv2::ExifData::const_iterator it = exifData.findKey(key); 0680 0681 if (it != exifData.end()) 0682 { 0683 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0684 dateTime.setTimeSpec(Qt::UTC); 0685 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0686 0687 if (dateTime.isValid() && dateMap.value(dateTime) > 4) 0688 { 0689 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Exif.Image.DateTime =>" << dateTime; 0690 0691 return dateTime; 0692 } 0693 } 0694 } 0695 } 0696 0697 // In second, trying to get Date & time from Xmp tags. 0698 0699 #ifdef _XMP_SUPPORT_ 0700 0701 if (!d->xmpMetadata().empty()) 0702 { 0703 Exiv2::XmpData xmpData(d->xmpMetadata()); 0704 { 0705 Exiv2::XmpKey key("Xmp.exif.DateTimeOriginal"); 0706 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0707 0708 if (it != xmpData.end()) 0709 { 0710 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0711 dateTime.setTimeSpec(Qt::UTC); 0712 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 2); 0713 0714 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0715 { 0716 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.exif.DateTimeOriginal =>" << dateTime; 0717 0718 return dateTime; 0719 } 0720 } 0721 } 0722 { 0723 Exiv2::XmpKey key("Xmp.exif.DateTimeDigitized"); 0724 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0725 0726 if (it != xmpData.end()) 0727 { 0728 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0729 dateTime.setTimeSpec(Qt::UTC); 0730 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0731 0732 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0733 { 0734 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.exif.DateTimeDigitized =>" << dateTime; 0735 0736 return dateTime; 0737 } 0738 } 0739 } 0740 { 0741 Exiv2::XmpKey key("Xmp.photoshop.DateCreated"); 0742 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0743 0744 if (it != xmpData.end()) 0745 { 0746 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0747 dateTime.setTimeSpec(Qt::UTC); 0748 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0749 0750 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0751 { 0752 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.photoshop.DateCreated =>" << dateTime; 0753 0754 return dateTime; 0755 } 0756 } 0757 } 0758 { 0759 Exiv2::XmpKey key("Xmp.xmp.CreateDate"); 0760 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0761 0762 if (it != xmpData.end()) 0763 { 0764 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0765 dateTime.setTimeSpec(Qt::UTC); 0766 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0767 0768 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0769 { 0770 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.xmp.CreateDate =>" << dateTime; 0771 0772 return dateTime; 0773 } 0774 } 0775 } 0776 { 0777 Exiv2::XmpKey key("Xmp.tiff.DateTime"); 0778 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0779 0780 if (it != xmpData.end()) 0781 { 0782 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0783 dateTime.setTimeSpec(Qt::UTC); 0784 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0785 0786 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0787 { 0788 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.tiff.DateTime =>" << dateTime; 0789 0790 return dateTime; 0791 } 0792 } 0793 } 0794 { 0795 Exiv2::XmpKey key("Xmp.xmp.ModifyDate"); 0796 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0797 0798 if (it != xmpData.end()) 0799 { 0800 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0801 dateTime.setTimeSpec(Qt::UTC); 0802 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0803 0804 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0805 { 0806 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.xmp.ModifyDate =>" << dateTime; 0807 0808 return dateTime; 0809 } 0810 } 0811 } 0812 { 0813 Exiv2::XmpKey key("Xmp.xmp.MetadataDate"); 0814 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0815 0816 if (it != xmpData.end()) 0817 { 0818 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0819 dateTime.setTimeSpec(Qt::UTC); 0820 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0821 0822 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0823 { 0824 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.xmp.MetadataDate =>" << dateTime; 0825 0826 return dateTime; 0827 } 0828 } 0829 } 0830 0831 // Video files support 0832 0833 { 0834 Exiv2::XmpKey key("Xmp.video.DateTimeOriginal"); 0835 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0836 0837 if (it != xmpData.end()) 0838 { 0839 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0840 dateTime.setTimeSpec(Qt::UTC); 0841 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 2); 0842 0843 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0844 { 0845 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.video.DateTimeOriginal =>" << dateTime; 0846 0847 return dateTime; 0848 } 0849 } 0850 } 0851 { 0852 Exiv2::XmpKey key("Xmp.video.DateUTC"); 0853 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0854 0855 if (it != xmpData.end()) 0856 { 0857 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0858 dateTime.setTimeSpec(Qt::UTC); 0859 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0860 0861 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0862 { 0863 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.video.DateUTC =>" << dateTime; 0864 0865 return dateTime; 0866 } 0867 } 0868 } 0869 { 0870 Exiv2::XmpKey key("Xmp.video.ModificationDate"); 0871 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0872 0873 if (it != xmpData.end()) 0874 { 0875 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0876 dateTime.setTimeSpec(Qt::UTC); 0877 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0878 0879 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0880 { 0881 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.video.ModificationDate =>" << dateTime; 0882 0883 return dateTime; 0884 } 0885 } 0886 } 0887 { 0888 Exiv2::XmpKey key("Xmp.video.DateTimeDigitized"); 0889 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 0890 0891 if (it != xmpData.end()) 0892 { 0893 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 0894 dateTime.setTimeSpec(Qt::UTC); 0895 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0896 0897 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0898 { 0899 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Xmp.video.DateTimeDigitized =>" << dateTime; 0900 0901 return dateTime; 0902 } 0903 } 0904 } 0905 } 0906 0907 #endif // _XMP_SUPPORT_ 0908 0909 // In third, trying to get Date & time from Iptc tags. 0910 0911 if (!d->iptcMetadata().empty()) 0912 { 0913 Exiv2::IptcData iptcData(d->iptcMetadata()); 0914 0915 // Try creation Iptc date & time entries. 0916 0917 Exiv2::IptcKey keyDateCreated("Iptc.Application2.DateCreated"); 0918 Exiv2::IptcData::const_iterator it = iptcData.findKey(keyDateCreated); 0919 0920 if (it != iptcData.end()) 0921 { 0922 QString IptcDateCreated(QString::fromStdString(it->toString())); 0923 Exiv2::IptcKey keyTimeCreated("Iptc.Application2.TimeCreated"); 0924 Exiv2::IptcData::const_iterator it2 = iptcData.findKey(keyTimeCreated); 0925 0926 if (it2 != iptcData.end()) 0927 { 0928 QString IptcTimeCreated(QString::fromStdString(it2->toString())); 0929 QDate date = QDate::fromString(IptcDateCreated, Qt::ISODate); 0930 QTime time = QTime::fromString(IptcTimeCreated, Qt::ISODate); 0931 QDateTime dateTime = QDateTime(date, time); 0932 dateTime.setTimeSpec(Qt::UTC); 0933 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0934 0935 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0936 { 0937 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Iptc.Application2.DateCreated =>" << dateTime; 0938 0939 return dateTime; 0940 } 0941 } 0942 } 0943 0944 // Try digitization Iptc date & time entries. 0945 0946 Exiv2::IptcKey keyDigitizationDate("Iptc.Application2.DigitizationDate"); 0947 Exiv2::IptcData::const_iterator it3 = iptcData.findKey(keyDigitizationDate); 0948 0949 if (it3 != iptcData.end()) 0950 { 0951 QString IptcDateDigitization(QString::fromStdString(it3->toString())); 0952 Exiv2::IptcKey keyDigitizationTime("Iptc.Application2.DigitizationTime"); 0953 Exiv2::IptcData::const_iterator it4 = iptcData.findKey(keyDigitizationTime); 0954 0955 if (it4 != iptcData.end()) 0956 { 0957 QString IptcTimeDigitization(QString::fromStdString(it4->toString())); 0958 QDate date = QDate::fromString(IptcDateDigitization, Qt::ISODate); 0959 QTime time = QTime::fromString(IptcTimeDigitization, Qt::ISODate); 0960 QDateTime dateTime = QDateTime(date, time); 0961 dateTime.setTimeSpec(Qt::UTC); 0962 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 0963 0964 if (dateTime.isValid() && (dateMap.value(dateTime) > 4)) 0965 { 0966 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => Iptc.Application2.DigitizationDate =>" << dateTime; 0967 0968 return dateTime; 0969 } 0970 } 0971 } 0972 } 0973 0974 if (!dateMap.isEmpty()) 0975 { 0976 int counter = 0; 0977 QDateTime dateTime; 0978 QMap<QDateTime, int>::const_iterator it; 0979 0980 for (it = dateMap.constBegin() ; it != dateMap.constEnd() ; ++it) 0981 { 0982 if (!it.key().isValid()) 0983 { 0984 continue; 0985 } 0986 else if (it.value() > counter) 0987 { 0988 counter = it.value(); 0989 dateTime = it.key(); 0990 } 0991 } 0992 0993 if (dateTime.isValid()) 0994 { 0995 //qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime => create date =>" << dateTime; 0996 0997 return dateTime; 0998 } 0999 } 1000 } 1001 catch (Exiv2::AnyError& e) 1002 { 1003 d->printExiv2ExceptionError(QLatin1String("Cannot parse Exif date & time tag with Exiv2:"), e); 1004 } 1005 catch (...) 1006 { 1007 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 1008 } 1009 1010 return QDateTime(); 1011 } 1012 1013 bool MetaEngine::setImageDateTime(const QDateTime& dateTime, bool setDateTimeDigitized) const 1014 { 1015 if (!dateTime.isValid()) 1016 { 1017 return false; 1018 } 1019 1020 QMutexLocker lock(&s_metaEngineMutex); 1021 1022 try 1023 { 1024 // In first we write date & time into Exif. 1025 1026 // DateTimeDigitized is set by slide scanners etc. when a picture is digitized. 1027 // DateTimeOriginal specifies the date/time when the picture was taken. 1028 // For digital cameras, these dates should be both set, and identical. 1029 // Reference: https://www.exif.org/Exif2-2.PDF, chapter 4.6.5, table 4, section F. 1030 1031 const std::string& exifdatetime(dateTime.toString(QString::fromLatin1("yyyy:MM:dd hh:mm:ss")).toLatin1().constData()); 1032 d->exifMetadata()["Exif.Image.DateTime"] = exifdatetime; 1033 d->exifMetadata()["Exif.Photo.DateTimeOriginal"] = exifdatetime; 1034 1035 if (setDateTimeDigitized) 1036 { 1037 d->exifMetadata()["Exif.Photo.DateTimeDigitized"] = exifdatetime; 1038 } 1039 1040 #ifdef _XMP_SUPPORT_ 1041 1042 // In second we write date & time into Xmp. 1043 1044 const std::string& xmpdatetime(dateTime.toString(Qt::ISODate).toLatin1().constData()); 1045 1046 Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText); 1047 xmpTxtVal->read(xmpdatetime); 1048 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.exif.DateTimeOriginal"), xmpTxtVal.get()); 1049 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.photoshop.DateCreated"), xmpTxtVal.get()); 1050 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.tiff.DateTime"), xmpTxtVal.get()); 1051 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.CreateDate"), xmpTxtVal.get()); 1052 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.MetadataDate"), xmpTxtVal.get()); 1053 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.ModifyDate"), xmpTxtVal.get()); 1054 1055 if (setDateTimeDigitized) 1056 { 1057 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.exif.DateTimeDigitized"), xmpTxtVal.get()); 1058 } 1059 1060 // We call this function from the FFmpeg parser and we don't have a filename yet. 1061 // Check getFilePath() for empty. 1062 1063 if (getFilePath().isEmpty() || 1064 QMimeDatabase().mimeTypeForFile(getFilePath()).name().startsWith(QLatin1String("video/"))) 1065 { 1066 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.video.DateTimeOriginal"), xmpTxtVal.get()); 1067 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.video.DateUTC"), xmpTxtVal.get()); 1068 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.video.ModificationDate"), xmpTxtVal.get()); 1069 1070 if (setDateTimeDigitized) 1071 { 1072 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.video.DateTimeDigitized"), xmpTxtVal.get()); 1073 } 1074 } 1075 1076 // Tag not updated: 1077 // "Xmp.dc.DateTime" is a sequence of date relevant of dublin core change. 1078 // This is not the picture date as well 1079 1080 #endif // _XMP_SUPPORT_ 1081 1082 // In third we write date & time into Iptc. 1083 1084 const std::string& iptcdate(dateTime.date().toString(Qt::ISODate).toLatin1().constData()); 1085 const std::string& iptctime(dateTime.time().toString(Qt::ISODate).toLatin1().constData()); 1086 d->iptcMetadata()["Iptc.Application2.DateCreated"] = iptcdate; 1087 d->iptcMetadata()["Iptc.Application2.TimeCreated"] = iptctime; 1088 1089 if (setDateTimeDigitized) 1090 { 1091 d->iptcMetadata()["Iptc.Application2.DigitizationDate"] = iptcdate; 1092 d->iptcMetadata()["Iptc.Application2.DigitizationTime"] = iptctime; 1093 } 1094 1095 return true; 1096 } 1097 catch (Exiv2::AnyError& e) 1098 { 1099 d->printExiv2ExceptionError(QLatin1String("Cannot set Date & Time into image with Exiv2:"), e); 1100 } 1101 catch (...) 1102 { 1103 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 1104 } 1105 1106 return false; 1107 } 1108 1109 QDateTime MetaEngine::getDigitizationDateTime(bool fallbackToCreationTime) const 1110 { 1111 QMutexLocker lock(&s_metaEngineMutex); 1112 1113 try 1114 { 1115 // In first, trying to get Date & time from Exif tags. 1116 1117 QMap<QDateTime, int> dateMap; 1118 1119 if (!d->exifMetadata().empty()) 1120 { 1121 // Try Exif date time digitized. 1122 1123 Exiv2::ExifData exifData(d->exifMetadata()); 1124 Exiv2::ExifKey key("Exif.Photo.DateTimeDigitized"); 1125 Exiv2::ExifData::const_iterator it = exifData.findKey(key); 1126 1127 if (it != exifData.end()) 1128 { 1129 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 1130 dateTime.setTimeSpec(Qt::UTC); 1131 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 1132 1133 if (dateTime.isValid()) 1134 { 1135 qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime (Exif digitalized):" << dateTime; 1136 } 1137 } 1138 } 1139 1140 // In second, we trying XMP 1141 1142 #ifdef _XMP_SUPPORT_ 1143 1144 if (!d->xmpMetadata().empty()) 1145 { 1146 Exiv2::XmpData xmpData(d->xmpMetadata()); 1147 { 1148 Exiv2::XmpKey key("Xmp.exif.DateTimeDigitized"); 1149 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 1150 1151 if (it != xmpData.end()) 1152 { 1153 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 1154 dateTime.setTimeSpec(Qt::UTC); 1155 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 1156 1157 if (dateTime.isValid() && (dateMap.value(dateTime) > 1)) 1158 { 1159 qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime (XMP-Exif digitalized):" << dateTime; 1160 1161 return dateTime; 1162 } 1163 } 1164 } 1165 { 1166 Exiv2::XmpKey key("Xmp.video.DateTimeDigitized"); 1167 Exiv2::XmpData::const_iterator it = xmpData.findKey(key); 1168 1169 if (it != xmpData.end()) 1170 { 1171 QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate); 1172 dateTime.setTimeSpec(Qt::UTC); 1173 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 1174 1175 if (dateTime.isValid() && (dateMap.value(dateTime) > 1)) 1176 { 1177 qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime (XMP-Video digitalized):" << dateTime; 1178 1179 return dateTime; 1180 } 1181 } 1182 } 1183 } 1184 1185 #endif // _XMP_SUPPORT_ 1186 1187 // In third, trying to get Date & time from Iptc tags. 1188 1189 if (!d->iptcMetadata().empty()) 1190 { 1191 // Try digitization Iptc date time entries. 1192 1193 Exiv2::IptcData iptcData(d->iptcMetadata()); 1194 Exiv2::IptcKey keyDigitizationDate("Iptc.Application2.DigitizationDate"); 1195 Exiv2::IptcData::const_iterator it = iptcData.findKey(keyDigitizationDate); 1196 1197 if (it != iptcData.end()) 1198 { 1199 QString IptcDateDigitization(QString::fromStdString(it->toString())); 1200 1201 Exiv2::IptcKey keyDigitizationTime("Iptc.Application2.DigitizationTime"); 1202 Exiv2::IptcData::const_iterator it2 = iptcData.findKey(keyDigitizationTime); 1203 1204 if (it2 != iptcData.end()) 1205 { 1206 QString IptcTimeDigitization(QString::fromStdString(it2->toString())); 1207 1208 QDate date = QDate::fromString(IptcDateDigitization, Qt::ISODate); 1209 QTime time = QTime::fromString(IptcTimeDigitization, Qt::ISODate); 1210 QDateTime dateTime = QDateTime(date, time); 1211 dateTime.setTimeSpec(Qt::UTC); 1212 dateMap.insert(dateTime, dateMap.value(dateTime, 0) + 1); 1213 1214 if (dateTime.isValid() && (dateMap.value(dateTime) > 1)) 1215 { 1216 qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime (IPTC digitalized):" << dateTime; 1217 1218 return dateTime; 1219 } 1220 } 1221 } 1222 } 1223 1224 if (!dateMap.isEmpty()) 1225 { 1226 int counter = 0; 1227 QDateTime dateTime; 1228 QMap<QDateTime, int>::const_iterator it; 1229 1230 for (it = dateMap.constBegin() ; it != dateMap.constEnd() ; ++it) 1231 { 1232 if (!it.key().isValid()) 1233 { 1234 continue; 1235 } 1236 else if (it.value() > counter) 1237 { 1238 counter = it.value(); 1239 dateTime = it.key(); 1240 } 1241 } 1242 1243 if (dateTime.isValid()) 1244 { 1245 qCDebug(DIGIKAM_METAENGINE_LOG) << "DateTime (digitization date):" << dateTime; 1246 return dateTime; 1247 } 1248 } 1249 1250 } 1251 catch (Exiv2::AnyError& e) 1252 { 1253 d->printExiv2ExceptionError(QLatin1String("Cannot parse Exif digitization date & time tag with Exiv2:"), e); 1254 } 1255 catch (...) 1256 { 1257 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 1258 } 1259 1260 if (fallbackToCreationTime) 1261 { 1262 return getItemDateTime(); 1263 } 1264 else 1265 { 1266 return QDateTime(); 1267 } 1268 } 1269 1270 bool MetaEngine::getItemPreview(QImage& preview) const 1271 { 1272 QMutexLocker lock(&s_metaEngineMutex); 1273 1274 try 1275 { 1276 // In first we trying to get from Iptc preview tag. 1277 1278 QByteArray imgData = getIptcTagData("Iptc.Application2.Preview"); 1279 1280 if (!imgData.isEmpty()) 1281 { 1282 if (preview.loadFromData(imgData)) 1283 { 1284 return true; 1285 } 1286 } 1287 1288 imgData = QByteArray::fromBase64(getXmpTagString("Xmp.digiKam.Preview", false).toUtf8()); 1289 1290 if (!imgData.isEmpty()) 1291 { 1292 if (preview.loadFromData(imgData)) 1293 { 1294 return true; 1295 } 1296 } 1297 1298 // TODO : Added here Makernotes preview extraction when Exiv2 will be fixed for that. 1299 } 1300 catch (Exiv2::AnyError& e) 1301 { 1302 d->printExiv2ExceptionError(QLatin1String("Cannot get image preview with Exiv2:"), e); 1303 } 1304 catch (...) 1305 { 1306 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 1307 } 1308 1309 return false; 1310 } 1311 1312 bool MetaEngine::setItemPreview(const QImage& preview) const 1313 { 1314 if (preview.isNull()) 1315 { 1316 removeIptcTag("Iptc.Application2.Preview"); 1317 removeIptcTag("Iptc.Application2.PreviewFormat"); 1318 removeIptcTag("Iptc.Application2.PreviewVersion"); 1319 removeXmpTag("Xmp.digiKam.Preview"); 1320 return true; 1321 } 1322 1323 QMutexLocker lock(&s_metaEngineMutex); 1324 1325 try 1326 { 1327 QByteArray data; 1328 QBuffer buffer(&data); 1329 buffer.open(QIODevice::WriteOnly); 1330 1331 // A little bit compressed preview jpeg image to limit IPTC size. 1332 1333 preview.save(&buffer, "JPEG"); 1334 buffer.close(); 1335 qCDebug(DIGIKAM_METAENGINE_LOG) << "JPEG image preview size: (" << preview.width() << "x" 1336 << preview.height() << ") pixels -" << data.size() << "bytes"; 1337 1338 Exiv2::DataValue val; 1339 val.read((Exiv2::byte *)data.data(), data.size()); 1340 d->iptcMetadata()["Iptc.Application2.Preview"] = val; 1341 1342 // See https://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf Appendix A for details. 1343 1344 d->iptcMetadata()["Iptc.Application2.PreviewFormat"] = 11; // JPEG 1345 d->iptcMetadata()["Iptc.Application2.PreviewVersion"] = 1; 1346 1347 QByteArray xmpPreview(data.toBase64()); 1348 1349 if (xmpPreview.size() < 62000) 1350 { 1351 qCDebug(DIGIKAM_METAENGINE_LOG) << "Set XMP image preview with:" 1352 << xmpPreview.size() << "bytes"; 1353 1354 setXmpTagString("Xmp.digiKam.Preview", QString::fromUtf8(xmpPreview)); 1355 } 1356 1357 return true; 1358 } 1359 catch (Exiv2::AnyError& e) 1360 { 1361 d->printExiv2ExceptionError(QLatin1String("Cannot get image preview with Exiv2:"), e); 1362 } 1363 catch (...) 1364 { 1365 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; 1366 } 1367 1368 return false; 1369 } 1370 1371 } // namespace Digikam 1372 1373 #if defined(Q_CC_CLANG) 1374 # pragma clang diagnostic pop 1375 #endif