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