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

0001 /*
0002     SPDX-FileCopyrightText: 2006-2015 Gilles Caulier <caulier dot gilles at gmail dot com>
0003     SPDX-FileCopyrightText: 2006-2012 Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 // Local includes
0009 
0010 #include "kexiv2.h"
0011 #include "kexiv2_p.h"
0012 #include "libkexiv2_debug.h"
0013 
0014 namespace KExiv2Iface
0015 {
0016 
0017 bool KExiv2::canWriteXmp(const QString& filePath)
0018 {
0019 #ifdef _XMP_SUPPORT_
0020     try
0021     {
0022 #if EXIV2_TEST_VERSION(0,28,0)
0023         Exiv2::Image::UniquePtr image =
0024 #else
0025         Exiv2::Image::AutoPtr image = 
0026 #endif
0027                                       Exiv2::ImageFactory::open((const char*)
0028                                       (QFile::encodeName(filePath).constData()));
0029 
0030         Exiv2::AccessMode mode = image->checkMode(Exiv2::mdXmp);
0031         return (mode == Exiv2::amWrite || mode == Exiv2::amReadWrite);
0032     }
0033     catch( Exiv2::Error& e )
0034     {
0035         std::string s(e.what());
0036         qCCritical(LIBKEXIV2_LOG) << "Cannot check Xmp access mode using Exiv2 (Error #"
0037 #if EXIV2_TEST_VERSION(0,28,0)
0038                     << Exiv2::Error(e.code()).what()
0039 #else
0040                     << e.code() << ": " << s.c_str()
0041 #endif
0042                     << ")";
0043     }
0044     catch(...)
0045     {
0046         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0047     }
0048 
0049 #else
0050 
0051     Q_UNUSED(filePath);
0052 
0053 #endif // _XMP_SUPPORT_
0054 
0055     return false;
0056 }
0057 
0058 bool KExiv2::hasXmp() const
0059 {
0060 #ifdef _XMP_SUPPORT_
0061 
0062     return !d->xmpMetadata().empty();
0063 
0064 #else
0065 
0066     return false;
0067 
0068 #endif // _XMP_SUPPORT_
0069 }
0070 
0071 bool KExiv2::clearXmp() const
0072 {
0073 #ifdef _XMP_SUPPORT_
0074 
0075     try
0076     {
0077         d->xmpMetadata().clear();
0078         return true;
0079     }
0080     catch( Exiv2::Error& e )
0081     {
0082         d->printExiv2ExceptionError(QString::fromLatin1("Cannot clear Xmp data using Exiv2 "), e);
0083     }
0084     catch(...)
0085     {
0086         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0087     }
0088 
0089 #endif // _XMP_SUPPORT_
0090 
0091     return false;
0092 }
0093 
0094 QByteArray KExiv2::getXmp() const
0095 {
0096 #ifdef _XMP_SUPPORT_
0097 
0098     try
0099     {
0100         if (!d->xmpMetadata().empty())
0101         {
0102 
0103             std::string xmpPacket;
0104             Exiv2::XmpParser::encode(xmpPacket, d->xmpMetadata());
0105             QByteArray data(xmpPacket.data(), xmpPacket.size());
0106             return data;
0107         }
0108     }
0109     catch( Exiv2::Error& e )
0110     {
0111         if (!d->filePath.isEmpty())
0112 
0113 
0114         d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Xmp data using Exiv2 "), e);
0115     }
0116     catch(...)
0117     {
0118         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0119     }
0120 
0121 #endif // _XMP_SUPPORT_
0122 
0123     return QByteArray();
0124 }
0125 
0126 bool KExiv2::setXmp(const QByteArray& data) const
0127 {
0128 #ifdef _XMP_SUPPORT_
0129 
0130     try
0131     {
0132         if (!data.isEmpty())
0133         {
0134             std::string xmpPacket;
0135             xmpPacket.assign(data.data(), data.size());
0136 
0137             if (Exiv2::XmpParser::decode(d->xmpMetadata(), xmpPacket) != 0)
0138                 return false;
0139             else
0140                 return true;
0141         }
0142     }
0143     catch( Exiv2::Error& e )
0144     {
0145         if (!d->filePath.isEmpty())
0146             qCCritical(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData();
0147 
0148         d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp data using Exiv2 "), e);
0149     }
0150     catch(...)
0151     {
0152         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0153     }
0154 
0155 #else
0156 
0157     Q_UNUSED(data);
0158 
0159 #endif // _XMP_SUPPORT_
0160 
0161     return false;
0162 }
0163 
0164 KExiv2::MetaDataMap KExiv2::getXmpTagsDataList(const QStringList& xmpKeysFilter, bool invertSelection) const
0165 {
0166 #ifdef _XMP_SUPPORT_
0167 
0168     if (d->xmpMetadata().empty())
0169        return MetaDataMap();
0170 
0171     try
0172     {
0173         Exiv2::XmpData xmpData = d->xmpMetadata();
0174         xmpData.sortByKey();
0175 
0176         QString     ifDItemName;
0177         MetaDataMap metaDataMap;
0178 
0179         for (Exiv2::XmpData::iterator md = xmpData.begin(); md != xmpData.end(); ++md)
0180         {
0181             QString key = QString::fromLatin1(md->key().c_str());
0182 
0183             // Decode the tag value with a user friendly output.
0184             std::ostringstream os;
0185             os << *md;
0186             QString value = QString::fromUtf8(os.str().c_str());
0187 
0188             // If the tag is a language alternative type, parse content to detect language.
0189             if (md->typeId() == Exiv2::langAlt)
0190             {
0191                 QString lang;
0192                 value = detectLanguageAlt(value, lang);
0193             }
0194             else
0195             {
0196                 value = QString::fromUtf8(os.str().c_str());
0197             }
0198 
0199             // To make a string just on one line.
0200             value.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
0201 
0202             // Some XMP key are redondancy. check if already one exist...
0203             MetaDataMap::iterator it = metaDataMap.find(key);
0204 
0205             // We apply a filter to get only the XMP tags that we need.
0206 
0207             if (!xmpKeysFilter.isEmpty())
0208             {
0209                 if (!invertSelection)
0210                 {
0211                     if (xmpKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
0212                     {
0213                         if (it == metaDataMap.end())
0214                         {
0215                             metaDataMap.insert(key, value);
0216                         }
0217                         else
0218                         {
0219                             QString v = *it;
0220                             v.append(QString::fromLatin1(", "));
0221                             v.append(value);
0222                             metaDataMap.insert(key, v);
0223                         }
0224                     }
0225                 }
0226                 else
0227                 {
0228                     if (!xmpKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
0229                     {
0230                         if (it == metaDataMap.end())
0231                         {
0232                             metaDataMap.insert(key, value);
0233                         }
0234                         else
0235                         {
0236                             QString v = *it;
0237                             v.append(QString::fromLatin1(", "));
0238                             v.append(value);
0239                             metaDataMap.insert(key, v);
0240                         }
0241                     }
0242                 }
0243             }
0244             else // else no filter at all.
0245             {
0246                 if (it == metaDataMap.end())
0247                 {
0248                     metaDataMap.insert(key, value);
0249                 }
0250                 else
0251                 {
0252                     QString v = *it;
0253                     v.append(QString::fromLatin1(", "));
0254                     v.append(value);
0255                     metaDataMap.insert(key, v);
0256                 }
0257             }
0258         }
0259 
0260         return metaDataMap;
0261     }
0262     catch (Exiv2::Error& e)
0263     {
0264         d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse Xmp metadata using Exiv2 "), e);
0265     }
0266     catch(...)
0267     {
0268         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0269     }
0270 
0271 #else
0272 
0273     Q_UNUSED(xmpKeysFilter);
0274     Q_UNUSED(invertSelection);
0275 
0276 #endif // _XMP_SUPPORT_
0277 
0278     return MetaDataMap();
0279 }
0280 
0281 QString KExiv2::getXmpTagTitle(const char* xmpTagName)
0282 {
0283 #ifdef _XMP_SUPPORT_
0284 
0285     try
0286     {
0287         std::string xmpkey(xmpTagName);
0288         Exiv2::XmpKey xk(xmpkey);
0289         return QString::fromLocal8Bit( Exiv2::XmpProperties::propertyTitle(xk) );
0290     }
0291     catch (Exiv2::Error& e)
0292     {
0293         d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Xmp metadata tag title using Exiv2 "), e);
0294     }
0295     catch(...)
0296     {
0297         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0298     }
0299 
0300 #else
0301 
0302     Q_UNUSED(xmpTagName);
0303 
0304 #endif // _XMP_SUPPORT_
0305 
0306     return QString();
0307 }
0308 
0309 QString KExiv2::getXmpTagDescription(const char* xmpTagName)
0310 {
0311 #ifdef _XMP_SUPPORT_
0312     try
0313     {
0314         std::string xmpkey(xmpTagName);
0315         Exiv2::XmpKey xk(xmpkey);
0316         return QString::fromLocal8Bit( Exiv2::XmpProperties::propertyDesc(xk) );
0317     }
0318     catch (Exiv2::Error& e)
0319     {
0320         d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Xmp metadata tag description using Exiv2 "), e);
0321     }
0322     catch(...)
0323     {
0324         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0325     }
0326 
0327 #else
0328 
0329     Q_UNUSED(xmpTagName);
0330 
0331 #endif // _XMP_SUPPORT_
0332 
0333     return QString();
0334 }
0335 
0336 QString KExiv2::getXmpTagString(const char* xmpTagName, bool escapeCR) const
0337 {
0338 #ifdef _XMP_SUPPORT_
0339 
0340     try
0341     {
0342         Exiv2::XmpData xmpData(d->xmpMetadata());
0343         Exiv2::XmpKey key(xmpTagName);
0344         Exiv2::XmpData::iterator it = xmpData.findKey(key);
0345 
0346         if (it != xmpData.end())
0347         {
0348             std::ostringstream os;
0349             os << *it;
0350             QString tagValue = QString::fromUtf8(os.str().c_str());
0351 
0352             if (escapeCR)
0353                 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
0354 
0355             return tagValue;
0356         }
0357     }
0358     catch( Exiv2::Error& e )
0359     {
0360         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
0361     }
0362     catch(...)
0363     {
0364         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0365     }
0366 
0367 #else
0368 
0369     Q_UNUSED(xmpTagName);
0370     Q_UNUSED(escapeCR);
0371 
0372 #endif // _XMP_SUPPORT_
0373 
0374     return QString();
0375 }
0376 
0377 bool KExiv2::setXmpTagString(const char* xmpTagName, const QString& value, bool setProgramName) const
0378 {
0379 #ifdef _XMP_SUPPORT_
0380 
0381     if (!setProgramId(setProgramName))
0382         return false;
0383 
0384     try
0385     {
0386         const std::string &txt(value.toUtf8().constData());
0387 #if EXIV2_TEST_VERSION(0,28,0)
0388         Exiv2::Value::UniquePtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
0389 #else
0390         Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
0391 #endif
0392         xmpTxtVal->read(txt);
0393         d->xmpMetadata()[xmpTagName].setValue(xmpTxtVal.get());
0394         return true;
0395     }
0396     catch( Exiv2::Error& e )
0397     {
0398         d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string into image using Exiv2 "), e);
0399     }
0400     catch(...)
0401     {
0402         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0403     }
0404 
0405 #else
0406 
0407     Q_UNUSED(xmpTagName);
0408     Q_UNUSED(value);
0409     Q_UNUSED(setProgramName);
0410 
0411 #endif // _XMP_SUPPORT_
0412 
0413     return false;
0414 }
0415 bool KExiv2::setXmpTagString(const char* xmpTagName, const QString& value,
0416                              KExiv2::XmpTagType type, bool setProgramName) const
0417 {
0418 #ifdef _XMP_SUPPORT_
0419 
0420     if (!setProgramId(setProgramName))
0421         return false;
0422 
0423     try
0424     {
0425         const std::string &txt(value.toUtf8().constData());
0426         Exiv2::XmpTextValue xmpTxtVal("");
0427 
0428         if (type == KExiv2::NormalTag) // normal type
0429         {
0430             xmpTxtVal.read(txt);
0431             d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName),&xmpTxtVal);
0432             return true;
0433         }
0434 
0435         if (type == KExiv2::ArrayBagTag) // xmp type = bag
0436         {
0437             xmpTxtVal.setXmpArrayType(Exiv2::XmpValue::xaBag);
0438             xmpTxtVal.read("");
0439             d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName),&xmpTxtVal);
0440         }
0441 
0442         if (type == KExiv2::StructureTag) // xmp type = struct
0443         {
0444             xmpTxtVal.setXmpStruct();
0445             d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName),&xmpTxtVal);
0446         }
0447     }
0448     catch( Exiv2::Error& e )
0449     {
0450         d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string into image using Exiv2 "), e);
0451     }
0452     catch(...)
0453     {
0454         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0455     }
0456 
0457 #else
0458 
0459     Q_UNUSED(xmpTagName);
0460     Q_UNUSED(value);
0461     Q_UNUSED(setProgramName);
0462 
0463 #endif // _XMP_SUPPORT_
0464 
0465     return false;
0466 }
0467 KExiv2::AltLangMap KExiv2::getXmpTagStringListLangAlt(const char* xmpTagName, bool escapeCR) const
0468 {
0469 #ifdef _XMP_SUPPORT_
0470 
0471     try
0472     {
0473         Exiv2::XmpData xmpData = d->xmpMetadata();
0474 
0475         for (Exiv2::XmpData::iterator it = xmpData.begin(); it != xmpData.end(); ++it)
0476         {
0477             if (it->key() == xmpTagName && it->typeId() == Exiv2::langAlt)
0478             {
0479                 AltLangMap map;
0480                 const Exiv2::LangAltValue &value = static_cast<const Exiv2::LangAltValue &>(it->value());
0481 
0482                 for (Exiv2::LangAltValue::ValueType::const_iterator it2 = value.value_.begin();
0483                      it2 != value.value_.end(); ++it2)
0484                 {
0485                     QString lang = QString::fromUtf8(it2->first.c_str());
0486                     QString text = QString::fromUtf8(it2->second.c_str());
0487 
0488                     if (escapeCR)
0489                         text.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
0490 
0491                     map.insert(lang, text);
0492                 }
0493 
0494                 return map;
0495             }
0496         }
0497     }
0498     catch( Exiv2::Error& e )
0499     {
0500         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
0501     }
0502     catch(...)
0503     {
0504         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0505     }
0506 
0507 #else
0508 
0509     Q_UNUSED(xmpTagName);
0510     Q_UNUSED(escapeCR);
0511 
0512 #endif // _XMP_SUPPORT_
0513 
0514     return AltLangMap();
0515 }
0516 
0517 bool KExiv2::setXmpTagStringListLangAlt(const char* xmpTagName, const KExiv2::AltLangMap& values,
0518                                         bool setProgramName) const
0519 {
0520 #ifdef _XMP_SUPPORT_
0521 
0522     if (!setProgramId(setProgramName))
0523         return false;
0524 
0525     try
0526     {
0527         // Remove old XMP alternative Language tag.
0528         removeXmpTag(xmpTagName);
0529 
0530         if (!values.isEmpty())
0531         {
0532 #if EXIV2_TEST_VERSION(0,28,0)
0533             Exiv2::Value::UniquePtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
0534 #else
0535             Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
0536 #endif
0537 
0538             for (AltLangMap::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
0539             {
0540                 QString lang = it.key();
0541                 QString text = it.value();
0542                 QString txtLangAlt = QString::fromLatin1("lang=%1 %2").arg(lang).arg(text);
0543                 const std::string &txt(txtLangAlt.toUtf8().constData());
0544                 xmpTxtVal->read(txt);
0545             }
0546 
0547             // ...and add the new one instead.
0548             d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName), xmpTxtVal.get());
0549         }
0550         return true;
0551     }
0552     catch( Exiv2::Error& e )
0553     {
0554         d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string lang-alt into image using Exiv2 "), e);
0555     }
0556     catch(...)
0557     {
0558         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0559     }
0560 
0561 #else
0562 
0563     Q_UNUSED(xmpTagName);
0564     Q_UNUSED(values);
0565     Q_UNUSED(setProgramName);
0566 
0567 #endif // _XMP_SUPPORT_
0568 
0569     return false;
0570 }
0571 
0572 QString KExiv2::getXmpTagStringLangAlt(const char* xmpTagName, const QString& langAlt, bool escapeCR) const
0573 {
0574 #ifdef _XMP_SUPPORT_
0575 
0576     try
0577     {
0578         Exiv2::XmpData xmpData(d->xmpMetadata());
0579         Exiv2::XmpKey key(xmpTagName);
0580 
0581         for (Exiv2::XmpData::iterator it = xmpData.begin(); it != xmpData.end(); ++it)
0582         {
0583             if (it->key() == xmpTagName && it->typeId() == Exiv2::langAlt)
0584             {
0585                 for (int i = 0; i < it->count(); i++)
0586                 {
0587                     std::ostringstream os;
0588                     os << it->toString(i);
0589                     QString lang;
0590                     QString tagValue = QString::fromUtf8(os.str().c_str());
0591                     tagValue = detectLanguageAlt(tagValue, lang);
0592 
0593                     if (langAlt == lang)
0594                     {
0595                         if (escapeCR)
0596                             tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
0597 
0598                         return tagValue;
0599                     }
0600                 }
0601             }
0602         }
0603     }
0604     catch( Exiv2::Error& e )
0605     {
0606         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
0607     }
0608     catch(...)
0609     {
0610         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0611     }
0612 
0613 #else
0614 
0615     Q_UNUSED(xmpTagName);
0616     Q_UNUSED(langAlt);
0617     Q_UNUSED(escapeCR);
0618 
0619 #endif // _XMP_SUPPORT_
0620 
0621     return QString();
0622 }
0623 
0624 bool KExiv2::setXmpTagStringLangAlt(const char* xmpTagName, const QString& value,
0625                                     const QString& langAlt, bool setProgramName) const
0626 {
0627 #ifdef _XMP_SUPPORT_
0628 
0629     if (!setProgramId(setProgramName))
0630         return false;
0631 
0632     try
0633     {
0634         QString language(QString::fromLatin1("x-default")); // default alternative language.
0635 
0636         if (!langAlt.isEmpty())
0637             language = langAlt;
0638 
0639         QString txtLangAlt = QString(QString::fromLatin1("lang=%1 %2")).arg(language).arg(value);
0640 
0641         const std::string &txt(txtLangAlt.toUtf8().constData());
0642 #if EXIV2_TEST_VERSION(0,28,0)
0643         Exiv2::Value::UniquePtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
0644 #else
0645         Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
0646 #endif
0647 
0648         // Search if an Xmp tag already exist.
0649 
0650         AltLangMap map = getXmpTagStringListLangAlt(xmpTagName, false);
0651 
0652         if (!map.isEmpty())
0653         {
0654             for (AltLangMap::iterator it = map.begin(); it != map.end(); ++it)
0655             {
0656                 if (it.key() != langAlt)
0657                 {
0658                     const std::string &val((*it).toUtf8().constData());
0659                     xmpTxtVal->read(val);
0660                     qCDebug(LIBKEXIV2_LOG) << *it;
0661                 }
0662             }
0663         }
0664 
0665         xmpTxtVal->read(txt);
0666         removeXmpTag(xmpTagName);
0667         d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName), xmpTxtVal.get());
0668         return true;
0669     }
0670     catch( Exiv2::Error& e )
0671     {
0672         d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string lang-alt into image using Exiv2 "), e);
0673     }
0674     catch(...)
0675     {
0676         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0677     }
0678 
0679 #else
0680 
0681     Q_UNUSED(xmpTagName);
0682     Q_UNUSED(value);
0683     Q_UNUSED(langAlt);
0684     Q_UNUSED(setProgramName);
0685 
0686 #endif // _XMP_SUPPORT_
0687 
0688     return false;
0689 }
0690 
0691 QStringList KExiv2::getXmpTagStringSeq(const char* xmpTagName, bool escapeCR) const
0692 {
0693 #ifdef _XMP_SUPPORT_
0694 
0695     try
0696     {
0697         Exiv2::XmpData xmpData(d->xmpMetadata());
0698         Exiv2::XmpKey key(xmpTagName);
0699         Exiv2::XmpData::iterator it = xmpData.findKey(key);
0700 
0701         if (it != xmpData.end())
0702         {
0703             if (it->typeId() == Exiv2::xmpSeq)
0704             {
0705                 QStringList seq;
0706 
0707                 for (int i = 0; i < it->count(); i++)
0708                 {
0709                     std::ostringstream os;
0710                     os << it->toString(i);
0711                     QString seqValue = QString::fromUtf8(os.str().c_str());
0712 
0713                     if (escapeCR)
0714                         seqValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
0715 
0716                     seq.append(seqValue);
0717                 }
0718                 qCDebug(LIBKEXIV2_LOG) << "XMP String Seq (" << xmpTagName << "): " << seq;
0719 
0720                 return seq;
0721             }
0722         }
0723     }
0724     catch( Exiv2::Error& e )
0725     {
0726         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
0727     }
0728     catch(...)
0729     {
0730         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0731     }
0732 
0733 #else
0734 
0735     Q_UNUSED(xmpTagName);
0736     Q_UNUSED(escapeCR);
0737 
0738 #endif // _XMP_SUPPORT_
0739 
0740     return QStringList();
0741 }
0742 
0743 bool KExiv2::setXmpTagStringSeq(const char* xmpTagName, const QStringList& seq,
0744                                 bool setProgramName) const
0745 {
0746 #ifdef _XMP_SUPPORT_
0747 
0748     if (!setProgramId(setProgramName))
0749         return false;
0750 
0751     try
0752     {
0753         if (seq.isEmpty())
0754         {
0755             removeXmpTag(xmpTagName);
0756         }
0757         else
0758         {
0759             const QStringList list = seq;
0760 #if EXIV2_TEST_VERSION(0,28,0)
0761             Exiv2::Value::UniquePtr xmpTxtSeq = Exiv2::Value::create(Exiv2::xmpSeq);
0762 #else
0763             Exiv2::Value::AutoPtr xmpTxtSeq = Exiv2::Value::create(Exiv2::xmpSeq);
0764 #endif
0765 
0766             for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
0767             {
0768                 const std::string &txt((*it).toUtf8().constData());
0769                 xmpTxtSeq->read(txt);
0770             }
0771 
0772             d->xmpMetadata()[xmpTagName].setValue(xmpTxtSeq.get());
0773         }
0774         return true;
0775     }
0776     catch( Exiv2::Error& e )
0777     {
0778         d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string Seq into image using Exiv2 "), e);
0779     }
0780     catch(...)
0781     {
0782         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0783     }
0784 
0785 #else
0786 
0787     Q_UNUSED(xmpTagName);
0788     Q_UNUSED(seq);
0789     Q_UNUSED(setProgramName);
0790 
0791 #endif // _XMP_SUPPORT_
0792 
0793     return false;
0794 }
0795 
0796 QStringList KExiv2::getXmpTagStringBag(const char* xmpTagName, bool escapeCR) const
0797 {
0798 #ifdef _XMP_SUPPORT_
0799 
0800     try
0801     {
0802         Exiv2::XmpData xmpData(d->xmpMetadata());
0803         Exiv2::XmpKey key(xmpTagName);
0804         Exiv2::XmpData::iterator it = xmpData.findKey(key);
0805 
0806         if (it != xmpData.end())
0807         {
0808             if (it->typeId() == Exiv2::xmpBag)
0809             {
0810                 QStringList bag;
0811 
0812                 for (int i = 0; i < it->count(); i++)
0813                 {
0814                     std::ostringstream os;
0815                     os << it->toString(i);
0816                     QString bagValue = QString::fromUtf8(os.str().c_str());
0817 
0818                     if (escapeCR)
0819                         bagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
0820 
0821                     bag.append(bagValue);
0822                 }
0823 
0824                 return bag;
0825             }
0826         }
0827     }
0828     catch( Exiv2::Error& e )
0829     {
0830         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
0831     }
0832     catch(...)
0833     {
0834         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0835     }
0836 
0837 #else
0838 
0839     Q_UNUSED(xmpTagName);
0840     Q_UNUSED(escapeCR);
0841 
0842 #endif // _XMP_SUPPORT_
0843 
0844     return QStringList();
0845 }
0846 
0847 bool KExiv2::setXmpTagStringBag(const char* xmpTagName, const QStringList& bag,
0848                                 bool setProgramName) const
0849 {
0850 #ifdef _XMP_SUPPORT_
0851 
0852     if (!setProgramId(setProgramName))
0853         return false;
0854 
0855     try
0856     {
0857         if (bag.isEmpty())
0858         {
0859             removeXmpTag(xmpTagName);
0860         }
0861         else
0862         {
0863             QStringList list = bag;
0864 #if EXIV2_TEST_VERSION(0,28,0)
0865             Exiv2::Value::UniquePtr xmpTxtBag = Exiv2::Value::create(Exiv2::xmpBag);
0866 #else
0867             Exiv2::Value::AutoPtr xmpTxtBag = Exiv2::Value::create(Exiv2::xmpBag);
0868 #endif
0869 
0870             for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
0871             {
0872                 const std::string &txt((*it).toUtf8().constData());
0873                 xmpTxtBag->read(txt);
0874             }
0875 
0876             d->xmpMetadata()[xmpTagName].setValue(xmpTxtBag.get());
0877         }
0878         return true;
0879     }
0880     catch( Exiv2::Error& e )
0881     {
0882         d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string Bag into image using Exiv2 "), e);
0883     }
0884     catch(...)
0885     {
0886         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0887     }
0888 
0889 #else
0890 
0891     Q_UNUSED(xmpTagName);
0892     Q_UNUSED(bag);
0893     Q_UNUSED(setProgramName);
0894 
0895 #endif // _XMP_SUPPORT_
0896 
0897     return false;
0898 }
0899 
0900 bool KExiv2::addToXmpTagStringBag(const char* xmpTagName, const QStringList& entriesToAdd,
0901                                      bool setProgramName) const
0902 {
0903     if (!setProgramId(setProgramName))
0904         return false;
0905 
0906     QStringList oldEntries = getXmpTagStringBag(xmpTagName, false);
0907     QStringList newEntries = entriesToAdd;
0908 
0909     // Create a list of keywords including old one which already exists.
0910     for (QStringList::const_iterator it = oldEntries.constBegin(); it != oldEntries.constEnd(); ++it )
0911     {
0912         if (!newEntries.contains(*it))
0913             newEntries.append(*it);
0914     }
0915 
0916     if (setXmpTagStringBag(xmpTagName, newEntries, false))
0917         return true;
0918 
0919     return false;
0920 }
0921 
0922 bool KExiv2::removeFromXmpTagStringBag(const char* xmpTagName, const QStringList& entriesToRemove,
0923                                        bool setProgramName) const
0924 {
0925     if (!setProgramId(setProgramName))
0926         return false;
0927 
0928     QStringList currentEntries = getXmpTagStringBag(xmpTagName, false);
0929     QStringList newEntries;
0930 
0931     // Create a list of current keywords except those that shall be removed
0932     for (QStringList::const_iterator it = currentEntries.constBegin(); it != currentEntries.constEnd(); ++it )
0933     {
0934         if (!entriesToRemove.contains(*it))
0935             newEntries.append(*it);
0936     }
0937 
0938     if (setXmpTagStringBag(xmpTagName, newEntries, false))
0939         return true;
0940 
0941     return false;
0942 }
0943 
0944 QVariant KExiv2::getXmpTagVariant(const char* xmpTagName, bool rationalAsListOfInts, bool stringEscapeCR) const
0945 {
0946 #ifdef _XMP_SUPPORT_
0947     try
0948     {
0949         Exiv2::XmpData xmpData(d->xmpMetadata());
0950         Exiv2::XmpKey key(xmpTagName);
0951         Exiv2::XmpData::iterator it = xmpData.findKey(key);
0952 
0953         if (it != xmpData.end())
0954         {
0955             switch (it->typeId())
0956             {
0957                 case Exiv2::unsignedByte:
0958                 case Exiv2::unsignedShort:
0959                 case Exiv2::unsignedLong:
0960                 case Exiv2::signedShort:
0961                 case Exiv2::signedLong:
0962 #if EXIV2_TEST_VERSION(0,28,0)
0963                     return QVariant((int)it->toUint32());
0964 #else
0965                     return QVariant((int)it->toLong());
0966 #endif
0967                 case Exiv2::unsignedRational:
0968                 case Exiv2::signedRational:
0969                     if (rationalAsListOfInts)
0970                     {
0971                         QList<QVariant> list;
0972                         list << (*it).toRational().first;
0973                         list << (*it).toRational().second;
0974                         return QVariant(list);
0975                     }
0976                     else
0977                     {
0978                         // prefer double precision
0979                         double num = (*it).toRational().first;
0980                         double den = (*it).toRational().second;
0981 
0982                         if (den == 0.0)
0983 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0984                             return QVariant(QMetaType(QMetaType::Double));
0985 #else
0986                             return QVariant(QVariant::Double);
0987 #endif
0988 
0989                         return QVariant(num / den);
0990                     }
0991                 case Exiv2::date:
0992                 case Exiv2::time:
0993                 {
0994                     QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
0995                     return QVariant(dateTime);
0996                 }
0997                 case Exiv2::asciiString:
0998                 case Exiv2::comment:
0999                 case Exiv2::string:
1000                 {
1001                     std::ostringstream os;
1002                     os << *it;
1003                     QString tagValue = QString::fromLocal8Bit(os.str().c_str());
1004 
1005                     if (stringEscapeCR)
1006                         tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
1007 
1008                     return QVariant(tagValue);
1009                 }
1010                 case Exiv2::xmpText:
1011                 {
1012                     std::ostringstream os;
1013                     os << *it;
1014                     QString tagValue = QString::fromUtf8(os.str().c_str());
1015 
1016                     if (stringEscapeCR)
1017                         tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
1018 
1019                     return tagValue;
1020                 }
1021                 case Exiv2::xmpBag:
1022                 case Exiv2::xmpSeq:
1023                 case Exiv2::xmpAlt:
1024                 {
1025                     QStringList list;
1026 
1027                     for (int i=0; i < it->count(); i++)
1028                     {
1029                         list << QString::fromUtf8(it->toString(i).c_str());
1030                     }
1031 
1032                     return list;
1033                 }
1034                 case Exiv2::langAlt:
1035                 {
1036                     // access the value directly
1037                     const Exiv2::LangAltValue &value = static_cast<const Exiv2::LangAltValue &>(it->value());
1038                     QMap<QString, QVariant> map;
1039                     // access the ValueType std::map< std::string, std::string>
1040                     Exiv2::LangAltValue::ValueType::const_iterator i;
1041 
1042                     for (i = value.value_.begin(); i != value.value_.end(); ++i)
1043                     {
1044                         map[QString::fromUtf8(i->first.c_str())] = QString::fromUtf8(i->second.c_str());
1045                     }
1046 
1047                     return map;
1048                 }
1049                 default:
1050                     break;
1051             }
1052         }
1053     }
1054     catch( Exiv2::Error& e )
1055     {
1056         d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
1057     }
1058     catch(...)
1059     {
1060         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1061     }
1062 
1063 #else
1064 
1065     Q_UNUSED(xmpTagName);
1066     Q_UNUSED(rationalAsListOfInts);
1067     Q_UNUSED(stringEscapeCR);
1068 
1069 #endif // _XMP_SUPPORT_
1070 
1071     return QVariant();
1072 }
1073 
1074 bool KExiv2::registerXmpNameSpace(const QString& uri, const QString& prefix)
1075 {
1076 #ifdef _XMP_SUPPORT_
1077 
1078     try
1079     {
1080         QString ns = uri;
1081 
1082         if (!uri.endsWith(QString::fromLatin1("/")))
1083             ns.append(QString::fromLatin1("/"));
1084 
1085         Exiv2::XmpProperties::registerNs(ns.toLatin1().constData(), prefix.toLatin1().constData());
1086         return true;
1087     }
1088     catch( Exiv2::Error& e )
1089     {
1090         KExiv2Private::printExiv2ExceptionError(QString::fromLatin1("Cannot register a new Xmp namespace using Exiv2 "), e);
1091     }
1092     catch(...)
1093     {
1094         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1095     }
1096 
1097 #else
1098 
1099     Q_UNUSED(uri);
1100     Q_UNUSED(prefix);
1101 
1102 #endif // _XMP_SUPPORT_
1103 
1104     return false;
1105 }
1106 
1107 bool KExiv2::unregisterXmpNameSpace(const QString& uri)
1108 {
1109 #ifdef _XMP_SUPPORT_
1110 
1111     try
1112     {
1113         QString ns = uri;
1114 
1115         if (!uri.endsWith(QString::fromLatin1("/")))
1116             ns.append(QString::fromLatin1("/"));
1117 
1118         Exiv2::XmpProperties::unregisterNs(ns.toLatin1().constData());
1119         return true;
1120     }
1121     catch( Exiv2::Error& e )
1122     {
1123         KExiv2Private::printExiv2ExceptionError(QString::fromLatin1("Cannot unregister a new Xmp namespace using Exiv2 "), e);
1124     }
1125     catch(...)
1126     {
1127         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1128     }
1129 
1130 #else
1131 
1132     Q_UNUSED(uri);
1133 
1134 #endif // _XMP_SUPPORT_
1135 
1136     return false;
1137 }
1138 
1139 bool KExiv2::removeXmpTag(const char* xmpTagName, bool setProgramName) const
1140 {
1141 #ifdef _XMP_SUPPORT_
1142 
1143     if (!setProgramId(setProgramName))
1144         return false;
1145 
1146     try
1147     {
1148         Exiv2::XmpKey xmpKey(xmpTagName);
1149         Exiv2::XmpData::iterator it = d->xmpMetadata().findKey(xmpKey);
1150 
1151         if (it != d->xmpMetadata().end())
1152         {
1153             d->xmpMetadata().erase(it);
1154             return true;
1155         }
1156     }
1157     catch( Exiv2::Error& e )
1158     {
1159         d->printExiv2ExceptionError(QString::fromLatin1("Cannot remove Xmp tag using Exiv2 "), e);
1160     }
1161     catch(...)
1162     {
1163         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1164     }
1165 
1166 #else
1167 
1168     Q_UNUSED(xmpTagName);
1169     Q_UNUSED(setProgramName);
1170 
1171 #endif // _XMP_SUPPORT_
1172 
1173     return false;
1174 }
1175 
1176 QStringList KExiv2::getXmpKeywords() const
1177 {
1178     return (getXmpTagStringBag("Xmp.dc.subject", false));
1179 }
1180 
1181 bool KExiv2::setXmpKeywords(const QStringList& newKeywords, bool setProgramName) const
1182 {
1183     return addToXmpTagStringBag("Xmp.dc.subject", newKeywords, setProgramName);
1184 }
1185 
1186 bool KExiv2::removeXmpKeywords(const QStringList& keywordsToRemove, bool setProgramName)
1187 {
1188     return removeFromXmpTagStringBag("Xmp.dc.subject", keywordsToRemove, setProgramName);
1189 }
1190 
1191 QStringList KExiv2::getXmpSubCategories() const
1192 {
1193     return (getXmpTagStringBag("Xmp.photoshop.SupplementalCategories", false));
1194 }
1195 
1196 bool KExiv2::setXmpSubCategories(const QStringList& newSubCategories, bool setProgramName) const
1197 {
1198     return addToXmpTagStringBag("Xmp.photoshop.SupplementalCategories", newSubCategories, setProgramName);
1199 }
1200 
1201 bool KExiv2::removeXmpSubCategories(const QStringList& subCategoriesToRemove, bool setProgramName)
1202 {
1203     return removeFromXmpTagStringBag("Xmp.photoshop.SupplementalCategories", subCategoriesToRemove, setProgramName);
1204 }
1205 
1206 QStringList KExiv2::getXmpSubjects() const
1207 {
1208     return (getXmpTagStringBag("Xmp.iptc.SubjectCode", false));
1209 }
1210 
1211 bool KExiv2::setXmpSubjects(const QStringList& newSubjects, bool setProgramName) const
1212 {
1213     return addToXmpTagStringBag("Xmp.iptc.SubjectCode", newSubjects, setProgramName);
1214 }
1215 
1216 bool KExiv2::removeXmpSubjects(const QStringList& subjectsToRemove, bool setProgramName)
1217 {
1218     return removeFromXmpTagStringBag("Xmp.iptc.SubjectCode", subjectsToRemove, setProgramName);
1219 }
1220 
1221 KExiv2::TagsMap KExiv2::getXmpTagsList() const
1222 {
1223     TagsMap tagsMap;
1224     d->getXMPTagsListFromPrefix(QString::fromLatin1("dc"),             tagsMap);
1225     d->getXMPTagsListFromPrefix(QString::fromLatin1("digiKam"),        tagsMap);
1226     d->getXMPTagsListFromPrefix(QString::fromLatin1("xmp"),            tagsMap);
1227     d->getXMPTagsListFromPrefix(QString::fromLatin1("xmpRights"),      tagsMap);
1228     d->getXMPTagsListFromPrefix(QString::fromLatin1("xmpMM"),          tagsMap);
1229     d->getXMPTagsListFromPrefix(QString::fromLatin1("xmpBJ"),          tagsMap);
1230     d->getXMPTagsListFromPrefix(QString::fromLatin1("xmpTPg"),         tagsMap);
1231     d->getXMPTagsListFromPrefix(QString::fromLatin1("xmpDM"),          tagsMap);
1232     d->getXMPTagsListFromPrefix(QString::fromLatin1("MicrosoftPhoto"), tagsMap);
1233     d->getXMPTagsListFromPrefix(QString::fromLatin1("pdf"),            tagsMap);
1234     d->getXMPTagsListFromPrefix(QString::fromLatin1("photoshop"),      tagsMap);
1235     d->getXMPTagsListFromPrefix(QString::fromLatin1("crs"),            tagsMap);
1236     d->getXMPTagsListFromPrefix(QString::fromLatin1("tiff"),           tagsMap);
1237     d->getXMPTagsListFromPrefix(QString::fromLatin1("exif"),           tagsMap);
1238     d->getXMPTagsListFromPrefix(QString::fromLatin1("aux"),            tagsMap);
1239     d->getXMPTagsListFromPrefix(QString::fromLatin1("iptc"),           tagsMap);
1240     d->getXMPTagsListFromPrefix(QString::fromLatin1("iptcExt"),        tagsMap);
1241     d->getXMPTagsListFromPrefix(QString::fromLatin1("plus"),           tagsMap);
1242     d->getXMPTagsListFromPrefix(QString::fromLatin1("mwg-rs"),         tagsMap);
1243     d->getXMPTagsListFromPrefix(QString::fromLatin1("dwc"),            tagsMap);
1244     return tagsMap;
1245 }
1246 
1247 }  // NameSpace KExiv2Iface