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