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 }