KExiv2

kexiv2xmp.cpp
1/*
2 SPDX-FileCopyrightText: 2006-2015 Gilles Caulier <caulier dot gilles at gmail dot com>
3 SPDX-FileCopyrightText: 2006-2012 Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8// Local includes
9
10#include "kexiv2.h"
11#include "kexiv2_p.h"
12#include "libkexiv2_debug.h"
13
14namespace KExiv2Iface
15{
16
17bool KExiv2::canWriteXmp(const QString& filePath)
18{
19#ifdef _XMP_SUPPORT_
20 try
21 {
22#if EXIV2_TEST_VERSION(0,28,0)
23 Exiv2::Image::UniquePtr image =
24#else
25 Exiv2::Image::AutoPtr image =
26#endif
27 Exiv2::ImageFactory::open((const char*)
28 (QFile::encodeName(filePath).constData()));
29
30 Exiv2::AccessMode mode = image->checkMode(Exiv2::mdXmp);
31 return (mode == Exiv2::amWrite || mode == Exiv2::amReadWrite);
32 }
33 catch( Exiv2::Error& e )
34 {
35 std::string s(e.what());
36 qCCritical(LIBKEXIV2_LOG) << "Cannot check Xmp access mode using Exiv2 (Error #"
37#if EXIV2_TEST_VERSION(0,28,0)
38 << Exiv2::Error(e.code()).what()
39#else
40 << e.code() << ": " << s.c_str()
41#endif
42 << ")";
43 }
44 catch(...)
45 {
46 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
47 }
48
49#else
50
51 Q_UNUSED(filePath);
52
53#endif // _XMP_SUPPORT_
54
55 return false;
56}
57
58bool KExiv2::hasXmp() const
59{
60#ifdef _XMP_SUPPORT_
61
62 return !d->xmpMetadata().empty();
63
64#else
65
66 return false;
67
68#endif // _XMP_SUPPORT_
69}
70
71bool KExiv2::clearXmp() const
72{
73#ifdef _XMP_SUPPORT_
74
75 try
76 {
77 d->xmpMetadata().clear();
78 return true;
79 }
80 catch( Exiv2::Error& e )
81 {
82 d->printExiv2ExceptionError(QString::fromLatin1("Cannot clear Xmp data using Exiv2 "), e);
83 }
84 catch(...)
85 {
86 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
87 }
88
89#endif // _XMP_SUPPORT_
90
91 return false;
92}
93
95{
96#ifdef _XMP_SUPPORT_
97
98 try
99 {
100 if (!d->xmpMetadata().empty())
101 {
102
103 std::string xmpPacket;
104 Exiv2::XmpParser::encode(xmpPacket, d->xmpMetadata());
105 QByteArray data(xmpPacket.data(), xmpPacket.size());
106 return data;
107 }
108 }
109 catch( Exiv2::Error& e )
110 {
111 if (!d->filePath.isEmpty())
112
113
114 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Xmp data using Exiv2 "), e);
115 }
116 catch(...)
117 {
118 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
119 }
120
121#endif // _XMP_SUPPORT_
122
123 return QByteArray();
124}
125
126bool KExiv2::setXmp(const QByteArray& data) const
127{
128#ifdef _XMP_SUPPORT_
129
130 try
131 {
132 if (!data.isEmpty())
133 {
134 std::string xmpPacket;
135 xmpPacket.assign(data.data(), data.size());
136
137 if (Exiv2::XmpParser::decode(d->xmpMetadata(), xmpPacket) != 0)
138 return false;
139 else
140 return true;
141 }
142 }
143 catch( Exiv2::Error& e )
144 {
145 if (!d->filePath.isEmpty())
146 qCCritical(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData();
147
148 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp data using Exiv2 "), e);
149 }
150 catch(...)
151 {
152 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
153 }
154
155#else
156
157 Q_UNUSED(data);
158
159#endif // _XMP_SUPPORT_
160
161 return false;
162}
163
164KExiv2::MetaDataMap KExiv2::getXmpTagsDataList(const QStringList& xmpKeysFilter, bool invertSelection) const
165{
166#ifdef _XMP_SUPPORT_
167
168 if (d->xmpMetadata().empty())
169 return MetaDataMap();
170
171 try
172 {
173 Exiv2::XmpData xmpData = d->xmpMetadata();
174 xmpData.sortByKey();
175
176 QString ifDItemName;
177 MetaDataMap metaDataMap;
178
179 for (Exiv2::XmpData::iterator md = xmpData.begin(); md != xmpData.end(); ++md)
180 {
181 QString key = QString::fromLatin1(md->key().c_str());
182
183 // Decode the tag value with a user friendly output.
184 std::ostringstream os;
185 os << *md;
186 QString value = QString::fromUtf8(os.str().c_str());
187
188 // If the tag is a language alternative type, parse content to detect language.
189 if (md->typeId() == Exiv2::langAlt)
190 {
191 QString lang;
192 value = detectLanguageAlt(value, lang);
193 }
194 else
195 {
196 value = QString::fromUtf8(os.str().c_str());
197 }
198
199 // To make a string just on one line.
201
202 // Some XMP key are redondancy. check if already one exist...
203 MetaDataMap::iterator it = metaDataMap.find(key);
204
205 // We apply a filter to get only the XMP tags that we need.
206
207 if (!xmpKeysFilter.isEmpty())
208 {
209 if (!invertSelection)
210 {
211 if (xmpKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
212 {
213 if (it == metaDataMap.end())
214 {
215 metaDataMap.insert(key, value);
216 }
217 else
218 {
219 QString v = *it;
221 v.append(value);
222 metaDataMap.insert(key, v);
223 }
224 }
225 }
226 else
227 {
228 if (!xmpKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
229 {
230 if (it == metaDataMap.end())
231 {
232 metaDataMap.insert(key, value);
233 }
234 else
235 {
236 QString v = *it;
238 v.append(value);
239 metaDataMap.insert(key, v);
240 }
241 }
242 }
243 }
244 else // else no filter at all.
245 {
246 if (it == metaDataMap.end())
247 {
248 metaDataMap.insert(key, value);
249 }
250 else
251 {
252 QString v = *it;
254 v.append(value);
255 metaDataMap.insert(key, v);
256 }
257 }
258 }
259
260 return metaDataMap;
261 }
262 catch (Exiv2::Error& e)
263 {
264 d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse Xmp metadata using Exiv2 "), e);
265 }
266 catch(...)
267 {
268 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
269 }
270
271#else
272
273 Q_UNUSED(xmpKeysFilter);
274 Q_UNUSED(invertSelection);
275
276#endif // _XMP_SUPPORT_
277
278 return MetaDataMap();
279}
280
281QString KExiv2::getXmpTagTitle(const char* xmpTagName)
282{
283#ifdef _XMP_SUPPORT_
284
285 try
286 {
287 std::string xmpkey(xmpTagName);
288 Exiv2::XmpKey xk(xmpkey);
289 return QString::fromLocal8Bit( Exiv2::XmpProperties::propertyTitle(xk) );
290 }
291 catch (Exiv2::Error& e)
292 {
293 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Xmp metadata tag title using Exiv2 "), e);
294 }
295 catch(...)
296 {
297 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
298 }
299
300#else
301
302 Q_UNUSED(xmpTagName);
303
304#endif // _XMP_SUPPORT_
305
306 return QString();
307}
308
310{
311#ifdef _XMP_SUPPORT_
312 try
313 {
314 std::string xmpkey(xmpTagName);
315 Exiv2::XmpKey xk(xmpkey);
316 return QString::fromLocal8Bit( Exiv2::XmpProperties::propertyDesc(xk) );
317 }
318 catch (Exiv2::Error& e)
319 {
320 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Xmp metadata tag description using Exiv2 "), e);
321 }
322 catch(...)
323 {
324 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
325 }
326
327#else
328
329 Q_UNUSED(xmpTagName);
330
331#endif // _XMP_SUPPORT_
332
333 return QString();
334}
335
336QString KExiv2::getXmpTagString(const char* xmpTagName, bool escapeCR) const
337{
338#ifdef _XMP_SUPPORT_
339
340 try
341 {
342 Exiv2::XmpData xmpData(d->xmpMetadata());
343 Exiv2::XmpKey key(xmpTagName);
344 Exiv2::XmpData::iterator it = xmpData.findKey(key);
345
346 if (it != xmpData.end())
347 {
348 std::ostringstream os;
349 os << *it;
350 QString tagValue = QString::fromUtf8(os.str().c_str());
351
352 if (escapeCR)
353 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
354
355 return tagValue;
356 }
357 }
358 catch( Exiv2::Error& e )
359 {
360 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
361 }
362 catch(...)
363 {
364 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
365 }
366
367#else
368
369 Q_UNUSED(xmpTagName);
370 Q_UNUSED(escapeCR);
371
372#endif // _XMP_SUPPORT_
373
374 return QString();
375}
376
377bool KExiv2::setXmpTagString(const char* xmpTagName, const QString& value, bool setProgramName) const
378{
379#ifdef _XMP_SUPPORT_
380
381 if (!setProgramId(setProgramName))
382 return false;
383
384 try
385 {
386 const std::string &txt(value.toUtf8().constData());
387#if EXIV2_TEST_VERSION(0,28,0)
388 Exiv2::Value::UniquePtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
389#else
390 Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
391#endif
392 xmpTxtVal->read(txt);
393 d->xmpMetadata()[xmpTagName].setValue(xmpTxtVal.get());
394 return true;
395 }
396 catch( Exiv2::Error& e )
397 {
398 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string into image using Exiv2 "), e);
399 }
400 catch(...)
401 {
402 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
403 }
404
405#else
406
407 Q_UNUSED(xmpTagName);
408 Q_UNUSED(value);
409 Q_UNUSED(setProgramName);
410
411#endif // _XMP_SUPPORT_
412
413 return false;
414}
415bool KExiv2::setXmpTagString(const char* xmpTagName, const QString& value,
416 KExiv2::XmpTagType type, bool setProgramName) const
417{
418#ifdef _XMP_SUPPORT_
419
420 if (!setProgramId(setProgramName))
421 return false;
422
423 try
424 {
425 const std::string &txt(value.toUtf8().constData());
426 Exiv2::XmpTextValue xmpTxtVal("");
427
428 if (type == KExiv2::NormalTag) // normal type
429 {
430 xmpTxtVal.read(txt);
431 d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName),&xmpTxtVal);
432 return true;
433 }
434
435 if (type == KExiv2::ArrayBagTag) // xmp type = bag
436 {
437 xmpTxtVal.setXmpArrayType(Exiv2::XmpValue::xaBag);
438 xmpTxtVal.read("");
439 d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName),&xmpTxtVal);
440 }
441
442 if (type == KExiv2::StructureTag) // xmp type = struct
443 {
444 xmpTxtVal.setXmpStruct();
445 d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName),&xmpTxtVal);
446 }
447 }
448 catch( Exiv2::Error& e )
449 {
450 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string into image using Exiv2 "), e);
451 }
452 catch(...)
453 {
454 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
455 }
456
457#else
458
459 Q_UNUSED(xmpTagName);
460 Q_UNUSED(value);
461 Q_UNUSED(setProgramName);
462
463#endif // _XMP_SUPPORT_
464
465 return false;
466}
467KExiv2::AltLangMap KExiv2::getXmpTagStringListLangAlt(const char* xmpTagName, bool escapeCR) const
468{
469#ifdef _XMP_SUPPORT_
470
471 try
472 {
473 Exiv2::XmpData xmpData = d->xmpMetadata();
474
475 for (Exiv2::XmpData::iterator it = xmpData.begin(); it != xmpData.end(); ++it)
476 {
477 if (it->key() == xmpTagName && it->typeId() == Exiv2::langAlt)
478 {
479 AltLangMap map;
480 const Exiv2::LangAltValue &value = static_cast<const Exiv2::LangAltValue &>(it->value());
481
482 for (Exiv2::LangAltValue::ValueType::const_iterator it2 = value.value_.begin();
483 it2 != value.value_.end(); ++it2)
484 {
485 QString lang = QString::fromUtf8(it2->first.c_str());
486 QString text = QString::fromUtf8(it2->second.c_str());
487
488 if (escapeCR)
490
491 map.insert(lang, text);
492 }
493
494 return map;
495 }
496 }
497 }
498 catch( Exiv2::Error& e )
499 {
500 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
501 }
502 catch(...)
503 {
504 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
505 }
506
507#else
508
509 Q_UNUSED(xmpTagName);
510 Q_UNUSED(escapeCR);
511
512#endif // _XMP_SUPPORT_
513
514 return AltLangMap();
515}
516
517bool KExiv2::setXmpTagStringListLangAlt(const char* xmpTagName, const KExiv2::AltLangMap& values,
518 bool setProgramName) const
519{
520#ifdef _XMP_SUPPORT_
521
522 if (!setProgramId(setProgramName))
523 return false;
524
525 try
526 {
527 // Remove old XMP alternative Language tag.
528 removeXmpTag(xmpTagName);
529
530 if (!values.isEmpty())
531 {
532#if EXIV2_TEST_VERSION(0,28,0)
533 Exiv2::Value::UniquePtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
534#else
535 Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
536#endif
537
538 for (AltLangMap::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
539 {
540 QString lang = it.key();
541 QString text = it.value();
542 QString txtLangAlt = QString::fromLatin1("lang=%1 %2").arg(lang).arg(text);
543 const std::string &txt(txtLangAlt.toUtf8().constData());
544 xmpTxtVal->read(txt);
545 }
546
547 // ...and add the new one instead.
548 d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName), xmpTxtVal.get());
549 }
550 return true;
551 }
552 catch( Exiv2::Error& e )
553 {
554 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string lang-alt into image using Exiv2 "), e);
555 }
556 catch(...)
557 {
558 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
559 }
560
561#else
562
563 Q_UNUSED(xmpTagName);
564 Q_UNUSED(values);
565 Q_UNUSED(setProgramName);
566
567#endif // _XMP_SUPPORT_
568
569 return false;
570}
571
572QString KExiv2::getXmpTagStringLangAlt(const char* xmpTagName, const QString& langAlt, bool escapeCR) const
573{
574#ifdef _XMP_SUPPORT_
575
576 try
577 {
578 Exiv2::XmpData xmpData(d->xmpMetadata());
579 Exiv2::XmpKey key(xmpTagName);
580
581 for (Exiv2::XmpData::iterator it = xmpData.begin(); it != xmpData.end(); ++it)
582 {
583 if (it->key() == xmpTagName && it->typeId() == Exiv2::langAlt)
584 {
585 for (int i = 0; i < it->count(); i++)
586 {
587 std::ostringstream os;
588 os << it->toString(i);
589 QString lang;
590 QString tagValue = QString::fromUtf8(os.str().c_str());
591 tagValue = detectLanguageAlt(tagValue, lang);
592
593 if (langAlt == lang)
594 {
595 if (escapeCR)
596 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
597
598 return tagValue;
599 }
600 }
601 }
602 }
603 }
604 catch( Exiv2::Error& e )
605 {
606 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
607 }
608 catch(...)
609 {
610 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
611 }
612
613#else
614
615 Q_UNUSED(xmpTagName);
616 Q_UNUSED(langAlt);
617 Q_UNUSED(escapeCR);
618
619#endif // _XMP_SUPPORT_
620
621 return QString();
622}
623
624bool KExiv2::setXmpTagStringLangAlt(const char* xmpTagName, const QString& value,
625 const QString& langAlt, bool setProgramName) const
626{
627#ifdef _XMP_SUPPORT_
628
629 if (!setProgramId(setProgramName))
630 return false;
631
632 try
633 {
634 QString language(QString::fromLatin1("x-default")); // default alternative language.
635
636 if (!langAlt.isEmpty())
637 language = langAlt;
638
639 QString txtLangAlt = QString(QString::fromLatin1("lang=%1 %2")).arg(language).arg(value);
640
641 const std::string &txt(txtLangAlt.toUtf8().constData());
642#if EXIV2_TEST_VERSION(0,28,0)
643 Exiv2::Value::UniquePtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
644#else
645 Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
646#endif
647
648 // Search if an Xmp tag already exist.
649
650 AltLangMap map = getXmpTagStringListLangAlt(xmpTagName, false);
651
652 if (!map.isEmpty())
653 {
654 for (AltLangMap::iterator it = map.begin(); it != map.end(); ++it)
655 {
656 if (it.key() != langAlt)
657 {
658 const std::string &val((*it).toUtf8().constData());
659 xmpTxtVal->read(val);
660 qCDebug(LIBKEXIV2_LOG) << *it;
661 }
662 }
663 }
664
665 xmpTxtVal->read(txt);
666 removeXmpTag(xmpTagName);
667 d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName), xmpTxtVal.get());
668 return true;
669 }
670 catch( Exiv2::Error& e )
671 {
672 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string lang-alt into image using Exiv2 "), e);
673 }
674 catch(...)
675 {
676 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
677 }
678
679#else
680
681 Q_UNUSED(xmpTagName);
682 Q_UNUSED(value);
683 Q_UNUSED(langAlt);
684 Q_UNUSED(setProgramName);
685
686#endif // _XMP_SUPPORT_
687
688 return false;
689}
690
691QStringList KExiv2::getXmpTagStringSeq(const char* xmpTagName, bool escapeCR) const
692{
693#ifdef _XMP_SUPPORT_
694
695 try
696 {
697 Exiv2::XmpData xmpData(d->xmpMetadata());
698 Exiv2::XmpKey key(xmpTagName);
699 Exiv2::XmpData::iterator it = xmpData.findKey(key);
700
701 if (it != xmpData.end())
702 {
703 if (it->typeId() == Exiv2::xmpSeq)
704 {
705 QStringList seq;
706
707 for (int i = 0; i < it->count(); i++)
708 {
709 std::ostringstream os;
710 os << it->toString(i);
711 QString seqValue = QString::fromUtf8(os.str().c_str());
712
713 if (escapeCR)
714 seqValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
715
716 seq.append(seqValue);
717 }
718 qCDebug(LIBKEXIV2_LOG) << "XMP String Seq (" << xmpTagName << "): " << seq;
719
720 return seq;
721 }
722 }
723 }
724 catch( Exiv2::Error& e )
725 {
726 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
727 }
728 catch(...)
729 {
730 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
731 }
732
733#else
734
735 Q_UNUSED(xmpTagName);
736 Q_UNUSED(escapeCR);
737
738#endif // _XMP_SUPPORT_
739
740 return QStringList();
741}
742
743bool KExiv2::setXmpTagStringSeq(const char* xmpTagName, const QStringList& seq,
744 bool setProgramName) const
745{
746#ifdef _XMP_SUPPORT_
747
748 if (!setProgramId(setProgramName))
749 return false;
750
751 try
752 {
753 if (seq.isEmpty())
754 {
755 removeXmpTag(xmpTagName);
756 }
757 else
758 {
759 const QStringList list = seq;
760#if EXIV2_TEST_VERSION(0,28,0)
761 Exiv2::Value::UniquePtr xmpTxtSeq = Exiv2::Value::create(Exiv2::xmpSeq);
762#else
763 Exiv2::Value::AutoPtr xmpTxtSeq = Exiv2::Value::create(Exiv2::xmpSeq);
764#endif
765
766 for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
767 {
768 const std::string &txt((*it).toUtf8().constData());
769 xmpTxtSeq->read(txt);
770 }
771
772 d->xmpMetadata()[xmpTagName].setValue(xmpTxtSeq.get());
773 }
774 return true;
775 }
776 catch( Exiv2::Error& e )
777 {
778 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string Seq into image using Exiv2 "), e);
779 }
780 catch(...)
781 {
782 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
783 }
784
785#else
786
787 Q_UNUSED(xmpTagName);
788 Q_UNUSED(seq);
789 Q_UNUSED(setProgramName);
790
791#endif // _XMP_SUPPORT_
792
793 return false;
794}
795
796QStringList KExiv2::getXmpTagStringBag(const char* xmpTagName, bool escapeCR) const
797{
798#ifdef _XMP_SUPPORT_
799
800 try
801 {
802 Exiv2::XmpData xmpData(d->xmpMetadata());
803 Exiv2::XmpKey key(xmpTagName);
804 Exiv2::XmpData::iterator it = xmpData.findKey(key);
805
806 if (it != xmpData.end())
807 {
808 if (it->typeId() == Exiv2::xmpBag)
809 {
810 QStringList bag;
811
812 for (int i = 0; i < it->count(); i++)
813 {
814 std::ostringstream os;
815 os << it->toString(i);
816 QString bagValue = QString::fromUtf8(os.str().c_str());
817
818 if (escapeCR)
819 bagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
820
821 bag.append(bagValue);
822 }
823
824 return bag;
825 }
826 }
827 }
828 catch( Exiv2::Error& e )
829 {
830 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
831 }
832 catch(...)
833 {
834 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
835 }
836
837#else
838
839 Q_UNUSED(xmpTagName);
840 Q_UNUSED(escapeCR);
841
842#endif // _XMP_SUPPORT_
843
844 return QStringList();
845}
846
847bool KExiv2::setXmpTagStringBag(const char* xmpTagName, const QStringList& bag,
848 bool setProgramName) const
849{
850#ifdef _XMP_SUPPORT_
851
852 if (!setProgramId(setProgramName))
853 return false;
854
855 try
856 {
857 if (bag.isEmpty())
858 {
859 removeXmpTag(xmpTagName);
860 }
861 else
862 {
863 QStringList list = bag;
864#if EXIV2_TEST_VERSION(0,28,0)
865 Exiv2::Value::UniquePtr xmpTxtBag = Exiv2::Value::create(Exiv2::xmpBag);
866#else
867 Exiv2::Value::AutoPtr xmpTxtBag = Exiv2::Value::create(Exiv2::xmpBag);
868#endif
869
870 for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
871 {
872 const std::string &txt((*it).toUtf8().constData());
873 xmpTxtBag->read(txt);
874 }
875
876 d->xmpMetadata()[xmpTagName].setValue(xmpTxtBag.get());
877 }
878 return true;
879 }
880 catch( Exiv2::Error& e )
881 {
882 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string Bag into image using Exiv2 "), e);
883 }
884 catch(...)
885 {
886 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
887 }
888
889#else
890
891 Q_UNUSED(xmpTagName);
892 Q_UNUSED(bag);
893 Q_UNUSED(setProgramName);
894
895#endif // _XMP_SUPPORT_
896
897 return false;
898}
899
900bool KExiv2::addToXmpTagStringBag(const char* xmpTagName, const QStringList& entriesToAdd,
901 bool setProgramName) const
902{
903 if (!setProgramId(setProgramName))
904 return false;
905
906 QStringList oldEntries = getXmpTagStringBag(xmpTagName, false);
907 QStringList newEntries = entriesToAdd;
908
909 // Create a list of keywords including old one which already exists.
910 for (QStringList::const_iterator it = oldEntries.constBegin(); it != oldEntries.constEnd(); ++it )
911 {
912 if (!newEntries.contains(*it))
913 newEntries.append(*it);
914 }
915
916 if (setXmpTagStringBag(xmpTagName, newEntries, false))
917 return true;
918
919 return false;
920}
921
922bool KExiv2::removeFromXmpTagStringBag(const char* xmpTagName, const QStringList& entriesToRemove,
923 bool setProgramName) const
924{
925 if (!setProgramId(setProgramName))
926 return false;
927
928 QStringList currentEntries = getXmpTagStringBag(xmpTagName, false);
929 QStringList newEntries;
930
931 // Create a list of current keywords except those that shall be removed
932 for (QStringList::const_iterator it = currentEntries.constBegin(); it != currentEntries.constEnd(); ++it )
933 {
934 if (!entriesToRemove.contains(*it))
935 newEntries.append(*it);
936 }
937
938 if (setXmpTagStringBag(xmpTagName, newEntries, false))
939 return true;
940
941 return false;
942}
943
944QVariant KExiv2::getXmpTagVariant(const char* xmpTagName, bool rationalAsListOfInts, bool stringEscapeCR) const
945{
946#ifdef _XMP_SUPPORT_
947 try
948 {
949 Exiv2::XmpData xmpData(d->xmpMetadata());
950 Exiv2::XmpKey key(xmpTagName);
951 Exiv2::XmpData::iterator it = xmpData.findKey(key);
952
953 if (it != xmpData.end())
954 {
955 switch (it->typeId())
956 {
957 case Exiv2::unsignedByte:
958 case Exiv2::unsignedShort:
959 case Exiv2::unsignedLong:
960 case Exiv2::signedShort:
961 case Exiv2::signedLong:
962#if EXIV2_TEST_VERSION(0,28,0)
963 return QVariant((int)it->toUint32());
964#else
965 return QVariant((int)it->toLong());
966#endif
967 case Exiv2::unsignedRational:
968 case Exiv2::signedRational:
969 if (rationalAsListOfInts)
970 {
971 QList<QVariant> list;
972 list << (*it).toRational().first;
973 list << (*it).toRational().second;
974 return QVariant(list);
975 }
976 else
977 {
978 // prefer double precision
979 double num = (*it).toRational().first;
980 double den = (*it).toRational().second;
981
982 if (den == 0.0)
983#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
985#else
987#endif
988
989 return QVariant(num / den);
990 }
991 case Exiv2::date:
992 case Exiv2::time:
993 {
994 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
995 return QVariant(dateTime);
996 }
997 case Exiv2::asciiString:
998 case Exiv2::comment:
999 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());
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
1074bool 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
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
1139bool 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
1177{
1178 return (getXmpTagStringBag("Xmp.dc.subject", false));
1179}
1180
1181bool KExiv2::setXmpKeywords(const QStringList& newKeywords, bool setProgramName) const
1182{
1183 return addToXmpTagStringBag("Xmp.dc.subject", newKeywords, setProgramName);
1184}
1185
1186bool KExiv2::removeXmpKeywords(const QStringList& keywordsToRemove, bool setProgramName)
1187{
1188 return removeFromXmpTagStringBag("Xmp.dc.subject", keywordsToRemove, setProgramName);
1189}
1190
1192{
1193 return (getXmpTagStringBag("Xmp.photoshop.SupplementalCategories", false));
1194}
1195
1196bool KExiv2::setXmpSubCategories(const QStringList& newSubCategories, bool setProgramName) const
1197{
1198 return addToXmpTagStringBag("Xmp.photoshop.SupplementalCategories", newSubCategories, setProgramName);
1199}
1200
1201bool KExiv2::removeXmpSubCategories(const QStringList& subCategoriesToRemove, bool setProgramName)
1202{
1203 return removeFromXmpTagStringBag("Xmp.photoshop.SupplementalCategories", subCategoriesToRemove, setProgramName);
1204}
1205
1207{
1208 return (getXmpTagStringBag("Xmp.iptc.SubjectCode", false));
1209}
1210
1211bool KExiv2::setXmpSubjects(const QStringList& newSubjects, bool setProgramName) const
1212{
1213 return addToXmpTagStringBag("Xmp.iptc.SubjectCode", newSubjects, setProgramName);
1214}
1215
1216bool KExiv2::removeXmpSubjects(const QStringList& subjectsToRemove, bool setProgramName)
1217{
1218 return removeFromXmpTagStringBag("Xmp.iptc.SubjectCode", subjectsToRemove, setProgramName);
1219}
1220
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
QString getXmpTagString(const char *xmpTagName, bool escapeCR=true) const
Get a Xmp tag content like a string.
QMap< QString, QString > MetaDataMap
A map used to store Tags Key and Tags Value.
Definition kexiv2.h:112
static bool unregisterXmpNameSpace(const QString &uri)
Unregister a previously registered custom namespace.
virtual bool setProgramId(bool on=true) const
Re-implement this method to set automatically the Program Name and Program Version information in Exi...
Definition kexiv2.cpp:526
static bool canWriteXmp(const QString &filePath)
Return 'true' if Xmp can be written in file.
Definition kexiv2xmp.cpp:17
bool hasXmp() const
Return 'true' if metadata container in memory as Xmp.
Definition kexiv2xmp.cpp:58
QStringList getXmpKeywords() const
Return a strings list of Xmp keywords from image.
bool addToXmpTagStringBag(const char *xmpTagName, const QStringList &entriesToAdd, bool setProgramName) const
Set an Xmp tag content using a list of strings defined by the 'entriesToAdd' parameter.
bool setXmp(const QByteArray &data) const
Set the Xmp data using a Qt byte array.
bool removeXmpKeywords(const QStringList &keywordsToRemove, bool setProgramName=true)
Remove those Xmp keywords that are listed in keywordsToRemove from the keywords in metadata.
QStringList getXmpSubjects() const
Return a strings list of Xmp subjects from image.
bool setXmpKeywords(const QStringList &newKeywords, bool setProgramName=true) const
Set Xmp keywords using a list of strings defined by 'newKeywords' parameter.
bool setXmpSubCategories(const QStringList &newSubCategories, bool setProgramName=true) const
Set Xmp sub-categories using a list of strings defined by 'newSubCategories' parameter.
bool setXmpTagStringListLangAlt(const char *xmpTagName, const KExiv2::AltLangMap &values, bool setProgramName) const
Set an Alternative Language Xmp tag content using a map.
KExiv2::TagsMap getXmpTagsList() const
Return a map of all standard Xmp tags supported by Exiv2.
QMap< QString, QString > AltLangMap
A map used to store a list of Alternative Language values.
Definition kexiv2.h:118
QString getXmpTagTitle(const char *xmpTagName)
Return the Xmp Tag title or a null string.
bool removeXmpSubCategories(const QStringList &categoriesToRemove, bool setProgramName=true)
Remove those Xmp sub-categories that are listed in categoriesToRemove from the sub-categories in meta...
KExiv2::AltLangMap getXmpTagStringListLangAlt(const char *xmpTagName, bool escapeCR=true) const
Get all redondant Alternative Language Xmp tags content like a map.
static QString detectLanguageAlt(const QString &value, QString &lang)
Language Alternative autodetection.
bool setXmpTagStringSeq(const char *xmpTagName, const QStringList &seq, bool setProgramName=true) const
Set a Xmp tag content using the sequence of strings 'seq'.
QStringList getXmpTagStringBag(const char *xmpTagName, bool escapeCR) const
Get a Xmp tag content like a bag of strings.
static bool registerXmpNameSpace(const QString &uri, const QString &prefix)
Register a namespace which Exiv2 doesn't know yet.
bool removeXmpSubjects(const QStringList &subjectsToRemove, bool setProgramName=true)
Remove those Xmp subjects that are listed in subjectsToRemove from the subjects in metadata.
QStringList getXmpTagStringSeq(const char *xmpTagName, bool escapeCR=true) const
Get a Xmp tag content like a sequence of strings.
bool setXmpTagStringBag(const char *xmpTagName, const QStringList &bag, bool setProgramName=true) const
Set a Xmp tag content using the bag of strings 'bag'.
bool removeFromXmpTagStringBag(const char *xmpTagName, const QStringList &entriesToRemove, bool setProgramName) const
Remove those Xmp tag entries that are listed in entriesToRemove from the entries in metadata.
bool setXmpSubjects(const QStringList &newSubjects, bool setProgramName=true) const
Set Xmp subjects using a list of strings defined by 'newSubjects' parameter.
QVariant getXmpTagVariant(const char *xmpTagName, bool rationalAsListOfInts=true, bool stringEscapeCR=true) const
Get an Xmp tag content as a QVariant.
QByteArray getXmp() const
Return a Qt byte array copy of XMp container get from current image.
Definition kexiv2xmp.cpp:94
bool clearXmp() const
Clear the Xmp metadata container in memory.
Definition kexiv2xmp.cpp:71
QString getXmpTagDescription(const char *xmpTagName)
Return the Xmp Tag description or a null string.
QStringList getXmpSubCategories() const
Return a strings list of Xmp sub-categories from image.
bool setXmpTagStringLangAlt(const char *xmpTagName, const QString &value, const QString &langAlt, bool setProgramName=true) const
Set a Xmp tag content using a string with an alternative language header.
KExiv2::MetaDataMap getXmpTagsDataList(const QStringList &xmpKeysFilter=QStringList(), bool invertSelection=false) const
Return a map of Xmp tags name/value found in metadata sorted by Xmp keys given by 'xmpKeysFilter'.
bool removeXmpTag(const char *xmpTagName, bool setProgramName=true) const
Remove the Xmp tag 'xmpTagName' from Xmp metadata.
bool setXmpTagString(const char *xmpTagName, const QString &value, bool setProgramName=true) const
Set a Xmp tag content using a string.
XmpTagType
Xmp tag types, used by setXmpTag, only first three types are used.
Definition kexiv2.h:102
QString getXmpTagStringLangAlt(const char *xmpTagName, const QString &langAlt, bool escapeCR) const
Get a Xmp tag content like a string set with an alternative language header 'langAlt' (like "fr-FR" f...
KExiv2Iface - Exiv2 library interface.
Definition kexiv2.cpp:17
const char * constData() const const
char * data()
bool isEmpty() const const
QByteArray & replace(QByteArrayView before, QByteArrayView after)
qsizetype size() const const
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
QByteArray encodeName(const QString &fileName)
void append(QList< T > &&value)
const_iterator constBegin() const const
const_iterator constEnd() const const
T & first()
bool isEmpty() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator end()
iterator find(const Key &key)
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromLatin1(QByteArrayView str)
QString fromLocal8Bit(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
QByteArray toLatin1() const const
QByteArray toUtf8() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:02 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.