KExiv2

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

KDE's Doxygen guidelines are available online.