File indexing completed on 2025-01-19 03:56:01

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  *               Iptc 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 // Local includes
0018 
0019 #include "metaengine_p.h"
0020 #include "digikam_debug.h"
0021 #include "digikam_config.h"
0022 
0023 #if defined(Q_CC_CLANG)
0024 #   pragma clang diagnostic push
0025 #   pragma clang diagnostic ignored "-Wdeprecated-declarations"
0026 #endif
0027 
0028 namespace Digikam
0029 {
0030 
0031 bool MetaEngine::canWriteIptc(const QString& filePath)
0032 {
0033     QMutexLocker lock(&s_metaEngineMutex);
0034 
0035     try
0036     {
0037 
0038 #if defined Q_OS_WIN && defined EXV_UNICODE_PATH
0039 
0040         Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((const wchar_t*)filePath.utf16());
0041 
0042 #elif defined Q_OS_WIN
0043 
0044         Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(QFile::encodeName(filePath).constData());
0045 
0046 #else
0047 
0048         Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filePath.toUtf8().constData());
0049 
0050 #endif
0051 
0052         Exiv2::AccessMode mode      = image->checkMode(Exiv2::mdIptc);
0053 
0054         return ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite));
0055     }
0056     catch (Exiv2::AnyError& e)
0057     {
0058         qCCritical(DIGIKAM_METAENGINE_LOG) << "Cannot check Iptc access mode with Exiv2:(Error #"
0059                                            << (int)e.code() << ": " << QString::fromStdString(e.what()) << ")";
0060     }
0061     catch (...)
0062     {
0063         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0064     }
0065 
0066     return false;
0067 }
0068 
0069 bool MetaEngine::hasIptc() const
0070 {
0071     return !d->iptcMetadata().empty();
0072 }
0073 
0074 bool MetaEngine::clearIptc() const
0075 {
0076     QMutexLocker lock(&s_metaEngineMutex);
0077 
0078     try
0079     {
0080         d->iptcMetadata().clear();
0081         return true;
0082     }
0083     catch (Exiv2::AnyError& e)
0084     {
0085         d->printExiv2ExceptionError(QLatin1String("Cannot clear Iptc data with Exiv2:"), e);
0086     }
0087     catch (...)
0088     {
0089         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0090     }
0091 
0092     return false;
0093 }
0094 
0095 QByteArray MetaEngine::getIptc(bool addIrbHeader) const
0096 {
0097     QMutexLocker lock(&s_metaEngineMutex);
0098 
0099     try
0100     {
0101         if (!d->iptcMetadata().empty())
0102         {
0103             Exiv2::IptcData& iptc = d->iptcMetadata();
0104             Exiv2::DataBuf c2;
0105 
0106             if (addIrbHeader)
0107             {
0108                 c2 = Exiv2::Photoshop::setIptcIrb(nullptr, 0, iptc);
0109             }
0110             else
0111             {
0112                 c2 = Exiv2::IptcParser::encode(d->iptcMetadata());
0113             }
0114 
0115 #if EXIV2_TEST_VERSION(0,27,99)
0116             QByteArray data((const char*)c2.data(), c2.size());
0117 #else
0118             QByteArray data((const char*)c2.pData_, c2.size_);
0119 #endif
0120 
0121             return data;
0122         }
0123     }
0124     catch (Exiv2::AnyError& e)
0125     {
0126         if (!d->filePath.isEmpty())
0127         {
0128             qCCritical(DIGIKAM_METAENGINE_LOG) << "From file " << d->filePath.toLatin1().constData();
0129         }
0130 
0131         d->printExiv2ExceptionError(QLatin1String("Cannot get Iptc data with Exiv2:"), e);
0132     }
0133     catch (...)
0134     {
0135         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0136     }
0137 
0138     return QByteArray();
0139 }
0140 
0141 bool MetaEngine::setIptc(const QByteArray& data) const
0142 {
0143     QMutexLocker lock(&s_metaEngineMutex);
0144 
0145     try
0146     {
0147         if (!data.isEmpty())
0148         {
0149             Exiv2::IptcParser::decode(d->iptcMetadata(), (const Exiv2::byte*)data.data(), data.size());
0150             return (!d->iptcMetadata().empty());
0151         }
0152     }
0153     catch (Exiv2::AnyError& e)
0154     {
0155         if (!d->filePath.isEmpty())
0156         {
0157             qCCritical(DIGIKAM_METAENGINE_LOG) << "From file " << d->filePath.toLatin1().constData();
0158         }
0159 
0160         d->printExiv2ExceptionError(QLatin1String("Cannot set Iptc data with Exiv2:"), e);
0161     }
0162     catch (...)
0163     {
0164         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0165     }
0166 
0167     return false;
0168 }
0169 
0170 MetaEngine::MetaDataMap MetaEngine::getIptcTagsDataList(const QStringList& iptcKeysFilter, bool invertSelection) const
0171 {
0172     if (d->iptcMetadata().empty())
0173     {
0174        return MetaDataMap();
0175     }
0176 
0177     QMutexLocker lock(&s_metaEngineMutex);
0178 
0179     try
0180     {
0181         Exiv2::IptcData iptcData = d->iptcMetadata();
0182         iptcData.sortByKey();
0183 
0184         QString     ifDItemName;
0185         MetaDataMap metaDataMap;
0186 
0187         for (Exiv2::IptcData::const_iterator md = iptcData.begin() ; md != iptcData.end() ; ++md)
0188         {
0189             QString key = QString::fromStdString(md->key());
0190             QString value;
0191 
0192             if (key == QLatin1String("Iptc.Envelope.CharacterSet"))
0193             {
0194                 value = QLatin1String(iptcData.detectCharset());
0195             }
0196             else
0197             {
0198                 value = d->extractIptcTagString(iptcData, *md);
0199             }
0200 
0201             // To make a string just on one line.
0202 
0203             value.replace(QLatin1Char('\n'), QLatin1String(" "));
0204 
0205             // Some Iptc key are redondancy. check if already one exist...
0206 
0207             MetaDataMap::const_iterator it = metaDataMap.constFind(key);
0208 
0209             // We apply a filter to get only the Iptc tags that we need.
0210 
0211             if (!iptcKeysFilter.isEmpty())
0212             {
0213                 if (!invertSelection)
0214                 {
0215                     if (iptcKeysFilter.contains(key.section(QLatin1Char('.'), 1, 1)))
0216                     {
0217                         if (it == metaDataMap.constEnd())
0218                         {
0219                             metaDataMap.insert(key, value);
0220                         }
0221                         else
0222                         {
0223                             QString v = *it;
0224                             v.append(QLatin1String(", "));
0225                             v.append(value);
0226                             metaDataMap.insert(key, v);
0227                         }
0228                     }
0229                 }
0230                 else
0231                 {
0232                     if (!iptcKeysFilter.contains(key.section(QLatin1Char('.'), 1, 1)))
0233                     {
0234                         if (it == metaDataMap.constEnd())
0235                         {
0236                             metaDataMap.insert(key, value);
0237                         }
0238                         else
0239                         {
0240                             QString v = *it;
0241                             v.append(QLatin1String(", "));
0242                             v.append(value);
0243                             metaDataMap.insert(key, v);
0244                         }
0245                     }
0246                 }
0247             }
0248             else // else no filter at all.
0249             {
0250                 if (it == metaDataMap.constEnd())
0251                 {
0252                     metaDataMap.insert(key, value);
0253                 }
0254                 else
0255                 {
0256                     QString v = *it;
0257                     v.append(QLatin1String(", "));
0258                     v.append(value);
0259                     metaDataMap.insert(key, v);
0260                 }
0261             }
0262         }
0263 
0264         return metaDataMap;
0265     }
0266     catch (Exiv2::AnyError& e)
0267     {
0268         d->printExiv2ExceptionError(QLatin1String("Cannot parse Iptc metadata with Exiv2:"), e);
0269     }
0270     catch (...)
0271     {
0272         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0273     }
0274 
0275     return MetaDataMap();
0276 }
0277 
0278 QString MetaEngine::getIptcTagTitle(const char* iptcTagName)
0279 {
0280     QMutexLocker lock(&s_metaEngineMutex);
0281 
0282     try
0283     {
0284         std::string iptckey(iptcTagName);
0285         Exiv2::IptcKey ik(iptckey);
0286 
0287         return QString::fromLocal8Bit(Exiv2::IptcDataSets::dataSetTitle(ik.tag(), ik.record()));
0288     }
0289     catch (Exiv2::AnyError& e)
0290     {
0291         d->printExiv2ExceptionError(QLatin1String("Cannot get metadata tag title with Exiv2:"), e);
0292     }
0293     catch (...)
0294     {
0295         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0296     }
0297 
0298     return QString();
0299 }
0300 
0301 QString MetaEngine::getIptcTagDescription(const char* iptcTagName)
0302 {
0303     QMutexLocker lock(&s_metaEngineMutex);
0304 
0305     try
0306     {
0307         std::string iptckey(iptcTagName);
0308         Exiv2::IptcKey ik(iptckey);
0309 
0310         return QString::fromLocal8Bit(Exiv2::IptcDataSets::dataSetDesc(ik.tag(), ik.record()));
0311     }
0312     catch (Exiv2::AnyError& e)
0313     {
0314         d->printExiv2ExceptionError(QLatin1String("Cannot get metadata tag description with Exiv2:"), e);
0315     }
0316     catch (...)
0317     {
0318         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0319     }
0320 
0321     return QString();
0322 }
0323 
0324 bool MetaEngine::removeIptcTag(const char* iptcTagName) const
0325 {
0326     QMutexLocker lock(&s_metaEngineMutex);
0327 
0328     try
0329     {
0330         Exiv2::IptcData::iterator it = d->iptcMetadata().begin();
0331         int i                        = 0;
0332 
0333         while(it != d->iptcMetadata().end())
0334         {
0335             QString key = QString::fromStdString(it->key());
0336 
0337             if (key == QLatin1String(iptcTagName))
0338             {
0339                 it = d->iptcMetadata().erase(it);
0340                 ++i;
0341             }
0342             else
0343             {
0344                 ++it;
0345             }
0346         };
0347 
0348         if (i > 0)
0349         {
0350             return true;
0351         }
0352     }
0353     catch (Exiv2::AnyError& e)
0354     {
0355         d->printExiv2ExceptionError(QLatin1String("Cannot remove Iptc tag with Exiv2:"), e);
0356     }
0357     catch (...)
0358     {
0359         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0360     }
0361 
0362     return false;
0363 }
0364 
0365 bool MetaEngine::setIptcTagData(const char* iptcTagName, const QByteArray& data) const
0366 {
0367     if (data.isEmpty())
0368     {
0369         return false;
0370     }
0371 
0372     QMutexLocker lock(&s_metaEngineMutex);
0373 
0374     try
0375     {
0376         Exiv2::DataValue val((Exiv2::byte*)data.data(), data.size());
0377         d->iptcMetadata()[iptcTagName] = val;
0378 
0379         return true;
0380     }
0381     catch (Exiv2::AnyError& e)
0382     {
0383         d->printExiv2ExceptionError(QLatin1String("Cannot set Iptc tag data into image with Exiv2:"), e);
0384     }
0385     catch (...)
0386     {
0387         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0388     }
0389 
0390     return false;
0391 }
0392 
0393 QByteArray MetaEngine::getIptcTagData(const char* iptcTagName) const
0394 {
0395     QMutexLocker lock(&s_metaEngineMutex);
0396 
0397     try
0398     {
0399         Exiv2::IptcKey  iptcKey(iptcTagName);
0400         Exiv2::IptcData iptcData(d->iptcMetadata());
0401         Exiv2::IptcData::const_iterator it = iptcData.findKey(iptcKey);
0402 
0403         if (it != iptcData.end())
0404         {
0405             QByteArray data((*it).size(), '\0');
0406             (*it).copy((Exiv2::byte*)data.data(), Exiv2::bigEndian);
0407 
0408             return data;
0409         }
0410     }
0411     catch (Exiv2::AnyError& e)
0412     {
0413         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Iptc key '%1' into image with Exiv2:")
0414                                     .arg(QLatin1String(iptcTagName)), e);
0415     }
0416     catch (...)
0417     {
0418         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0419     }
0420 
0421     return QByteArray();
0422 }
0423 
0424 QString MetaEngine::getIptcTagString(const char* iptcTagName, bool escapeCR) const
0425 {
0426     QMutexLocker lock(&s_metaEngineMutex);
0427 
0428     try
0429     {
0430         Exiv2::IptcKey  iptcKey(iptcTagName);
0431         Exiv2::IptcData iptcData(d->iptcMetadata());
0432         Exiv2::IptcData::const_iterator it = iptcData.findKey(iptcKey);
0433         QString charSet                    = QLatin1String(iptcData.detectCharset());
0434 
0435         if (it != iptcData.end())
0436         {
0437             QString tagValue = d->extractIptcTagString(iptcData, *it);
0438 
0439             if (escapeCR)
0440             {
0441                 tagValue.replace(QLatin1Char('\n'), QLatin1String(" "));
0442             }
0443 
0444             return tagValue;
0445         }
0446     }
0447     catch (Exiv2::AnyError& e)
0448     {
0449         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Iptc key '%1' into image with Exiv2:")
0450                                     .arg(QLatin1String(iptcTagName)), e);
0451     }
0452     catch (...)
0453     {
0454         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0455     }
0456 
0457     return QString();
0458 }
0459 
0460 bool MetaEngine::setIptcTagString(const char* iptcTagName, const QString& value) const
0461 {
0462     QMutexLocker lock(&s_metaEngineMutex);
0463 
0464     try
0465     {
0466         d->iptcMetadata()[iptcTagName] = value.toStdString();
0467 
0468         // Make sure we have set the charset to UTF-8
0469 
0470         d->iptcMetadata()["Iptc.Envelope.CharacterSet"] = "\33%G";
0471 
0472         return true;
0473     }
0474     catch (Exiv2::AnyError& e)
0475     {
0476         d->printExiv2ExceptionError(QLatin1String("Cannot set Iptc tag string into image with Exiv2:"), e);
0477     }
0478     catch (...)
0479     {
0480         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0481     }
0482 
0483     return false;
0484 }
0485 
0486 QStringList MetaEngine::getIptcTagsStringList(const char* iptcTagName, bool escapeCR) const
0487 {
0488     QMutexLocker lock(&s_metaEngineMutex);
0489 
0490     try
0491     {
0492         if (!d->iptcMetadata().empty())
0493         {
0494             QStringList values;
0495             Exiv2::IptcData iptcData(d->iptcMetadata());
0496 
0497             for (Exiv2::IptcData::const_iterator it = iptcData.begin() ; it != iptcData.end() ; ++it)
0498             {
0499                 QString key = QString::fromStdString(it->key());
0500 
0501                 if (key == QLatin1String(iptcTagName))
0502                 {
0503                     QString tagValue = d->extractIptcTagString(iptcData, *it);
0504 
0505                     if (escapeCR)
0506                     {
0507                         tagValue.replace(QLatin1Char('\n'), QLatin1String(" "));
0508                     }
0509 
0510                     values.append(tagValue);
0511                 }
0512             }
0513 
0514             return values;
0515         }
0516     }
0517     catch (Exiv2::AnyError& e)
0518     {
0519         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Iptc key '%1' into image with Exiv2:")
0520                                     .arg(QLatin1String(iptcTagName)), e);
0521     }
0522     catch (...)
0523     {
0524         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0525     }
0526 
0527     return QStringList();
0528 }
0529 
0530 bool MetaEngine::setIptcTagsStringList(const char* iptcTagName, int maxSize,
0531                                        const QStringList& oldValues,
0532                                        const QStringList& newValues) const
0533 {
0534     QMutexLocker lock(&s_metaEngineMutex);
0535 
0536     try
0537     {
0538         QStringList oldvals = oldValues;
0539         QStringList newvals = newValues;
0540 
0541         qCDebug(DIGIKAM_METAENGINE_LOG) << d->filePath.toLatin1().constData()
0542                                         << " : " << iptcTagName
0543                                         << " => " << newvals.join(QString::fromLatin1(",")).toLatin1().constData();
0544 
0545         // Remove all old values.
0546 
0547         Exiv2::IptcData iptcData(d->iptcMetadata());
0548         Exiv2::IptcData::iterator it2 = iptcData.begin();
0549 
0550         while (it2 != iptcData.end())
0551         {
0552             QString key = QString::fromStdString(it2->key());
0553             QString val = QString::fromStdString(it2->toString());
0554 
0555             // Also remove new values to avoid duplicates. They will be added again below.
0556 
0557             if ((key == QLatin1String(iptcTagName)) &&
0558                 (oldvals.contains(val) || newvals.contains(val)))
0559             {
0560                 it2 = iptcData.erase(it2);
0561             }
0562             else
0563             {
0564                 ++it2;
0565             }
0566         };
0567 
0568         // Add new values.
0569 
0570         Exiv2::IptcKey iptcTag(iptcTagName);
0571 
0572         for (QStringList::const_iterator it = newvals.constBegin() ; it != newvals.constEnd() ; ++it)
0573         {
0574             QString key = *it;
0575             key.truncate(maxSize);
0576 
0577             Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
0578             val->read(key.toUtf8().constData());
0579             iptcData.add(iptcTag, val.get());
0580         }
0581 
0582         d->iptcMetadata() = iptcData;
0583 
0584         // Make sure character set is UTF-8
0585 
0586         setIptcTagString("Iptc.Envelope.CharacterSet", QLatin1String("\33%G"));
0587 
0588         return true;
0589     }
0590     catch (Exiv2::AnyError& e)
0591     {
0592         d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Iptc key '%1' into image with Exiv2:")
0593                                     .arg(QLatin1String(iptcTagName)), e);
0594     }
0595     catch (...)
0596     {
0597         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0598     }
0599 
0600     return false;
0601 }
0602 
0603 QStringList MetaEngine::getIptcKeywords() const
0604 {
0605     QMutexLocker lock(&s_metaEngineMutex);
0606 
0607     try
0608     {
0609         if (!d->iptcMetadata().empty())
0610         {
0611             QStringList keywords;
0612             Exiv2::IptcData iptcData(d->iptcMetadata());
0613 
0614             for (Exiv2::IptcData::const_iterator it = iptcData.begin() ; it != iptcData.end() ; ++it)
0615             {
0616                 QString key = QString::fromStdString(it->key());
0617 
0618                 if (key == QLatin1String("Iptc.Application2.Keywords"))
0619                 {
0620                     QString val = d->extractIptcTagString(iptcData, *it);
0621                     keywords.append(val);
0622                 }
0623             }
0624 /*
0625             qCDebug(DIGIKAM_METAENGINE_LOG) << d->filePath << " ==> Read Iptc Keywords: " << keywords;
0626 */
0627             return keywords;
0628         }
0629     }
0630     catch (Exiv2::AnyError& e)
0631     {
0632         d->printExiv2ExceptionError(QLatin1String("Cannot get Iptc Keywords from image with Exiv2:"), e);
0633     }
0634     catch (...)
0635     {
0636         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0637     }
0638 
0639     return QStringList();
0640 }
0641 
0642 bool MetaEngine::setIptcKeywords(const QStringList& oldKeywords, const QStringList& newKeywords) const
0643 {
0644     QMutexLocker lock(&s_metaEngineMutex);
0645 
0646     try
0647     {
0648         QStringList oldkeys = oldKeywords;
0649         QStringList newkeys = newKeywords;
0650 
0651         qCDebug(DIGIKAM_METAENGINE_LOG) << d->filePath << " ==> New Iptc Keywords: " << newkeys;
0652 
0653         // Remove all old keywords.
0654 
0655         Exiv2::IptcData iptcData(d->iptcMetadata());
0656         Exiv2::IptcData::iterator it2 = iptcData.begin();
0657 
0658         while (it2 != iptcData.end())
0659         {
0660             QString key = QString::fromStdString(it2->key());
0661             QString val = QString::fromStdString(it2->toString());       // FIXME: check charset
0662 
0663             // Also remove new keywords to avoid duplicates. They will be added again below.
0664 
0665             if ((key == QLatin1String("Iptc.Application2.Keywords")) &&
0666                 (oldKeywords.contains(val) || newKeywords.contains(val))
0667                )
0668             {
0669                 it2 = iptcData.erase(it2);
0670             }
0671             else
0672             {
0673                 ++it2;
0674             }
0675         };
0676 
0677         // Add new keywords. Note that Keywords Iptc tag is limited to 64 char but can be redondant.
0678 
0679         Exiv2::IptcKey iptcTag("Iptc.Application2.Keywords");
0680 
0681         for (QStringList::const_iterator it = newkeys.constBegin() ; it != newkeys.constEnd() ; ++it)
0682         {
0683             QString key = *it;
0684             key.truncate(64);
0685 
0686             Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
0687             val->read(key.toUtf8().constData());
0688             iptcData.add(iptcTag, val.get());
0689         }
0690 
0691         d->iptcMetadata() = iptcData;
0692 
0693         // Make sure character set is UTF-8
0694 
0695         setIptcTagString("Iptc.Envelope.CharacterSet", QLatin1String("\33%G"));
0696 
0697         return true;
0698     }
0699     catch (Exiv2::AnyError& e)
0700     {
0701         d->printExiv2ExceptionError(QLatin1String("Cannot set Iptc Keywords into image with Exiv2:"), e);
0702     }
0703     catch (...)
0704     {
0705         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0706     }
0707 
0708     return false;
0709 }
0710 
0711 QStringList MetaEngine::getIptcSubjects() const
0712 {
0713     QMutexLocker lock(&s_metaEngineMutex);
0714 
0715     try
0716     {
0717         if (!d->iptcMetadata().empty())
0718         {
0719             QStringList subjects;
0720             Exiv2::IptcData iptcData(d->iptcMetadata());
0721 
0722             for (Exiv2::IptcData::const_iterator it = iptcData.begin() ; it != iptcData.end() ; ++it)
0723             {
0724                 QString tagValue = d->extractIptcTagString(iptcData, *it);
0725                 QString key      = QString::fromStdString(it->key());
0726 
0727                 if (key == QLatin1String("Iptc.Application2.Subject"))
0728                 {
0729                     QString val = d->extractIptcTagString(iptcData, *it);
0730                     subjects.append(val);
0731                 }
0732             }
0733 
0734             return subjects;
0735         }
0736     }
0737     catch (Exiv2::AnyError& e)
0738     {
0739         d->printExiv2ExceptionError(QLatin1String("Cannot get Iptc Subjects from image with Exiv2:"), e);
0740     }
0741     catch (...)
0742     {
0743         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0744     }
0745 
0746     return QStringList();
0747 }
0748 
0749 bool MetaEngine::setIptcSubjects(const QStringList& oldSubjects, const QStringList& newSubjects) const
0750 {
0751     QMutexLocker lock(&s_metaEngineMutex);
0752 
0753     try
0754     {
0755         QStringList oldDef = oldSubjects;
0756         QStringList newDef = newSubjects;
0757 
0758         // Remove all old subjects.
0759 
0760         Exiv2::IptcData iptcData(d->iptcMetadata());
0761         Exiv2::IptcData::iterator it2 = iptcData.begin();
0762 
0763         while (it2 != iptcData.end())
0764         {
0765             QString key = QString::fromStdString(it2->key());
0766             QString val = QString::fromStdString(it2->toString());               // FIXME: check charset
0767 
0768             if (key == QLatin1String("Iptc.Application2.Subject") && oldDef.contains(val))
0769             {
0770                 it2 = iptcData.erase(it2);
0771             }
0772             else
0773             {
0774                 ++it2;
0775             }
0776         };
0777 
0778         // Add new subjects. Note that Keywords Iptc tag is limited to 236 char but can be redondant.
0779 
0780         Exiv2::IptcKey iptcTag("Iptc.Application2.Subject");
0781 
0782         for (QStringList::const_iterator it = newDef.constBegin() ; it != newDef.constEnd() ; ++it)
0783         {
0784             QString key = *it;
0785             key.truncate(236);
0786 
0787             Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
0788             val->read(key.toUtf8().constData());
0789             iptcData.add(iptcTag, val.get());
0790         }
0791 
0792         d->iptcMetadata() = iptcData;
0793 
0794         // Make sure character set is UTF-8
0795 
0796         setIptcTagString("Iptc.Envelope.CharacterSet", QLatin1String("\33%G"));
0797 
0798         return true;
0799     }
0800     catch (Exiv2::AnyError& e)
0801     {
0802         d->printExiv2ExceptionError(QLatin1String("Cannot set Iptc Subjects into image with Exiv2:"), e);
0803     }
0804     catch (...)
0805     {
0806         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0807     }
0808 
0809     return false;
0810 }
0811 
0812 QStringList MetaEngine::getIptcSubCategories() const
0813 {
0814     QMutexLocker lock(&s_metaEngineMutex);
0815 
0816     try
0817     {
0818         if (!d->iptcMetadata().empty())
0819         {
0820             QStringList subCategories;
0821             Exiv2::IptcData iptcData(d->iptcMetadata());
0822 
0823             for (Exiv2::IptcData::const_iterator it = iptcData.begin() ; it != iptcData.end() ; ++it)
0824             {
0825                 QString key = QString::fromStdString(it->key());
0826 
0827                 if (key == QLatin1String("Iptc.Application2.SuppCategory"))
0828                 {
0829                     QString val = d->extractIptcTagString(iptcData, *it);
0830                     subCategories.append(val);
0831                 }
0832             }
0833 
0834             return subCategories;
0835         }
0836     }
0837     catch (Exiv2::AnyError& e)
0838     {
0839         d->printExiv2ExceptionError(QLatin1String("Cannot get Iptc Sub Categories from image with Exiv2:"), e);
0840     }
0841     catch (...)
0842     {
0843         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0844     }
0845 
0846     return QStringList();
0847 }
0848 
0849 bool MetaEngine::setIptcSubCategories(const QStringList& oldSubCategories, const QStringList& newSubCategories) const
0850 {
0851     QMutexLocker lock(&s_metaEngineMutex);
0852 
0853     try
0854     {
0855         QStringList oldkeys           = oldSubCategories;
0856         QStringList newkeys           = newSubCategories;
0857 
0858         // Remove all old Sub Categories.
0859 
0860         Exiv2::IptcData iptcData(d->iptcMetadata());
0861         Exiv2::IptcData::iterator it2 = iptcData.begin();
0862 
0863         while (it2 != iptcData.end())
0864         {
0865             QString key = QString::fromStdString(it2->key());
0866             QString val = QString::fromStdString(it2->toString());       // FIXME: check charset
0867 
0868             if ((key == QLatin1String("Iptc.Application2.SuppCategory")) && oldSubCategories.contains(val))
0869             {
0870                 it2 = iptcData.erase(it2);
0871             }
0872             else
0873             {
0874                 ++it2;
0875             }
0876         };
0877 
0878         // Add new Sub Categories. Note that SubCategories Iptc tag is limited to 32
0879         // characters but can be redondant.
0880 
0881         Exiv2::IptcKey iptcTag("Iptc.Application2.SuppCategory");
0882 
0883         for (QStringList::const_iterator it = newkeys.constBegin() ; it != newkeys.constEnd() ; ++it)
0884         {
0885             QString key               = *it;
0886             key.truncate(32);
0887 
0888             Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
0889             val->read(key.toUtf8().constData());
0890             iptcData.add(iptcTag, val.get());
0891         }
0892 
0893         d->iptcMetadata() = iptcData;
0894 
0895         // Make sure character set is UTF-8
0896 
0897         setIptcTagString("Iptc.Envelope.CharacterSet", QLatin1String("\33%G"));
0898 
0899         return true;
0900     }
0901     catch (Exiv2::AnyError& e)
0902     {
0903         d->printExiv2ExceptionError(QLatin1String("Cannot set Iptc Sub Categories into image with Exiv2:"), e);
0904     }
0905     catch (...)
0906     {
0907         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0908     }
0909 
0910     return false;
0911 }
0912 
0913 MetaEngine::TagsMap MetaEngine::getIptcTagsList() const
0914 {
0915     QMutexLocker lock(&s_metaEngineMutex);
0916 
0917     try
0918     {
0919         QList<const Exiv2::DataSet*> tags;
0920         tags << Exiv2::IptcDataSets::envelopeRecordList()
0921              << Exiv2::IptcDataSets::application2RecordList();
0922 
0923         TagsMap tagsMap;
0924 
0925         for (QList<const Exiv2::DataSet*>::iterator it = tags.begin() ; it != tags.end() ; ++it)
0926         {
0927             do
0928             {
0929                 QString key = QLatin1String(Exiv2::IptcKey( (*it)->number_, (*it)->recordId_).key().c_str());
0930                 QStringList values;
0931                 values << QLatin1String((*it)->name_) << QLatin1String((*it)->title_) << QLatin1String((*it)->desc_);
0932                 tagsMap.insert(key, values);
0933                 ++(*it);
0934             }
0935             while ((*it)->number_ != 0xffff);
0936         }
0937 
0938         return tagsMap;
0939     }
0940     catch (Exiv2::AnyError& e)
0941     {
0942         d->printExiv2ExceptionError(QLatin1String("Cannot get Iptc Tags list with Exiv2:"), e);
0943     }
0944     catch (...)
0945     {
0946         qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
0947     }
0948 
0949     return TagsMap();
0950 }
0951 
0952 } // namespace Digikam
0953 
0954 #if defined(Q_CC_CLANG)
0955 #   pragma clang diagnostic pop
0956 #endif