File indexing completed on 2025-01-05 03:56:27

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2006-09-15
0007  * Description : Exiv2 library interface.
0008  *               Exif manipulation methods
0009  *
0010  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "metaengine_p.h"
0018 
0019 // C++ includes
0020 
0021 #include <cctype>
0022 
0023 // Qt includes
0024 
0025 #include <QBuffer>
0026 
0027 // KDE includes
0028 
0029 #include <klocalizedstring.h>
0030 
0031 // Local includes
0032 
0033 #include "metaengine_rotation.h"
0034 #include "digikam_config.h"
0035 #include "digikam_debug.h"
0036 
0037 #if defined(Q_CC_CLANG)
0038 #   pragma clang diagnostic push
0039 #   pragma clang diagnostic ignored "-Wdeprecated-declarations"
0040 #endif
0041 
0042 namespace Digikam
0043 {
0044 
0045 bool MetaEngine::canWriteExif(const QString& filePath)
0046 {
0047     QMutexLocker lock(&s_metaEngineMutex);
0048 
0049     try
0050     {
0051 
0052 #if defined Q_OS_WIN && defined EXV_UNICODE_PATH
0053 
0054         Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((const wchar_t*)filePath.utf16());
0055 
0056 #elif defined Q_OS_WIN
0057 
0058         Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(QFile::encodeName(filePath).constData());
0059 
0060 #else
0061 
0062         Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filePath.toUtf8().constData());
0063 
0064 #endif
0065 
0066         Exiv2::AccessMode mode      = image->checkMode(Exiv2::mdExif);
0067 
0068         return ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite));
0069     }
0070     catch (Exiv2::AnyError& e)
0071     {
0072         qCCritical(DIGIKAM_METAENGINE_LOG) << "Cannot check Exif access mode with Exiv2:(Error #"
0073                                            << (int)e.code() << ": " << QString::fromStdString(e.what()) << ")";
0074     }
0075     catch (...)
0076     {
0077         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0078     }
0079 
0080     return false;
0081 }
0082 
0083 bool MetaEngine::hasExif() const
0084 {
0085     return !d->exifMetadata().empty();
0086 }
0087 
0088 bool MetaEngine::clearExif() const
0089 {
0090     QMutexLocker lock(&s_metaEngineMutex);
0091 
0092     try
0093     {
0094         d->exifMetadata().clear();
0095 
0096         return true;
0097     }
0098     catch (Exiv2::AnyError& e)
0099     {
0100         d->printExiv2ExceptionError(QLatin1String("Cannot clear Exif data with Exiv2:"), e);
0101     }
0102     catch (...)
0103     {
0104         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0105     }
0106 
0107     return false;
0108 }
0109 
0110 QByteArray MetaEngine::getExifEncoded(bool addExifHeader) const
0111 {
0112     QMutexLocker lock(&s_metaEngineMutex);
0113 
0114     try
0115     {
0116         if (!d->exifMetadata().empty())
0117         {
0118             QByteArray data;
0119             Exiv2::ExifData& exif = d->exifMetadata();
0120             Exiv2::Blob blob;
0121             Exiv2::ExifParser::encode(blob, Exiv2::bigEndian, exif);
0122             QByteArray ba((const char*)&blob[0], (int)blob.size());
0123 
0124             if (addExifHeader)
0125             {
0126                 const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
0127                 data.resize(ba.size() + sizeof(ExifHeader));
0128                 memcpy(data.data(), ExifHeader, sizeof(ExifHeader));
0129                 memcpy(data.data() + sizeof(ExifHeader), ba.data(), ba.size());
0130             }
0131             else
0132             {
0133                 data = ba;
0134             }
0135 
0136             return data;
0137         }
0138     }
0139     catch (Exiv2::AnyError& e)
0140     {
0141         if (!d->filePath.isEmpty())
0142         {
0143             qCDebug(DIGIKAM_METAENGINE_LOG) << "From file " << d->filePath.toLatin1().constData();
0144         }
0145 
0146         d->printExiv2ExceptionError(QLatin1String("Cannot get Exif data with Exiv2:"), e);
0147     }
0148     catch (...)
0149     {
0150         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0151     }
0152 
0153     return QByteArray();
0154 }
0155 
0156 bool MetaEngine::setExif(const QByteArray& data) const
0157 {
0158     QMutexLocker lock(&s_metaEngineMutex);
0159 
0160     try
0161     {
0162         if (!data.isEmpty())
0163         {
0164             Exiv2::ExifParser::decode(d->exifMetadata(), (const Exiv2::byte*)data.data(), data.size());
0165 
0166             return (!d->exifMetadata().empty());
0167         }
0168     }
0169     catch (Exiv2::AnyError& e)
0170     {
0171         if (!d->filePath.isEmpty())
0172         {
0173             qCCritical(DIGIKAM_METAENGINE_LOG) << "From file " << d->filePath.toLatin1().constData();
0174         }
0175 
0176         d->printExiv2ExceptionError(QLatin1String("Cannot set Exif data with Exiv2:"), e);
0177     }
0178     catch (...)
0179     {
0180         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0181     }
0182 
0183     return false;
0184 }
0185 
0186 QString MetaEngine::getExifComment(bool readDescription) const
0187 {
0188     QString comment = getExifTagComment("Exif.Photo.UserComment");
0189 
0190     if (!comment.isEmpty())
0191     {
0192         return comment;
0193     }
0194 
0195     if (readDescription)
0196     {
0197         comment = getExifTagComment("Exif.Image.ImageDescription");
0198 
0199         return comment;
0200     }
0201 
0202     return QString();
0203 }
0204 
0205 QString MetaEngine::getExifTagComment(const char* exifTagName) const
0206 {
0207     QMutexLocker lock(&s_metaEngineMutex);
0208 
0209     try
0210     {
0211         // Since Exiv2-0.27.3 empty comment fields are output
0212         // as "binary comment". As workaround we filter it out.
0213 
0214         QStringList blackList;
0215         blackList << QLatin1String("binary comment");
0216 
0217         if (!d->exifMetadata().empty())
0218         {
0219             std::string exifkey(exifTagName);
0220             Exiv2::ExifData exifData(d->exifMetadata());
0221             Exiv2::ExifKey key(exifkey);
0222             Exiv2::ExifData::const_iterator it = exifData.findKey(key);
0223 
0224             if (it != exifData.end())
0225             {
0226                 QString exifComment;
0227 
0228                 if (QLatin1String(exifTagName) == QLatin1String("Exif.Image.XPComment"))
0229                 {
0230                     exifComment = QString::fromStdString(it->print(&exifData));
0231                 }
0232                 else
0233                 {
0234                     exifComment = d->convertCommentValue(*it);
0235                 }
0236 
0237                 QString trimmedComment = exifComment.trimmed();
0238 
0239                 // Some cameras fill in nonsense default values
0240 
0241                 if (QLatin1String(exifTagName) == QLatin1String("Exif.Image.ImageDescription"))
0242                 {
0243                     blackList << QLatin1String("SONY DSC"); // + whitespace
0244                     blackList << QLatin1String("OLYMPUS DIGITAL CAMERA");
0245                     blackList << QLatin1String("MINOLTA DIGITAL CAMERA");
0246                 }
0247 
0248                 // some cameras fill the UserComment with whitespace
0249 
0250                 if (!exifComment.isEmpty() && !trimmedComment.isEmpty() && !blackList.contains(trimmedComment))
0251                 {
0252                     return exifComment;
0253                 }
0254             }
0255         }
0256     }
0257     catch (Exiv2::AnyError& e)
0258     {
0259         d->printExiv2ExceptionError(QLatin1String("Cannot find Exif User Comment with Exiv2:"), e);
0260     }
0261     catch (...)
0262     {
0263         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0264     }
0265 
0266     return QString();
0267 }
0268 
0269 static bool is7BitAscii(const QString& s)
0270 {
0271     for (int i = 0 ; i < s.size() ; ++i)
0272     {
0273         if (s.at(i).unicode() > 0x7f)
0274         {
0275             return false;
0276         }
0277     }
0278 
0279     return true;
0280 }
0281 
0282 bool MetaEngine::setExifComment(const QString& comment, bool writeDescription) const
0283 {
0284     QMutexLocker lock(&s_metaEngineMutex);
0285 
0286     try
0287     {
0288         if (writeDescription)
0289         {
0290             removeExifTag("Exif.Image.ImageDescription");
0291         }
0292 
0293         removeExifTag("Exif.Photo.UserComment");
0294 
0295         if (!comment.isNull())
0296         {
0297             if (writeDescription)
0298             {
0299                 setExifTagString("Exif.Image.ImageDescription", comment);
0300             }
0301 
0302             // Write as Unicode only when necessary.
0303             // Check if it's in the ASCII 7bit range
0304 
0305             if (is7BitAscii(comment))
0306             {
0307                 // write as ASCII
0308 
0309                 QString exifComment(QString::fromLatin1("charset=Ascii %1").arg(comment));
0310                 d->exifMetadata()["Exif.Photo.UserComment"] = exifComment.toStdString();
0311 
0312                 return true;
0313             }
0314 
0315             // write as Unicode (UCS-2)
0316 
0317             QString exifComment(QString::fromUtf8("charset=Unicode %1").arg(comment));
0318             d->exifMetadata()["Exif.Photo.UserComment"] = exifComment.toStdString();
0319         }
0320 
0321         return true;
0322     }
0323     catch (Exiv2::AnyError& e)
0324     {
0325         d->printExiv2ExceptionError(QLatin1String("Cannot set Exif Comment with Exiv2:"), e);
0326     }
0327     catch (...)
0328     {
0329         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0330     }
0331 
0332     return false;
0333 }
0334 
0335 QString MetaEngine::getExifTagTitle(const char* exifTagName)
0336 {
0337     QMutexLocker lock(&s_metaEngineMutex);
0338 
0339     try
0340     {
0341         std::string exifkey(exifTagName);
0342         Exiv2::ExifKey ek(exifkey);
0343 
0344         return QString::fromStdString(ek.tagLabel());
0345     }
0346     catch (Exiv2::AnyError& e)
0347     {
0348         d->printExiv2ExceptionError(QLatin1String("Cannot get metadata tag title with Exiv2:"), e);
0349     }
0350     catch (...)
0351     {
0352         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0353     }
0354 
0355     return QString();
0356 }
0357 
0358 QString MetaEngine::getExifTagDescription(const char* exifTagName)
0359 {
0360     QMutexLocker lock(&s_metaEngineMutex);
0361 
0362     try
0363     {
0364         std::string exifkey(exifTagName);
0365         Exiv2::ExifKey ek(exifkey);
0366 
0367         return QString::fromStdString(ek.tagDesc());
0368     }
0369     catch (Exiv2::AnyError& e)
0370     {
0371         d->printExiv2ExceptionError(QLatin1String("Cannot get metadata tag description with Exiv2:"), e);
0372     }
0373     catch (...)
0374     {
0375         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0376     }
0377 
0378     return QString();
0379 }
0380 
0381 bool MetaEngine::removeExifTag(const char* exifTagName) const
0382 {
0383     QMutexLocker lock(&s_metaEngineMutex);
0384 
0385     try
0386     {
0387         Exiv2::ExifKey exifKey(exifTagName);
0388         Exiv2::ExifData::iterator it = d->exifMetadata().findKey(exifKey);
0389 
0390         if (it != d->exifMetadata().end())
0391         {
0392             d->exifMetadata().erase(it);
0393 
0394             return true;
0395         }
0396     }
0397     catch (Exiv2::AnyError& e)
0398     {
0399         d->printExiv2ExceptionError(QLatin1String("Cannot remove Exif tag with Exiv2:"), e);
0400     }
0401     catch (...)
0402     {
0403         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0404     }
0405 
0406     return false;
0407 }
0408 
0409 bool MetaEngine::getExifTagRational(const char* exifTagName, long int& num, long int& den, int component) const
0410 {
0411     QMutexLocker lock(&s_metaEngineMutex);
0412 
0413     try
0414     {
0415         Exiv2::ExifKey exifKey(exifTagName);
0416         Exiv2::ExifData exifData(d->exifMetadata());
0417         Exiv2::ExifData::const_iterator it = exifData.findKey(exifKey);
0418 
0419         if (it != exifData.end())
0420         {
0421             if ((*it).count())
0422             {
0423                 num = (*it).toRational(component).first;
0424                 den = (*it).toRational(component).second;
0425 
0426                 return true;
0427             }
0428         }
0429     }
0430     catch (Exiv2::AnyError& e)
0431     {
0432         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif Rational value from key '%1' into image with Exiv2:")
0433                                     .arg(QLatin1String(exifTagName)), e);
0434     }
0435     catch (...)
0436     {
0437         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0438     }
0439 
0440     return false;
0441 }
0442 
0443 bool MetaEngine::setExifTagLong(const char* exifTagName, long val) const
0444 {
0445     QMutexLocker lock(&s_metaEngineMutex);
0446 
0447     try
0448     {
0449         d->exifMetadata()[exifTagName] = static_cast<int32_t>(val);     // krazy:exclude=typedefs
0450 
0451         return true;
0452     }
0453     catch (Exiv2::AnyError& e)
0454     {
0455         d->printExiv2ExceptionError(QLatin1String("Cannot set Exif tag long value into image with Exiv2:"), e);
0456     }
0457     catch (...)
0458     {
0459         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0460     }
0461 
0462     return false;
0463 }
0464 
0465 bool MetaEngine::setExifTagRational(const char* exifTagName, long int num, long int den) const
0466 {
0467     QMutexLocker lock(&s_metaEngineMutex);
0468 
0469     try
0470     {
0471         d->exifMetadata()[exifTagName] = Exiv2::Rational(num, den);
0472 
0473         return true;
0474     }
0475     catch (Exiv2::AnyError& e)
0476     {
0477         d->printExiv2ExceptionError(QLatin1String("Cannot set Exif tag rational value into image with Exiv2:"), e);
0478     }
0479     catch (...)
0480     {
0481         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0482     }
0483 
0484     return false;
0485 }
0486 
0487 bool MetaEngine::setExifTagData(const char* exifTagName, const QByteArray& data) const
0488 {
0489     if (data.isEmpty())
0490     {
0491         return false;
0492     }
0493 
0494     QMutexLocker lock(&s_metaEngineMutex);
0495 
0496     try
0497     {
0498         Exiv2::DataValue val((Exiv2::byte*)data.data(), data.size());
0499         d->exifMetadata()[exifTagName] = val;
0500 
0501         return true;
0502     }
0503     catch (Exiv2::AnyError& e)
0504     {
0505         d->printExiv2ExceptionError(QLatin1String("Cannot set Exif tag data into image with Exiv2:"), e);
0506     }
0507     catch (...)
0508     {
0509         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0510     }
0511 
0512     return false;
0513 }
0514 
0515 bool MetaEngine::setExifTagVariant(const char* exifTagName, const QVariant& val,
0516                                    bool rationalWantSmallDenominator) const
0517 {
0518 
0519 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0520 
0521     switch (val.typeId())
0522 
0523 #else
0524 
0525     switch (val.type())
0526 
0527 #endif
0528 
0529     {
0530         case QVariant::Int:
0531         case QVariant::UInt:
0532         case QVariant::Bool:
0533         case QVariant::LongLong:
0534         case QVariant::ULongLong:
0535         {
0536             return setExifTagLong(exifTagName, val.toInt());
0537         }
0538 
0539         case QVariant::Double:
0540         {
0541             long num, den;
0542 
0543             if (rationalWantSmallDenominator)
0544             {
0545                 convertToRationalSmallDenominator(val.toDouble(), &num, &den);
0546             }
0547             else
0548             {
0549                 convertToRational(val.toDouble(), &num, &den, 4);
0550             }
0551 
0552             return setExifTagRational(exifTagName, num, den);
0553         }
0554 
0555         case QVariant::List:
0556         {
0557             long num = 0, den = 1;
0558             QList<QVariant> list = val.toList();
0559 
0560             if (list.size() >= 1)
0561             {
0562                 num = list[0].toInt();
0563             }
0564 
0565             if (list.size() >= 2)
0566             {
0567                 den = list[1].toInt();
0568             }
0569 
0570             return setExifTagRational(exifTagName, num, den);
0571         }
0572 
0573         case QVariant::Date:
0574         case QVariant::DateTime:
0575         {
0576             QDateTime dateTime = val.toDateTime();
0577             dateTime.setTimeSpec(Qt::UTC);
0578 
0579             if (!dateTime.isValid())
0580             {
0581                 return false;
0582             }
0583 
0584             try
0585             {
0586                 d->exifMetadata()[exifTagName] = dateTime.toString(QLatin1String("yyyy:MM:dd hh:mm:ss")).toStdString();
0587             }
0588             catch (Exiv2::AnyError& e)
0589             {
0590                 d->printExiv2ExceptionError(QLatin1String("Cannot set Date & Time in image with Exiv2:"), e);
0591             }
0592             catch (...)
0593             {
0594                 qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0595             }
0596 
0597             return false;
0598         }
0599 
0600         case QVariant::String:
0601         case QVariant::Char:
0602         {
0603             return setExifTagString(exifTagName, val.toString());
0604         }
0605 
0606         case QVariant::ByteArray:
0607         {
0608             return setExifTagData(exifTagName, val.toByteArray());
0609         }
0610 
0611         default:
0612         {
0613             break;
0614         }
0615     }
0616 
0617     return false;
0618 }
0619 
0620 QString MetaEngine::createExifUserStringFromValue(const char* exifTagName, const QVariant& val, bool escapeCR)
0621 {
0622     QMutexLocker lock(&s_metaEngineMutex);
0623 
0624     try
0625     {
0626         Exiv2::ExifKey key(exifTagName);
0627         Exiv2::Exifdatum datum(key);
0628 
0629 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0630 
0631         switch (val.typeId())
0632 
0633 #else
0634 
0635         switch (val.type())
0636 
0637 #endif
0638 
0639         {
0640             case QVariant::Int:
0641             case QVariant::Bool:
0642             case QVariant::LongLong:
0643             case QVariant::ULongLong:
0644             {
0645                 datum = (int32_t)val.toInt();
0646                 break;
0647             }
0648 
0649             case QVariant::UInt:
0650             {
0651                 datum = (uint32_t)val.toUInt();
0652                 break;
0653             }
0654 
0655             case QVariant::Double:
0656             {
0657                 long num, den;
0658                 convertToRationalSmallDenominator(val.toDouble(), &num, &den);
0659                 Exiv2::Rational rational;
0660                 rational.first  = num;
0661                 rational.second = den;
0662                 datum           = rational;
0663                 break;
0664             }
0665 
0666             case QVariant::List:
0667             {
0668                 long num             = 0;
0669                 long den             = 1;
0670                 QList<QVariant> list = val.toList();
0671 
0672                 if (list.size() >= 1)
0673                 {
0674                     num = list[0].toInt();
0675                 }
0676 
0677                 if (list.size() >= 2)
0678                 {
0679                     den = list[1].toInt();
0680                 }
0681 
0682                 Exiv2::Rational rational;
0683                 rational.first  = num;
0684                 rational.second = den;
0685                 datum           = rational;
0686                 break;
0687             }
0688 
0689             case QVariant::Date:
0690             case QVariant::DateTime:
0691             {
0692                 QDateTime dateTime = val.toDateTime();
0693                 dateTime.setTimeSpec(Qt::UTC);
0694 
0695                 if (!dateTime.isValid())
0696                 {
0697                     break;
0698                 }
0699 
0700                 datum = dateTime.toString(QLatin1String("yyyy:MM:dd hh:mm:ss")).toStdString();
0701                 break;
0702             }
0703 
0704             case QVariant::ByteArray:
0705             case QVariant::String:
0706             case QVariant::Char:
0707             {
0708                 datum = val.toString().toStdString();
0709                 break;
0710             }
0711 
0712             default:
0713             {
0714                 break;
0715             }
0716         }
0717 
0718         std::ostringstream os;
0719         os << datum;
0720         QString tagValue = QString::fromStdString(os.str());
0721 
0722         if (escapeCR)
0723         {
0724             tagValue.replace(QLatin1Char('\n'), QLatin1String(" "));
0725         }
0726 
0727         return tagValue;
0728     }
0729     catch (Exiv2::AnyError& e)
0730     {
0731         d->printExiv2ExceptionError(QLatin1String("Cannot get Exif tag user string with Exiv2:"), e);
0732     }
0733     catch (...)
0734     {
0735         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0736     }
0737 
0738     return QString();
0739 }
0740 
0741 bool MetaEngine::getExifTagLong(const char* exifTagName, long& val) const
0742 {
0743     return getExifTagLong(exifTagName, val, 0);
0744 }
0745 
0746 bool MetaEngine::getExifTagLong(const char* exifTagName, long& val, int component) const
0747 {
0748     QMutexLocker lock(&s_metaEngineMutex);
0749 
0750     try
0751     {
0752         Exiv2::ExifKey exifKey(exifTagName);
0753         Exiv2::ExifData exifData(d->exifMetadata());
0754         Exiv2::ExifData::const_iterator it = exifData.findKey(exifKey);
0755 
0756         if ((it != exifData.end()) && (it->count() > 0))
0757         {
0758 #if EXIV2_TEST_VERSION(0,27,99)
0759             val = it->toInt64(component);
0760 #else
0761             val = it->toLong(component);
0762 #endif
0763             return true;
0764         }
0765     }
0766     catch (Exiv2::AnyError& e)
0767     {
0768         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image with Exiv2:")
0769                                     .arg(QLatin1String(exifTagName)), e);
0770     }
0771     catch (...)
0772     {
0773         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0774     }
0775 
0776     return false;
0777 }
0778 
0779 QByteArray MetaEngine::getExifTagData(const char* exifTagName) const
0780 {
0781     QMutexLocker lock(&s_metaEngineMutex);
0782 
0783     try
0784     {
0785         Exiv2::ExifKey exifKey(exifTagName);
0786         Exiv2::ExifData exifData(d->exifMetadata());
0787         Exiv2::ExifData::const_iterator it = exifData.findKey(exifKey);
0788 
0789         if (it != exifData.end())
0790         {
0791             QByteArray data((*it).size(), '\0');
0792             (*it).copy((Exiv2::byte*)data.data(), Exiv2::bigEndian);
0793 
0794             return data;
0795         }
0796     }
0797     catch (Exiv2::AnyError& e)
0798     {
0799         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image with Exiv2:")
0800                                     .arg(QLatin1String(exifTagName)), e);
0801     }
0802     catch (...)
0803     {
0804         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0805     }
0806 
0807     return QByteArray();
0808 }
0809 
0810 QVariant MetaEngine::getExifTagVariant(const char* exifTagName, bool rationalAsListOfInts, bool stringEscapeCR, int component) const
0811 {
0812     QMutexLocker lock(&s_metaEngineMutex);
0813 
0814     try
0815     {
0816         Exiv2::ExifKey exifKey(exifTagName);
0817         Exiv2::ExifData exifData(d->exifMetadata());
0818         Exiv2::ExifData::const_iterator it = exifData.findKey(exifKey);
0819 
0820         if (it != exifData.end())
0821         {
0822             switch (it->typeId())
0823             {
0824                 case Exiv2::unsignedByte:
0825                 case Exiv2::unsignedShort:
0826                 case Exiv2::unsignedLong:
0827                 case Exiv2::signedShort:
0828                 case Exiv2::signedLong:
0829                 {
0830                     if ((int)it->count() > component)
0831                     {
0832 #if EXIV2_TEST_VERSION(0,27,99)
0833                         return QVariant((int)it->toInt64(component));
0834 #else
0835                         return QVariant((int)it->toLong(component));
0836 #endif
0837                     }
0838                     else
0839                     {
0840 
0841 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0842 
0843                         return QVariant(QMetaType(QMetaType::Int));
0844 
0845 #else
0846 
0847                         return QVariant(QVariant::Int);
0848 
0849 #endif
0850 
0851                     }
0852                 }
0853 
0854                 case Exiv2::unsignedRational:
0855                 case Exiv2::signedRational:
0856                 {
0857                     if (rationalAsListOfInts)
0858                     {
0859                         if ((int)it->count() <= component)
0860                         {
0861 
0862 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0863 
0864                             return QVariant(QMetaType(QMetaType::QVariantList));
0865 
0866 #else
0867 
0868                             return QVariant(QVariant::List);
0869 
0870 #endif
0871 
0872                         }
0873 
0874                         QList<QVariant> list;
0875                         list << (*it).toRational(component).first;
0876                         list << (*it).toRational(component).second;
0877 
0878 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0879 
0880                         return QVariant(QMetaType(QMetaType::QVariantList));
0881 
0882 #else
0883 
0884                         return QVariant(list);
0885 
0886 #endif
0887 
0888                     }
0889                     else
0890                     {
0891                         if ((int)it->count() <= component)
0892                         {
0893 
0894 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0895 
0896                             return QVariant(QMetaType(QMetaType::Double));
0897 
0898 #else
0899 
0900                             return QVariant(QVariant::Double);
0901 
0902 #endif
0903 
0904                         }
0905 
0906                         // prefer double precision
0907 
0908                         double num = (*it).toRational(component).first;
0909                         double den = (*it).toRational(component).second;
0910 
0911                         if (den == 0.0)
0912                         {
0913 
0914 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0915 
0916                             return QVariant(QMetaType(QMetaType::Double));
0917 
0918 #else
0919 
0920                             return QVariant(QVariant::Double);
0921 
0922 #endif
0923 
0924                         }
0925 
0926                         return QVariant(num / den);
0927                     }
0928                 }
0929 
0930                 case Exiv2::date:
0931                 case Exiv2::time:
0932                 {
0933                     QDateTime dateTime = QDateTime::fromString(QString::fromStdString(it->toString()), Qt::ISODate);
0934                     dateTime.setTimeSpec(Qt::UTC);
0935 
0936                     return QVariant(dateTime);
0937                 }
0938 
0939                 case Exiv2::asciiString:
0940                 case Exiv2::comment:
0941                 case Exiv2::string:
0942                 {
0943                     std::ostringstream os;
0944                     it->write(os, &exifData);
0945                     QString tagValue = QString::fromStdString(os.str());
0946 
0947                     if (stringEscapeCR)
0948                     {
0949                         tagValue.replace(QLatin1Char('\n'), QLatin1String(" "));
0950                     }
0951 
0952                     return QVariant(tagValue);
0953                 }
0954 
0955                 default:
0956                 {
0957                     break;
0958                 }
0959             }
0960         }
0961     }
0962     catch (Exiv2::AnyError& e)
0963     {
0964         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' in the image with Exiv2:")
0965                                     .arg(QLatin1String(exifTagName)), e);
0966     }
0967     catch (...)
0968     {
0969         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0970     }
0971 
0972     return QVariant();
0973 }
0974 
0975 QString MetaEngine::getExifTagString(const char* exifTagName, bool escapeCR) const
0976 {
0977     QMutexLocker lock(&s_metaEngineMutex);
0978 
0979     try
0980     {
0981         Exiv2::ExifKey exifKey(exifTagName);
0982         Exiv2::ExifData exifData(d->exifMetadata());
0983         Exiv2::ExifData::const_iterator it = exifData.findKey(exifKey);
0984 
0985         if (it != exifData.end())
0986         {
0987             QString tagValue;
0988             QString key = QLatin1String(it->key().c_str());
0989 
0990             if (
0991                 (key == QLatin1String("Exif.CanonCs.LensType")) &&
0992 #if EXIV2_TEST_VERSION(0,27,99)
0993                 (it->toInt64() == 65535)
0994 #else
0995                 (it->toLong()  == 65535)
0996 #endif
0997                )
0998             {
0999                 // FIXME: workaround for a possible crash in Exiv2 pretty-print function for the Exif.CanonCs.LensType.
1000 
1001                 tagValue = QString::fromStdString(it->toString());
1002             }
1003             else
1004             {
1005                 // See BUG #184156 comment #13
1006 
1007                 tagValue = QString::fromStdString(it->print(&exifData));
1008             }
1009 
1010             if (escapeCR)
1011             {
1012                 tagValue.replace(QLatin1Char('\n'), QLatin1String(" "));
1013             }
1014 
1015             return tagValue;
1016         }
1017     }
1018     catch (Exiv2::AnyError& e)
1019     {
1020         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image with Exiv2:")
1021                                     .arg(QLatin1String(exifTagName)), e);
1022     }
1023     catch (...)
1024     {
1025         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
1026     }
1027 
1028     return QString();
1029 }
1030 
1031 bool MetaEngine::setExifTagString(const char* exifTagName, const QString& value) const
1032 {
1033     QMutexLocker lock(&s_metaEngineMutex);
1034 
1035     try
1036     {
1037         d->exifMetadata()[exifTagName] = value.toStdString();
1038 
1039         return true;
1040     }
1041     catch (Exiv2::AnyError& e)
1042     {
1043         d->printExiv2ExceptionError(QLatin1String("Cannot set Exif tag string into image with Exiv2:"), e);
1044     }
1045     catch (...)
1046     {
1047         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
1048     }
1049 
1050     return false;
1051 }
1052 
1053 QImage MetaEngine::getExifThumbnail(bool fixOrientation) const
1054 {
1055     QImage thumbnail;
1056 
1057     if (d->exifMetadata().empty())
1058     {
1059        return thumbnail;
1060     }
1061 
1062     QMutexLocker lock(&s_metaEngineMutex);
1063 
1064     try
1065     {
1066         Exiv2::ExifThumbC thumb(d->exifMetadata());
1067         Exiv2::DataBuf const c1 = thumb.copy();
1068 
1069 #if EXIV2_TEST_VERSION(0,27,99)
1070         if (c1.size() == 0)
1071         {
1072             return thumbnail;
1073         }
1074 
1075         thumbnail.loadFromData(c1.c_data(), c1.size());
1076 #else
1077         if (c1.size_ == 0)
1078         {
1079             return thumbnail;
1080         }
1081 
1082         thumbnail.loadFromData(c1.pData_, c1.size_);
1083 #endif
1084 
1085         if (!thumbnail.isNull())
1086         {
1087             if (fixOrientation)
1088             {
1089                 Exiv2::ExifKey key1("Exif.Thumbnail.Orientation");
1090                 Exiv2::ExifKey key2("Exif.Image.Orientation");
1091                 Exiv2::ExifData exifData(d->exifMetadata());
1092                 Exiv2::ExifData::const_iterator it = exifData.findKey(key1);
1093 
1094                 if (it == exifData.end())
1095                 {
1096                     it = exifData.findKey(key2);
1097                 }
1098 
1099                 if (it != exifData.end() && it->count())
1100                 {
1101 #if EXIV2_TEST_VERSION(0,27,99)
1102                     long orientation = it->toInt64();
1103 #else
1104                     long orientation = it->toLong();
1105 #endif
1106 
1107                     //qCDebug(DIGIKAM_METAENGINE_LOG) << "Exif Thumbnail Orientation: " << (int)orientation;
1108 
1109                     rotateExifQImage(thumbnail, (ImageOrientation)orientation);
1110                 }
1111 
1112                 return thumbnail;
1113             }
1114         }
1115     }
1116     catch (Exiv2::AnyError& e)
1117     {
1118         d->printExiv2ExceptionError(QLatin1String("Cannot get Exif Thumbnail with Exiv2:"), e);
1119     }
1120     catch (...)
1121     {
1122         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
1123     }
1124 
1125     return thumbnail;
1126 }
1127 
1128 bool MetaEngine::rotateExifQImage(QImage& image, ImageOrientation orientation) const
1129 {
1130     QTransform matrix = MetaEngineRotation::toTransform(orientation);
1131 
1132     if ((orientation != ORIENTATION_NORMAL) && (orientation != ORIENTATION_UNSPECIFIED))
1133     {
1134         image = image.transformed(matrix);
1135 
1136         return true;
1137     }
1138 
1139     return false;
1140 }
1141 
1142 bool MetaEngine::setExifThumbnail(const QImage& thumbImage) const
1143 {
1144     if (thumbImage.isNull())
1145     {
1146         return removeExifThumbnail();
1147     }
1148 
1149     QMutexLocker lock(&s_metaEngineMutex);
1150 
1151     try
1152     {
1153         QByteArray data;
1154         QBuffer buffer(&data);
1155         buffer.open(QIODevice::WriteOnly);
1156         thumbImage.save(&buffer, "JPEG");
1157         buffer.close();
1158         Exiv2::ExifThumb thumb(d->exifMetadata());
1159         thumb.setJpegThumbnail((Exiv2::byte*)data.data(), data.size());
1160 
1161         return true;
1162     }
1163     catch (Exiv2::AnyError& e)
1164     {
1165         d->printExiv2ExceptionError(QLatin1String("Cannot set Exif Thumbnail with Exiv2:"), e);
1166     }
1167     catch (...)
1168     {
1169         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
1170     }
1171 
1172     return false;
1173 }
1174 
1175 bool MetaEngine::setTiffThumbnail(const QImage& thumbImage) const
1176 {
1177     removeExifThumbnail();
1178 
1179     QMutexLocker lock(&s_metaEngineMutex);
1180 
1181     try
1182     {
1183         // Make sure IFD0 is explicitly marked as a main image
1184 
1185         Exiv2::ExifData::const_iterator pos = d->exifMetadata().findKey(Exiv2::ExifKey("Exif.Image.NewSubfileType"));
1186 
1187         if (
1188             (pos            == d->exifMetadata().end()) ||
1189             (pos->count()   != 1)                       ||
1190 #if EXIV2_TEST_VERSION(0,27,99)
1191             (pos->toInt64() != 0)
1192 #else
1193             (pos->toLong()  != 0)
1194 #endif
1195            )
1196         {
1197 
1198 #if EXIV2_TEST_VERSION(0,27,0)
1199 
1200             throw Exiv2::Error(Exiv2::kerErrorMessage, "Exif.Image.NewSubfileType missing or not set as main image");
1201 
1202 #else
1203 
1204             throw Exiv2::Error(1, "Exif.Image.NewSubfileType missing or not set as main image");
1205 
1206 #endif
1207 
1208         }
1209 
1210         // Remove sub-IFD tags
1211 
1212         std::string subImage1("SubImage1");
1213 
1214         for (Exiv2::ExifData::iterator md = d->exifMetadata().begin() ; md != d->exifMetadata().end() ; )
1215         {
1216             if (md->groupName() == subImage1)
1217             {
1218                 md = d->exifMetadata().erase(md);
1219             }
1220             else
1221             {
1222                 ++md;
1223             }
1224         }
1225 
1226         if (!thumbImage.isNull())
1227         {
1228             // Set thumbnail tags
1229 
1230             QByteArray data;
1231             QBuffer buffer(&data);
1232             buffer.open(QIODevice::WriteOnly);
1233             thumbImage.save(&buffer, "JPEG");
1234             buffer.close();
1235 
1236             Exiv2::DataBuf buf((Exiv2::byte*)data.data(), data.size());
1237             Exiv2::ULongValue val;
1238             val.read("0");
1239 #if EXIV2_TEST_VERSION(0,27,99)
1240             val.setDataArea(buf.data(), buf.size());
1241 #else
1242             val.setDataArea(buf.pData_, buf.size_);
1243 #endif
1244             d->exifMetadata()["Exif.SubImage1.JPEGInterchangeFormat"]       = val;
1245 #if EXIV2_TEST_VERSION(0,27,99)
1246             d->exifMetadata()["Exif.SubImage1.JPEGInterchangeFormatLength"] = uint32_t(buf.size());
1247 #else
1248             d->exifMetadata()["Exif.SubImage1.JPEGInterchangeFormatLength"] = uint32_t(buf.size_);
1249 #endif
1250             d->exifMetadata()["Exif.SubImage1.Compression"]                 = uint16_t(6);          // JPEG (old-style)
1251             d->exifMetadata()["Exif.SubImage1.NewSubfileType"]              = uint32_t(1);          // Thumbnail image
1252 
1253             return true;
1254         }
1255     }
1256     catch (Exiv2::AnyError& e)
1257     {
1258         d->printExiv2ExceptionError(QLatin1String("Cannot set TIFF Thumbnail with Exiv2:"), e);
1259     }
1260     catch (...)
1261     {
1262         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
1263     }
1264 
1265     return false;
1266 }
1267 
1268 bool MetaEngine::removeExifThumbnail() const
1269 {
1270     QMutexLocker lock(&s_metaEngineMutex);
1271 
1272     try
1273     {
1274         // Remove all IFD0 subimages.
1275 
1276         Exiv2::ExifThumb thumb(d->exifMetadata());
1277         thumb.erase();
1278 
1279         return true;
1280     }
1281     catch (Exiv2::AnyError& e)
1282     {
1283         d->printExiv2ExceptionError(QLatin1String("Cannot remove Exif Thumbnail with Exiv2:"), e);
1284     }
1285     catch (...)
1286     {
1287         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
1288     }
1289 
1290     return false;
1291 }
1292 
1293 MetaEngine::MetaDataMap MetaEngine::getExifTagsDataList(const QStringList& exifKeysFilter,
1294                                                         bool invertSelection,
1295                                                         bool extractBinary) const
1296 {
1297     if (d->exifMetadata().empty())
1298     {
1299        return MetaDataMap();
1300     }
1301 
1302     QMutexLocker lock(&s_metaEngineMutex);
1303 
1304     try
1305     {
1306         Exiv2::ExifData exifData = d->exifMetadata();
1307         exifData.sortByKey();
1308 
1309         QString            ifDItemName;
1310         MetaDataMap        metaDataMap;
1311         std::ostringstream os;
1312         QString            key;
1313         QString            tagValue;
1314         double             num   = 0.0;
1315         double             den   = 0.0;
1316 
1317         for (Exiv2::ExifData::const_iterator md = exifData.begin() ; md != exifData.end() ; ++md)
1318         {
1319             key = QLatin1String(md->key().c_str());
1320 
1321             // Decode the tag value with a user friendly output.
1322 
1323             if      (key == QLatin1String("Exif.Photo.UserComment"))
1324             {
1325                 tagValue = d->convertCommentValue(*md);
1326             }
1327             else if (key == QLatin1String("Exif.GPSInfo.GPSAreaInformation"))
1328             {
1329                 // NOTE: special cases to render contents of these GPS info tags. See bug #425884.
1330 
1331                 tagValue = d->convertCommentValue(*md);
1332             }
1333             else if (key == QLatin1String("Exif.GPSInfo.GPSProcessingMethod"))
1334             {
1335                 // NOTE: special cases to render contents of these GPS info tags. See bug #425884.
1336 
1337                 tagValue = d->convertCommentValue(*md);
1338             }
1339             else if ((key == QLatin1String("Exif.GPSInfo.GPSTrack"))      ||
1340                      (key == QLatin1String("Exif.GPSInfo.GPSImgDirection")))
1341             {
1342                 // NOTE: special cases to render contents of these GPS info tags. See bug #435317.
1343                 // Check value byte size, otherwise Exiv2 crashes in the toRational() function.
1344 
1345                 if ((*md).count())
1346                 {
1347                     num  = (*md).toRational().first;
1348                     den  = (*md).toRational().second;
1349                 }
1350 
1351                 tagValue = (den == 0.0) ? QString::fromStdString(md->print())
1352                                         : QString::fromLatin1("%1 deg").arg(num / den);
1353             }
1354             else if (key == QLatin1String("Exif.GPSInfo.GPSSpeed"))
1355             {
1356                 // NOTE: special cases to render contents of these GPS info tags. See bug #435317.
1357                 // Check value byte size, otherwise Exiv2 crashes in the toRational() function.
1358 
1359                 if ((*md).count())
1360                 {
1361                     num  = (*md).toRational().first;
1362                     den  = (*md).toRational().second;
1363                 }
1364 
1365                 tagValue = (den == 0.0) ? QString::fromStdString(md->print())
1366                                         : tagValue = QString::number(num / den);
1367             }
1368             else if (key == QLatin1String("Exif.Image.0x935c"))
1369             {
1370                 tagValue = QString::number(md->value().size());
1371             }
1372             else if (
1373                      (key           == QLatin1String("Exif.CanonCs.LensType")) && 
1374 #if EXIV2_TEST_VERSION(0,27,99)
1375                      (md->toInt64() == 65535)
1376 #else
1377                      (md->toLong()  == 65535)
1378 #endif
1379                     )
1380             {
1381                 // FIXME: workaround for a possible crash in Exiv2 pretty-print function for the Exif.CanonCs.LensType.
1382 
1383                 tagValue = QString::fromStdString(md->toString());
1384             }
1385             else
1386             {
1387                 if (!extractBinary && (md->typeId() == Exiv2::undefined))
1388                 {
1389                     tagValue = i18nc("info", "Binary data %1 bytes", md->size());
1390                 }
1391                 else
1392                 {
1393                     os.str("");
1394                     os.clear();
1395                     md->write(os, &exifData);
1396 
1397                     // Exif tag contents can be a translated strings, not only simple ascii.
1398 
1399                     tagValue = QString::fromStdString(os.str());
1400                 }
1401             }
1402 
1403             tagValue.replace(QLatin1Char('\n'), QLatin1String(" "));
1404 
1405             // We apply a filter to get only the Exif tags that we need.
1406 
1407             if (!exifKeysFilter.isEmpty())
1408             {
1409                 if (!invertSelection)
1410                 {
1411                     if (exifKeysFilter.contains(key.section(QLatin1Char('.'), 1, 1)))
1412                     {
1413                         metaDataMap.insert(key, tagValue);
1414                     }
1415                 }
1416                 else
1417                 {
1418                     if (!exifKeysFilter.contains(key.section(QLatin1Char('.'), 1, 1)))
1419                     {
1420                         metaDataMap.insert(key, tagValue);
1421                     }
1422                 }
1423             }
1424             else // else no filter at all.
1425             {
1426                 metaDataMap.insert(key, tagValue);
1427             }
1428         }
1429 
1430         return metaDataMap;
1431     }
1432     catch (Exiv2::AnyError& e)
1433     {
1434         d->printExiv2ExceptionError(QLatin1String("Cannot parse EXIF metadata with Exiv2:"), e);
1435     }
1436     catch (...)
1437     {
1438         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
1439     }
1440 
1441     return MetaDataMap();
1442 }
1443 
1444 MetaEngine::TagsMap MetaEngine::getStdExifTagsList() const
1445 {
1446     QMutexLocker lock(&s_metaEngineMutex);
1447 
1448     try
1449     {
1450         QList<const Exiv2::TagInfo*> tags;
1451         TagsMap                      tagsMap;
1452 
1453         const Exiv2::GroupInfo* gi = Exiv2::ExifTags::groupList();
1454 
1455         while (gi->tagList_ != nullptr)
1456         {
1457             // NOTE: See BUG #375809 : MPF tags = exception Exiv2 0.26
1458 
1459             if (QLatin1String(gi->ifdName_) != QLatin1String("Makernote"))
1460             {
1461                 Exiv2::TagListFct tl     = gi->tagList_;
1462                 const Exiv2::TagInfo* ti = tl();
1463 
1464                 while (ti->tag_ != 0xFFFF)
1465                 {
1466                     tags << ti;
1467                     ++ti;
1468                 }
1469             }
1470 
1471             ++gi;
1472         }
1473 
1474         for (QList<const Exiv2::TagInfo*>::iterator it = tags.begin() ; it != tags.end() ; ++it)
1475         {
1476             do
1477             {
1478                 const Exiv2::TagInfo* const ti = *it;
1479 
1480                 if (ti)
1481                 {
1482                     QString key = QLatin1String(Exiv2::ExifKey(*ti).key().c_str());
1483                     QStringList values;
1484                     values << QLatin1String(ti->name_) << QLatin1String(ti->title_) << QLatin1String(ti->desc_);
1485                     tagsMap.insert(key, values);
1486                 }
1487 
1488                 ++(*it);
1489             }
1490             while ((*it)->tag_ != 0xffff);
1491         }
1492 
1493         return tagsMap;
1494     }
1495     catch (Exiv2::AnyError& e)
1496     {
1497         d->printExiv2ExceptionError(QLatin1String("Cannot get Exif Tags list with Exiv2:"), e);
1498     }
1499     catch (...)
1500     {
1501         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
1502     }
1503 
1504     return TagsMap();
1505 }
1506 
1507 MetaEngine::TagsMap MetaEngine::getMakernoteTagsList() const
1508 {
1509     QMutexLocker lock(&s_metaEngineMutex);
1510 
1511     try
1512     {
1513         QList<const Exiv2::TagInfo*> tags;
1514         TagsMap                      tagsMap;
1515 
1516         const Exiv2::GroupInfo* gi = Exiv2::ExifTags::groupList();
1517 
1518         while (gi->tagList_ != nullptr)
1519         {
1520             if (QLatin1String(gi->ifdName_) == QLatin1String("Makernote"))
1521             {
1522                 Exiv2::TagListFct tl     = gi->tagList_;
1523                 const Exiv2::TagInfo* ti = tl();
1524 
1525                 while (ti->tag_ != 0xFFFF)
1526                 {
1527                     tags << ti;
1528                     ++ti;
1529                 }
1530             }
1531 
1532             ++gi;
1533         }
1534 
1535         for (QList<const Exiv2::TagInfo*>::iterator it = tags.begin() ; it != tags.end() ; ++it)
1536         {
1537             do
1538             {
1539                 const Exiv2::TagInfo* const ti = *it;
1540 
1541                 if (ti)
1542                 {
1543                     QString key = QLatin1String(Exiv2::ExifKey(*ti).key().c_str());
1544                     QStringList values;
1545                     values << QLatin1String(ti->name_) << QLatin1String(ti->title_) << QLatin1String(ti->desc_);
1546                     tagsMap.insert(key, values);
1547                 }
1548 
1549                 ++(*it);
1550             }
1551             while ((*it)->tag_ != 0xffff);
1552         }
1553 
1554         return tagsMap;
1555     }
1556     catch (Exiv2::AnyError& e)
1557     {
1558         d->printExiv2ExceptionError(QLatin1String("Cannot get Makernote Tags list with Exiv2:"), e);
1559     }
1560     catch (...)
1561     {
1562         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
1563     }
1564 
1565     return TagsMap();
1566 }
1567 
1568 } // namespace Digikam
1569 
1570 #if defined(Q_CC_CLANG)
1571 #   pragma clang diagnostic pop
1572 #endif