10#include <exiv2/error.hpp>
11#include <exiv2/exif.hpp>
34#if EXIV2_TEST_VERSION(0,28,0)
40 const Exiv2::DataValue *dvalue =
dynamic_cast<const Exiv2::DataValue *
>(&*
value);
43 QByteArray array(dvalue->count(), 0);
44 dvalue->copy((Exiv2::byte *)array.data());
47 Q_ASSERT(
value->typeId() == Exiv2::asciiString);
55 Exiv2::DataValue *dvalue =
new Exiv2::DataValue;
56 QString ver =
value.asVariant().toString();
57 dvalue->read((
const Exiv2::byte *)ver.toLatin1().constData(), ver.size());
62#if EXIV2_TEST_VERSION(0,28,0)
69 const Exiv2::DataValue *dvalue =
dynamic_cast<const Exiv2::DataValue *
>(&*
value);
71#if EXIV2_TEST_VERSION(0,28,0)
72 for (
size_t i = 0; i < dvalue->count(); i++) {
73 v.push_back({(int)dvalue->toUint32(i)});
75 for (
long i = 0; i < dvalue->count(); i++) {
76 v.push_back({(int)dvalue->toLong(i)});
80 Q_ASSERT(
value->typeId() == Exiv2::asciiString);
81 QString str = QString::fromLatin1(
value->toString().c_str());
90 std::vector<Exiv2::byte>
v;
92 v.push_back(
static_cast<uint8_t
>(it.asVariant().toInt(0)));
94 return new Exiv2::DataValue(
v.data(),
static_cast<long>(
v.size()));
97#if EXIV2_TEST_VERSION(0,28,0)
103 return QDateTime::fromString(
value->toString().c_str(), Qt::ISODate);
110 case Exiv2::invalidByteOrder:
112 case Exiv2::littleEndian:
113 return qFromLittleEndian<T>(
v);
114 case Exiv2::bigEndian:
115 return qFromBigEndian<T>(
v);
117 warnKrita <<
"KisExifIO: unknown byte order";
124 case Exiv2::littleEndian:
125 return Exiv2::bigEndian;
126 case Exiv2::bigEndian:
127 return Exiv2::littleEndian;
128 case Exiv2::invalidByteOrder:
129 warnKrita <<
"KisExifIO: Can't invert Exiv2::invalidByteOrder";
130 return Exiv2::invalidByteOrder;
132 return Exiv2::invalidByteOrder;
135#if EXIV2_TEST_VERSION(0,28,0)
141 QMap<QString, KisMetaData::Value> oecfStructure;
142 const Exiv2::DataValue *dvalue =
dynamic_cast<const Exiv2::DataValue *
>(&*
value);
144 QByteArray array(dvalue->count(), 0);
146 dvalue->copy((Exiv2::byte *)array.data());
147#if EXIV2_TEST_VERSION(0,28,0)
148 size_t columns = fixEndianness<qsizetype>((
reinterpret_cast<qsizetype *
>(array.data()))[0], order);
149 size_t rows = fixEndianness<qsizetype>((
reinterpret_cast<qsizetype *
>(array.data()))[1], order);
151 int columns = fixEndianness<quint16>((
reinterpret_cast<quint16 *
>(array.data()))[0], order);
152 int rows = fixEndianness<quint16>((
reinterpret_cast<quint16 *
>(array.data()))[1], order);
155 if ((columns * rows + 4)
159#if EXIV2_TEST_VERSION(0,28,0)
160 columns = fixEndianness<qsizetype>((
reinterpret_cast<qsizetype *
>(array.data()))[0], order);
161 rows = fixEndianness<qsizetype>((
reinterpret_cast<qsizetype *
>(array.data()))[1], order);
163 columns = fixEndianness<quint16>((
reinterpret_cast<quint16 *
>(array.data()))[0], order);
164 rows = fixEndianness<quint16>((
reinterpret_cast<quint16 *
>(array.data()))[1], order);
166 Q_ASSERT((columns * rows + 4) > dvalue->count());
168 QVariant qcolumns, qrows;
169 qcolumns.setValue(columns);
170 qrows.setValue(rows);
175#if EXIV2_TEST_VERSION(0,28,0)
176 for (
size_t i = 0; i < columns; i++) {
178 for (
int i = 0; i < columns; i++) {
180 int lastIndex = array.indexOf((
char)0, index);
181 QString name = array.mid(index, lastIndex - index);
182 if (index != lastIndex) {
183 index = lastIndex + 1;
193 qint32 *dataIt =
reinterpret_cast<qint32 *
>(array.data() + index);
194#if EXIV2_TEST_VERSION(0,28,0)
195 for (
size_t i = 0; i < columns; i++) {
196 for (
size_t j = 0; j < rows; j++) {
198 for (
int i = 0; i < columns; i++) {
199 for (
int j = 0; j < rows; j++) {
202 KisMetaData::Rational(fixEndianness<qint32>(dataIt[0], order), fixEndianness<qint32>(dataIt[1], order))));
213 QMap<QString, KisMetaData::Value> oecfStructure =
value.asStructure();
214 const quint16 columns =
static_cast<quint16
>(oecfStructure[
"Columns"].asVariant().toUInt());
215 const quint16 rows =
static_cast<quint16
>(oecfStructure[
"Rows"].asVariant().toUInt());
219 Q_ASSERT(columns * rows == values.size());
220 int length = 4 + rows * columns * 8;
221 bool saveNames = (!names.empty() && names[0].asVariant().toString().size() > 0);
223 for (
int i = 0; i < columns; i++) {
224 length += names[i].asVariant().toString().size() + 1;
227 QByteArray array(
length, 0);
228 (
reinterpret_cast<quint16 *
>(array.data()))[0] = columns;
229 (
reinterpret_cast<quint16 *
>(array.data()))[1] = rows;
232 for (
int i = 0; i < columns; i++) {
233 QByteArray name = names[i].asVariant().toString().toLatin1();
234 name.append((
char)0);
235 memcpy(array.data() + index, name.data(),
static_cast<size_t>(name.size()));
236 index += name.size();
239 qint32 *dataIt =
reinterpret_cast<qint32 *
>(array.data() + index);
241 dataIt[0] = it.asRational().numerator;
242 dataIt[1] = it.asRational().denominator;
245 return new Exiv2::DataValue((
const Exiv2::byte *)array.data(), array.size());
248#if EXIV2_TEST_VERSION(0,28,0)
254 QMap<QString, KisMetaData::Value> deviceSettingStructure;
257 const Exiv2::DataValue *dvalue =
dynamic_cast<const Exiv2::DataValue *
>(&*
value);
259 array.resize(dvalue->count());
260 dvalue->copy((Exiv2::byte *)array.data());
262 Q_ASSERT(
value->typeId() == Exiv2::unsignedShort);
263 array.resize(2 *
value->count());
264 value->copy((Exiv2::byte *)array.data(), Exiv2::littleEndian);
266 int columns = (
reinterpret_cast<quint16 *
>(array.data()))[0];
267 int rows = (
reinterpret_cast<quint16 *
>(array.data()))[1];
271 QByteArray null(2, 0);
273 for (
int index = 4; index < array.size();) {
274 const int lastIndex = array.indexOf(null, index);
277 const int numChars = (lastIndex - index) / 2;
279 QString setting = QString::fromUtf16((ushort *)(
void *)(array.data() + index), numChars);
280 index = lastIndex + 2;
290 QMap<QString, KisMetaData::Value> deviceSettingStructure =
value.asStructure();
291 const quint16 columns =
static_cast<quint16
>(deviceSettingStructure[
"Columns"].asVariant().toUInt());
292 quint16 rows =
static_cast<quint16
>(deviceSettingStructure[
"Rows"].asVariant().toUInt());
294 QTextCodec *codec = QTextCodec::codecForName(
"UTF-16");
297 QByteArray array(4, 0);
298 (
reinterpret_cast<quint16 *
>(array.data()))[0] = columns;
299 (
reinterpret_cast<quint16 *
>(array.data()))[1] = rows;
301 const QString str =
v.asVariant().toString();
302 QByteArray setting = codec->fromUnicode(str);
303 array.append(setting);
305 return new Exiv2::DataValue((
const Exiv2::byte *)array.data(), array.size());
308#if EXIV2_TEST_VERSION(0,28,0)
314 QMap<QString, KisMetaData::Value> cfaPatternStructure;
315 const Exiv2::DataValue *dvalue =
dynamic_cast<const Exiv2::DataValue *
>(&*
value);
317 QByteArray array(dvalue->count(), 0);
318 dvalue->copy((Exiv2::byte *)array.data());
319#if EXIV2_TEST_VERSION(0,28,0)
320 size_t columns = fixEndianness<qsizetype>((
reinterpret_cast<qsizetype *
>(array.data()))[0], order);
321 size_t rows = fixEndianness<qsizetype>((
reinterpret_cast<qsizetype *
>(array.data()))[1], order);
323 int columns = fixEndianness<quint16>((
reinterpret_cast<quint16 *
>(array.data()))[0], order);
324 int rows = fixEndianness<quint16>((
reinterpret_cast<quint16 *
>(array.data()))[1], order);
326 if ((columns * rows + 4)
327 != dvalue->count()) {
330 columns = fixEndianness<quint16>((
reinterpret_cast<quint16 *
>(array.data()))[0], order);
331 rows = fixEndianness<quint16>((
reinterpret_cast<quint16 *
>(array.data()))[1], order);
333 QVariant qcolumns, qrows;
334 qcolumns.setValue(columns);
335 qrows.setValue(rows);
340 for (
int i = 0; i < columns * rows; i++) {
346 <<
ppVar(dvalue->count());
352 QMap<QString, KisMetaData::Value> cfaStructure =
value.asStructure();
353 const quint16 columns =
static_cast<quint16
>(cfaStructure[
"Columns"].asVariant().toUInt());
354 const quint16 rows =
static_cast<quint16
>(cfaStructure[
"Rows"].asVariant().toUInt());
357 Q_ASSERT(columns * rows == values.size());
358 QByteArray array(4 + columns * rows, 0);
359 (
reinterpret_cast<quint16 *
>(array.data()))[0] = columns;
360 (
reinterpret_cast<quint16 *
>(array.data()))[1] = rows;
361 for (
int i = 0; i < columns * rows; i++) {
362 const quint8 val = (quint8)values[i].asVariant().toUInt();
363 *(array.data() + 4 + i) = (
char)val;
366 return new Exiv2::DataValue((
const Exiv2::byte *)array.data(), array.size());
371#if EXIV2_TEST_VERSION(0,28,0)
377#if EXIV2_TEST_VERSION(0,28,0)
378 const uint16_t
v =
static_cast<uint16_t
>(
value->toUint32());
380 const uint16_t
v =
static_cast<uint16_t
>(
value->toLong());
382 QMap<QString, KisMetaData::Value> flashStructure;
383 bool fired = (
v & 0x01);
384 flashStructure[
"Fired"] = QVariant(fired);
385 int ret = ((
v >> 1) & 0x03);
386 flashStructure[
"Return"] = QVariant(ret);
387 int mode = ((
v >> 3) & 0x03);
388 flashStructure[
"Mode"] = QVariant(mode);
389 bool function = ((
v >> 5) & 0x01);
390 flashStructure[
"Function"] = QVariant(function);
391 bool redEye = ((
v >> 6) & 0x01);
392 flashStructure[
"RedEyeMode"] = QVariant(redEye);
399 QMap<QString, KisMetaData::Value> flashStructure =
value.asStructure();
400 v = flashStructure[
"Fired"].asVariant().toBool();
401 v |= ((flashStructure[
"Return"].asVariant().toInt() & 0x03) << 1);
402 v |= ((flashStructure[
"Mode"].asVariant().toInt() & 0x03) << 3);
403 v |= ((flashStructure[
"Function"].asVariant().toInt() & 0x03) << 5);
404 v |= ((flashStructure[
"RedEyeMode"].asVariant().toInt() & 0x03) << 6);
405 return new Exiv2::ValueType<uint16_t>(
v);
420 ioDevice->open(QIODevice::WriteOnly);
421 Exiv2::ExifData exifData;
423 QByteArray header(6, 0);
430 ioDevice->write(header);
435 dbgMetaData <<
"Trying to save: " << entry.name() <<
" of " << entry.schema()->prefix() <<
":"
436 << entry.schema()->uri();
439 exivKey =
"Exif.Image." + entry.name();
440 }
else if (entry.schema()->uri()
442 if (entry.name().left(3) ==
"GPS") {
443 exivKey =
"Exif.GPSInfo." + entry.name();
445 exivKey =
"Exif.Photo." + entry.name();
448 if (entry.name() ==
"description") {
449 exivKey =
"Exif.Image.ImageDescription";
450 }
else if (entry.name() ==
"creator") {
451 exivKey =
"Exif.Image.Artist";
452 }
else if (entry.name() ==
"rights") {
453 exivKey =
"Exif.Image.Copyright";
456 if (entry.name() ==
"ModifyDate") {
457 exivKey =
"Exif.Image.DateTime";
458 }
else if (entry.name() ==
"CreatorTool") {
459 exivKey =
"Exif.Image.Software";
462 if (entry.name() ==
"RawData") {
463 exivKey =
"Exif.Photo.MakerNote";
466 dbgMetaData <<
"Saving " << entry.name() <<
" to " << exivKey;
467 if (exivKey.isEmpty()) {
468 dbgMetaData << entry.qualifiedName() <<
" is unsavable to EXIF";
470 Exiv2::ExifKey exifKey(qPrintable(exivKey));
472 if (exivKey ==
"Exif.Photo.ExifVersion" || exivKey ==
"Exif.Photo.FlashpixVersion") {
474 }
else if (exivKey ==
"Exif.Photo.FileSource") {
476 v =
new Exiv2::DataValue((
const Exiv2::byte *)s, 1);
477 }
else if (exivKey ==
"Exif.Photo.SceneType") {
479 v =
new Exiv2::DataValue((
const Exiv2::byte *)s, 1);
480 }
else if (exivKey ==
"Exif.Photo.ComponentsConfiguration") {
482 }
else if (exivKey ==
"Exif.Image.Artist") {
484 if (entry.value().asArray().size() > 0) {
485 creator = entry.value().
asArray()[0];
487#if !EXIV2_TEST_VERSION(0, 21, 0)
492 }
else if (exivKey ==
"Exif.Photo.OECF") {
494 }
else if (exivKey ==
"Exif.Photo.DeviceSettingDescription") {
496 }
else if (exivKey ==
"Exif.Photo.CFAPattern") {
498 }
else if (exivKey ==
"Exif.Photo.Flash") {
500 }
else if (exivKey ==
"Exif.Photo.UserComment") {
502 QMap<QString, KisMetaData::Value> langArr = entry.value().asLangArray();
503 if (langArr.contains(
"x-default")) {
504#if !EXIV2_TEST_VERSION(0, 21, 0)
506 Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
510 }
else if (langArr.size() > 0) {
511#if !EXIV2_TEST_VERSION(0, 21, 0)
513 Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
520#if !EXIV2_TEST_VERSION(0, 21, 0)
521 v =
kmdValueToExivValue(entry.value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
526 if (
v &&
v->typeId() != Exiv2::invalidTypeId) {
527 dbgMetaData <<
"Saving key" << exivKey <<
" of KMD value" << entry.value();
528 exifData.add(exifKey,
v);
530 dbgMetaData <<
"No exif value was created for" << entry.qualifiedName() <<
" as"
534#if EXIV2_TEST_VERSION(0,28,0)
535 }
catch (Exiv2::Error &e) {
537 }
catch (Exiv2::AnyError &e) {
542#if !EXIV2_TEST_VERSION(0, 18, 0)
543 Exiv2::DataBuf rawData = exifData.copy();
544 ioDevice->write((
const char *)rawData.pData_, rawData.size_);
547 Exiv2::ExifParser::encode(rawData, Exiv2::littleEndian, exifData);
548 ioDevice->write((
const char *)&*rawData.begin(),
static_cast<int>(rawData.size()));
561 if (!ioDevice->open(QIODevice::ReadOnly)) {
564 QByteArray arr(ioDevice->readAll());
565 Exiv2::ExifData exifData;
566 Exiv2::ByteOrder byteOrder;
567#if !EXIV2_TEST_VERSION(0, 18, 0)
568 exifData.load((
const Exiv2::byte *)arr.data(), arr.size());
569 byteOrder = exifData.byteOrder();
573 Exiv2::ExifParser::decode(exifData, (
const Exiv2::byte *)arr.data(),
static_cast<uint32_t
>(arr.size()));
574 }
catch (
const std::exception &ex) {
575 warnKrita <<
"Received exception trying to parse exiv data" << ex.what();
578 dbgKrita <<
"Received unknown exception trying to parse exiv data";
582 dbgMetaData <<
"Byte order = " << byteOrder <<
ppVar(Exiv2::bigEndian) <<
ppVar(Exiv2::littleEndian);
583 dbgMetaData <<
"There are" << exifData.count() <<
" entries in the exif section";
586 Q_ASSERT(tiffSchema);
589 Q_ASSERT(exifSchema);
598 Q_ASSERT(makerNoteSchema);
600 for (
const Exiv2::Exifdatum &it : exifData) {
601 const uint16_t tag = it.tag();
605 || it.tagName() ==
"0x0000") {
620 }
else if (it.groupName() ==
"Image") {
622 const QString fixedTN(it.tagName().c_str());
630 }
else if (it.groupName() ==
"Photo") {
652 if (it.getValue()->typeId() != Exiv2::undefined) {
657 if (commentVar.type() == QVariant::String) {
658 comment = commentVar.toString();
659 }
else if (commentVar.type() == QVariant::ByteArray) {
660 const QByteArray commentString = commentVar.toByteArray();
661 comment = QString::fromLatin1(commentString.constData(), commentString.size());
663 warnKrita <<
"KisExifIO: Unhandled UserComment value type.";
668 alt.append(vcomment);
672 bool forceSeq =
false;
686 store->
addEntry({exifSchema, it.tagName().c_str(), metaDataValue});
688 }
else if (it.groupName() ==
"Thumbnail") {
689 dbgMetaData <<
"Ignoring thumbnail tag :" << it.key().c_str();
690 }
else if (it.groupName() ==
"GPSInfo") {
693 dbgMetaData <<
"Unknown exif tag, cannot load:" << it.key().c_str();
qreal length(const QPointF &vec)
float value(const T *src, size_t ch)
bool saveTo(const KisMetaData::Store *store, QIODevice *ioDevice, HeaderType headerType=NoHeader) const override
bool loadFrom(KisMetaData::Store *store, QIODevice *ioDevice) const override
bool canSaveAllEntries(KisMetaData::Store *store) const override
static bool isValidName(const QString &_name)
Exiv2::Value * deviceSettingDescriptionKMDToExif(const KisMetaData::Value &value)
Exiv2::Value * kmdIntOrderedArrayToExifArray(const KisMetaData::Value &value)
Exiv2::Value * cfaPatternKMDToExif(const KisMetaData::Value &value)
KisMetaData::Value deviceSettingDescriptionExifToKMD(const Exiv2::Value::AutoPtr value)
T fixEndianness(T v, Exiv2::ByteOrder order)
KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
Exiv2::Value * flashKMDToExif(const KisMetaData::Value &value)
Exiv2::Value * kmdOECFStructureToExifOECF(const KisMetaData::Value &value)
Exiv2::ByteOrder invertByteOrder(Exiv2::ByteOrder order)
KisMetaData::Value exifArrayToKMDIntOrderedArray(const Exiv2::Value::AutoPtr value)
Exiv2::Value * kmdValueToExifVersion(const KisMetaData::Value &value)
QDateTime exivValueToDateTime(const Exiv2::Value::AutoPtr value)
KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
KisMetaData::Value flashExifToKMD(const Exiv2::Value::AutoPtr value)
KisMetaData::Value exifVersionToKMDValue(const Exiv2::Value::AutoPtr value)
Exiv2::Value * kmdValueToExivValue(const KisMetaData::Value &value, Exiv2::TypeId type)
Convert a KisMetaData to an Exiv value.
KisMetaData::Value exivValueToKMDValue(const Exiv2::Value::AutoPtr &value, bool forceSeq, KisMetaData::Value::ValueType arrayType=KisMetaData::Value::UnorderedArray)
const uint16_t StripByteCounts
const uint16_t RowsPerStrip
const uint16_t StripOffsets
const uint16_t JPEGInterchangeFormat
const uint16_t ImageDescription
const uint16_t JPEGInterchangeFormatLength
const uint16_t DateTimeDigitized
const uint16_t UserComment
const uint16_t DeviceSettingDescription
const uint16_t ComponentsConfiguration
const uint16_t FileSource
const uint16_t FlashpixVersion
const uint16_t CFAPattern
const uint16_t ISOSpeedRatings
const uint16_t InteroperabilityTag
const uint16_t ExifVersion
const uint16_t DateTimeOriginal