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