File indexing completed on 2024-12-22 04:16:02

0001 /*
0002  *  SPDX-FileCopyrightText: 2007 Cyrille Berger <cberger@cberger.net>
0003  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "kis_exif_io.h"
0009 
0010 #include <exiv2/error.hpp>
0011 #include <exiv2/exif.hpp>
0012 
0013 #include <QByteArray>
0014 #include <QDate>
0015 #include <QDateTime>
0016 #include <QIODevice>
0017 #include <QTextCodec>
0018 #include <QTime>
0019 #include <QVariant>
0020 #include <QtEndian>
0021 
0022 #include <kis_debug.h>
0023 #include <kis_exiv2_common.h>
0024 #include <kis_meta_data_entry.h>
0025 #include <kis_meta_data_schema.h>
0026 #include <kis_meta_data_schema_registry.h>
0027 #include <kis_meta_data_store.h>
0028 #include <kis_meta_data_tags.h>
0029 #include <kis_meta_data_value.h>
0030 
0031 // ---- Exception conversion functions ---- //
0032 
0033 // convert ExifVersion and FlashpixVersion to a KisMetaData value
0034 #if EXIV2_TEST_VERSION(0,28,0)
0035 KisMetaData::Value exifVersionToKMDValue(const Exiv2::Value::UniquePtr value)
0036 #else
0037 KisMetaData::Value exifVersionToKMDValue(const Exiv2::Value::AutoPtr value)
0038 #endif
0039 {
0040     const Exiv2::DataValue *dvalue = dynamic_cast<const Exiv2::DataValue *>(&*value);
0041     if (dvalue) {
0042         Q_ASSERT(dvalue);
0043         QByteArray array(dvalue->count(), 0);
0044         dvalue->copy((Exiv2::byte *)array.data());
0045         return KisMetaData::Value(QString(array));
0046     } else {
0047         Q_ASSERT(value->typeId() == Exiv2::asciiString);
0048         return KisMetaData::Value(QString::fromLatin1(value->toString().c_str()));
0049     }
0050 }
0051 
0052 // convert from KisMetaData value to ExifVersion and FlashpixVersion
0053 Exiv2::Value *kmdValueToExifVersion(const KisMetaData::Value &value)
0054 {
0055     Exiv2::DataValue *dvalue = new Exiv2::DataValue;
0056     QString ver = value.asVariant().toString();
0057     dvalue->read((const Exiv2::byte *)ver.toLatin1().constData(), ver.size());
0058     return dvalue;
0059 }
0060 
0061 // Convert an exif array of integer string to a KisMetaData array of integer
0062 #if EXIV2_TEST_VERSION(0,28,0)
0063 KisMetaData::Value exifArrayToKMDIntOrderedArray(const Exiv2::Value::UniquePtr value)
0064 #else
0065 KisMetaData::Value exifArrayToKMDIntOrderedArray(const Exiv2::Value::AutoPtr value)
0066 #endif
0067 {
0068     QList<KisMetaData::Value> v;
0069     const Exiv2::DataValue *dvalue = dynamic_cast<const Exiv2::DataValue *>(&*value);
0070     if (dvalue) {
0071 #if EXIV2_TEST_VERSION(0,28,0)
0072         for (size_t i = 0; i < dvalue->count(); i++) {
0073             v.push_back({(int)dvalue->toUint32(i)});
0074 #else
0075         for (long i = 0; i < dvalue->count(); i++) {
0076             v.push_back({(int)dvalue->toLong(i)});
0077 #endif
0078         }
0079     } else {
0080         Q_ASSERT(value->typeId() == Exiv2::asciiString);
0081         QString str = QString::fromLatin1(value->toString().c_str());
0082         v.push_back(KisMetaData::Value(str.toInt()));
0083     }
0084     return KisMetaData::Value(v, KisMetaData::Value::OrderedArray);
0085 }
0086 
0087 // Convert a KisMetaData array of integer to an exif array of integer string
0088 Exiv2::Value *kmdIntOrderedArrayToExifArray(const KisMetaData::Value &value)
0089 {
0090     std::vector<Exiv2::byte> v;
0091     for (const KisMetaData::Value &it : value.asArray()) {
0092         v.push_back(static_cast<uint8_t>(it.asVariant().toInt(0)));
0093     }
0094     return new Exiv2::DataValue(v.data(), static_cast<long>(v.size()));
0095 }
0096 
0097 #if EXIV2_TEST_VERSION(0,28,0)
0098 QDateTime exivValueToDateTime(const Exiv2::Value::UniquePtr value)
0099 #else
0100 QDateTime exivValueToDateTime(const Exiv2::Value::AutoPtr value)
0101 #endif
0102 {
0103     return QDateTime::fromString(value->toString().c_str(), Qt::ISODate);
0104 }
0105 
0106 template<typename T>
0107 inline T fixEndianness(T v, Exiv2::ByteOrder order)
0108 {
0109     switch (order) {
0110     case Exiv2::invalidByteOrder:
0111         return v;
0112     case Exiv2::littleEndian:
0113         return qFromLittleEndian<T>(v);
0114     case Exiv2::bigEndian:
0115         return qFromBigEndian<T>(v);
0116     }
0117     warnKrita << "KisExifIO: unknown byte order";
0118     return v;
0119 }
0120 
0121 Exiv2::ByteOrder invertByteOrder(Exiv2::ByteOrder order)
0122 {
0123     switch (order) {
0124     case Exiv2::littleEndian:
0125         return Exiv2::bigEndian;
0126     case Exiv2::bigEndian:
0127         return Exiv2::littleEndian;
0128     case Exiv2::invalidByteOrder:
0129         warnKrita << "KisExifIO: Can't invert Exiv2::invalidByteOrder";
0130         return Exiv2::invalidByteOrder;
0131     }
0132     return Exiv2::invalidByteOrder;
0133 }
0134 
0135 #if EXIV2_TEST_VERSION(0,28,0)
0136 KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::UniquePtr value, Exiv2::ByteOrder order)
0137 #else
0138 KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
0139 #endif
0140 {
0141     QMap<QString, KisMetaData::Value> oecfStructure;
0142     const Exiv2::DataValue *dvalue = dynamic_cast<const Exiv2::DataValue *>(&*value);
0143     Q_ASSERT(dvalue);
0144     QByteArray array(dvalue->count(), 0);
0145 
0146     dvalue->copy((Exiv2::byte *)array.data());
0147 #if EXIV2_TEST_VERSION(0,28,0)
0148     size_t columns = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[0], order);
0149     size_t rows = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[1], order);
0150 #else
0151     int columns = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[0], order);
0152     int rows = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[1], order);
0153 #endif
0154 
0155     if ((columns * rows + 4)
0156         > dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library,
0157                              // or any library that doesn't save back with the same byte order as the camera)
0158         order = invertByteOrder(order);
0159 #if EXIV2_TEST_VERSION(0,28,0)
0160         columns = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[0], order);
0161         rows = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[1], order);
0162 #else
0163         columns = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[0], order);
0164         rows = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[1], order);
0165 #endif
0166         Q_ASSERT((columns * rows + 4) > dvalue->count());
0167     }
0168     QVariant qcolumns, qrows;
0169     qcolumns.setValue(columns);
0170     qrows.setValue(rows);
0171     oecfStructure["Columns"] = KisMetaData::Value(qcolumns);
0172     oecfStructure["Rows"] = KisMetaData::Value(qrows);
0173     int index = 4;
0174     QList<KisMetaData::Value> names;
0175 #if EXIV2_TEST_VERSION(0,28,0)
0176     for (size_t i = 0; i < columns; i++) {
0177 #else
0178     for (int i = 0; i < columns; i++) {
0179 #endif
0180         int lastIndex = array.indexOf((char)0, index);
0181         QString name = array.mid(index, lastIndex - index);
0182         if (index != lastIndex) {
0183             index = lastIndex + 1;
0184             dbgMetaData << "Name [" << i << "] =" << name;
0185             names.append(KisMetaData::Value(name));
0186         } else {
0187             names.append(KisMetaData::Value(""));
0188         }
0189     }
0190 
0191     oecfStructure["Names"] = KisMetaData::Value(names, KisMetaData::Value::OrderedArray);
0192     QList<KisMetaData::Value> values;
0193     qint32 *dataIt = reinterpret_cast<qint32 *>(array.data() + index);
0194 #if EXIV2_TEST_VERSION(0,28,0)
0195     for (size_t i = 0; i < columns; i++) {
0196         for (size_t j = 0; j < rows; j++) {
0197 #else
0198     for (int i = 0; i < columns; i++) {
0199         for (int j = 0; j < rows; j++) {
0200 #endif
0201             values.append(KisMetaData::Value(
0202                 KisMetaData::Rational(fixEndianness<qint32>(dataIt[0], order), fixEndianness<qint32>(dataIt[1], order))));
0203             dataIt += 2;
0204         }
0205     }
0206     oecfStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray);
0207     dbgMetaData << "OECF: " << ppVar(columns) << ppVar(rows) << ppVar(dvalue->count());
0208     return KisMetaData::Value(oecfStructure);
0209 }
0210 
0211 Exiv2::Value *kmdOECFStructureToExifOECF(const KisMetaData::Value &value)
0212 {
0213     QMap<QString, KisMetaData::Value> oecfStructure = value.asStructure();
0214     const quint16 columns = static_cast<quint16>(oecfStructure["Columns"].asVariant().toUInt());
0215     const quint16 rows = static_cast<quint16>(oecfStructure["Rows"].asVariant().toUInt());
0216 
0217     QList<KisMetaData::Value> names = oecfStructure["Names"].asArray();
0218     QList<KisMetaData::Value> values = oecfStructure["Values"].asArray();
0219     Q_ASSERT(columns * rows == values.size());
0220     int length = 4 + rows * columns * 8; // The 4 byte for storing rows/columns and the rows*columns*sizeof(rational)
0221     bool saveNames = (!names.empty() && names[0].asVariant().toString().size() > 0);
0222     if (saveNames) {
0223         for (int i = 0; i < columns; i++) {
0224             length += names[i].asVariant().toString().size() + 1;
0225         }
0226     }
0227     QByteArray array(length, 0);
0228     (reinterpret_cast<quint16 *>(array.data()))[0] = columns;
0229     (reinterpret_cast<quint16 *>(array.data()))[1] = rows;
0230     int index = 4;
0231     if (saveNames) {
0232         for (int i = 0; i < columns; i++) {
0233             QByteArray name = names[i].asVariant().toString().toLatin1();
0234             name.append((char)0);
0235             memcpy(array.data() + index, name.data(), static_cast<size_t>(name.size()));
0236             index += name.size();
0237         }
0238     }
0239     qint32 *dataIt = reinterpret_cast<qint32 *>(array.data() + index);
0240     for (const KisMetaData::Value &it : values) {
0241         dataIt[0] = it.asRational().numerator;
0242         dataIt[1] = it.asRational().denominator;
0243         dataIt += 2;
0244     }
0245     return new Exiv2::DataValue((const Exiv2::byte *)array.data(), array.size());
0246 }
0247 
0248 #if EXIV2_TEST_VERSION(0,28,0)
0249 KisMetaData::Value deviceSettingDescriptionExifToKMD(const Exiv2::Value::UniquePtr value)
0250 #else
0251 KisMetaData::Value deviceSettingDescriptionExifToKMD(const Exiv2::Value::AutoPtr value)
0252 #endif
0253 {
0254     QMap<QString, KisMetaData::Value> deviceSettingStructure;
0255     QByteArray array;
0256 
0257     const Exiv2::DataValue *dvalue = dynamic_cast<const Exiv2::DataValue *>(&*value);
0258     if (dvalue) {
0259         array.resize(dvalue->count());
0260         dvalue->copy((Exiv2::byte *)array.data());
0261     } else {
0262         Q_ASSERT(value->typeId() == Exiv2::unsignedShort);
0263         array.resize(2 * value->count());
0264         value->copy((Exiv2::byte *)array.data(), Exiv2::littleEndian);
0265     }
0266     int columns = (reinterpret_cast<quint16 *>(array.data()))[0];
0267     int rows = (reinterpret_cast<quint16 *>(array.data()))[1];
0268     deviceSettingStructure["Columns"] = KisMetaData::Value(columns);
0269     deviceSettingStructure["Rows"] = KisMetaData::Value(rows);
0270     QList<KisMetaData::Value> settings;
0271     QByteArray null(2, 0);
0272 
0273     for (int index = 4; index < array.size();) {
0274         const int lastIndex = array.indexOf(null, index);
0275         if (lastIndex < 0)
0276             break; // Data is not a String, ignore
0277         const int numChars = (lastIndex - index) / 2; // including trailing zero
0278 
0279         QString setting = QString::fromUtf16((ushort *)(void *)(array.data() + index), numChars);
0280         index = lastIndex + 2;
0281         dbgMetaData << "Setting << " << setting;
0282         settings.append(KisMetaData::Value(setting));
0283     }
0284     deviceSettingStructure["Settings"] = KisMetaData::Value(settings, KisMetaData::Value::OrderedArray);
0285     return KisMetaData::Value(deviceSettingStructure);
0286 }
0287 
0288 Exiv2::Value *deviceSettingDescriptionKMDToExif(const KisMetaData::Value &value)
0289 {
0290     QMap<QString, KisMetaData::Value> deviceSettingStructure = value.asStructure();
0291     const quint16 columns = static_cast<quint16>(deviceSettingStructure["Columns"].asVariant().toUInt());
0292     quint16 rows = static_cast<quint16>(deviceSettingStructure["Rows"].asVariant().toUInt());
0293 
0294     QTextCodec *codec = QTextCodec::codecForName("UTF-16");
0295 
0296     QList<KisMetaData::Value> settings = deviceSettingStructure["Settings"].asArray();
0297     QByteArray array(4, 0);
0298     (reinterpret_cast<quint16 *>(array.data()))[0] = columns;
0299     (reinterpret_cast<quint16 *>(array.data()))[1] = rows;
0300     for (const KisMetaData::Value &v : settings) {
0301         const QString str = v.asVariant().toString();
0302         QByteArray setting = codec->fromUnicode(str);
0303         array.append(setting);
0304     }
0305     return new Exiv2::DataValue((const Exiv2::byte *)array.data(), array.size());
0306 }
0307 
0308 #if EXIV2_TEST_VERSION(0,28,0)
0309 KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::UniquePtr value, Exiv2::ByteOrder order)
0310 #else
0311 KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
0312 #endif
0313 {
0314     QMap<QString, KisMetaData::Value> cfaPatternStructure;
0315     const Exiv2::DataValue *dvalue = dynamic_cast<const Exiv2::DataValue *>(&*value);
0316     Q_ASSERT(dvalue);
0317     QByteArray array(dvalue->count(), 0);
0318     dvalue->copy((Exiv2::byte *)array.data());
0319 #if EXIV2_TEST_VERSION(0,28,0)
0320     size_t columns = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[0], order);
0321     size_t rows = fixEndianness<qsizetype>((reinterpret_cast<qsizetype *>(array.data()))[1], order);
0322 #else
0323     int columns = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[0], order);
0324     int rows = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[1], order);
0325 #endif
0326     if ((columns * rows + 4)
0327         != dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library,
0328                               // or any library that doesn't save back with the same byte order as the camera)
0329         order = invertByteOrder(order);
0330         columns = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[0], order);
0331         rows = fixEndianness<quint16>((reinterpret_cast<quint16 *>(array.data()))[1], order);
0332     }
0333     QVariant qcolumns, qrows;
0334     qcolumns.setValue(columns);
0335     qrows.setValue(rows);
0336     cfaPatternStructure["Columns"] = KisMetaData::Value(qcolumns);
0337     cfaPatternStructure["Rows"] = KisMetaData::Value(qrows);
0338     QList<KisMetaData::Value> values;
0339     int index = 4;
0340     for (int i = 0; i < columns * rows; i++) {
0341         values.append(KisMetaData::Value(*(array.data() + index)));
0342         index++;
0343     }
0344     cfaPatternStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray);
0345     dbgMetaData << "CFAPattern " << ppVar(columns) << " " << ppVar(rows) << ppVar(values.size())
0346                 << ppVar(dvalue->count());
0347     return KisMetaData::Value(cfaPatternStructure);
0348 }
0349 
0350 Exiv2::Value *cfaPatternKMDToExif(const KisMetaData::Value &value)
0351 {
0352     QMap<QString, KisMetaData::Value> cfaStructure = value.asStructure();
0353     const quint16 columns = static_cast<quint16>(cfaStructure["Columns"].asVariant().toUInt());
0354     const quint16 rows = static_cast<quint16>(cfaStructure["Rows"].asVariant().toUInt());
0355 
0356     QList<KisMetaData::Value> values = cfaStructure["Values"].asArray();
0357     Q_ASSERT(columns * rows == values.size());
0358     QByteArray array(4 + columns * rows, 0);
0359     (reinterpret_cast<quint16 *>(array.data()))[0] = columns;
0360     (reinterpret_cast<quint16 *>(array.data()))[1] = rows;
0361     for (int i = 0; i < columns * rows; i++) {
0362         const quint8 val = (quint8)values[i].asVariant().toUInt();
0363         *(array.data() + 4 + i) = (char)val;
0364     }
0365     dbgMetaData << "Cfa Array " << ppVar(columns) << ppVar(rows) << ppVar(array.size());
0366     return new Exiv2::DataValue((const Exiv2::byte *)array.data(), array.size());
0367 }
0368 
0369 // Read and write Flash //
0370 
0371 #if EXIV2_TEST_VERSION(0,28,0)
0372 KisMetaData::Value flashExifToKMD(const Exiv2::Value::UniquePtr value)
0373 #else
0374 KisMetaData::Value flashExifToKMD(const Exiv2::Value::AutoPtr value)
0375 #endif
0376 {
0377 #if EXIV2_TEST_VERSION(0,28,0)
0378     const uint16_t v = static_cast<uint16_t>(value->toUint32());
0379 #else
0380     const uint16_t v = static_cast<uint16_t>(value->toLong());
0381 #endif
0382     QMap<QString, KisMetaData::Value> flashStructure;
0383     bool fired = (v & 0x01); // bit 1 is whether flash was fired or not
0384     flashStructure["Fired"] = QVariant(fired);
0385     int ret = ((v >> 1) & 0x03); // bit 2 and 3 are Return
0386     flashStructure["Return"] = QVariant(ret);
0387     int mode = ((v >> 3) & 0x03); // bit 4 and 5 are Mode
0388     flashStructure["Mode"] = QVariant(mode);
0389     bool function = ((v >> 5) & 0x01); // bit 6 if function
0390     flashStructure["Function"] = QVariant(function);
0391     bool redEye = ((v >> 6) & 0x01); // bit 7 if function
0392     flashStructure["RedEyeMode"] = QVariant(redEye);
0393     return KisMetaData::Value(flashStructure);
0394 }
0395 
0396 Exiv2::Value *flashKMDToExif(const KisMetaData::Value &value)
0397 {
0398     uint16_t v = 0;
0399     QMap<QString, KisMetaData::Value> flashStructure = value.asStructure();
0400     v = flashStructure["Fired"].asVariant().toBool();
0401     v |= ((flashStructure["Return"].asVariant().toInt() & 0x03) << 1);
0402     v |= ((flashStructure["Mode"].asVariant().toInt() & 0x03) << 3);
0403     v |= ((flashStructure["Function"].asVariant().toInt() & 0x03) << 5);
0404     v |= ((flashStructure["RedEyeMode"].asVariant().toInt() & 0x03) << 6);
0405     return new Exiv2::ValueType<uint16_t>(v);
0406 }
0407 
0408 // ---- Implementation of KisExifIO ----//
0409 KisExifIO::KisExifIO()
0410     : KisMetaData::IOBackend()
0411 {
0412 }
0413 
0414 KisExifIO::~KisExifIO()
0415 {
0416 }
0417 
0418 bool KisExifIO::saveTo(const KisMetaData::Store *store, QIODevice *ioDevice, HeaderType headerType) const
0419 {
0420     ioDevice->open(QIODevice::WriteOnly);
0421     Exiv2::ExifData exifData;
0422     if (headerType == KisMetaData::IOBackend::JpegHeader) {
0423         QByteArray header(6, 0);
0424         header[0] = 0x45;
0425         header[1] = 0x78;
0426         header[2] = 0x69;
0427         header[3] = 0x66;
0428         header[4] = 0x00;
0429         header[5] = 0x00;
0430         ioDevice->write(header);
0431     }
0432 
0433     for (const KisMetaData::Entry &entry : *store) {
0434         try {
0435             dbgMetaData << "Trying to save: " << entry.name() << " of " << entry.schema()->prefix() << ":"
0436                         << entry.schema()->uri();
0437             QString exivKey;
0438             if (entry.schema()->uri() == KisMetaData::Schema::TIFFSchemaUri) {
0439                 exivKey = "Exif.Image." + entry.name();
0440             } else if (entry.schema()->uri()
0441                        == KisMetaData::Schema::EXIFSchemaUri) { // Distinguish between exif and gps
0442                 if (entry.name().left(3) == "GPS") {
0443                     exivKey = "Exif.GPSInfo." + entry.name();
0444                 } else {
0445                     exivKey = "Exif.Photo." + entry.name();
0446                 }
0447             } else if (entry.schema()->uri() == KisMetaData::Schema::DublinCoreSchemaUri) {
0448                 if (entry.name() == "description") {
0449                     exivKey = "Exif.Image.ImageDescription";
0450                 } else if (entry.name() == "creator") {
0451                     exivKey = "Exif.Image.Artist";
0452                 } else if (entry.name() == "rights") {
0453                     exivKey = "Exif.Image.Copyright";
0454                 }
0455             } else if (entry.schema()->uri() == KisMetaData::Schema::XMPSchemaUri) {
0456                 if (entry.name() == "ModifyDate") {
0457                     exivKey = "Exif.Image.DateTime";
0458                 } else if (entry.name() == "CreatorTool") {
0459                     exivKey = "Exif.Image.Software";
0460                 }
0461             } else if (entry.schema()->uri() == KisMetaData::Schema::MakerNoteSchemaUri) {
0462                 if (entry.name() == "RawData") {
0463                     exivKey = "Exif.Photo.MakerNote";
0464                 }
0465             }
0466             dbgMetaData << "Saving " << entry.name() << " to " << exivKey;
0467             if (exivKey.isEmpty()) {
0468                 dbgMetaData << entry.qualifiedName() << " is unsavable to EXIF";
0469             } else {
0470                 Exiv2::ExifKey exifKey(qPrintable(exivKey));
0471                 Exiv2::Value *v = 0;
0472                 if (exivKey == "Exif.Photo.ExifVersion" || exivKey == "Exif.Photo.FlashpixVersion") {
0473                     v = kmdValueToExifVersion(entry.value());
0474                 } else if (exivKey == "Exif.Photo.FileSource") {
0475                     char s[] = {0x03};
0476                     v = new Exiv2::DataValue((const Exiv2::byte *)s, 1);
0477                 } else if (exivKey == "Exif.Photo.SceneType") {
0478                     char s[] = {0x01};
0479                     v = new Exiv2::DataValue((const Exiv2::byte *)s, 1);
0480                 } else if (exivKey == "Exif.Photo.ComponentsConfiguration") {
0481                     v = kmdIntOrderedArrayToExifArray(entry.value());
0482                 } else if (exivKey == "Exif.Image.Artist") { // load as dc:creator
0483                     KisMetaData::Value creator = entry.value();
0484                     if (entry.value().asArray().size() > 0) {
0485                         creator = entry.value().asArray()[0];
0486                     }
0487 #if !EXIV2_TEST_VERSION(0, 21, 0)
0488                     v = kmdValueToExivValue(creator, Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
0489 #else
0490                     v = kmdValueToExivValue(creator, exifKey.defaultTypeId());
0491 #endif
0492                 } else if (exivKey == "Exif.Photo.OECF") {
0493                     v = kmdOECFStructureToExifOECF(entry.value());
0494                 } else if (exivKey == "Exif.Photo.DeviceSettingDescription") {
0495                     v = deviceSettingDescriptionKMDToExif(entry.value());
0496                 } else if (exivKey == "Exif.Photo.CFAPattern") {
0497                     v = cfaPatternKMDToExif(entry.value());
0498                 } else if (exivKey == "Exif.Photo.Flash") {
0499                     v = flashKMDToExif(entry.value());
0500                 } else if (exivKey == "Exif.Photo.UserComment") {
0501                     Q_ASSERT(entry.value().type() == KisMetaData::Value::LangArray);
0502                     QMap<QString, KisMetaData::Value> langArr = entry.value().asLangArray();
0503                     if (langArr.contains("x-default")) {
0504 #if !EXIV2_TEST_VERSION(0, 21, 0)
0505                         v = kmdValueToExivValue(langArr.value("x-default"),
0506                                                 Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
0507 #else
0508                         v = kmdValueToExivValue(langArr.value("x-default"), exifKey.defaultTypeId());
0509 #endif
0510                     } else if (langArr.size() > 0) {
0511 #if !EXIV2_TEST_VERSION(0, 21, 0)
0512                         v = kmdValueToExivValue(langArr.begin().value(),
0513                                                 Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
0514 #else
0515                         v = kmdValueToExivValue(langArr.begin().value(), exifKey.defaultTypeId());
0516 #endif
0517                     }
0518                 } else {
0519                     dbgMetaData << exifKey.tag();
0520 #if !EXIV2_TEST_VERSION(0, 21, 0)
0521                     v = kmdValueToExivValue(entry.value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
0522 #else
0523                     v = kmdValueToExivValue(entry.value(), exifKey.defaultTypeId());
0524 #endif
0525                 }
0526                 if (v && v->typeId() != Exiv2::invalidTypeId) {
0527                     dbgMetaData << "Saving key" << exivKey << " of KMD value" << entry.value();
0528                     exifData.add(exifKey, v);
0529                 } else {
0530                     dbgMetaData << "No exif value was created for" << entry.qualifiedName() << " as"
0531                                 << exivKey; // << " of KMD value" << entry.value();
0532                 }
0533             }
0534 #if EXIV2_TEST_VERSION(0,28,0)
0535         } catch (Exiv2::Error &e) {
0536 #else
0537         } catch (Exiv2::AnyError &e) {
0538 #endif
0539             dbgMetaData << "exiv error " << e.what();
0540         }
0541     }
0542 #if !EXIV2_TEST_VERSION(0, 18, 0)
0543     Exiv2::DataBuf rawData = exifData.copy();
0544     ioDevice->write((const char *)rawData.pData_, rawData.size_);
0545 #else
0546     Exiv2::Blob rawData;
0547     Exiv2::ExifParser::encode(rawData, Exiv2::littleEndian, exifData);
0548     ioDevice->write((const char *)&*rawData.begin(), static_cast<int>(rawData.size()));
0549 #endif
0550     ioDevice->close();
0551     return true;
0552 }
0553 
0554 bool KisExifIO::canSaveAllEntries(KisMetaData::Store * /*store*/) const
0555 {
0556     return false; // It's a known fact that exif can't save all information, but TODO: write the check
0557 }
0558 
0559 bool KisExifIO::loadFrom(KisMetaData::Store *store, QIODevice *ioDevice) const
0560 {
0561     if (!ioDevice->open(QIODevice::ReadOnly)) {
0562         return false;
0563     }
0564     QByteArray arr(ioDevice->readAll());
0565     Exiv2::ExifData exifData;
0566     Exiv2::ByteOrder byteOrder;
0567 #if !EXIV2_TEST_VERSION(0, 18, 0)
0568     exifData.load((const Exiv2::byte *)arr.data(), arr.size());
0569     byteOrder = exifData.byteOrder();
0570 #else
0571     try {
0572         byteOrder =
0573             Exiv2::ExifParser::decode(exifData, (const Exiv2::byte *)arr.data(), static_cast<uint32_t>(arr.size()));
0574     } catch (const std::exception &ex) {
0575         warnKrita << "Received exception trying to parse exiv data" << ex.what();
0576         return false;
0577     } catch (...) {
0578         dbgKrita << "Received unknown exception trying to parse exiv data";
0579         return false;
0580     }
0581 #endif
0582     dbgMetaData << "Byte order = " << byteOrder << ppVar(Exiv2::bigEndian) << ppVar(Exiv2::littleEndian);
0583     dbgMetaData << "There are" << exifData.count() << " entries in the exif section";
0584     const KisMetaData::Schema *tiffSchema =
0585         KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::TIFFSchemaUri);
0586     Q_ASSERT(tiffSchema);
0587     const KisMetaData::Schema *exifSchema =
0588         KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::EXIFSchemaUri);
0589     Q_ASSERT(exifSchema);
0590     const KisMetaData::Schema *dcSchema =
0591         KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::DublinCoreSchemaUri);
0592     Q_ASSERT(dcSchema);
0593     const KisMetaData::Schema *xmpSchema =
0594         KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::XMPSchemaUri);
0595     Q_ASSERT(xmpSchema);
0596     const KisMetaData::Schema *makerNoteSchema =
0597         KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::MakerNoteSchemaUri);
0598     Q_ASSERT(makerNoteSchema);
0599 
0600     for (const Exiv2::Exifdatum &it : exifData) {
0601         const uint16_t tag = it.tag();
0602 
0603         if (tag == Exif::Image::StripOffsets || tag == Exif::Image::RowsPerStrip || tag == Exif::Image::StripByteCounts
0604             || tag == Exif::Image::JPEGInterchangeFormat || tag == Exif::Image::JPEGInterchangeFormatLength
0605             || it.tagName() == "0x0000") {
0606             dbgMetaData << it.key().c_str() << " is ignored";
0607         } else if (tag == Exif::Photo::MakerNote) {
0608             store->addEntry({makerNoteSchema, "RawData", exivValueToKMDValue(it.getValue(), false)});
0609         } else if (tag == Exif::Image::DateTime) { // load as xmp:ModifyDate
0610             store->addEntry({xmpSchema, "ModifyDate", exivValueToKMDValue(it.getValue(), false)});
0611         } else if (tag == Exif::Image::ImageDescription) { // load as "dc:description"
0612             store->addEntry({dcSchema, "description", exivValueToKMDValue(it.getValue(), false)});
0613         } else if (tag == Exif::Image::Software) { // load as "xmp:CreatorTool"
0614             store->addEntry({xmpSchema, "CreatorTool", exivValueToKMDValue(it.getValue(), false)});
0615         } else if (tag == Exif::Image::Artist) { // load as dc:creator
0616             QList<KisMetaData::Value> creators = {exivValueToKMDValue(it.getValue(), false)};
0617             store->addEntry({dcSchema, "creator", {creators, KisMetaData::Value::OrderedArray}});
0618         } else if (tag == Exif::Image::Copyright) { // load as dc:rights
0619             store->addEntry({dcSchema, "rights", exivValueToKMDValue(it.getValue(), false)});
0620         } else if (it.groupName() == "Image") {
0621             // Tiff tags
0622             const QString fixedTN(it.tagName().c_str());
0623             if (tag == Exif::Image::ExifTag || tag == Exif::Image::GPSTag) {
0624                 dbgMetaData << "Ignoring " << it.key().c_str();
0625             } else if (KisMetaData::Entry::isValidName(fixedTN)) {
0626                 store->addEntry({tiffSchema, fixedTN, exivValueToKMDValue(it.getValue(), false)});
0627             } else {
0628                 dbgMetaData << "Invalid tag name: " << fixedTN;
0629             }
0630         } else if (it.groupName() == "Photo") {
0631             // Exif tags
0632             KisMetaData::Value metaDataValue;
0633             if (tag == Exif::Photo::ExifVersion || tag == Exif::Photo::FlashpixVersion) {
0634                 metaDataValue = exifVersionToKMDValue(it.getValue());
0635             } else if (tag == Exif::Photo::FileSource) {
0636                 metaDataValue = KisMetaData::Value(3);
0637             } else if (tag == Exif::Photo::SceneType) {
0638                 metaDataValue = KisMetaData::Value(1);
0639             } else if (tag == Exif::Photo::ComponentsConfiguration) {
0640                 metaDataValue = exifArrayToKMDIntOrderedArray(it.getValue());
0641             } else if (tag == Exif::Photo::OECF) {
0642                 metaDataValue = exifOECFToKMDOECFStructure(it.getValue(), byteOrder);
0643             } else if (tag == Exif::Photo::DateTimeDigitized || tag == Exif::Photo::DateTimeOriginal) {
0644                 metaDataValue = exivValueToKMDValue(it.getValue(), false);
0645             } else if (tag == Exif::Photo::DeviceSettingDescription) {
0646                 metaDataValue = deviceSettingDescriptionExifToKMD(it.getValue());
0647             } else if (tag == Exif::Photo::CFAPattern) {
0648                 metaDataValue = cfaPatternExifToKMD(it.getValue(), byteOrder);
0649             } else if (tag == Exif::Photo::Flash) {
0650                 metaDataValue = flashExifToKMD(it.getValue());
0651             } else if (tag == Exif::Photo::UserComment) {
0652                 if (it.getValue()->typeId() != Exiv2::undefined) {
0653                     KisMetaData::Value vUC = exivValueToKMDValue(it.getValue(), false);
0654                     Q_ASSERT(vUC.type() == KisMetaData::Value::Variant);
0655                     QVariant commentVar = vUC.asVariant();
0656                     QString comment;
0657                     if (commentVar.type() == QVariant::String) {
0658                         comment = commentVar.toString();
0659                     } else if (commentVar.type() == QVariant::ByteArray) {
0660                         const QByteArray commentString = commentVar.toByteArray();
0661                         comment = QString::fromLatin1(commentString.constData(), commentString.size());
0662                     } else {
0663                         warnKrita << "KisExifIO: Unhandled UserComment value type.";
0664                     }
0665                     KisMetaData::Value vcomment(comment);
0666                     vcomment.addPropertyQualifier("xml:lang", KisMetaData::Value("x-default"));
0667                     QList<KisMetaData::Value> alt;
0668                     alt.append(vcomment);
0669                     metaDataValue = KisMetaData::Value(alt, KisMetaData::Value::LangArray);
0670                 }
0671             } else {
0672                 bool forceSeq = false;
0673                 KisMetaData::Value::ValueType arrayType = KisMetaData::Value::UnorderedArray;
0674                 if (tag == Exif::Photo::ISOSpeedRatings) {
0675                     forceSeq = true;
0676                     arrayType = KisMetaData::Value::OrderedArray;
0677                 }
0678                 metaDataValue = exivValueToKMDValue(it.getValue(), forceSeq, arrayType);
0679             }
0680             if (tag == Exif::Photo::InteroperabilityTag || tag == 0xea1d
0681                 || metaDataValue.type() == KisMetaData::Value::Invalid) { // InteroperabilityTag isn't useful for XMP,
0682                 // 0xea1d isn't a valid Exif tag
0683                 warnMetaData << "Ignoring " << it.key().c_str();
0684 
0685             } else {
0686                 store->addEntry({exifSchema, it.tagName().c_str(), metaDataValue});
0687             }
0688         } else if (it.groupName() == "Thumbnail") {
0689             dbgMetaData << "Ignoring thumbnail tag :" << it.key().c_str();
0690         } else if (it.groupName() == "GPSInfo") {
0691             store->addEntry({exifSchema, it.tagName().c_str(), exivValueToKMDValue(it.getValue(), false)});
0692         } else {
0693             dbgMetaData << "Unknown exif tag, cannot load:" << it.key().c_str();
0694         }
0695     }
0696     ioDevice->close();
0697     return true;
0698 }