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

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