KExiv2

kexiv2exif.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#include "kexiv2.h"
9#include "kexiv2_p.h"
10
11// C++ includes
12
13#include <cctype>
14
15// Qt includes
16
17#include <QBuffer>
18
19// Local includes
20
21#include "rotationmatrix.h"
22#include "libkexiv2_debug.h"
23
24namespace KExiv2Iface
25{
26
27bool KExiv2::canWriteExif(const QString& filePath)
28{
29 try
30 {
31#if EXIV2_TEST_VERSION(0,28,0)
32 Exiv2::Image::UniquePtr image =
33#else
34 Exiv2::Image::AutoPtr image =
35#endif
36 Exiv2::ImageFactory::open((const char*)
37 (QFile::encodeName(filePath).constData()));
38
39 Exiv2::AccessMode mode = image->checkMode(Exiv2::mdExif);
40
41 return (mode == Exiv2::amWrite || mode == Exiv2::amReadWrite);
42 }
43 catch( Exiv2::Error& e )
44 {
45 std::string s(e.what());
46 qCCritical(LIBKEXIV2_LOG) << "Cannot check Exif access mode using Exiv2 (Error #"
47#if EXIV2_TEST_VERSION(0,28,0)
48 << Exiv2::Error(e.code()).what()
49#else
50 << e.code() << ": " << s.c_str()
51#endif
52 << ")";
53 }
54 catch(...)
55 {
56 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
57 }
58
59 return false;
60}
61
62bool KExiv2::hasExif() const
63{
64 return !d->exifMetadata().empty();
65}
66
68{
69 try
70 {
71 d->exifMetadata().clear();
72 return true;
73 }
74 catch( Exiv2::Error& e )
75 {
76 d->printExiv2ExceptionError(QString::fromLatin1("Cannot clear Exif data using Exiv2 "), e);
77 }
78 catch(...)
79 {
80 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
81 }
82
83 return false;
84}
85
86QByteArray KExiv2::getExifEncoded(bool addExifHeader) const
87{
88 try
89 {
90 if (!d->exifMetadata().empty())
91 {
92 QByteArray data;
93 Exiv2::ExifData& exif = d->exifMetadata();
94 Exiv2::Blob blob;
95 Exiv2::ExifParser::encode(blob, Exiv2::bigEndian, exif);
96 QByteArray ba((const char*)&blob[0], blob.size());
97
98 if (addExifHeader)
99 {
100 const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
101 data.resize(ba.size() + sizeof(ExifHeader));
102 memcpy(data.data(), ExifHeader, sizeof(ExifHeader));
103 memcpy(data.data() + sizeof(ExifHeader), ba.data(), ba.size());
104 }
105 else
106 {
107 data = ba;
108 }
109 return data;
110 }
111 }
112 catch( Exiv2::Error& e )
113 {
114 if (!d->filePath.isEmpty())
115 qCDebug(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData();
116
117 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Exif data using Exiv2 "), e);
118 }
119 catch(...)
120 {
121 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
122 }
123
124 return QByteArray();
125}
126
127bool KExiv2::setExif(const QByteArray& data) const
128{
129 try
130 {
131 if (!data.isEmpty())
132 {
133 Exiv2::ExifParser::decode(d->exifMetadata(), (const Exiv2::byte*)data.data(), data.size());
134 return (!d->exifMetadata().empty());
135 }
136 }
137 catch( Exiv2::Error& e )
138 {
139 if (!d->filePath.isEmpty())
140 qCCritical(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData();
141
142 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif data using Exiv2 "), e);
143 }
144 catch(...)
145 {
146 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
147 }
148
149 return false;
150}
151
152KExiv2::MetaDataMap KExiv2::getExifTagsDataList(const QStringList& exifKeysFilter, bool invertSelection) const
153{
154 if (d->exifMetadata().empty())
155 return MetaDataMap();
156
157 try
158 {
159 Exiv2::ExifData exifData = d->exifMetadata();
160 exifData.sortByKey();
161
162 QString ifDItemName;
163 MetaDataMap metaDataMap;
164
165 for (Exiv2::ExifData::iterator md = exifData.begin(); md != exifData.end(); ++md)
166 {
167 QString key = QString::fromLatin1(md->key().c_str());
168
169 // Decode the tag value with a user friendly output.
170 QString tagValue;
171
172 if (key == QString::fromLatin1("Exif.Photo.UserComment"))
173 {
174 tagValue = d->convertCommentValue(*md);
175 }
176 else if (key == QString::fromLatin1("Exif.Image.0x935c"))
177 {
178 tagValue = QString::number(md->value().size());
179 }
180 else
181 {
182 std::ostringstream os;
183 os << *md;
184
185 // Exif tag contents can be an translated strings, no only simple ascii.
186 tagValue = QString::fromLocal8Bit(os.str().c_str());
187 }
188
189 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
190
191 // We apply a filter to get only the Exif tags that we need.
192
193 if (!exifKeysFilter.isEmpty())
194 {
195 if (!invertSelection)
196 {
197 if (exifKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
198 metaDataMap.insert(key, tagValue);
199 }
200 else
201 {
202 if (!exifKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
203 metaDataMap.insert(key, tagValue);
204 }
205 }
206 else // else no filter at all.
207 {
208 metaDataMap.insert(key, tagValue);
209 }
210 }
211
212 return metaDataMap;
213 }
214 catch (Exiv2::Error& e)
215 {
216 d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse EXIF metadata using Exiv2 "), e);
217 }
218 catch(...)
219 {
220 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
221 }
222
223 return MetaDataMap();
224}
225
227{
228 try
229 {
230 if (!d->exifMetadata().empty())
231 {
232 Exiv2::ExifData exifData(d->exifMetadata());
233 Exiv2::ExifKey key("Exif.Photo.UserComment");
234 Exiv2::ExifData::iterator it = exifData.findKey(key);
235
236 if (it != exifData.end())
237 {
238 QString exifComment = d->convertCommentValue(*it);
239
240 // some cameras fill the UserComment with whitespace
241 if (!exifComment.isEmpty() && !exifComment.trimmed().isEmpty())
242 return exifComment;
243 }
244
245 Exiv2::ExifKey key2("Exif.Image.ImageDescription");
246 Exiv2::ExifData::iterator it2 = exifData.findKey(key2);
247
248 if (it2 != exifData.end())
249 {
250 QString exifComment = d->convertCommentValue(*it2);
251
252 // Some cameras fill in nonsense default values
253 QStringList blackList;
254 blackList << QString::fromLatin1("SONY DSC"); // + whitespace
255 blackList << QString::fromLatin1("OLYMPUS DIGITAL CAMERA");
256 blackList << QString::fromLatin1("MINOLTA DIGITAL CAMERA");
257
258 QString trimmedComment = exifComment.trimmed();
259
260 // some cameras fill the UserComment with whitespace
261 if (!exifComment.isEmpty() && !trimmedComment.isEmpty() && !blackList.contains(trimmedComment))
262 return exifComment;
263 }
264 }
265 }
266 catch( Exiv2::Error& e )
267 {
268 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif User Comment using Exiv2 "), e);
269 }
270 catch(...)
271 {
272 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
273 }
274
275 return QString();
276}
277
278static bool is7BitAscii(const QString& s)
279{
280 return std::all_of(s.begin(), s.end(), [](QChar c) { return c.unicode() <= 0x7f; });
281}
282
283bool KExiv2::setExifComment(const QString& comment, bool setProgramName) const
284{
285 if (!setProgramId(setProgramName))
286 return false;
287
288 try
289 {
290 removeExifTag("Exif.Image.ImageDescription");
291 removeExifTag("Exif.Photo.UserComment");
292
293 if (!comment.isNull())
294 {
295 setExifTagString("Exif.Image.ImageDescription", comment, setProgramName);
296
297 // Write as Unicode only when necessary.
298 if (is7BitAscii(comment))
299 {
300 // write as ASCII
301 std::string exifComment("charset=\"Ascii\" ");
302 exifComment += comment.toLatin1().constData();
303 d->exifMetadata()["Exif.Photo.UserComment"] = exifComment;
304 return true;
305 }
306 // write as Unicode (UCS-2)
307 std::string exifComment("charset=\"Unicode\" ");
308 exifComment += comment.toUtf8().constData();
309 d->exifMetadata()["Exif.Photo.UserComment"] = exifComment;
310 }
311
312 return true;
313 }
314 catch( Exiv2::Error& e )
315 {
316 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif Comment using Exiv2 "), e);
317 }
318 catch(...)
319 {
320 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
321 }
322
323 return false;
324}
325
326QString KExiv2::getExifTagTitle(const char* exifTagName)
327{
328 try
329 {
330 std::string exifkey(exifTagName);
331 Exiv2::ExifKey ek(exifkey);
332
333 return QString::fromLocal8Bit( ek.tagLabel().c_str() );
334 }
335 catch (Exiv2::Error& e)
336 {
337 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get metadata tag title using Exiv2 "), e);
338 }
339 catch(...)
340 {
341 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
342 }
343
344 return QString();
345}
346
348{
349 try
350 {
351 std::string exifkey(exifTagName);
352 Exiv2::ExifKey ek(exifkey);
353
354 return QString::fromLocal8Bit( ek.tagDesc().c_str() );
355 }
356 catch (Exiv2::Error& e)
357 {
358 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get metadata tag description using Exiv2 "), e);
359 }
360 catch(...)
361 {
362 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
363 }
364
365 return QString();
366}
367
368bool KExiv2::removeExifTag(const char* exifTagName, bool setProgramName) const
369{
370 if (!setProgramId(setProgramName))
371 return false;
372
373 try
374 {
375 Exiv2::ExifKey exifKey(exifTagName);
376 Exiv2::ExifData::iterator it = d->exifMetadata().findKey(exifKey);
377
378 if (it != d->exifMetadata().end())
379 {
380 d->exifMetadata().erase(it);
381 return true;
382 }
383 }
384 catch( Exiv2::Error& e )
385 {
386 d->printExiv2ExceptionError(QString::fromLatin1("Cannot remove Exif tag using Exiv2 "), e);
387 }
388 catch(...)
389 {
390 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
391 }
392
393 return false;
394}
395
396bool KExiv2::getExifTagRational(const char* exifTagName, long int& num, long int& den, int component) const
397{
398 try
399 {
400 Exiv2::ExifKey exifKey(exifTagName);
401 Exiv2::ExifData exifData(d->exifMetadata());
402 Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
403
404 if (it != exifData.end())
405 {
406 num = (*it).toRational(component).first;
407 den = (*it).toRational(component).second;
408
409 return true;
410 }
411 }
412 catch( Exiv2::Error& e )
413 {
414 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif Rational value from key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e);
415 }
416 catch(...)
417 {
418 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
419 }
420
421 return false;
422}
423
424bool KExiv2::setExifTagLong(const char* exifTagName, long val, bool setProgramName) const
425{
426 if (!setProgramId(setProgramName))
427 return false;
428
429 try
430 {
431 d->exifMetadata()[exifTagName] = static_cast<int32_t>(val);
432 return true;
433 }
434 catch( Exiv2::Error& e )
435 {
436 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag long value into image using Exiv2 "), e);
437 }
438 catch(...)
439 {
440 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
441 }
442
443 return false;
444}
445
446bool KExiv2::setExifTagRational(const char* exifTagName, long int num, long int den, bool setProgramName) const
447{
448 if (!setProgramId(setProgramName))
449 return false;
450
451 try
452 {
453 d->exifMetadata()[exifTagName] = Exiv2::Rational(num, den);
454 return true;
455 }
456 catch( Exiv2::Error& e )
457 {
458 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag rational value into image using Exiv2 "), e);
459 }
460 catch(...)
461 {
462 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
463 }
464
465 return false;
466}
467
468bool KExiv2::setExifTagData(const char* exifTagName, const QByteArray& data, bool setProgramName) const
469{
470 if (data.isEmpty())
471 return false;
472
473 if (!setProgramId(setProgramName))
474 return false;
475
476 try
477 {
478 Exiv2::DataValue val((Exiv2::byte*)data.data(), data.size());
479 d->exifMetadata()[exifTagName] = val;
480 return true;
481 }
482 catch( Exiv2::Error& e )
483 {
484 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag data into image using Exiv2 "), e);
485 }
486 catch(...)
487 {
488 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
489 }
490
491 return false;
492}
493
494bool KExiv2::setExifTagVariant(const char* exifTagName, const QVariant& val,
495 bool rationalWantSmallDenominator, bool setProgramName) const
496{
497#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
498 switch (val.metaType().id())
499#else
500 switch (val.type())
501#endif
502 {
503 case QMetaType::Int:
504 case QMetaType::UInt:
505 case QMetaType::Bool:
508 return setExifTagLong(exifTagName, val.toInt(), setProgramName);
509
511 {
512 long num, den;
513
514 if (rationalWantSmallDenominator)
516 else
517 convertToRational(val.toDouble(), &num, &den, 4);
518
519 return setExifTagRational(exifTagName, num, den, setProgramName);
520 }
522 {
523 long num = 0, den = 1;
524 QList<QVariant> list = val.toList();
525
526 if (list.size() >= 1)
527 num = list[0].toInt();
528
529 if (list.size() >= 2)
530 den = list[1].toInt();
531
532 return setExifTagRational(exifTagName, num, den, setProgramName);
533 }
534
535 case QMetaType::QDate:
537 {
538 QDateTime dateTime = val.toDateTime();
539
540 if(!dateTime.isValid())
541 return false;
542
543 if (!setProgramId(setProgramName))
544 return false;
545
546 try
547 {
548 const std::string &exifdatetime(dateTime.toString(QString::fromLatin1("yyyy:MM:dd hh:mm:ss")).toLatin1().constData());
549 d->exifMetadata()[exifTagName] = exifdatetime;
550 }
551 catch( Exiv2::Error &e )
552 {
553 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Date & Time in image using Exiv2 "), e);
554 }
555 catch(...)
556 {
557 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
558 }
559
560 return false;
561 }
562
564 case QMetaType::QChar:
565 return setExifTagString(exifTagName, val.toString(), setProgramName);
566
568 return setExifTagData(exifTagName, val.toByteArray(), setProgramName);
569 default:
570 break;
571 }
572 return false;
573}
574
575QString KExiv2::createExifUserStringFromValue(const char* exifTagName, const QVariant& val, bool escapeCR)
576{
577 try
578 {
579 Exiv2::ExifKey key(exifTagName);
580 Exiv2::Exifdatum datum(key);
581
582#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
583 switch (val.metaType().id())
584#else
585 switch (val.type())
586#endif
587 {
588 case QMetaType::Int:
589 case QMetaType::Bool:
592 datum = (int32_t)val.toInt();
593 break;
594 case QMetaType::UInt:
595 datum = (uint32_t)val.toUInt();
596 break;
597
599 {
600 long num, den;
602 Exiv2::Rational rational;
603 rational.first = num;
604 rational.second = den;
605 datum = rational;
606 break;
607 }
609 {
610 long num = 0, den = 1;
611 QList<QVariant> list = val.toList();
612 if (list.size() >= 1)
613 num = list[0].toInt();
614 if (list.size() >= 2)
615 den = list[1].toInt();
616 Exiv2::Rational rational;
617 rational.first = num;
618 rational.second = den;
619 datum = rational;
620 break;
621 }
622
623 case QMetaType::QDate:
625 {
626 QDateTime dateTime = val.toDateTime();
627 if(!dateTime.isValid())
628 break;
629
630 const std::string &exifdatetime(dateTime.toString(QString::fromLatin1("yyyy:MM:dd hh:mm:ss")).toLatin1().constData());
631 datum = exifdatetime;
632 break;
633 }
634
636 case QMetaType::QChar:
637 datum = (std::string)val.toString().toLatin1().constData();
638 break;
639 default:
640 break;
641 }
642
643 std::ostringstream os;
644 os << datum;
645 QString tagValue = QString::fromLocal8Bit(os.str().c_str());
646
647 if (escapeCR)
648 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
649
650 return tagValue;
651 }
652 catch( Exiv2::Error& e )
653 {
654 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Iptc tag string into image using Exiv2 "), e);
655 }
656 catch(...)
657 {
658 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
659 }
660
661 return QString();
662}
663
664bool KExiv2::getExifTagLong(const char* exifTagName, long& val) const
665{
666 return getExifTagLong(exifTagName, val, 0);
667}
668
669bool KExiv2::getExifTagLong(const char* exifTagName, long& val, int component) const
670{
671 try
672 {
673 Exiv2::ExifKey exifKey(exifTagName);
674 Exiv2::ExifData exifData(d->exifMetadata());
675 Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
676
677 if (it != exifData.end() && it->count() > 0)
678 {
679#if EXIV2_TEST_VERSION(0,28,0)
680 val = it->toUint32(component);
681#else
682 val = it->toLong(component);
683#endif
684 return true;
685 }
686 }
687 catch( Exiv2::Error& e )
688 {
689 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e);
690 }
691 catch(...)
692 {
693 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
694 }
695
696 return false;
697}
698
699QByteArray KExiv2::getExifTagData(const char* exifTagName) const
700{
701 try
702 {
703 Exiv2::ExifKey exifKey(exifTagName);
704 Exiv2::ExifData exifData(d->exifMetadata());
705 Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
706
707 if (it != exifData.end())
708 {
709 char* const s = new char[(*it).size()];
710 (*it).copy((Exiv2::byte*)s, Exiv2::bigEndian);
711 QByteArray data(s, (*it).size());
712 delete[] s;
713
714 return data;
715 }
716 }
717 catch( Exiv2::Error& e )
718 {
719 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e);
720 }
721 catch(...)
722 {
723 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
724 }
725
726 return QByteArray();
727}
728
729QVariant KExiv2::getExifTagVariant(const char* exifTagName, bool rationalAsListOfInts, bool stringEscapeCR, int component) const
730{
731 try
732 {
733 Exiv2::ExifKey exifKey(exifTagName);
734 Exiv2::ExifData exifData(d->exifMetadata());
735 Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
736
737 if (it != exifData.end())
738 {
739 switch (it->typeId())
740 {
741 case Exiv2::unsignedByte:
742 case Exiv2::unsignedShort:
743 case Exiv2::unsignedLong:
744 case Exiv2::signedShort:
745 case Exiv2::signedLong:
746 if (it->count() > component)
747#if EXIV2_TEST_VERSION(0,28,0)
748 return QVariant((int)it->toUint32(component));
749#else
750 return QVariant((int)it->toLong(component));
751#endif
752 else
753#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
755#else
756 return QVariant(QVariant::Int);
757#endif
758 case Exiv2::unsignedRational:
759 case Exiv2::signedRational:
760
761 if (rationalAsListOfInts)
762 {
763 if (it->count() <= component)
764#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
766#else
767 return QVariant(QVariant::List);
768#endif
769
770 QList<QVariant> list;
771 list << (*it).toRational(component).first;
772 list << (*it).toRational(component).second;
773
774 return QVariant(list);
775 }
776 else
777 {
778 if (it->count() <= component)
779#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
781#else
783#endif
784
785 // prefer double precision
786 double num = (*it).toRational(component).first;
787 double den = (*it).toRational(component).second;
788
789 if (den == 0.0)
790#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
792#else
794#endif
795
796 return QVariant(num / den);
797 }
798 case Exiv2::date:
799 case Exiv2::time:
800 {
801 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
802 return QVariant(dateTime);
803 }
804 case Exiv2::asciiString:
805 case Exiv2::comment:
806 case Exiv2::string:
807 {
808 std::ostringstream os;
809 os << *it;
810 QString tagValue = QString::fromLocal8Bit(os.str().c_str());
811
812 if (stringEscapeCR)
813 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
814
815 return QVariant(tagValue);
816 }
817 default:
818 break;
819 }
820 }
821 }
822 catch( Exiv2::Error& e )
823 {
824 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' in the image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e);
825 }
826 catch(...)
827 {
828 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
829 }
830
831 return QVariant();
832}
833
834QString KExiv2::getExifTagString(const char* exifTagName, bool escapeCR) const
835{
836 try
837 {
838 Exiv2::ExifKey exifKey(exifTagName);
839 Exiv2::ExifData exifData(d->exifMetadata());
840 Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
841
842 if (it != exifData.end())
843 {
844 // See B.K.O #184156 comment #13
845 std::string val = it->print(&exifData);
846 QString tagValue = QString::fromLocal8Bit(val.c_str());
847
848 if (escapeCR)
849 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
850
851 return tagValue;
852 }
853 }
854 catch( Exiv2::Error& e )
855 {
856 d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e);
857 }
858 catch(...)
859 {
860 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
861 }
862
863 return QString();
864}
865
866bool KExiv2::setExifTagString(const char* exifTagName, const QString& value, bool setProgramName) const
867{
868 if (!setProgramId(setProgramName))
869 return false;
870
871 try
872 {
873 d->exifMetadata()[exifTagName] = std::string(value.toLatin1().constData());
874 return true;
875 }
876 catch( Exiv2::Error& e )
877 {
878 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag string into image using Exiv2 "), e);
879 }
880 catch(...)
881 {
882 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
883 }
884
885 return false;
886}
887
888QImage KExiv2::getExifThumbnail(bool fixOrientation) const
889{
890 QImage thumbnail;
891
892 if (d->exifMetadata().empty())
893 return thumbnail;
894
895 try
896 {
897 Exiv2::ExifThumbC thumb(d->exifMetadata());
898 Exiv2::DataBuf const c1 = thumb.copy();
899#if EXIV2_TEST_VERSION(0,28,0)
900 thumbnail.loadFromData(c1.c_data(), c1.size());
901#else
902 thumbnail.loadFromData(c1.pData_, c1.size_);
903#endif
904
905 if (!thumbnail.isNull())
906 {
907 if (fixOrientation)
908 {
909 Exiv2::ExifKey key1("Exif.Thumbnail.Orientation");
910 Exiv2::ExifKey key2("Exif.Image.Orientation");
911 Exiv2::ExifData exifData(d->exifMetadata());
912 Exiv2::ExifData::iterator it = exifData.findKey(key1);
913
914 if (it == exifData.end())
915 it = exifData.findKey(key2);
916
917 if (it != exifData.end() && it->count())
918 {
919#if EXIV2_TEST_VERSION(0,28,0)
920 long orientation = it->toUint32();
921#else
922 long orientation = it->toLong();
923#endif
924 qCDebug(LIBKEXIV2_LOG) << "Exif Thumbnail Orientation: " << (int)orientation;
925 rotateExifQImage(thumbnail, (ImageOrientation)orientation);
926 }
927
928 return thumbnail;
929 }
930 }
931 }
932 catch( Exiv2::Error& e )
933 {
934 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Exif Thumbnail using Exiv2 "), e);
935 }
936 catch(...)
937 {
938 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
939 }
940
941 return thumbnail;
942}
943
944bool KExiv2::rotateExifQImage(QImage& image, ImageOrientation orientation) const
945{
946 QTransform matrix = RotationMatrix::toTransform(orientation);
947
948 if ((orientation != ORIENTATION_NORMAL) && (orientation != ORIENTATION_UNSPECIFIED))
949 {
950 image = image.transformed(matrix);
951 return true;
952 }
953
954 return false;
955}
956
957bool KExiv2::setExifThumbnail(const QImage& thumbImage, bool setProgramName) const
958{
959 if (!setProgramId(setProgramName))
960 return false;
961
962 if (thumbImage.isNull())
963 {
964 return removeExifThumbnail();
965 }
966
967 try
968 {
969 QByteArray data;
970 QBuffer buffer(&data);
972 thumbImage.save(&buffer, "JPEG");
973 Exiv2::ExifThumb thumb(d->exifMetadata());
974 thumb.setJpegThumbnail((Exiv2::byte *)data.data(), data.size());
975 return true;
976 }
977 catch( Exiv2::Error& e )
978 {
979 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif Thumbnail using Exiv2 "), e);
980 }
981 catch(...)
982 {
983 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
984 }
985
986 return false;
987}
988
989bool KExiv2::setTiffThumbnail(const QImage& thumbImage, bool setProgramName) const
990{
991 if (!setProgramId(setProgramName))
992 return false;
993
995
996 try
997 {
998 // Make sure IFD0 is explicitly marked as a main image
999 Exiv2::ExifData::const_iterator pos = d->exifMetadata().findKey(Exiv2::ExifKey("Exif.Image.NewSubfileType"));
1000
1001#if EXIV2_TEST_VERSION(0,28,0)
1002 if (pos == d->exifMetadata().end() || pos->count() != 1 || pos->toUint32() != 0)
1003#else
1004 if (pos == d->exifMetadata().end() || pos->count() != 1 || pos->toLong() != 0)
1005#endif
1006 {
1007#if EXIV2_TEST_VERSION(0,28,0)
1008 throw Exiv2::Error(Exiv2::ErrorCode::kerErrorMessage, "Exif.Image.NewSubfileType missing or not set as main image");
1009#elif EXIV2_TEST_VERSION(0,27,0)
1010 throw Exiv2::Error(Exiv2::kerErrorMessage, "Exif.Image.NewSubfileType missing or not set as main image");
1011#else
1012 throw Exiv2::Error(1, "Exif.Image.NewSubfileType missing or not set as main image");
1013#endif
1014 }
1015
1016 // Remove sub-IFD tags
1017 std::string subImage1("SubImage1");
1018
1019 for (Exiv2::ExifData::iterator md = d->exifMetadata().begin(); md != d->exifMetadata().end();)
1020 {
1021 if (md->groupName() == subImage1)
1022 md = d->exifMetadata().erase(md);
1023 else
1024 ++md;
1025 }
1026
1027 if (!thumbImage.isNull())
1028 {
1029 // Set thumbnail tags
1030 QByteArray data;
1031 QBuffer buffer(&data);
1032 buffer.open(QIODevice::WriteOnly);
1033 thumbImage.save(&buffer, "JPEG");
1034
1035 Exiv2::DataBuf buf((Exiv2::byte *)data.data(), data.size());
1036 Exiv2::ULongValue val;
1037 val.read("0");
1038#if EXIV2_TEST_VERSION(0,28,0)
1039 val.setDataArea(buf.c_data(), buf.size());
1040#else
1041 val.setDataArea(buf.pData_, buf.size_);
1042#endif
1043 d->exifMetadata()["Exif.SubImage1.JPEGInterchangeFormat"] = val;
1044#if EXIV2_TEST_VERSION(0,28,0)
1045 d->exifMetadata()["Exif.SubImage1.JPEGInterchangeFormatLength"] = uint32_t(buf.size());
1046#else
1047 d->exifMetadata()["Exif.SubImage1.JPEGInterchangeFormatLength"] = uint32_t(buf.size_);
1048#endif
1049 d->exifMetadata()["Exif.SubImage1.Compression"] = uint16_t(6); // JPEG (old-style)
1050 d->exifMetadata()["Exif.SubImage1.NewSubfileType"] = uint32_t(1); // Thumbnail image
1051 return true;
1052 }
1053 }
1054 catch( Exiv2::Error& e )
1055 {
1056 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set TIFF Thumbnail using Exiv2 "), e);
1057 }
1058 catch(...)
1059 {
1060 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1061 }
1062
1063 return false;
1064}
1065
1067{
1068 try
1069 {
1070 // Remove all IFD0 subimages.
1071 Exiv2::ExifThumb thumb(d->exifMetadata());
1072 thumb.erase();
1073 return true;
1074 }
1075 catch( Exiv2::Error& e )
1076 {
1077 d->printExiv2ExceptionError(QString::fromLatin1("Cannot remove Exif Thumbnail using Exiv2 "), e);
1078 }
1079 catch(...)
1080 {
1081 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1082 }
1083
1084 return false;
1085}
1086
1088{
1089 try
1090 {
1092 TagsMap tagsMap;
1093
1094 const Exiv2::GroupInfo* gi = Exiv2::ExifTags::groupList();
1095
1096 while (gi->tagList_ != nullptr)
1097 {
1098 if (QString::fromLatin1(gi->ifdName_) != QString::fromLatin1("Makernote"))
1099 {
1100 Exiv2::TagListFct tl = gi->tagList_;
1101 const Exiv2::TagInfo* ti = tl();
1102
1103 while (ti->tag_ != 0xFFFF)
1104 {
1105 tags << ti;
1106 ++ti;
1107 }
1108 }
1109 ++gi;
1110 }
1111
1112 for (QList<const Exiv2::TagInfo*>::iterator it = tags.begin(); it != tags.end(); ++it)
1113 {
1114 do
1115 {
1116 const Exiv2::TagInfo* const ti = *it;
1117 QString key = QLatin1String(Exiv2::ExifKey(*ti).key().c_str());
1118 QStringList values;
1119 values << QString::fromLatin1(ti->name_) << QString::fromLatin1(ti->title_) << QString::fromLatin1(ti->desc_);
1120 tagsMap.insert(key, values);
1121 ++(*it);
1122 }
1123 while((*it)->tag_ != 0xffff);
1124 }
1125 return tagsMap;
1126 }
1127 catch( Exiv2::Error& e )
1128 {
1129 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Exif Tags list using Exiv2 "), e);
1130 }
1131 catch(...)
1132 {
1133 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1134 }
1135
1136 return TagsMap();
1137}
1138
1140{
1141 try
1142 {
1144 TagsMap tagsMap;
1145
1146 const Exiv2::GroupInfo* gi = Exiv2::ExifTags::groupList();
1147
1148 while (gi->tagList_ != nullptr)
1149 {
1150 if (QString::fromLatin1(gi->ifdName_) == QString::fromLatin1("Makernote"))
1151 {
1152 Exiv2::TagListFct tl = gi->tagList_;
1153 const Exiv2::TagInfo* ti = tl();
1154
1155 while (ti->tag_ != 0xFFFF)
1156 {
1157 tags << ti;
1158 ++ti;
1159 }
1160 }
1161 ++gi;
1162 }
1163
1164 for (QList<const Exiv2::TagInfo*>::iterator it = tags.begin(); it != tags.end(); ++it)
1165 {
1166 do
1167 {
1168 const Exiv2::TagInfo* const ti = *it;
1169 QString key = QLatin1String(Exiv2::ExifKey(*ti).key().c_str());
1170 QStringList values;
1171 values << QString::fromLatin1(ti->name_) << QString::fromLatin1(ti->title_) << QString::fromLatin1(ti->desc_);
1172 tagsMap.insert(key, values);
1173 ++(*it);
1174 }
1175 while((*it)->tag_ != 0xffff);
1176 }
1177
1178 return tagsMap;
1179 }
1180 catch( Exiv2::Error& e )
1181 {
1182 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Makernote Tags list using Exiv2 "), e);
1183 }
1184 catch(...)
1185 {
1186 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1187 }
1188
1189 return TagsMap();
1190}
1191
1192} // NameSpace KExiv2Iface
QMap< QString, QStringList > TagsMap
A map used to store Tags Key and a list of Tags properties :
Definition kexiv2.h:125
bool rotateExifQImage(QImage &image, ImageOrientation orientation) const
Fix orientation of a QImage image accordingly with Exif orientation tag.
bool removeExifTag(const char *exifTagName, bool setProgramName=true) const
Remove the Exif tag 'exifTagName' from Exif metadata.
TagsMap getMakernoteTagsList() const
Return a map of all non-standard Exif tags (makernotes) supported by Exiv2.
static void convertToRationalSmallDenominator(const double number, long int *const numerator, long int *const denominator)
This method convert a 'number' to a rational value, returned in 'numerator' and 'denominator' paramet...
bool setExifTagString(const char *exifTagName, const QString &value, bool setProgramName=true) const
Set an Exif tag content using a string.
bool getExifTagLong(const char *exifTagName, long &val) const
Get an Exif tag content like a long value.
QMap< QString, QString > MetaDataMap
A map used to store Tags Key and Tags Value.
Definition kexiv2.h:112
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
KExiv2::MetaDataMap getExifTagsDataList(const QStringList &exifKeysFilter=QStringList(), bool invertSelection=false) const
Return a map of Exif tags name/value found in metadata sorted by Exif keys given by 'exifKeysFilter'.
QString getExifTagString(const char *exifTagName, bool escapeCR=true) const
Get an Exif tags content like a string.
static bool canWriteExif(const QString &filePath)
Return 'true' if Exif can be written in file.
static void convertToRational(const double number, long int *const numerator, long int *const denominator, const int rounding)
This method converts 'number' to a rational value, returned in the 'numerator' and 'denominator' para...
bool setExifTagVariant(const char *exifTagName, const QVariant &data, bool rationalWantSmallDenominator=true, bool setProgramName=true) const
Set an Exif tag content using a QVariant.
ImageOrientation
The image orientation values given by Exif metadata.
Definition kexiv2.h:86
bool setTiffThumbnail(const QImage &thumb, bool setProgramName=true) const
Adds a JPEG thumbnail to a TIFF images.
QByteArray getExifEncoded(bool addExifHeader=false) const
Returns the exif data encoded to a QByteArray in a form suitable for storage in a JPEG image.
TagsMap getStdExifTagsList() const
Return a map of all standard Exif tags supported by Exiv2.
QString getExifTagDescription(const char *exifTagName)
Return the Exif Tag description or a null string.
bool setExifThumbnail(const QImage &thumb, bool setProgramName=true) const
Set the Exif Thumbnail image.
QString getExifComment() const
Return a QString copy of Exif user comments.
QImage getExifThumbnail(bool fixOrientation) const
Return a QImage copy of Exif thumbnail image.
bool clearExif() const
Clear the Exif metadata container in memory.
QByteArray getExifTagData(const char *exifTagName) const
Get an Exif tag content like a bytes array.
bool setExifTagRational(const char *exifTagName, long int num, long int den, bool setProgramName=true) const
Set an Exif tag content using a rational value.
QString createExifUserStringFromValue(const char *exifTagName, const QVariant &val, bool escapeCR=true)
Takes a QVariant value as it could have been retrieved by getExifTagVariant with the given exifTagNam...
bool setExifComment(const QString &comment, bool setProgramName=true) const
Set the Exif user comments from image.
bool setExifTagData(const char *exifTagName, const QByteArray &data, bool setProgramName=true) const
Set an Exif tag content using a bytes array.
bool removeExifThumbnail() const
Remove the Exif Thumbnail from the image.
QVariant getExifTagVariant(const char *exifTagName, bool rationalAsListOfInts=true, bool escapeCR=true, int component=0) const
Get an Exif tags content as a QVariant.
bool setExifTagLong(const char *exifTagName, long val, bool setProgramName=true) const
Set an Exif tag content using a long value.
bool hasExif() const
Return 'true' if metadata container in memory as Exif.
bool getExifTagRational(const char *exifTagName, long int &num, long int &den, int component=0) const
Get the 'component' index of an Exif tags content like a rational value.
QString getExifTagTitle(const char *exifTagName)
Return the Exif Tag title or a null string.
bool setExif(const QByteArray &data) const
Set the Exif data using a Qt byte array.
QTransform toTransform() const
Returns a QTransform representing this matrix.
KExiv2Iface - Exiv2 library interface.
Definition kexiv2.cpp:17
virtual bool open(OpenMode flags) override
const char * constData() const const
char * data()
bool isEmpty() const const
QByteArray & replace(QByteArrayView before, QByteArrayView after)
void resize(qsizetype newSize, char c)
qsizetype size() const const
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool isValid() const const
QString toString(QStringView format, QCalendar cal) const const
QByteArray encodeName(const QString &fileName)
bool isNull() const const
bool loadFromData(QByteArrayView data, const char *format)
bool save(QIODevice *device, const char *format, int quality) const const
QImage transformed(const QTransform &matrix, Qt::TransformationMode mode) const const
iterator begin()
iterator end()
T & first()
bool isEmpty() const const
qsizetype size() const const
iterator insert(const Key &key, const T &value)
int id() const const
iterator begin()
iterator end()
QString fromLatin1(QByteArrayView str)
QString fromLocal8Bit(QByteArrayView str)
bool isEmpty() const const
bool isNull() const const
QString number(double n, char format, int precision)
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
QByteArray toLatin1() const const
QByteArray toUtf8() const const
QString trimmed() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
Type type() const const
QMetaType metaType() const const
QByteArray toByteArray() const const
QDateTime toDateTime() const const
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
QList< QVariant > toList() const const
QString toString() const const
uint toUInt(bool *ok) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Apr 27 2024 22:09:48 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.