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