File indexing completed on 2024-05-05 08:41:27
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 // C++ includes 0012 0013 #include <cctype> 0014 0015 // Qt includes 0016 0017 #include <QBuffer> 0018 0019 // Local includes 0020 0021 #include "rotationmatrix.h" 0022 #include "libkexiv2_debug.h" 0023 0024 namespace KExiv2Iface 0025 { 0026 0027 bool KExiv2::canWriteExif(const QString& filePath) 0028 { 0029 try 0030 { 0031 #if EXIV2_TEST_VERSION(0,28,0) 0032 Exiv2::Image::UniquePtr image = 0033 #else 0034 Exiv2::Image::AutoPtr image = 0035 #endif 0036 Exiv2::ImageFactory::open((const char*) 0037 (QFile::encodeName(filePath).constData())); 0038 0039 Exiv2::AccessMode mode = image->checkMode(Exiv2::mdExif); 0040 0041 return (mode == Exiv2::amWrite || mode == Exiv2::amReadWrite); 0042 } 0043 catch( Exiv2::Error& e ) 0044 { 0045 std::string s(e.what()); 0046 qCCritical(LIBKEXIV2_LOG) << "Cannot check Exif access mode using Exiv2 (Error #" 0047 #if EXIV2_TEST_VERSION(0,28,0) 0048 << Exiv2::Error(e.code()).what() 0049 #else 0050 << e.code() << ": " << s.c_str() 0051 #endif 0052 << ")"; 0053 } 0054 catch(...) 0055 { 0056 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0057 } 0058 0059 return false; 0060 } 0061 0062 bool KExiv2::hasExif() const 0063 { 0064 return !d->exifMetadata().empty(); 0065 } 0066 0067 bool KExiv2::clearExif() const 0068 { 0069 try 0070 { 0071 d->exifMetadata().clear(); 0072 return true; 0073 } 0074 catch( Exiv2::Error& e ) 0075 { 0076 d->printExiv2ExceptionError(QString::fromLatin1("Cannot clear Exif data using Exiv2 "), e); 0077 } 0078 catch(...) 0079 { 0080 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0081 } 0082 0083 return false; 0084 } 0085 0086 QByteArray KExiv2::getExifEncoded(bool addExifHeader) const 0087 { 0088 try 0089 { 0090 if (!d->exifMetadata().empty()) 0091 { 0092 QByteArray data; 0093 Exiv2::ExifData& exif = d->exifMetadata(); 0094 Exiv2::Blob blob; 0095 Exiv2::ExifParser::encode(blob, Exiv2::bigEndian, exif); 0096 QByteArray ba((const char*)&blob[0], blob.size()); 0097 0098 if (addExifHeader) 0099 { 0100 const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; 0101 data.resize(ba.size() + sizeof(ExifHeader)); 0102 memcpy(data.data(), ExifHeader, sizeof(ExifHeader)); 0103 memcpy(data.data() + sizeof(ExifHeader), ba.data(), ba.size()); 0104 } 0105 else 0106 { 0107 data = ba; 0108 } 0109 return data; 0110 } 0111 } 0112 catch( Exiv2::Error& e ) 0113 { 0114 if (!d->filePath.isEmpty()) 0115 qCDebug(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData(); 0116 0117 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Exif data using Exiv2 "), e); 0118 } 0119 catch(...) 0120 { 0121 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0122 } 0123 0124 return QByteArray(); 0125 } 0126 0127 bool KExiv2::setExif(const QByteArray& data) const 0128 { 0129 try 0130 { 0131 if (!data.isEmpty()) 0132 { 0133 Exiv2::ExifParser::decode(d->exifMetadata(), (const Exiv2::byte*)data.data(), data.size()); 0134 return (!d->exifMetadata().empty()); 0135 } 0136 } 0137 catch( Exiv2::Error& e ) 0138 { 0139 if (!d->filePath.isEmpty()) 0140 qCCritical(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData(); 0141 0142 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif data using Exiv2 "), e); 0143 } 0144 catch(...) 0145 { 0146 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0147 } 0148 0149 return false; 0150 } 0151 0152 KExiv2::MetaDataMap KExiv2::getExifTagsDataList(const QStringList& exifKeysFilter, bool invertSelection) const 0153 { 0154 if (d->exifMetadata().empty()) 0155 return MetaDataMap(); 0156 0157 try 0158 { 0159 Exiv2::ExifData exifData = d->exifMetadata(); 0160 exifData.sortByKey(); 0161 0162 QString ifDItemName; 0163 MetaDataMap metaDataMap; 0164 0165 for (Exiv2::ExifData::iterator md = exifData.begin(); md != exifData.end(); ++md) 0166 { 0167 QString key = QString::fromLatin1(md->key().c_str()); 0168 0169 // Decode the tag value with a user friendly output. 0170 QString tagValue; 0171 0172 if (key == QString::fromLatin1("Exif.Photo.UserComment")) 0173 { 0174 tagValue = d->convertCommentValue(*md); 0175 } 0176 else if (key == QString::fromLatin1("Exif.Image.0x935c")) 0177 { 0178 tagValue = QString::number(md->value().size()); 0179 } 0180 else 0181 { 0182 std::ostringstream os; 0183 os << *md; 0184 0185 // Exif tag contents can be an translated strings, no only simple ascii. 0186 tagValue = QString::fromLocal8Bit(os.str().c_str()); 0187 } 0188 0189 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" ")); 0190 0191 // We apply a filter to get only the Exif tags that we need. 0192 0193 if (!exifKeysFilter.isEmpty()) 0194 { 0195 if (!invertSelection) 0196 { 0197 if (exifKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1))) 0198 metaDataMap.insert(key, tagValue); 0199 } 0200 else 0201 { 0202 if (!exifKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1))) 0203 metaDataMap.insert(key, tagValue); 0204 } 0205 } 0206 else // else no filter at all. 0207 { 0208 metaDataMap.insert(key, tagValue); 0209 } 0210 } 0211 0212 return metaDataMap; 0213 } 0214 catch (Exiv2::Error& e) 0215 { 0216 d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse EXIF metadata using Exiv2 "), e); 0217 } 0218 catch(...) 0219 { 0220 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0221 } 0222 0223 return MetaDataMap(); 0224 } 0225 0226 QString KExiv2::getExifComment() const 0227 { 0228 try 0229 { 0230 if (!d->exifMetadata().empty()) 0231 { 0232 Exiv2::ExifData exifData(d->exifMetadata()); 0233 Exiv2::ExifKey key("Exif.Photo.UserComment"); 0234 Exiv2::ExifData::iterator it = exifData.findKey(key); 0235 0236 if (it != exifData.end()) 0237 { 0238 QString exifComment = d->convertCommentValue(*it); 0239 0240 // some cameras fill the UserComment with whitespace 0241 if (!exifComment.isEmpty() && !exifComment.trimmed().isEmpty()) 0242 return exifComment; 0243 } 0244 0245 Exiv2::ExifKey key2("Exif.Image.ImageDescription"); 0246 Exiv2::ExifData::iterator it2 = exifData.findKey(key2); 0247 0248 if (it2 != exifData.end()) 0249 { 0250 QString exifComment = d->convertCommentValue(*it2); 0251 0252 // Some cameras fill in nonsense default values 0253 QStringList blackList; 0254 blackList << QString::fromLatin1("SONY DSC"); // + whitespace 0255 blackList << QString::fromLatin1("OLYMPUS DIGITAL CAMERA"); 0256 blackList << QString::fromLatin1("MINOLTA DIGITAL CAMERA"); 0257 0258 QString trimmedComment = exifComment.trimmed(); 0259 0260 // some cameras fill the UserComment with whitespace 0261 if (!exifComment.isEmpty() && !trimmedComment.isEmpty() && !blackList.contains(trimmedComment)) 0262 return exifComment; 0263 } 0264 } 0265 } 0266 catch( Exiv2::Error& e ) 0267 { 0268 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif User Comment using Exiv2 "), e); 0269 } 0270 catch(...) 0271 { 0272 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0273 } 0274 0275 return QString(); 0276 } 0277 0278 static bool is7BitAscii(const QString& s) 0279 { 0280 return std::all_of(s.begin(), s.end(), [](QChar c) { return c.unicode() <= 0x7f; }); 0281 } 0282 0283 bool KExiv2::setExifComment(const QString& comment, bool setProgramName) const 0284 { 0285 if (!setProgramId(setProgramName)) 0286 return false; 0287 0288 try 0289 { 0290 removeExifTag("Exif.Image.ImageDescription"); 0291 removeExifTag("Exif.Photo.UserComment"); 0292 0293 if (!comment.isNull()) 0294 { 0295 setExifTagString("Exif.Image.ImageDescription", comment, setProgramName); 0296 0297 // Write as Unicode only when necessary. 0298 if (is7BitAscii(comment)) 0299 { 0300 // write as ASCII 0301 std::string exifComment("charset=\"Ascii\" "); 0302 exifComment += comment.toLatin1().constData(); 0303 d->exifMetadata()["Exif.Photo.UserComment"] = exifComment; 0304 return true; 0305 } 0306 // write as Unicode (UCS-2) 0307 std::string exifComment("charset=\"Unicode\" "); 0308 exifComment += comment.toUtf8().constData(); 0309 d->exifMetadata()["Exif.Photo.UserComment"] = exifComment; 0310 } 0311 0312 return true; 0313 } 0314 catch( Exiv2::Error& e ) 0315 { 0316 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif Comment using Exiv2 "), e); 0317 } 0318 catch(...) 0319 { 0320 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0321 } 0322 0323 return false; 0324 } 0325 0326 QString KExiv2::getExifTagTitle(const char* exifTagName) 0327 { 0328 try 0329 { 0330 std::string exifkey(exifTagName); 0331 Exiv2::ExifKey ek(exifkey); 0332 0333 return QString::fromLocal8Bit( ek.tagLabel().c_str() ); 0334 } 0335 catch (Exiv2::Error& e) 0336 { 0337 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get metadata tag title using Exiv2 "), e); 0338 } 0339 catch(...) 0340 { 0341 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0342 } 0343 0344 return QString(); 0345 } 0346 0347 QString KExiv2::getExifTagDescription(const char* exifTagName) 0348 { 0349 try 0350 { 0351 std::string exifkey(exifTagName); 0352 Exiv2::ExifKey ek(exifkey); 0353 0354 return QString::fromLocal8Bit( ek.tagDesc().c_str() ); 0355 } 0356 catch (Exiv2::Error& e) 0357 { 0358 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get metadata tag description using Exiv2 "), e); 0359 } 0360 catch(...) 0361 { 0362 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0363 } 0364 0365 return QString(); 0366 } 0367 0368 bool KExiv2::removeExifTag(const char* exifTagName, bool setProgramName) const 0369 { 0370 if (!setProgramId(setProgramName)) 0371 return false; 0372 0373 try 0374 { 0375 Exiv2::ExifKey exifKey(exifTagName); 0376 Exiv2::ExifData::iterator it = d->exifMetadata().findKey(exifKey); 0377 0378 if (it != d->exifMetadata().end()) 0379 { 0380 d->exifMetadata().erase(it); 0381 return true; 0382 } 0383 } 0384 catch( Exiv2::Error& e ) 0385 { 0386 d->printExiv2ExceptionError(QString::fromLatin1("Cannot remove Exif tag using Exiv2 "), e); 0387 } 0388 catch(...) 0389 { 0390 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0391 } 0392 0393 return false; 0394 } 0395 0396 bool KExiv2::getExifTagRational(const char* exifTagName, long int& num, long int& den, int component) const 0397 { 0398 try 0399 { 0400 Exiv2::ExifKey exifKey(exifTagName); 0401 Exiv2::ExifData exifData(d->exifMetadata()); 0402 Exiv2::ExifData::iterator it = exifData.findKey(exifKey); 0403 0404 if (it != exifData.end()) 0405 { 0406 num = (*it).toRational(component).first; 0407 den = (*it).toRational(component).second; 0408 0409 return true; 0410 } 0411 } 0412 catch( Exiv2::Error& e ) 0413 { 0414 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif Rational value from key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e); 0415 } 0416 catch(...) 0417 { 0418 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0419 } 0420 0421 return false; 0422 } 0423 0424 bool KExiv2::setExifTagLong(const char* exifTagName, long val, bool setProgramName) const 0425 { 0426 if (!setProgramId(setProgramName)) 0427 return false; 0428 0429 try 0430 { 0431 d->exifMetadata()[exifTagName] = static_cast<int32_t>(val); 0432 return true; 0433 } 0434 catch( Exiv2::Error& e ) 0435 { 0436 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag long value into image using Exiv2 "), e); 0437 } 0438 catch(...) 0439 { 0440 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0441 } 0442 0443 return false; 0444 } 0445 0446 bool KExiv2::setExifTagRational(const char* exifTagName, long int num, long int den, bool setProgramName) const 0447 { 0448 if (!setProgramId(setProgramName)) 0449 return false; 0450 0451 try 0452 { 0453 d->exifMetadata()[exifTagName] = Exiv2::Rational(num, den); 0454 return true; 0455 } 0456 catch( Exiv2::Error& e ) 0457 { 0458 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag rational value into image using Exiv2 "), e); 0459 } 0460 catch(...) 0461 { 0462 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0463 } 0464 0465 return false; 0466 } 0467 0468 bool KExiv2::setExifTagData(const char* exifTagName, const QByteArray& data, bool setProgramName) const 0469 { 0470 if (data.isEmpty()) 0471 return false; 0472 0473 if (!setProgramId(setProgramName)) 0474 return false; 0475 0476 try 0477 { 0478 Exiv2::DataValue val((Exiv2::byte*)data.data(), data.size()); 0479 d->exifMetadata()[exifTagName] = val; 0480 return true; 0481 } 0482 catch( Exiv2::Error& e ) 0483 { 0484 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag data into image using Exiv2 "), e); 0485 } 0486 catch(...) 0487 { 0488 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0489 } 0490 0491 return false; 0492 } 0493 0494 bool KExiv2::setExifTagVariant(const char* exifTagName, const QVariant& val, 0495 bool rationalWantSmallDenominator, bool setProgramName) const 0496 { 0497 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0498 switch (val.metaType().id()) 0499 #else 0500 switch (val.type()) 0501 #endif 0502 { 0503 case QMetaType::Int: 0504 case QMetaType::UInt: 0505 case QMetaType::Bool: 0506 case QMetaType::LongLong: 0507 case QMetaType::ULongLong: 0508 return setExifTagLong(exifTagName, val.toInt(), setProgramName); 0509 0510 case QMetaType::Double: 0511 { 0512 long num, den; 0513 0514 if (rationalWantSmallDenominator) 0515 convertToRationalSmallDenominator(val.toDouble(), &num, &den); 0516 else 0517 convertToRational(val.toDouble(), &num, &den, 4); 0518 0519 return setExifTagRational(exifTagName, num, den, setProgramName); 0520 } 0521 case QMetaType::QVariantList: 0522 { 0523 long num = 0, den = 1; 0524 QList<QVariant> list = val.toList(); 0525 0526 if (list.size() >= 1) 0527 num = list[0].toInt(); 0528 0529 if (list.size() >= 2) 0530 den = list[1].toInt(); 0531 0532 return setExifTagRational(exifTagName, num, den, setProgramName); 0533 } 0534 0535 case QMetaType::QDate: 0536 case QMetaType::QDateTime: 0537 { 0538 QDateTime dateTime = val.toDateTime(); 0539 0540 if(!dateTime.isValid()) 0541 return false; 0542 0543 if (!setProgramId(setProgramName)) 0544 return false; 0545 0546 try 0547 { 0548 const std::string &exifdatetime(dateTime.toString(QString::fromLatin1("yyyy:MM:dd hh:mm:ss")).toLatin1().constData()); 0549 d->exifMetadata()[exifTagName] = exifdatetime; 0550 } 0551 catch( Exiv2::Error &e ) 0552 { 0553 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Date & Time in image using Exiv2 "), e); 0554 } 0555 catch(...) 0556 { 0557 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0558 } 0559 0560 return false; 0561 } 0562 0563 case QMetaType::QString: 0564 case QMetaType::QChar: 0565 return setExifTagString(exifTagName, val.toString(), setProgramName); 0566 0567 case QMetaType::QByteArray: 0568 return setExifTagData(exifTagName, val.toByteArray(), setProgramName); 0569 default: 0570 break; 0571 } 0572 return false; 0573 } 0574 0575 QString KExiv2::createExifUserStringFromValue(const char* exifTagName, const QVariant& val, bool escapeCR) 0576 { 0577 try 0578 { 0579 Exiv2::ExifKey key(exifTagName); 0580 Exiv2::Exifdatum datum(key); 0581 0582 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0583 switch (val.metaType().id()) 0584 #else 0585 switch (val.type()) 0586 #endif 0587 { 0588 case QMetaType::Int: 0589 case QMetaType::Bool: 0590 case QMetaType::LongLong: 0591 case QMetaType::ULongLong: 0592 datum = (int32_t)val.toInt(); 0593 break; 0594 case QMetaType::UInt: 0595 datum = (uint32_t)val.toUInt(); 0596 break; 0597 0598 case QMetaType::Double: 0599 { 0600 long num, den; 0601 convertToRationalSmallDenominator(val.toDouble(), &num, &den); 0602 Exiv2::Rational rational; 0603 rational.first = num; 0604 rational.second = den; 0605 datum = rational; 0606 break; 0607 } 0608 case QMetaType::QVariantList: 0609 { 0610 long num = 0, den = 1; 0611 QList<QVariant> list = val.toList(); 0612 if (list.size() >= 1) 0613 num = list[0].toInt(); 0614 if (list.size() >= 2) 0615 den = list[1].toInt(); 0616 Exiv2::Rational rational; 0617 rational.first = num; 0618 rational.second = den; 0619 datum = rational; 0620 break; 0621 } 0622 0623 case QMetaType::QDate: 0624 case QMetaType::QDateTime: 0625 { 0626 QDateTime dateTime = val.toDateTime(); 0627 if(!dateTime.isValid()) 0628 break; 0629 0630 const std::string &exifdatetime(dateTime.toString(QString::fromLatin1("yyyy:MM:dd hh:mm:ss")).toLatin1().constData()); 0631 datum = exifdatetime; 0632 break; 0633 } 0634 0635 case QMetaType::QString: 0636 case QMetaType::QChar: 0637 datum = (std::string)val.toString().toLatin1().constData(); 0638 break; 0639 default: 0640 break; 0641 } 0642 0643 std::ostringstream os; 0644 os << datum; 0645 QString tagValue = QString::fromLocal8Bit(os.str().c_str()); 0646 0647 if (escapeCR) 0648 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" ")); 0649 0650 return tagValue; 0651 } 0652 catch( Exiv2::Error& e ) 0653 { 0654 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Iptc tag string into image using Exiv2 "), e); 0655 } 0656 catch(...) 0657 { 0658 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0659 } 0660 0661 return QString(); 0662 } 0663 0664 bool KExiv2::getExifTagLong(const char* exifTagName, long& val) const 0665 { 0666 return getExifTagLong(exifTagName, val, 0); 0667 } 0668 0669 bool KExiv2::getExifTagLong(const char* exifTagName, long& val, int component) const 0670 { 0671 try 0672 { 0673 Exiv2::ExifKey exifKey(exifTagName); 0674 Exiv2::ExifData exifData(d->exifMetadata()); 0675 Exiv2::ExifData::iterator it = exifData.findKey(exifKey); 0676 0677 if (it != exifData.end() && it->count() > 0) 0678 { 0679 #if EXIV2_TEST_VERSION(0,28,0) 0680 val = it->toUint32(component); 0681 #else 0682 val = it->toLong(component); 0683 #endif 0684 return true; 0685 } 0686 } 0687 catch( Exiv2::Error& e ) 0688 { 0689 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e); 0690 } 0691 catch(...) 0692 { 0693 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0694 } 0695 0696 return false; 0697 } 0698 0699 QByteArray KExiv2::getExifTagData(const char* exifTagName) const 0700 { 0701 try 0702 { 0703 Exiv2::ExifKey exifKey(exifTagName); 0704 Exiv2::ExifData exifData(d->exifMetadata()); 0705 Exiv2::ExifData::iterator it = exifData.findKey(exifKey); 0706 0707 if (it != exifData.end()) 0708 { 0709 char* const s = new char[(*it).size()]; 0710 (*it).copy((Exiv2::byte*)s, Exiv2::bigEndian); 0711 QByteArray data(s, (*it).size()); 0712 delete[] s; 0713 0714 return data; 0715 } 0716 } 0717 catch( Exiv2::Error& e ) 0718 { 0719 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e); 0720 } 0721 catch(...) 0722 { 0723 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0724 } 0725 0726 return QByteArray(); 0727 } 0728 0729 QVariant KExiv2::getExifTagVariant(const char* exifTagName, bool rationalAsListOfInts, bool stringEscapeCR, int component) const 0730 { 0731 try 0732 { 0733 Exiv2::ExifKey exifKey(exifTagName); 0734 Exiv2::ExifData exifData(d->exifMetadata()); 0735 Exiv2::ExifData::iterator it = exifData.findKey(exifKey); 0736 0737 if (it != exifData.end()) 0738 { 0739 switch (it->typeId()) 0740 { 0741 case Exiv2::unsignedByte: 0742 case Exiv2::unsignedShort: 0743 case Exiv2::unsignedLong: 0744 case Exiv2::signedShort: 0745 case Exiv2::signedLong: 0746 if (it->count() > component) 0747 #if EXIV2_TEST_VERSION(0,28,0) 0748 return QVariant((int)it->toUint32(component)); 0749 #else 0750 return QVariant((int)it->toLong(component)); 0751 #endif 0752 else 0753 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0754 return QVariant(QMetaType(QMetaType::Int)); 0755 #else 0756 return QVariant(QVariant::Int); 0757 #endif 0758 case Exiv2::unsignedRational: 0759 case Exiv2::signedRational: 0760 0761 if (rationalAsListOfInts) 0762 { 0763 if (it->count() <= component) 0764 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0765 return QVariant(QMetaType(QMetaType::QVariantList)); 0766 #else 0767 return QVariant(QVariant::List); 0768 #endif 0769 0770 QList<QVariant> list; 0771 list << (*it).toRational(component).first; 0772 list << (*it).toRational(component).second; 0773 0774 return QVariant(list); 0775 } 0776 else 0777 { 0778 if (it->count() <= component) 0779 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0780 return QVariant(QMetaType(QMetaType::Double)); 0781 #else 0782 return QVariant(QVariant::Double); 0783 #endif 0784 0785 // prefer double precision 0786 double num = (*it).toRational(component).first; 0787 double den = (*it).toRational(component).second; 0788 0789 if (den == 0.0) 0790 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0791 return QVariant(QMetaType(QMetaType::Double)); 0792 #else 0793 return QVariant(QVariant::Double); 0794 #endif 0795 0796 return QVariant(num / den); 0797 } 0798 case Exiv2::date: 0799 case Exiv2::time: 0800 { 0801 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate); 0802 return QVariant(dateTime); 0803 } 0804 case Exiv2::asciiString: 0805 case Exiv2::comment: 0806 case Exiv2::string: 0807 { 0808 std::ostringstream os; 0809 os << *it; 0810 QString tagValue = QString::fromLocal8Bit(os.str().c_str()); 0811 0812 if (stringEscapeCR) 0813 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" ")); 0814 0815 return QVariant(tagValue); 0816 } 0817 default: 0818 break; 0819 } 0820 } 0821 } 0822 catch( Exiv2::Error& e ) 0823 { 0824 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' in the image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e); 0825 } 0826 catch(...) 0827 { 0828 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0829 } 0830 0831 return QVariant(); 0832 } 0833 0834 QString KExiv2::getExifTagString(const char* exifTagName, bool escapeCR) const 0835 { 0836 try 0837 { 0838 Exiv2::ExifKey exifKey(exifTagName); 0839 Exiv2::ExifData exifData(d->exifMetadata()); 0840 Exiv2::ExifData::iterator it = exifData.findKey(exifKey); 0841 0842 if (it != exifData.end()) 0843 { 0844 // See B.K.O #184156 comment #13 0845 std::string val = it->print(&exifData); 0846 QString tagValue = QString::fromLocal8Bit(val.c_str()); 0847 0848 if (escapeCR) 0849 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" ")); 0850 0851 return tagValue; 0852 } 0853 } 0854 catch( Exiv2::Error& e ) 0855 { 0856 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e); 0857 } 0858 catch(...) 0859 { 0860 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0861 } 0862 0863 return QString(); 0864 } 0865 0866 bool KExiv2::setExifTagString(const char* exifTagName, const QString& value, bool setProgramName) const 0867 { 0868 if (!setProgramId(setProgramName)) 0869 return false; 0870 0871 try 0872 { 0873 d->exifMetadata()[exifTagName] = std::string(value.toLatin1().constData()); 0874 return true; 0875 } 0876 catch( Exiv2::Error& e ) 0877 { 0878 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag string into image using Exiv2 "), e); 0879 } 0880 catch(...) 0881 { 0882 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0883 } 0884 0885 return false; 0886 } 0887 0888 QImage KExiv2::getExifThumbnail(bool fixOrientation) const 0889 { 0890 QImage thumbnail; 0891 0892 if (d->exifMetadata().empty()) 0893 return thumbnail; 0894 0895 try 0896 { 0897 Exiv2::ExifThumbC thumb(d->exifMetadata()); 0898 Exiv2::DataBuf const c1 = thumb.copy(); 0899 #if EXIV2_TEST_VERSION(0,28,0) 0900 thumbnail.loadFromData(c1.c_data(), c1.size()); 0901 #else 0902 thumbnail.loadFromData(c1.pData_, c1.size_); 0903 #endif 0904 0905 if (!thumbnail.isNull()) 0906 { 0907 if (fixOrientation) 0908 { 0909 Exiv2::ExifKey key1("Exif.Thumbnail.Orientation"); 0910 Exiv2::ExifKey key2("Exif.Image.Orientation"); 0911 Exiv2::ExifData exifData(d->exifMetadata()); 0912 Exiv2::ExifData::iterator it = exifData.findKey(key1); 0913 0914 if (it == exifData.end()) 0915 it = exifData.findKey(key2); 0916 0917 if (it != exifData.end() && it->count()) 0918 { 0919 #if EXIV2_TEST_VERSION(0,28,0) 0920 long orientation = it->toUint32(); 0921 #else 0922 long orientation = it->toLong(); 0923 #endif 0924 qCDebug(LIBKEXIV2_LOG) << "Exif Thumbnail Orientation: " << (int)orientation; 0925 rotateExifQImage(thumbnail, (ImageOrientation)orientation); 0926 } 0927 0928 return thumbnail; 0929 } 0930 } 0931 } 0932 catch( Exiv2::Error& e ) 0933 { 0934 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Exif Thumbnail using Exiv2 "), e); 0935 } 0936 catch(...) 0937 { 0938 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0939 } 0940 0941 return thumbnail; 0942 } 0943 0944 bool KExiv2::rotateExifQImage(QImage& image, ImageOrientation orientation) const 0945 { 0946 QTransform matrix = RotationMatrix::toTransform(orientation); 0947 0948 if ((orientation != ORIENTATION_NORMAL) && (orientation != ORIENTATION_UNSPECIFIED)) 0949 { 0950 image = image.transformed(matrix); 0951 return true; 0952 } 0953 0954 return false; 0955 } 0956 0957 bool KExiv2::setExifThumbnail(const QImage& thumbImage, bool setProgramName) const 0958 { 0959 if (!setProgramId(setProgramName)) 0960 return false; 0961 0962 if (thumbImage.isNull()) 0963 { 0964 return removeExifThumbnail(); 0965 } 0966 0967 try 0968 { 0969 QByteArray data; 0970 QBuffer buffer(&data); 0971 buffer.open(QIODevice::WriteOnly); 0972 thumbImage.save(&buffer, "JPEG"); 0973 Exiv2::ExifThumb thumb(d->exifMetadata()); 0974 thumb.setJpegThumbnail((Exiv2::byte *)data.data(), data.size()); 0975 return true; 0976 } 0977 catch( Exiv2::Error& e ) 0978 { 0979 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif Thumbnail using Exiv2 "), e); 0980 } 0981 catch(...) 0982 { 0983 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 0984 } 0985 0986 return false; 0987 } 0988 0989 bool KExiv2::setTiffThumbnail(const QImage& thumbImage, bool setProgramName) const 0990 { 0991 if (!setProgramId(setProgramName)) 0992 return false; 0993 0994 removeExifThumbnail(); 0995 0996 try 0997 { 0998 // Make sure IFD0 is explicitly marked as a main image 0999 Exiv2::ExifData::const_iterator pos = d->exifMetadata().findKey(Exiv2::ExifKey("Exif.Image.NewSubfileType")); 1000 1001 #if EXIV2_TEST_VERSION(0,28,0) 1002 if (pos == d->exifMetadata().end() || pos->count() != 1 || pos->toUint32() != 0) 1003 #else 1004 if (pos == d->exifMetadata().end() || pos->count() != 1 || pos->toLong() != 0) 1005 #endif 1006 { 1007 #if EXIV2_TEST_VERSION(0,28,0) 1008 throw Exiv2::Error(Exiv2::ErrorCode::kerErrorMessage, "Exif.Image.NewSubfileType missing or not set as main image"); 1009 #elif EXIV2_TEST_VERSION(0,27,0) 1010 throw Exiv2::Error(Exiv2::kerErrorMessage, "Exif.Image.NewSubfileType missing or not set as main image"); 1011 #else 1012 throw Exiv2::Error(1, "Exif.Image.NewSubfileType missing or not set as main image"); 1013 #endif 1014 } 1015 1016 // Remove sub-IFD tags 1017 std::string subImage1("SubImage1"); 1018 1019 for (Exiv2::ExifData::iterator md = d->exifMetadata().begin(); md != d->exifMetadata().end();) 1020 { 1021 if (md->groupName() == subImage1) 1022 md = d->exifMetadata().erase(md); 1023 else 1024 ++md; 1025 } 1026 1027 if (!thumbImage.isNull()) 1028 { 1029 // Set thumbnail tags 1030 QByteArray data; 1031 QBuffer buffer(&data); 1032 buffer.open(QIODevice::WriteOnly); 1033 thumbImage.save(&buffer, "JPEG"); 1034 1035 Exiv2::DataBuf buf((Exiv2::byte *)data.data(), data.size()); 1036 Exiv2::ULongValue val; 1037 val.read("0"); 1038 #if EXIV2_TEST_VERSION(0,28,0) 1039 val.setDataArea(buf.c_data(), buf.size()); 1040 #else 1041 val.setDataArea(buf.pData_, buf.size_); 1042 #endif 1043 d->exifMetadata()["Exif.SubImage1.JPEGInterchangeFormat"] = val; 1044 #if EXIV2_TEST_VERSION(0,28,0) 1045 d->exifMetadata()["Exif.SubImage1.JPEGInterchangeFormatLength"] = uint32_t(buf.size()); 1046 #else 1047 d->exifMetadata()["Exif.SubImage1.JPEGInterchangeFormatLength"] = uint32_t(buf.size_); 1048 #endif 1049 d->exifMetadata()["Exif.SubImage1.Compression"] = uint16_t(6); // JPEG (old-style) 1050 d->exifMetadata()["Exif.SubImage1.NewSubfileType"] = uint32_t(1); // Thumbnail image 1051 return true; 1052 } 1053 } 1054 catch( Exiv2::Error& e ) 1055 { 1056 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set TIFF Thumbnail using Exiv2 "), e); 1057 } 1058 catch(...) 1059 { 1060 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 1061 } 1062 1063 return false; 1064 } 1065 1066 bool KExiv2::removeExifThumbnail() const 1067 { 1068 try 1069 { 1070 // Remove all IFD0 subimages. 1071 Exiv2::ExifThumb thumb(d->exifMetadata()); 1072 thumb.erase(); 1073 return true; 1074 } 1075 catch( Exiv2::Error& e ) 1076 { 1077 d->printExiv2ExceptionError(QString::fromLatin1("Cannot remove Exif Thumbnail using Exiv2 "), e); 1078 } 1079 catch(...) 1080 { 1081 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 1082 } 1083 1084 return false; 1085 } 1086 1087 KExiv2::TagsMap KExiv2::getStdExifTagsList() const 1088 { 1089 try 1090 { 1091 QList<const Exiv2::TagInfo*> tags; 1092 TagsMap tagsMap; 1093 1094 const Exiv2::GroupInfo* gi = Exiv2::ExifTags::groupList(); 1095 1096 while (gi->tagList_ != nullptr) 1097 { 1098 if (QString::fromLatin1(gi->ifdName_) != QString::fromLatin1("Makernote")) 1099 { 1100 Exiv2::TagListFct tl = gi->tagList_; 1101 const Exiv2::TagInfo* ti = tl(); 1102 1103 while (ti->tag_ != 0xFFFF) 1104 { 1105 tags << ti; 1106 ++ti; 1107 } 1108 } 1109 ++gi; 1110 } 1111 1112 for (QList<const Exiv2::TagInfo*>::iterator it = tags.begin(); it != tags.end(); ++it) 1113 { 1114 do 1115 { 1116 const Exiv2::TagInfo* const ti = *it; 1117 QString key = QLatin1String(Exiv2::ExifKey(*ti).key().c_str()); 1118 QStringList values; 1119 values << QString::fromLatin1(ti->name_) << QString::fromLatin1(ti->title_) << QString::fromLatin1(ti->desc_); 1120 tagsMap.insert(key, values); 1121 ++(*it); 1122 } 1123 while((*it)->tag_ != 0xffff); 1124 } 1125 return tagsMap; 1126 } 1127 catch( Exiv2::Error& e ) 1128 { 1129 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Exif Tags list using Exiv2 "), e); 1130 } 1131 catch(...) 1132 { 1133 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 1134 } 1135 1136 return TagsMap(); 1137 } 1138 1139 KExiv2::TagsMap KExiv2::getMakernoteTagsList() const 1140 { 1141 try 1142 { 1143 QList<const Exiv2::TagInfo*> tags; 1144 TagsMap tagsMap; 1145 1146 const Exiv2::GroupInfo* gi = Exiv2::ExifTags::groupList(); 1147 1148 while (gi->tagList_ != nullptr) 1149 { 1150 if (QString::fromLatin1(gi->ifdName_) == QString::fromLatin1("Makernote")) 1151 { 1152 Exiv2::TagListFct tl = gi->tagList_; 1153 const Exiv2::TagInfo* ti = tl(); 1154 1155 while (ti->tag_ != 0xFFFF) 1156 { 1157 tags << ti; 1158 ++ti; 1159 } 1160 } 1161 ++gi; 1162 } 1163 1164 for (QList<const Exiv2::TagInfo*>::iterator it = tags.begin(); it != tags.end(); ++it) 1165 { 1166 do 1167 { 1168 const Exiv2::TagInfo* const ti = *it; 1169 QString key = QLatin1String(Exiv2::ExifKey(*ti).key().c_str()); 1170 QStringList values; 1171 values << QString::fromLatin1(ti->name_) << QString::fromLatin1(ti->title_) << QString::fromLatin1(ti->desc_); 1172 tagsMap.insert(key, values); 1173 ++(*it); 1174 } 1175 while((*it)->tag_ != 0xffff); 1176 } 1177 1178 return tagsMap; 1179 } 1180 catch( Exiv2::Error& e ) 1181 { 1182 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Makernote Tags list using Exiv2 "), e); 1183 } 1184 catch(...) 1185 { 1186 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2"; 1187 } 1188 1189 return TagsMap(); 1190 } 1191 1192 } // NameSpace KExiv2Iface