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