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