Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_exif_io.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#include "kis_exif_io.h"
9
10#include <exiv2/error.hpp>
11#include <exiv2/exif.hpp>
12
13#include <QByteArray>
14#include <QDate>
15#include <QDateTime>
16#include <QIODevice>
17#include <QTextCodec>
18#include <QTime>
19#include <QVariant>
20#include <QtEndian>
21
22#include <kis_debug.h>
23#include <kis_exiv2_common.h>
24#include <kis_meta_data_entry.h>
27#include <kis_meta_data_store.h>
28#include <kis_meta_data_tags.h>
29#include <kis_meta_data_value.h>
30
31// ---- Exception conversion functions ---- //
32
33// convert ExifVersion and FlashpixVersion to a KisMetaData value
34#if EXIV2_TEST_VERSION(0,28,0)
35KisMetaData::Value exifVersionToKMDValue(const Exiv2::Value::UniquePtr value)
36#else
38#endif
39{
40 const Exiv2::DataValue *dvalue = dynamic_cast<const Exiv2::DataValue *>(&*value);
41 if (dvalue) {
42 Q_ASSERT(dvalue);
43 QByteArray array(dvalue->count(), 0);
44 dvalue->copy((Exiv2::byte *)array.data());
45 return KisMetaData::Value(QString(array));
46 } else {
47 Q_ASSERT(value->typeId() == Exiv2::asciiString);
48 return KisMetaData::Value(QString::fromLatin1(value->toString().c_str()));
49 }
50}
51
52// convert from KisMetaData value to ExifVersion and FlashpixVersion
54{
55 Exiv2::DataValue *dvalue = new Exiv2::DataValue;
56 QString ver = value.asVariant().toString();
57 dvalue->read((const Exiv2::byte *)ver.toLatin1().constData(), ver.size());
58 return dvalue;
59}
60
61// Convert an exif array of integer string to a KisMetaData array of integer
62#if EXIV2_TEST_VERSION(0,28,0)
63KisMetaData::Value exifArrayToKMDIntOrderedArray(const Exiv2::Value::UniquePtr value)
64#else
66#endif
67{
69 const Exiv2::DataValue *dvalue = dynamic_cast<const Exiv2::DataValue *>(&*value);
70 if (dvalue) {
71#if EXIV2_TEST_VERSION(0,28,0)
72 for (size_t i = 0; i < dvalue->count(); i++) {
73 v.push_back({(int)dvalue->toUint32(i)});
74#else
75 for (long i = 0; i < dvalue->count(); i++) {
76 v.push_back({(int)dvalue->toLong(i)});
77#endif
78 }
79 } else {
80 Q_ASSERT(value->typeId() == Exiv2::asciiString);
81 QString str = QString::fromLatin1(value->toString().c_str());
82 v.push_back(KisMetaData::Value(str.toInt()));
83 }
85}
86
87// Convert a KisMetaData array of integer to an exif array of integer string
89{
90 std::vector<Exiv2::byte> v;
91 for (const KisMetaData::Value &it : value.asArray()) {
92 v.push_back(static_cast<uint8_t>(it.asVariant().toInt(0)));
93 }
94 return new Exiv2::DataValue(v.data(), static_cast<long>(v.size()));
95}
96
97#if EXIV2_TEST_VERSION(0,28,0)
98QDateTime exivValueToDateTime(const Exiv2::Value::UniquePtr value)
99#else
100QDateTime exivValueToDateTime(const Exiv2::Value::AutoPtr value)
101#endif
102{
103 return QDateTime::fromString(value->toString().c_str(), Qt::ISODate);
104}
105
106template<typename T>
107inline T fixEndianness(T v, Exiv2::ByteOrder order)
108{
109 switch (order) {
110 case Exiv2::invalidByteOrder:
111 return v;
112 case Exiv2::littleEndian:
113 return qFromLittleEndian<T>(v);
114 case Exiv2::bigEndian:
115 return qFromBigEndian<T>(v);
116 }
117 warnKrita << "KisExifIO: unknown byte order";
118 return v;
119}
120
121Exiv2::ByteOrder invertByteOrder(Exiv2::ByteOrder order)
122{
123 switch (order) {
124 case Exiv2::littleEndian:
125 return Exiv2::bigEndian;
126 case Exiv2::bigEndian:
127 return Exiv2::littleEndian;
128 case Exiv2::invalidByteOrder:
129 warnKrita << "KisExifIO: Can't invert Exiv2::invalidByteOrder";
130 return Exiv2::invalidByteOrder;
131 }
132 return Exiv2::invalidByteOrder;
133}
134
135#if EXIV2_TEST_VERSION(0,28,0)
136KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::UniquePtr value, Exiv2::ByteOrder order)
137#else
138KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
139#endif
140{
141 QMap<QString, KisMetaData::Value> oecfStructure;
142 const Exiv2::DataValue *dvalue = dynamic_cast<const Exiv2::DataValue *>(&*value);
143 Q_ASSERT(dvalue);
144 QByteArray array(dvalue->count(), 0);
145
146 dvalue->copy((Exiv2::byte *)array.data());
147#if EXIV2_TEST_VERSION(0,28,0)
148 size_t columns = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[0], order);
149 size_t rows = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[1], order);
150#else
151 int columns = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[0], order);
152 int rows = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[1], order);
153#endif
154
155 if ((columns * rows + 4)
156 > dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library,
157 // or any library that doesn't save back with the same byte order as the camera)
158 order = invertByteOrder(order);
159#if EXIV2_TEST_VERSION(0,28,0)
160 columns = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[0], order);
161 rows = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[1], order);
162#else
163 columns = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[0], order);
164 rows = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[1], order);
165#endif
166 Q_ASSERT((columns * rows + 4) > dvalue->count());
167 }
168 QVariant qcolumns, qrows;
169 qcolumns.setValue(columns);
170 qrows.setValue(rows);
171 oecfStructure["Columns"] = KisMetaData::Value(qcolumns);
172 oecfStructure["Rows"] = KisMetaData::Value(qrows);
173 int index = 4;
175#if EXIV2_TEST_VERSION(0,28,0)
176 for (size_t i = 0; i < columns; i++) {
177#else
178 for (int i = 0; i < columns; i++) {
179#endif
180 int lastIndex = array.indexOf((char)0, index);
181 QString name = array.mid(index, lastIndex - index);
182 if (index != lastIndex) {
183 index = lastIndex + 1;
184 dbgMetaData << "Name [" << i << "] =" << name;
185 names.append(KisMetaData::Value(name));
186 } else {
187 names.append(KisMetaData::Value(""));
188 }
189 }
190
191 oecfStructure["Names"] = KisMetaData::Value(names, KisMetaData::Value::OrderedArray);
193 qint32 *dataIt = reinterpret_cast<qint32 *>(array.data() + index);
194#if EXIV2_TEST_VERSION(0,28,0)
195 for (size_t i = 0; i < columns; i++) {
196 for (size_t j = 0; j < rows; j++) {
197#else
198 for (int i = 0; i < columns; i++) {
199 for (int j = 0; j < rows; j++) {
200#endif
201 values.append(KisMetaData::Value(
202 KisMetaData::Rational(fixEndianness<qint32>(dataIt[0], order), fixEndianness<qint32>(dataIt[1], order))));
203 dataIt += 2;
204 }
205 }
206 oecfStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray);
207 dbgMetaData << "OECF: " << ppVar(columns) << ppVar(rows) << ppVar(dvalue->count());
208 return KisMetaData::Value(oecfStructure);
209}
210
212{
213 QMap<QString, KisMetaData::Value> oecfStructure = value.asStructure();
214 const quint16 columns = static_cast<quint16>(oecfStructure["Columns"].asVariant().toUInt());
215 const quint16 rows = static_cast<quint16>(oecfStructure["Rows"].asVariant().toUInt());
216
217 QList<KisMetaData::Value> names = oecfStructure["Names"].asArray();
218 QList<KisMetaData::Value> values = oecfStructure["Values"].asArray();
219 Q_ASSERT(columns * rows == values.size());
220 int length = 4 + rows * columns * 8; // The 4 byte for storing rows/columns and the rows*columns*sizeof(rational)
221 bool saveNames = (!names.empty() && names[0].asVariant().toString().size() > 0);
222 if (saveNames) {
223 for (int i = 0; i < columns; i++) {
224 length += names[i].asVariant().toString().size() + 1;
225 }
226 }
227 QByteArray array(length, 0);
228 (reinterpret_cast<quint16 *>(array.data()))[0] = columns;
229 (reinterpret_cast<quint16 *>(array.data()))[1] = rows;
230 int index = 4;
231 if (saveNames) {
232 for (int i = 0; i < columns; i++) {
233 QByteArray name = names[i].asVariant().toString().toLatin1();
234 name.append((char)0);
235 memcpy(array.data() + index, name.data(), static_cast<size_t>(name.size()));
236 index += name.size();
237 }
238 }
239 qint32 *dataIt = reinterpret_cast<qint32 *>(array.data() + index);
240 for (const KisMetaData::Value &it : values) {
241 dataIt[0] = it.asRational().numerator;
242 dataIt[1] = it.asRational().denominator;
243 dataIt += 2;
244 }
245 return new Exiv2::DataValue((const Exiv2::byte *)array.data(), array.size());
246}
247
248#if EXIV2_TEST_VERSION(0,28,0)
250#else
252#endif
253{
254 QMap<QString, KisMetaData::Value> deviceSettingStructure;
255 QByteArray array;
256
257 const Exiv2::DataValue *dvalue = dynamic_cast<const Exiv2::DataValue *>(&*value);
258 if (dvalue) {
259 array.resize(dvalue->count());
260 dvalue->copy((Exiv2::byte *)array.data());
261 } else {
262 Q_ASSERT(value->typeId() == Exiv2::unsignedShort);
263 array.resize(2 * value->count());
264 value->copy((Exiv2::byte *)array.data(), Exiv2::littleEndian);
265 }
266 int columns = (reinterpret_cast<quint16 *>(array.data()))[0];
267 int rows = (reinterpret_cast<quint16 *>(array.data()))[1];
268 deviceSettingStructure["Columns"] = KisMetaData::Value(columns);
269 deviceSettingStructure["Rows"] = KisMetaData::Value(rows);
271 QByteArray null(2, 0);
272
273 for (int index = 4; index < array.size();) {
274 const int lastIndex = array.indexOf(null, index);
275 if (lastIndex < 0)
276 break; // Data is not a String, ignore
277 const int numChars = (lastIndex - index) / 2; // including trailing zero
278
279 QString setting = QString::fromUtf16((ushort *)(void *)(array.data() + index), numChars);
280 index = lastIndex + 2;
281 dbgMetaData << "Setting << " << setting;
282 settings.append(KisMetaData::Value(setting));
283 }
284 deviceSettingStructure["Settings"] = KisMetaData::Value(settings, KisMetaData::Value::OrderedArray);
285 return KisMetaData::Value(deviceSettingStructure);
286}
287
289{
290 QMap<QString, KisMetaData::Value> deviceSettingStructure = value.asStructure();
291 const quint16 columns = static_cast<quint16>(deviceSettingStructure["Columns"].asVariant().toUInt());
292 quint16 rows = static_cast<quint16>(deviceSettingStructure["Rows"].asVariant().toUInt());
293
294 QTextCodec *codec = QTextCodec::codecForName("UTF-16");
295
296 QList<KisMetaData::Value> settings = deviceSettingStructure["Settings"].asArray();
297 QByteArray array(4, 0);
298 (reinterpret_cast<quint16 *>(array.data()))[0] = columns;
299 (reinterpret_cast<quint16 *>(array.data()))[1] = rows;
300 for (const KisMetaData::Value &v : settings) {
301 const QString str = v.asVariant().toString();
302 QByteArray setting = codec->fromUnicode(str);
303 array.append(setting);
304 }
305 return new Exiv2::DataValue((const Exiv2::byte *)array.data(), array.size());
306}
307
308#if EXIV2_TEST_VERSION(0,28,0)
309KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::UniquePtr value, Exiv2::ByteOrder order)
310#else
311KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
312#endif
313{
314 QMap<QString, KisMetaData::Value> cfaPatternStructure;
315 const Exiv2::DataValue *dvalue = dynamic_cast<const Exiv2::DataValue *>(&*value);
316 Q_ASSERT(dvalue);
317 QByteArray array(dvalue->count(), 0);
318 dvalue->copy((Exiv2::byte *)array.data());
319#if EXIV2_TEST_VERSION(0,28,0)
320 size_t columns = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[0], order);
321 size_t rows = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[1], order);
322#else
323 int columns = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[0], order);
324 int rows = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[1], order);
325#endif
326 if ((columns * rows + 4)
327 != dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library,
328 // or any library that doesn't save back with the same byte order as the camera)
329 order = invertByteOrder(order);
330 columns = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[0], order);
331 rows = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[1], order);
332 }
333 QVariant qcolumns, qrows;
334 qcolumns.setValue(columns);
335 qrows.setValue(rows);
336 cfaPatternStructure["Columns"] = KisMetaData::Value(qcolumns);
337 cfaPatternStructure["Rows"] = KisMetaData::Value(qrows);
339 int index = 4;
340 for (int i = 0; i < columns * rows; i++) {
341 values.append(KisMetaData::Value(*(array.data() + index)));
342 index++;
343 }
344 cfaPatternStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray);
345 dbgMetaData << "CFAPattern " << ppVar(columns) << " " << ppVar(rows) << ppVar(values.size())
346 << ppVar(dvalue->count());
347 return KisMetaData::Value(cfaPatternStructure);
348}
349
351{
352 QMap<QString, KisMetaData::Value> cfaStructure = value.asStructure();
353 const quint16 columns = static_cast<quint16>(cfaStructure["Columns"].asVariant().toUInt());
354 const quint16 rows = static_cast<quint16>(cfaStructure["Rows"].asVariant().toUInt());
355
356 QList<KisMetaData::Value> values = cfaStructure["Values"].asArray();
357 Q_ASSERT(columns * rows == values.size());
358 QByteArray array(4 + columns * rows, 0);
359 (reinterpret_cast<quint16 *>(array.data()))[0] = columns;
360 (reinterpret_cast<quint16 *>(array.data()))[1] = rows;
361 for (int i = 0; i < columns * rows; i++) {
362 const quint8 val = (quint8)values[i].asVariant().toUInt();
363 *(array.data() + 4 + i) = (char)val;
364 }
365 dbgMetaData << "Cfa Array " << ppVar(columns) << ppVar(rows) << ppVar(array.size());
366 return new Exiv2::DataValue((const Exiv2::byte *)array.data(), array.size());
367}
368
369// Read and write Flash //
370
371#if EXIV2_TEST_VERSION(0,28,0)
372KisMetaData::Value flashExifToKMD(const Exiv2::Value::UniquePtr value)
373#else
374KisMetaData::Value flashExifToKMD(const Exiv2::Value::AutoPtr value)
375#endif
376{
377#if EXIV2_TEST_VERSION(0,28,0)
378 const uint16_t v = static_cast<uint16_t>(value->toUint32());
379#else
380 const uint16_t v = static_cast<uint16_t>(value->toLong());
381#endif
382 QMap<QString, KisMetaData::Value> flashStructure;
383 bool fired = (v & 0x01); // bit 1 is whether flash was fired or not
384 flashStructure["Fired"] = QVariant(fired);
385 int ret = ((v >> 1) & 0x03); // bit 2 and 3 are Return
386 flashStructure["Return"] = QVariant(ret);
387 int mode = ((v >> 3) & 0x03); // bit 4 and 5 are Mode
388 flashStructure["Mode"] = QVariant(mode);
389 bool function = ((v >> 5) & 0x01); // bit 6 if function
390 flashStructure["Function"] = QVariant(function);
391 bool redEye = ((v >> 6) & 0x01); // bit 7 if function
392 flashStructure["RedEyeMode"] = QVariant(redEye);
393 return KisMetaData::Value(flashStructure);
394}
395
397{
398 uint16_t v = 0;
399 QMap<QString, KisMetaData::Value> flashStructure = value.asStructure();
400 v = flashStructure["Fired"].asVariant().toBool();
401 v |= ((flashStructure["Return"].asVariant().toInt() & 0x03) << 1);
402 v |= ((flashStructure["Mode"].asVariant().toInt() & 0x03) << 3);
403 v |= ((flashStructure["Function"].asVariant().toInt() & 0x03) << 5);
404 v |= ((flashStructure["RedEyeMode"].asVariant().toInt() & 0x03) << 6);
405 return new Exiv2::ValueType<uint16_t>(v);
406}
407
408// ---- Implementation of KisExifIO ----//
410 : KisMetaData::IOBackend()
411{
412}
413
417
418bool KisExifIO::saveTo(const KisMetaData::Store *store, QIODevice *ioDevice, HeaderType headerType) const
419{
420 ioDevice->open(QIODevice::WriteOnly);
421 Exiv2::ExifData exifData;
422 if (headerType == KisMetaData::IOBackend::JpegHeader) {
423 QByteArray header(6, 0);
424 header[0] = 0x45;
425 header[1] = 0x78;
426 header[2] = 0x69;
427 header[3] = 0x66;
428 header[4] = 0x00;
429 header[5] = 0x00;
430 ioDevice->write(header);
431 }
432
433 for (const KisMetaData::Entry &entry : *store) {
434 try {
435 dbgMetaData << "Trying to save: " << entry.name() << " of " << entry.schema()->prefix() << ":"
436 << entry.schema()->uri();
437 QString exivKey;
438 if (entry.schema()->uri() == KisMetaData::Schema::TIFFSchemaUri) {
439 exivKey = "Exif.Image." + entry.name();
440 } else if (entry.schema()->uri()
441 == KisMetaData::Schema::EXIFSchemaUri) { // Distinguish between exif and gps
442 if (entry.name().left(3) == "GPS") {
443 exivKey = "Exif.GPSInfo." + entry.name();
444 } else {
445 exivKey = "Exif.Photo." + entry.name();
446 }
447 } else if (entry.schema()->uri() == KisMetaData::Schema::DublinCoreSchemaUri) {
448 if (entry.name() == "description") {
449 exivKey = "Exif.Image.ImageDescription";
450 } else if (entry.name() == "creator") {
451 exivKey = "Exif.Image.Artist";
452 } else if (entry.name() == "rights") {
453 exivKey = "Exif.Image.Copyright";
454 }
455 } else if (entry.schema()->uri() == KisMetaData::Schema::XMPSchemaUri) {
456 if (entry.name() == "ModifyDate") {
457 exivKey = "Exif.Image.DateTime";
458 } else if (entry.name() == "CreatorTool") {
459 exivKey = "Exif.Image.Software";
460 }
461 } else if (entry.schema()->uri() == KisMetaData::Schema::MakerNoteSchemaUri) {
462 if (entry.name() == "RawData") {
463 exivKey = "Exif.Photo.MakerNote";
464 }
465 }
466 dbgMetaData << "Saving " << entry.name() << " to " << exivKey;
467 if (exivKey.isEmpty()) {
468 dbgMetaData << entry.qualifiedName() << " is unsavable to EXIF";
469 } else {
470 Exiv2::ExifKey exifKey(qPrintable(exivKey));
471 Exiv2::Value *v = 0;
472 if (exivKey == "Exif.Photo.ExifVersion" || exivKey == "Exif.Photo.FlashpixVersion") {
473 v = kmdValueToExifVersion(entry.value());
474 } else if (exivKey == "Exif.Photo.FileSource") {
475 char s[] = {0x03};
476 v = new Exiv2::DataValue((const Exiv2::byte *)s, 1);
477 } else if (exivKey == "Exif.Photo.SceneType") {
478 char s[] = {0x01};
479 v = new Exiv2::DataValue((const Exiv2::byte *)s, 1);
480 } else if (exivKey == "Exif.Photo.ComponentsConfiguration") {
481 v = kmdIntOrderedArrayToExifArray(entry.value());
482 } else if (exivKey == "Exif.Image.Artist") { // load as dc:creator
483 KisMetaData::Value creator = entry.value();
484 if (entry.value().asArray().size() > 0) {
485 creator = entry.value().asArray()[0];
486 }
487#if !EXIV2_TEST_VERSION(0, 21, 0)
488 v = kmdValueToExivValue(creator, Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
489#else
490 v = kmdValueToExivValue(creator, exifKey.defaultTypeId());
491#endif
492 } else if (exivKey == "Exif.Photo.OECF") {
493 v = kmdOECFStructureToExifOECF(entry.value());
494 } else if (exivKey == "Exif.Photo.DeviceSettingDescription") {
495 v = deviceSettingDescriptionKMDToExif(entry.value());
496 } else if (exivKey == "Exif.Photo.CFAPattern") {
497 v = cfaPatternKMDToExif(entry.value());
498 } else if (exivKey == "Exif.Photo.Flash") {
499 v = flashKMDToExif(entry.value());
500 } else if (exivKey == "Exif.Photo.UserComment") {
501 Q_ASSERT(entry.value().type() == KisMetaData::Value::LangArray);
502 QMap<QString, KisMetaData::Value> langArr = entry.value().asLangArray();
503 if (langArr.contains("x-default")) {
504#if !EXIV2_TEST_VERSION(0, 21, 0)
505 v = kmdValueToExivValue(langArr.value("x-default"),
506 Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
507#else
508 v = kmdValueToExivValue(langArr.value("x-default"), exifKey.defaultTypeId());
509#endif
510 } else if (langArr.size() > 0) {
511#if !EXIV2_TEST_VERSION(0, 21, 0)
512 v = kmdValueToExivValue(langArr.begin().value(),
513 Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
514#else
515 v = kmdValueToExivValue(langArr.begin().value(), exifKey.defaultTypeId());
516#endif
517 }
518 } else {
519 dbgMetaData << exifKey.tag();
520#if !EXIV2_TEST_VERSION(0, 21, 0)
521 v = kmdValueToExivValue(entry.value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
522#else
523 v = kmdValueToExivValue(entry.value(), exifKey.defaultTypeId());
524#endif
525 }
526 if (v && v->typeId() != Exiv2::invalidTypeId) {
527 dbgMetaData << "Saving key" << exivKey << " of KMD value" << entry.value();
528 exifData.add(exifKey, v);
529 } else {
530 dbgMetaData << "No exif value was created for" << entry.qualifiedName() << " as"
531 << exivKey; // << " of KMD value" << entry.value();
532 }
533 }
534#if EXIV2_TEST_VERSION(0,28,0)
535 } catch (Exiv2::Error &e) {
536#else
537 } catch (Exiv2::AnyError &e) {
538#endif
539 dbgMetaData << "exiv error " << e.what();
540 }
541 }
542#if !EXIV2_TEST_VERSION(0, 18, 0)
543 Exiv2::DataBuf rawData = exifData.copy();
544 ioDevice->write((const char *)rawData.pData_, rawData.size_);
545#else
546 Exiv2::Blob rawData;
547 Exiv2::ExifParser::encode(rawData, Exiv2::littleEndian, exifData);
548 ioDevice->write((const char *)&*rawData.begin(), static_cast<int>(rawData.size()));
549#endif
550 ioDevice->close();
551 return true;
552}
553
555{
556 return false; // It's a known fact that exif can't save all information, but TODO: write the check
557}
558
559bool KisExifIO::loadFrom(KisMetaData::Store *store, QIODevice *ioDevice) const
560{
561 if (!ioDevice->open(QIODevice::ReadOnly)) {
562 return false;
563 }
564 QByteArray arr(ioDevice->readAll());
565 Exiv2::ExifData exifData;
566 Exiv2::ByteOrder byteOrder;
567#if !EXIV2_TEST_VERSION(0, 18, 0)
568 exifData.load((const Exiv2::byte *)arr.data(), arr.size());
569 byteOrder = exifData.byteOrder();
570#else
571 try {
572 byteOrder =
573 Exiv2::ExifParser::decode(exifData, (const Exiv2::byte *)arr.data(), static_cast<uint32_t>(arr.size()));
574 } catch (const std::exception &ex) {
575 warnKrita << "Received exception trying to parse exiv data" << ex.what();
576 return false;
577 } catch (...) {
578 dbgKrita << "Received unknown exception trying to parse exiv data";
579 return false;
580 }
581#endif
582 dbgMetaData << "Byte order = " << byteOrder << ppVar(Exiv2::bigEndian) << ppVar(Exiv2::littleEndian);
583 dbgMetaData << "There are" << exifData.count() << " entries in the exif section";
584 const KisMetaData::Schema *tiffSchema =
586 Q_ASSERT(tiffSchema);
587 const KisMetaData::Schema *exifSchema =
589 Q_ASSERT(exifSchema);
590 const KisMetaData::Schema *dcSchema =
592 Q_ASSERT(dcSchema);
593 const KisMetaData::Schema *xmpSchema =
595 Q_ASSERT(xmpSchema);
596 const KisMetaData::Schema *makerNoteSchema =
598 Q_ASSERT(makerNoteSchema);
599
600 for (const Exiv2::Exifdatum &it : exifData) {
601 const uint16_t tag = it.tag();
602
605 || it.tagName() == "0x0000") {
606 dbgMetaData << it.key().c_str() << " is ignored";
607 } else if (tag == Exif::Photo::MakerNote) {
608 store->addEntry({makerNoteSchema, "RawData", exivValueToKMDValue(it.getValue(), false)});
609 } else if (tag == Exif::Image::DateTime) { // load as xmp:ModifyDate
610 store->addEntry({xmpSchema, "ModifyDate", exivValueToKMDValue(it.getValue(), false)});
611 } else if (tag == Exif::Image::ImageDescription) { // load as "dc:description"
612 store->addEntry({dcSchema, "description", exivValueToKMDValue(it.getValue(), false)});
613 } else if (tag == Exif::Image::Software) { // load as "xmp:CreatorTool"
614 store->addEntry({xmpSchema, "CreatorTool", exivValueToKMDValue(it.getValue(), false)});
615 } else if (tag == Exif::Image::Artist) { // load as dc:creator
616 QList<KisMetaData::Value> creators = {exivValueToKMDValue(it.getValue(), false)};
617 store->addEntry({dcSchema, "creator", {creators, KisMetaData::Value::OrderedArray}});
618 } else if (tag == Exif::Image::Copyright) { // load as dc:rights
619 store->addEntry({dcSchema, "rights", exivValueToKMDValue(it.getValue(), false)});
620 } else if (it.groupName() == "Image") {
621 // Tiff tags
622 const QString fixedTN(it.tagName().c_str());
623 if (tag == Exif::Image::ExifTag || tag == Exif::Image::GPSTag) {
624 dbgMetaData << "Ignoring " << it.key().c_str();
625 } else if (KisMetaData::Entry::isValidName(fixedTN)) {
626 store->addEntry({tiffSchema, fixedTN, exivValueToKMDValue(it.getValue(), false)});
627 } else {
628 dbgMetaData << "Invalid tag name: " << fixedTN;
629 }
630 } else if (it.groupName() == "Photo") {
631 // Exif tags
632 KisMetaData::Value metaDataValue;
634 metaDataValue = exifVersionToKMDValue(it.getValue());
635 } else if (tag == Exif::Photo::FileSource) {
636 metaDataValue = KisMetaData::Value(3);
637 } else if (tag == Exif::Photo::SceneType) {
638 metaDataValue = KisMetaData::Value(1);
639 } else if (tag == Exif::Photo::ComponentsConfiguration) {
640 metaDataValue = exifArrayToKMDIntOrderedArray(it.getValue());
641 } else if (tag == Exif::Photo::OECF) {
642 metaDataValue = exifOECFToKMDOECFStructure(it.getValue(), byteOrder);
644 metaDataValue = exivValueToKMDValue(it.getValue(), false);
645 } else if (tag == Exif::Photo::DeviceSettingDescription) {
646 metaDataValue = deviceSettingDescriptionExifToKMD(it.getValue());
647 } else if (tag == Exif::Photo::CFAPattern) {
648 metaDataValue = cfaPatternExifToKMD(it.getValue(), byteOrder);
649 } else if (tag == Exif::Photo::Flash) {
650 metaDataValue = flashExifToKMD(it.getValue());
651 } else if (tag == Exif::Photo::UserComment) {
652 if (it.getValue()->typeId() != Exiv2::undefined) {
653 KisMetaData::Value vUC = exivValueToKMDValue(it.getValue(), false);
654 Q_ASSERT(vUC.type() == KisMetaData::Value::Variant);
655 QVariant commentVar = vUC.asVariant();
656 QString comment;
657 if (commentVar.type() == QVariant::String) {
658 comment = commentVar.toString();
659 } else if (commentVar.type() == QVariant::ByteArray) {
660 const QByteArray commentString = commentVar.toByteArray();
661 comment = QString::fromLatin1(commentString.constData(), commentString.size());
662 } else {
663 warnKrita << "KisExifIO: Unhandled UserComment value type.";
664 }
665 KisMetaData::Value vcomment(comment);
666 vcomment.addPropertyQualifier("xml:lang", KisMetaData::Value("x-default"));
668 alt.append(vcomment);
670 }
671 } else {
672 bool forceSeq = false;
674 if (tag == Exif::Photo::ISOSpeedRatings) {
675 forceSeq = true;
677 }
678 metaDataValue = exivValueToKMDValue(it.getValue(), forceSeq, arrayType);
679 }
680 if (tag == Exif::Photo::InteroperabilityTag || tag == 0xea1d
681 || metaDataValue.type() == KisMetaData::Value::Invalid) { // InteroperabilityTag isn't useful for XMP,
682 // 0xea1d isn't a valid Exif tag
683 warnMetaData << "Ignoring " << it.key().c_str();
684
685 } else {
686 store->addEntry({exifSchema, it.tagName().c_str(), metaDataValue});
687 }
688 } else if (it.groupName() == "Thumbnail") {
689 dbgMetaData << "Ignoring thumbnail tag :" << it.key().c_str();
690 } else if (it.groupName() == "GPSInfo") {
691 store->addEntry({exifSchema, it.tagName().c_str(), exivValueToKMDValue(it.getValue(), false)});
692 } else {
693 dbgMetaData << "Unknown exif tag, cannot load:" << it.key().c_str();
694 }
695 }
696 ioDevice->close();
697 return true;
698}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
float value(const T *src, size_t ch)
qreal v
bool saveTo(const KisMetaData::Store *store, QIODevice *ioDevice, HeaderType headerType=NoHeader) const override
bool loadFrom(KisMetaData::Store *store, QIODevice *ioDevice) const override
bool canSaveAllEntries(KisMetaData::Store *store) const override
~KisExifIO() override
static bool isValidName(const QString &_name)
@ JpegHeader
Append Jpeg-style header.
static KisMetaData::SchemaRegistry * instance()
const Schema * schemaFromUri(const QString &uri) const
static const QString TIFFSchemaUri
static const QString DublinCoreSchemaUri
static const QString EXIFSchemaUri
static const QString XMPSchemaUri
static const QString MakerNoteSchemaUri
bool addEntry(const Entry &entry)
QList< KisMetaData::Value > asArray() const
QVariant asVariant() const
ValueType
Define the possible value type.
void addPropertyQualifier(const QString &_name, const Value &)
ValueType type() const
#define dbgKrita
Definition kis_debug.h:45
#define warnMetaData
Definition kis_debug.h:103
#define warnKrita
Definition kis_debug.h:87
#define ppVar(var)
Definition kis_debug.h:155
#define dbgMetaData
Definition kis_debug.h:61
Exiv2::Value * deviceSettingDescriptionKMDToExif(const KisMetaData::Value &value)
Exiv2::Value * kmdIntOrderedArrayToExifArray(const KisMetaData::Value &value)
Exiv2::Value * cfaPatternKMDToExif(const KisMetaData::Value &value)
KisMetaData::Value deviceSettingDescriptionExifToKMD(const Exiv2::Value::AutoPtr value)
T fixEndianness(T v, Exiv2::ByteOrder order)
KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
Exiv2::Value * flashKMDToExif(const KisMetaData::Value &value)
Exiv2::Value * kmdOECFStructureToExifOECF(const KisMetaData::Value &value)
Exiv2::ByteOrder invertByteOrder(Exiv2::ByteOrder order)
KisMetaData::Value exifArrayToKMDIntOrderedArray(const Exiv2::Value::AutoPtr value)
Exiv2::Value * kmdValueToExifVersion(const KisMetaData::Value &value)
QDateTime exivValueToDateTime(const Exiv2::Value::AutoPtr value)
KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
KisMetaData::Value flashExifToKMD(const Exiv2::Value::AutoPtr value)
KisMetaData::Value exifVersionToKMDValue(const Exiv2::Value::AutoPtr value)
Exiv2::Value * kmdValueToExivValue(const KisMetaData::Value &value, Exiv2::TypeId type)
Convert a KisMetaData to an Exiv value.
KisMetaData::Value exivValueToKMDValue(const Exiv2::Value::AutoPtr &value, bool forceSeq, KisMetaData::Value::ValueType arrayType=KisMetaData::Value::UnorderedArray)
const uint16_t DateTime
const uint16_t StripByteCounts
const uint16_t RowsPerStrip
const uint16_t StripOffsets
const uint16_t JPEGInterchangeFormat
const uint16_t ImageDescription
const uint16_t GPSTag
const uint16_t ExifTag
const uint16_t JPEGInterchangeFormatLength
const uint16_t Copyright
const uint16_t Artist
const uint16_t Software
const uint16_t DateTimeDigitized
const uint16_t UserComment
const uint16_t DeviceSettingDescription
const uint16_t ComponentsConfiguration
const uint16_t FileSource
const uint16_t FlashpixVersion
const uint16_t SceneType
const uint16_t Flash
const uint16_t CFAPattern
const uint16_t ISOSpeedRatings
const uint16_t InteroperabilityTag
const uint16_t ExifVersion
const uint16_t MakerNote
const uint16_t DateTimeOriginal
const uint16_t OECF