KExiv2

kexiv2image.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// Qt includes
12
13#include <QBuffer>
14
15// Local includes
16
17#include "rotationmatrix.h"
18#include "libkexiv2_debug.h"
19
20namespace KExiv2Iface
21{
22
23bool KExiv2::setImageProgramId(const QString& program, const QString& version) const
24{
25 try
26 {
27 QString software(program);
28 software.append(QString::fromLatin1("-"));
29 software.append(version);
30
31 // Set program info into Exif.Image.ProcessingSoftware tag (only available with Exiv2 >= 0.14.0).
32
33 d->exifMetadata()["Exif.Image.ProcessingSoftware"] = std::string(software.toLatin1().constData());
34
35 // See B.K.O #142564: Check if Exif.Image.Software already exist. If yes, do not touch this tag.
36
37 if (!d->exifMetadata().empty())
38 {
39 Exiv2::ExifData exifData(d->exifMetadata());
40 Exiv2::ExifKey key("Exif.Image.Software");
41 Exiv2::ExifData::iterator it = exifData.findKey(key);
42
43 if (it == exifData.end())
44 d->exifMetadata()["Exif.Image.Software"] = std::string(software.toLatin1().constData());
45 }
46
47 // set program info into XMP tags.
48
49#ifdef _XMP_SUPPORT_
50
51 if (!d->xmpMetadata().empty())
52 {
53 // Only create Xmp.xmp.CreatorTool if it do not exist.
54 Exiv2::XmpData xmpData(d->xmpMetadata());
55 Exiv2::XmpKey key("Xmp.xmp.CreatorTool");
56 Exiv2::XmpData::iterator it = xmpData.findKey(key);
57
58 if (it == xmpData.end())
59 setXmpTagString("Xmp.xmp.CreatorTool", software, false);
60 }
61
62 setXmpTagString("Xmp.tiff.Software", software, false);
63
64#endif // _XMP_SUPPORT_
65
66 // Set program info into IPTC tags.
67
68 d->iptcMetadata()["Iptc.Application2.Program"] = std::string(program.toLatin1().constData());
69 d->iptcMetadata()["Iptc.Application2.ProgramVersion"] = std::string(version.toLatin1().constData());
70 return true;
71 }
72 catch( Exiv2::Error& e )
73 {
74 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Program identity into image using Exiv2 "), e);
75 }
76 catch(...)
77 {
78 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
79 }
80
81 return false;
82}
83
85{
86 try
87 {
88 long width=-1, height=-1;
89
90 // Try to get Exif.Photo tags
91
92 Exiv2::ExifData exifData(d->exifMetadata());
93 Exiv2::ExifKey key("Exif.Photo.PixelXDimension");
94 Exiv2::ExifData::iterator it = exifData.findKey(key);
95
96 if (it != exifData.end() && it->count())
97#if EXIV2_TEST_VERSION(0,28,0)
98 width = it->toUint32();
99#else
100 width = it->toLong();
101#endif
102
103 Exiv2::ExifKey key2("Exif.Photo.PixelYDimension");
104 Exiv2::ExifData::iterator it2 = exifData.findKey(key2);
105
106 if (it2 != exifData.end() && it2->count())
107#if EXIV2_TEST_VERSION(0,28,0)
108 height = it2->toUint32();
109#else
110 height = it2->toLong();
111#endif
112
113 if (width != -1 && height != -1)
114 return QSize(width, height);
115
116 // Try to get Exif.Image tags
117
118 width = -1;
119 height = -1;
120
121 Exiv2::ExifKey key3("Exif.Image.ImageWidth");
122 Exiv2::ExifData::iterator it3 = exifData.findKey(key3);
123
124 if (it3 != exifData.end() && it3->count())
125#if EXIV2_TEST_VERSION(0,28,0)
126 width = it3->toUint32();
127#else
128 width = it3->toLong();
129#endif
130
131 Exiv2::ExifKey key4("Exif.Image.ImageLength");
132 Exiv2::ExifData::iterator it4 = exifData.findKey(key4);
133
134 if (it4 != exifData.end() && it4->count())
135#if EXIV2_TEST_VERSION(0,28,0)
136 height = it4->toUint32();
137#else
138 height = it4->toLong();
139#endif
140
141 if (width != -1 && height != -1)
142 return QSize(width, height);
143
144#ifdef _XMP_SUPPORT_
145
146 // Try to get Xmp.tiff tags
147
148 width = -1;
149 height = -1;
150 bool wOk = false;
151 bool hOk = false;
152
153 QString str = getXmpTagString("Xmp.tiff.ImageWidth");
154
155 if (!str.isEmpty())
156 width = str.toInt(&wOk);
157
158 str = getXmpTagString("Xmp.tiff.ImageLength");
159
160 if (!str.isEmpty())
161 height = str.toInt(&hOk);
162
163 if (wOk && hOk)
164 return QSize(width, height);
165
166 // Try to get Xmp.exif tags
167
168 width = -1;
169 height = -1;
170 wOk = false;
171 hOk = false;
172
173 str = getXmpTagString("Xmp.exif.PixelXDimension");
174
175 if (!str.isEmpty())
176 width = str.toInt(&wOk);
177
178 str = getXmpTagString("Xmp.exif.PixelYDimension");
179
180 if (!str.isEmpty())
181 height = str.toInt(&hOk);
182
183 if (wOk && hOk)
184 return QSize(width, height);
185
186#endif // _XMP_SUPPORT_
187
188 }
189 catch( Exiv2::Error& e )
190 {
191 d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse image dimensions tag using Exiv2 "), e);
192 }
193 catch(...)
194 {
195 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
196 }
197
198 return QSize();
199}
200
201bool KExiv2::setImageDimensions(const QSize& size, bool setProgramName) const
202{
203 if (!setProgramId(setProgramName))
204 return false;
205
206 try
207 {
208 // Set Exif values.
209
210 // NOTE: see B.K.O #144604: need to cast to record an unsigned integer value.
211 d->exifMetadata()["Exif.Image.ImageWidth"] = static_cast<uint32_t>(size.width());
212 d->exifMetadata()["Exif.Image.ImageLength"] = static_cast<uint32_t>(size.height());
213 d->exifMetadata()["Exif.Photo.PixelXDimension"] = static_cast<uint32_t>(size.width());
214 d->exifMetadata()["Exif.Photo.PixelYDimension"] = static_cast<uint32_t>(size.height());
215
216 // Set Xmp values.
217
218#ifdef _XMP_SUPPORT_
219
220 setXmpTagString("Xmp.tiff.ImageWidth", QString::number(size.width()), false);
221 setXmpTagString("Xmp.tiff.ImageLength", QString::number(size.height()), false);
222 setXmpTagString("Xmp.exif.PixelXDimension", QString::number(size.width()), false);
223 setXmpTagString("Xmp.exif.PixelYDimension", QString::number(size.height()), false);
224
225#endif // _XMP_SUPPORT_
226
227 return true;
228 }
229 catch( Exiv2::Error& e )
230 {
231 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set image dimensions using Exiv2 "), e);
232 }
233 catch(...)
234 {
235 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
236 }
237
238 return false;
239}
240
242{
243 try
244 {
245 Exiv2::ExifData exifData(d->exifMetadata());
246 Exiv2::ExifData::iterator it;
247 long orientation;
248 ImageOrientation imageOrient = ORIENTATION_NORMAL;
249
250 // -- Standard Xmp tag --------------------------------
251
252#ifdef _XMP_SUPPORT_
253
254 bool ok = false;
255 QString str = getXmpTagString("Xmp.tiff.Orientation");
256
257 if (!str.isEmpty())
258 {
259 orientation = str.toLong(&ok);
260
261 if (ok)
262 {
263 qCDebug(LIBKEXIV2_LOG) << "Orientation => Xmp.tiff.Orientation => " << (int)orientation;
264 return (ImageOrientation)orientation;
265 }
266 }
267
268#endif // _XMP_SUPPORT_
269
270 // Because some camera set a wrong standard exif orientation tag,
271 // We need to check makernote tags in first!
272
273 // -- Minolta Cameras ----------------------------------
274
275 Exiv2::ExifKey minoltaKey1("Exif.MinoltaCs7D.Rotation");
276 it = exifData.findKey(minoltaKey1);
277
278 if (it != exifData.end() && it->count())
279 {
280#if EXIV2_TEST_VERSION(0,28,0)
281 orientation = it->toUint32();
282#else
283 orientation = it->toLong();
284#endif
285 qCDebug(LIBKEXIV2_LOG) << "Orientation => Exif.MinoltaCs7D.Rotation => " << (int)orientation;
286
287 switch(orientation)
288 {
289 case 76:
290 imageOrient = ORIENTATION_ROT_90;
291 break;
292 case 82:
293 imageOrient = ORIENTATION_ROT_270;
294 break;
295 }
296
297 return imageOrient;
298 }
299
300 Exiv2::ExifKey minoltaKey2("Exif.MinoltaCs5D.Rotation");
301 it = exifData.findKey(minoltaKey2);
302
303 if (it != exifData.end() && it->count())
304 {
305#if EXIV2_TEST_VERSION(0,28,0)
306 orientation = it->toUint32();
307#else
308 orientation = it->toLong();
309#endif
310 qCDebug(LIBKEXIV2_LOG) << "Orientation => Exif.MinoltaCs5D.Rotation => " << (int)orientation;
311
312 switch(orientation)
313 {
314 case 76:
315 imageOrient = ORIENTATION_ROT_90;
316 break;
317 case 82:
318 imageOrient = ORIENTATION_ROT_270;
319 break;
320 }
321
322 return imageOrient;
323 }
324
325 // -- Standard Exif tag --------------------------------
326
327 Exiv2::ExifKey keyStd("Exif.Image.Orientation");
328 it = exifData.findKey(keyStd);
329
330 if (it != exifData.end() && it->count())
331 {
332#if EXIV2_TEST_VERSION(0,28,0)
333 orientation = it->toUint32();
334#else
335 orientation = it->toLong();
336#endif
337 qCDebug(LIBKEXIV2_LOG) << "Orientation => Exif.Image.Orientation => " << (int)orientation;
338 return (ImageOrientation)orientation;
339 }
340
341 }
342 catch( Exiv2::Error& e )
343 {
344 d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse Exif Orientation tag using Exiv2 "), e);
345 }
346 catch(...)
347 {
348 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
349 }
350
351 return ORIENTATION_UNSPECIFIED;
352}
353
354bool KExiv2::setImageOrientation(ImageOrientation orientation, bool setProgramName) const
355{
356 if (!setProgramId(setProgramName))
357 return false;
358
359 try
360 {
361 if (orientation < ORIENTATION_UNSPECIFIED || orientation > ORIENTATION_ROT_270)
362 {
363 qCDebug(LIBKEXIV2_LOG) << "Image orientation value is not correct!";
364 return false;
365 }
366
367 // Set Exif values.
368
369 d->exifMetadata()["Exif.Image.Orientation"] = static_cast<uint16_t>(orientation);
370 qCDebug(LIBKEXIV2_LOG) << "Exif.Image.Orientation tag set to: " << (int)orientation;
371
372 // Set Xmp values.
373
374#ifdef _XMP_SUPPORT_
375
376 setXmpTagString("Xmp.tiff.Orientation", QString::number((int)orientation), false);
377
378#endif // _XMP_SUPPORT_
379
380 // -- Minolta/Sony Cameras ----------------------------------
381
382 // Minolta and Sony camera store image rotation in Makernote.
383 // We remove these information to prevent duplicate values.
384
385 Exiv2::ExifData::iterator it;
386
387 Exiv2::ExifKey minoltaKey1("Exif.MinoltaCs7D.Rotation");
388 it = d->exifMetadata().findKey(minoltaKey1);
389
390 if (it != d->exifMetadata().end())
391 {
392 d->exifMetadata().erase(it);
393 qCDebug(LIBKEXIV2_LOG) << "Removing Exif.MinoltaCs7D.Rotation tag";
394 }
395
396 Exiv2::ExifKey minoltaKey2("Exif.MinoltaCs5D.Rotation");
397 it = d->exifMetadata().findKey(minoltaKey2);
398
399 if (it != d->exifMetadata().end())
400 {
401 d->exifMetadata().erase(it);
402 qCDebug(LIBKEXIV2_LOG) << "Removing Exif.MinoltaCs5D.Rotation tag";
403 }
404
405 // -- Exif embedded thumbnail ----------------------------------
406
407 Exiv2::ExifKey thumbKey("Exif.Thumbnail.Orientation");
408 it = d->exifMetadata().findKey(thumbKey);
409
410 if (it != d->exifMetadata().end() && it->count())
411 {
412#if EXIV2_TEST_VERSION(0,28,0)
413 RotationMatrix operation((KExiv2Iface::KExiv2::ImageOrientation)it->toUint32());
414#else
416#endif
417 operation *= orientation;
418 (*it) = static_cast<uint16_t>(operation.exifOrientation());
419 }
420
421 return true;
422 }
423 catch( Exiv2::Error& e )
424 {
425 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif Orientation tag using Exiv2 "), e);
426 }
427 catch(...)
428 {
429 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
430 }
431
432 return false;
433}
434
436{
437 // Check Exif values.
438
439 long exifColorSpace = -1;
440
441 if (!getExifTagLong("Exif.Photo.ColorSpace", exifColorSpace))
442 {
443#ifdef _XMP_SUPPORT_
444 QVariant var = getXmpTagVariant("Xmp.exif.ColorSpace");
445 if (!var.isNull())
446 exifColorSpace = var.toInt();
447#endif // _XMP_SUPPORT_
448 }
449
450 if (exifColorSpace == 1)
451 {
452 return WORKSPACE_SRGB; // as specified by standard
453 }
454 else if (exifColorSpace == 2)
455 {
456 return WORKSPACE_ADOBERGB; // not in the standard!
457 }
458 else
459 {
460 if (exifColorSpace == 65535)
461 {
462 // A lot of cameras set the Exif.Iop.InteroperabilityIndex,
463 // as documented for ExifTool
464 QString interopIndex = getExifTagString("Exif.Iop.InteroperabilityIndex");
465
466 if (!interopIndex.isNull())
467 {
468 if (interopIndex == QString::fromLatin1("R03"))
469 return WORKSPACE_ADOBERGB;
470 else if (interopIndex == QString::fromLatin1("R98"))
471 return WORKSPACE_SRGB;
472 }
473 }
474
475 // Note: Text EXIF ColorSpace tag may just not be present (NEF files)
476
477 // Nikon camera set Exif.Photo.ColorSpace to uncalibrated or just skip this field,
478 // then add additional information into the makernotes.
479 // Exif.Nikon3.ColorSpace: 1 => sRGB, 2 => AdobeRGB
480 long nikonColorSpace;
481
482 if (getExifTagLong("Exif.Nikon3.ColorSpace", nikonColorSpace))
483 {
484 if (nikonColorSpace == 1)
485 return WORKSPACE_SRGB;
486 else if (nikonColorSpace == 2)
487 return WORKSPACE_ADOBERGB;
488 }
489 // Exif.Nikon3.ColorMode is set to "MODE2" for AdobeRGB, but there are sometimes two ColorMode fields
490 // in a NEF, with the first one "COLOR" and the second one "MODE2"; but in this case, ColorSpace (above) was set.
491 if (getExifTagString("Exif.Nikon3.ColorMode").contains(QString::fromLatin1("MODE2")))
492 return WORKSPACE_ADOBERGB;
493
494 //TODO: This makernote tag (0x00b4) must be added to libexiv2
495 /*
496 long canonColorSpace;
497 if (getExifTagLong("Exif.Canon.ColorSpace", canonColorSpace))
498 {
499 if (canonColorSpace == 1)
500 return WORKSPACE_SRGB;
501 else if (canonColorSpace == 2)
502 return WORKSPACE_ADOBERGB;
503 }
504 */
505
506 // TODO : add more Makernote parsing here ...
507
508 if (exifColorSpace == 65535)
509 return WORKSPACE_UNCALIBRATED;
510 }
511
512 return WORKSPACE_UNSPECIFIED;
513}
514
515bool KExiv2::setImageColorWorkSpace(ImageColorWorkSpace workspace, bool setProgramName) const
516{
517 if (!setProgramId(setProgramName))
518 return false;
519
520 try
521 {
522 // Set Exif value.
523
524 d->exifMetadata()["Exif.Photo.ColorSpace"] = static_cast<uint16_t>(workspace);
525
526 // Set Xmp value.
527
528#ifdef _XMP_SUPPORT_
529
530 setXmpTagString("Xmp.exif.ColorSpace", QString::number((int)workspace), false);
531
532#endif // _XMP_SUPPORT_
533
534 return true;
535 }
536 catch( Exiv2::Error& e )
537 {
538 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif color workspace tag using Exiv2 "), e);
539 }
540 catch(...)
541 {
542 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
543 }
544
545 return false;
546}
547
549{
550 try
551 {
552 // In first, trying to get Date & time from Exif tags.
553
554 if (!d->exifMetadata().empty())
555 {
556 Exiv2::ExifData exifData(d->exifMetadata());
557 {
558 Exiv2::ExifKey key("Exif.Photo.DateTimeOriginal");
559 Exiv2::ExifData::iterator it = exifData.findKey(key);
560
561 if (it != exifData.end())
562 {
563 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
564
565 if (dateTime.isValid())
566 {
567 qCDebug(LIBKEXIV2_LOG) << "DateTime => Exif.Photo.DateTimeOriginal => " << dateTime;
568 return dateTime;
569 }
570 }
571 }
572 {
573 Exiv2::ExifKey key("Exif.Photo.DateTimeDigitized");
574 Exiv2::ExifData::iterator it = exifData.findKey(key);
575
576 if (it != exifData.end())
577 {
578 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
579
580 if (dateTime.isValid())
581 {
582 qCDebug(LIBKEXIV2_LOG) << "DateTime => Exif.Photo.DateTimeDigitized => " << dateTime;
583 return dateTime;
584 }
585 }
586 }
587 {
588 Exiv2::ExifKey key("Exif.Image.DateTime");
589 Exiv2::ExifData::iterator it = exifData.findKey(key);
590
591 if (it != exifData.end())
592 {
593 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
594
595 if (dateTime.isValid())
596 {
597 qCDebug(LIBKEXIV2_LOG) << "DateTime => Exif.Image.DateTime => " << dateTime;
598 return dateTime;
599 }
600 }
601 }
602 }
603
604 // In second, trying to get Date & time from Xmp tags.
605
606#ifdef _XMP_SUPPORT_
607
608 if (!d->xmpMetadata().empty())
609 {
610 Exiv2::XmpData xmpData(d->xmpMetadata());
611 {
612 Exiv2::XmpKey key("Xmp.exif.DateTimeOriginal");
613 Exiv2::XmpData::iterator it = xmpData.findKey(key);
614
615 if (it != xmpData.end())
616 {
617 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
618
619 if (dateTime.isValid())
620 {
621 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.exif.DateTimeOriginal => " << dateTime;
622 return dateTime;
623 }
624 }
625 }
626 {
627 Exiv2::XmpKey key("Xmp.exif.DateTimeDigitized");
628 Exiv2::XmpData::iterator it = xmpData.findKey(key);
629
630 if (it != xmpData.end())
631 {
632 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
633
634 if (dateTime.isValid())
635 {
636 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.exif.DateTimeDigitized => " << dateTime;
637 return dateTime;
638 }
639 }
640 }
641 {
642 Exiv2::XmpKey key("Xmp.photoshop.DateCreated");
643 Exiv2::XmpData::iterator it = xmpData.findKey(key);
644
645 if (it != xmpData.end())
646 {
647 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
648
649 if (dateTime.isValid())
650 {
651 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.photoshop.DateCreated => " << dateTime;
652 return dateTime;
653 }
654 }
655 }
656 {
657 Exiv2::XmpKey key("Xmp.xmp.CreateDate");
658 Exiv2::XmpData::iterator it = xmpData.findKey(key);
659
660 if (it != xmpData.end())
661 {
662 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
663
664 if (dateTime.isValid())
665 {
666 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.xmp.CreateDate => " << dateTime;
667 return dateTime;
668 }
669 }
670 }
671 {
672 Exiv2::XmpKey key("Xmp.tiff.DateTime");
673 Exiv2::XmpData::iterator it = xmpData.findKey(key);
674
675 if (it != xmpData.end())
676 {
677 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
678
679 if (dateTime.isValid())
680 {
681 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.tiff.DateTime => " << dateTime;
682 return dateTime;
683 }
684 }
685 }
686 {
687 Exiv2::XmpKey key("Xmp.xmp.ModifyDate");
688 Exiv2::XmpData::iterator it = xmpData.findKey(key);
689
690 if (it != xmpData.end())
691 {
692 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
693
694 if (dateTime.isValid())
695 {
696 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.xmp.ModifyDate => " << dateTime;
697 return dateTime;
698 }
699 }
700 }
701 {
702 Exiv2::XmpKey key("Xmp.xmp.MetadataDate");
703 Exiv2::XmpData::iterator it = xmpData.findKey(key);
704
705 if (it != xmpData.end())
706 {
707 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
708
709 if (dateTime.isValid())
710 {
711 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.xmp.MetadataDate => " << dateTime;
712 return dateTime;
713 }
714 }
715 }
716
717 // Video files support
718
719 {
720 Exiv2::XmpKey key("Xmp.video.DateTimeOriginal");
721 Exiv2::XmpData::iterator it = xmpData.findKey(key);
722
723 if (it != xmpData.end())
724 {
725 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
726
727 if (dateTime.isValid())
728 {
729 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.video.DateTimeOriginal => " << dateTime;
730 return dateTime;
731 }
732 }
733 }
734 {
735 Exiv2::XmpKey key("Xmp.video.DateUTC");
736 Exiv2::XmpData::iterator it = xmpData.findKey(key);
737
738 if (it != xmpData.end())
739 {
740 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
741
742 if (dateTime.isValid())
743 {
744 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.video.DateUTC => " << dateTime;
745 return dateTime;
746 }
747 }
748 }
749 {
750 Exiv2::XmpKey key("Xmp.video.ModificationDate");
751 Exiv2::XmpData::iterator it = xmpData.findKey(key);
752
753 if (it != xmpData.end())
754 {
755 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
756
757 if (dateTime.isValid())
758 {
759 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.video.ModificationDate => " << dateTime;
760 return dateTime;
761 }
762 }
763 }
764 {
765 Exiv2::XmpKey key("Xmp.video.DateTimeDigitized");
766 Exiv2::XmpData::iterator it = xmpData.findKey(key);
767
768 if (it != xmpData.end())
769 {
770 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
771
772 if (dateTime.isValid())
773 {
774 qCDebug(LIBKEXIV2_LOG) << "DateTime => Xmp.video.DateTimeDigitized => " << dateTime;
775 return dateTime;
776 }
777 }
778 }
779 }
780
781#endif // _XMP_SUPPORT_
782
783 // In third, trying to get Date & time from Iptc tags.
784
785 if (!d->iptcMetadata().empty())
786 {
787 Exiv2::IptcData iptcData(d->iptcMetadata());
788
789 // Try creation Iptc date & time entries.
790
791 Exiv2::IptcKey keyDateCreated("Iptc.Application2.DateCreated");
792 Exiv2::IptcData::iterator it = iptcData.findKey(keyDateCreated);
793
794 if (it != iptcData.end())
795 {
796 QString IptcDateCreated(QString::fromLatin1(it->toString().c_str()));
797 Exiv2::IptcKey keyTimeCreated("Iptc.Application2.TimeCreated");
798 Exiv2::IptcData::iterator it2 = iptcData.findKey(keyTimeCreated);
799
800 if (it2 != iptcData.end())
801 {
802 QString IptcTimeCreated(QString::fromLatin1(it2->toString().c_str()));
803 QDate date = QDate::fromString(IptcDateCreated, Qt::ISODate);
804 QTime time = QTime::fromString(IptcTimeCreated, Qt::ISODate);
805 QDateTime dateTime = QDateTime(date, time);
806
807 if (dateTime.isValid())
808 {
809 qCDebug(LIBKEXIV2_LOG) << "DateTime => Iptc.Application2.DateCreated => " << dateTime;
810 return dateTime;
811 }
812 }
813 }
814
815 // Try digitization Iptc date & time entries.
816
817 Exiv2::IptcKey keyDigitizationDate("Iptc.Application2.DigitizationDate");
818 Exiv2::IptcData::iterator it3 = iptcData.findKey(keyDigitizationDate);
819
820 if (it3 != iptcData.end())
821 {
822 QString IptcDateDigitization(QString::fromLatin1(it3->toString().c_str()));
823 Exiv2::IptcKey keyDigitizationTime("Iptc.Application2.DigitizationTime");
824 Exiv2::IptcData::iterator it4 = iptcData.findKey(keyDigitizationTime);
825
826 if (it4 != iptcData.end())
827 {
828 QString IptcTimeDigitization(QString::fromLatin1(it4->toString().c_str()));
829 QDate date = QDate::fromString(IptcDateDigitization, Qt::ISODate);
830 QTime time = QTime::fromString(IptcTimeDigitization, Qt::ISODate);
831 QDateTime dateTime = QDateTime(date, time);
832
833 if (dateTime.isValid())
834 {
835 qCDebug(LIBKEXIV2_LOG) << "DateTime => Iptc.Application2.DigitizationDate => " << dateTime;
836 return dateTime;
837 }
838 }
839 }
840 }
841 }
842 catch( Exiv2::Error& e )
843 {
844 d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse Exif date & time tag using Exiv2 "), e);
845 }
846 catch(...)
847 {
848 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
849 }
850
851 return QDateTime();
852}
853
854bool KExiv2::setImageDateTime(const QDateTime& dateTime, bool setDateTimeDigitized, bool setProgramName) const
855{
856 if(!dateTime.isValid())
857 return false;
858
859 if (!setProgramId(setProgramName))
860 return false;
861
862 try
863 {
864 // In first we write date & time into Exif.
865
866 // DateTimeDigitized is set by slide scanners etc. when a picture is digitized.
867 // DateTimeOriginal specifies the date/time when the picture was taken.
868 // For digital cameras, these dates should be both set, and identical.
869 // Reference: https://www.exif.org/Exif2-2.PDF, chapter 4.6.5, table 4, section F.
870
871 const std::string &exifdatetime(dateTime.toString(QString::fromLatin1("yyyy:MM:dd hh:mm:ss")).toLatin1().constData());
872 d->exifMetadata()["Exif.Image.DateTime"] = exifdatetime;
873 d->exifMetadata()["Exif.Photo.DateTimeOriginal"] = exifdatetime;
874
875 if(setDateTimeDigitized)
876 d->exifMetadata()["Exif.Photo.DateTimeDigitized"] = exifdatetime;
877
878#ifdef _XMP_SUPPORT_
879
880 // In second we write date & time into Xmp.
881
882 const std::string &xmpdatetime(dateTime.toString(Qt::ISODate).toLatin1().constData());
883
884#if EXIV2_TEST_VERSION(0,28,0)
885 Exiv2::Value::UniquePtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
886#else
887 Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
888#endif
889 xmpTxtVal->read(xmpdatetime);
890 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.exif.DateTimeOriginal"), xmpTxtVal.get());
891 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.photoshop.DateCreated"), xmpTxtVal.get());
892 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.tiff.DateTime"), xmpTxtVal.get());
893 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.CreateDate"), xmpTxtVal.get());
894 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.MetadataDate"), xmpTxtVal.get());
895 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.ModifyDate"), xmpTxtVal.get());
896 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.video.DateTimeOriginal"), xmpTxtVal.get());
897 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.video.DateUTC"), xmpTxtVal.get());
898 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.video.ModificationDate"), xmpTxtVal.get());
899
900 if(setDateTimeDigitized)
901 {
902 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.exif.DateTimeDigitized"), xmpTxtVal.get());
903 d->xmpMetadata().add(Exiv2::XmpKey("Xmp.video.DateTimeDigitized"), xmpTxtVal.get());
904 }
905
906 // Tag not updated:
907 // "Xmp.dc.DateTime" is a sequence of date relevant of dublin core change.
908 // This is not the picture date as well
909
910#endif // _XMP_SUPPORT_
911
912 // In third we write date & time into Iptc.
913
914 const std::string &iptcdate(dateTime.date().toString(Qt::ISODate).toLatin1().constData());
915 const std::string &iptctime(dateTime.time().toString(Qt::ISODate).toLatin1().constData());
916 d->iptcMetadata()["Iptc.Application2.DateCreated"] = iptcdate;
917 d->iptcMetadata()["Iptc.Application2.TimeCreated"] = iptctime;
918
919 if(setDateTimeDigitized)
920 {
921 d->iptcMetadata()["Iptc.Application2.DigitizationDate"] = iptcdate;
922 d->iptcMetadata()["Iptc.Application2.DigitizationTime"] = iptctime;
923 }
924
925 return true;
926 }
927 catch( Exiv2::Error& e )
928 {
929 d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Date & Time into image using Exiv2 "), e);
930 }
931 catch(...)
932 {
933 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
934 }
935
936 return false;
937}
938
939QDateTime KExiv2::getDigitizationDateTime(bool fallbackToCreationTime) const
940{
941 try
942 {
943 // In first, trying to get Date & time from Exif tags.
944
945 if (!d->exifMetadata().empty())
946 {
947 // Try Exif date time digitized.
948
949 Exiv2::ExifData exifData(d->exifMetadata());
950 Exiv2::ExifKey key("Exif.Photo.DateTimeDigitized");
951 Exiv2::ExifData::iterator it = exifData.findKey(key);
952
953 if (it != exifData.end())
954 {
955 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
956
957 if (dateTime.isValid())
958 {
959 qCDebug(LIBKEXIV2_LOG) << "DateTime (Exif digitalized): " << dateTime.toString().toLatin1().constData();
960 return dateTime;
961 }
962 }
963 }
964
965 // In second, we trying XMP
966
967#ifdef _XMP_SUPPORT_
968
969 if (!d->xmpMetadata().empty())
970 {
971 Exiv2::XmpData xmpData(d->xmpMetadata());
972 {
973 Exiv2::XmpKey key("Xmp.exif.DateTimeDigitized");
974 Exiv2::XmpData::iterator it = xmpData.findKey(key);
975
976 if (it != xmpData.end())
977 {
978 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
979
980 if (dateTime.isValid())
981 {
982 qCDebug(LIBKEXIV2_LOG) << "DateTime (XMP-Exif digitalized): " << dateTime.toString().toLatin1().constData();
983 return dateTime;
984 }
985 }
986 }
987 {
988 Exiv2::XmpKey key("Xmp.video.DateTimeDigitized");
989 Exiv2::XmpData::iterator it = xmpData.findKey(key);
990
991 if (it != xmpData.end())
992 {
993 QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
994
995 if (dateTime.isValid())
996 {
997 qCDebug(LIBKEXIV2_LOG) << "DateTime (XMP-Video digitalized): " << dateTime.toString().toLatin1().constData();
998 return dateTime;
999 }
1000 }
1001 }
1002 }
1003
1004#endif // _XMP_SUPPORT_
1005
1006 // In third, trying to get Date & time from Iptc tags.
1007
1008 if (!d->iptcMetadata().empty())
1009 {
1010 // Try digitization Iptc date time entries.
1011
1012 Exiv2::IptcData iptcData(d->iptcMetadata());
1013 Exiv2::IptcKey keyDigitizationDate("Iptc.Application2.DigitizationDate");
1014 Exiv2::IptcData::iterator it = iptcData.findKey(keyDigitizationDate);
1015
1016 if (it != iptcData.end())
1017 {
1018 QString IptcDateDigitization(QString::fromLatin1(it->toString().c_str()));
1019
1020 Exiv2::IptcKey keyDigitizationTime("Iptc.Application2.DigitizationTime");
1021 Exiv2::IptcData::iterator it2 = iptcData.findKey(keyDigitizationTime);
1022
1023 if (it2 != iptcData.end())
1024 {
1025 QString IptcTimeDigitization(QString::fromLatin1(it2->toString().c_str()));
1026
1027 QDate date = QDate::fromString(IptcDateDigitization, Qt::ISODate);
1028 QTime time = QTime::fromString(IptcTimeDigitization, Qt::ISODate);
1029 QDateTime dateTime = QDateTime(date, time);
1030
1031 if (dateTime.isValid())
1032 {
1033 qCDebug(LIBKEXIV2_LOG) << "Date (IPTC digitalized): " << dateTime.toString().toLatin1().constData();
1034 return dateTime;
1035 }
1036 }
1037 }
1038 }
1039 }
1040 catch( Exiv2::Error& e )
1041 {
1042 d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse Exif digitization date & time tag using Exiv2 "), e);
1043 }
1044 catch(...)
1045 {
1046 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1047 }
1048
1049 if (fallbackToCreationTime)
1050 return getImageDateTime();
1051 else
1052 return QDateTime();
1053}
1054
1056{
1057 try
1058 {
1059 // In first we trying to get from Iptc preview tag.
1060 if (preview.loadFromData(getIptcTagData("Iptc.Application2.Preview")) )
1061 return true;
1062
1063 // TODO : Added here Makernotes preview extraction when Exiv2 will be fixed for that.
1064 }
1065 catch( Exiv2::Error& e )
1066 {
1067 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get image preview using Exiv2 "), e);
1068 }
1069 catch(...)
1070 {
1071 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1072 }
1073
1074 return false;
1075}
1076
1077bool KExiv2::setImagePreview(const QImage& preview, bool setProgramName) const
1078{
1079 if (!setProgramId(setProgramName))
1080 return false;
1081
1082 if (preview.isNull())
1083 {
1084 removeIptcTag("Iptc.Application2.Preview");
1085 removeIptcTag("Iptc.Application2.PreviewFormat");
1086 removeIptcTag("Iptc.Application2.PreviewVersion");
1087 return true;
1088 }
1089
1090 try
1091 {
1092 QByteArray data;
1093 QBuffer buffer(&data);
1094 buffer.open(QIODevice::WriteOnly);
1095
1096 // A little bit compressed preview jpeg image to limit IPTC size.
1097 preview.save(&buffer, "JPEG");
1098 qCDebug(LIBKEXIV2_LOG) << "JPEG image preview size: (" << preview.width() << " x "
1099 << preview.height() << ") pixels - " << data.size() << " bytes";
1100
1101 Exiv2::DataValue val;
1102 val.read((Exiv2::byte *)data.data(), data.size());
1103 d->iptcMetadata()["Iptc.Application2.Preview"] = val;
1104
1105 // See https://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf Appendix A for details.
1106 d->iptcMetadata()["Iptc.Application2.PreviewFormat"] = 11; // JPEG
1107 d->iptcMetadata()["Iptc.Application2.PreviewVersion"] = 1;
1108
1109 return true;
1110 }
1111 catch( Exiv2::Error& e )
1112 {
1113 d->printExiv2ExceptionError(QString::fromLatin1("Cannot get image preview using Exiv2 "), e);
1114 }
1115 catch(...)
1116 {
1117 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1118 }
1119
1120 return false;
1121}
1122
1123} // NameSpace KExiv2Iface
QString getXmpTagString(const char *xmpTagName, bool escapeCR=true) const
Get a Xmp tag content like a string.
bool getExifTagLong(const char *exifTagName, long &val) const
Get an Exif tag content like a long value.
KExiv2::ImageOrientation getImageOrientation() const
Return the image orientation set in Exif metadata.
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::ImageColorWorkSpace getImageColorWorkSpace() const
Return the image color-space set in Exif metadata.
virtual bool setImagePreview(const QImage &preview, bool setProgramName=true) const
Set the Iptc preview image.
bool setImageColorWorkSpace(ImageColorWorkSpace workspace, bool setProgramName=true) const
Set the Exif color-space tag of image.
QString getExifTagString(const char *exifTagName, bool escapeCR=true) const
Get an Exif tags content like a string.
ImageOrientation
The image orientation values given by Exif metadata.
Definition kexiv2.h:86
bool getImagePreview(QImage &preview) const
Return a QImage copy of Iptc preview image.
QDateTime getDigitizationDateTime(bool fallbackToCreationTime=false) const
Return the digitization time stamp of the image.
ImageColorWorkSpace
The image color workspace values given by Exif metadata.
Definition kexiv2.h:76
bool setImageDimensions(const QSize &size, bool setProgramName=true) const
Set the size of image in pixels in Exif tags.
static QString version()
Return a string version of libkexiv2 release.
Definition kexiv2.cpp:140
QSize getImageDimensions() const
Return the size of image in pixels using Exif tags.
QVariant getXmpTagVariant(const char *xmpTagName, bool rationalAsListOfInts=true, bool stringEscapeCR=true) const
Get an Xmp tag content as a QVariant.
QDateTime getImageDateTime() const
Return the time stamp of image.
bool removeIptcTag(const char *iptcTagName, bool setProgramName=true) const
Remove the all instance of Iptc tags 'iptcTagName' from Iptc metadata.
QByteArray getIptcTagData(const char *iptcTagName) const
Get an Iptc tag content as a bytes array.
bool setImageOrientation(ImageOrientation orientation, bool setProgramName=true) const
Set the Exif orientation tag of image.
bool setImageProgramId(const QString &program, const QString &version) const
Set Program name and program version in Exif and Iptc Metadata.
bool setXmpTagString(const char *xmpTagName, const QString &value, bool setProgramName=true) const
Set a Xmp tag content using a string.
bool setImageDateTime(const QDateTime &dateTime, bool setDateTimeDigitized=false, bool setProgramName=true) const
Set the Exif and Iptc time stamp.
KExiv2::ImageOrientation exifOrientation() const
Returns the Exif orienation flag describing this matrix.
KExiv2Iface - Exiv2 library interface.
Definition kexiv2.cpp:17
virtual bool open(OpenMode flags) override
const char * constData() const const
char * data()
qsizetype size() const const
QDate fromString(QStringView string, QStringView format, QCalendar cal)
QString toString(QStringView format, QCalendar cal) const const
QDate date() const const
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool isValid() const const
QTime time() const const
QString toString(QStringView format, QCalendar cal) const const
int height() const const
bool isNull() const const
bool loadFromData(QByteArrayView data, const char *format)
bool save(QIODevice *device, const char *format, int quality) const const
int width() const const
int height() const const
int width() const const
QString & append(QChar ch)
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
bool isNull() const const
QString number(double n, char format, int precision)
int toInt(bool *ok, int base) const const
QByteArray toLatin1() const const
long toLong(bool *ok, int base) const const
QTime fromString(QStringView string, QStringView format)
QString toString(QStringView format) const const
bool isNull() const const
int toInt(bool *ok) 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.