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

KDE's Doxygen guidelines are available online.