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

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