diff --git a/autotests/read/jxl/gimp_exif.jxl.json b/autotests/read/jxl/gimp_exif.jxl.json index 0b115c7..aefe7c4 100644 --- a/autotests/read/jxl/gimp_exif.jxl.json +++ b/autotests/read/jxl/gimp_exif.jxl.json @@ -3,12 +3,12 @@ "fileName" : "gimp_exif.png", "metadata" : [ { - "Key" : "CreationDate", - "Value" : "2025-01-05T10:18:16" + "key" : "CreationDate", + "value" : "2025-01-05T10:18:16" }, { - "Key" : "Software" , - "Value" : "GIMP 3.0.0-RC2" + "key" : "Software" , + "value" : "GIMP 3.0.0-RC2" } ], "resolution" : { diff --git a/autotests/read/psd/mch-16bits.psd.json b/autotests/read/psd/mch-16bits.psd.json index 8d63adc..92bcd3e 100644 --- a/autotests/read/psd/mch-16bits.psd.json +++ b/autotests/read/psd/mch-16bits.psd.json @@ -1,7 +1,21 @@ [ { "minQtVersion" : "6.8.0", - "fileName" : "mch-16bits_qt_6_8.tif" + "fileName" : "mch-16bits_qt_6_8.tif", + "resolution" : { + "dotsPerMeterX" : 4685, + "dotsPerMeterY" : 4685 + }, + "metadata" : [ + { + "key" : "CreationDate", + "value" : "2022-11-11T14:27:52" + }, + { + "key" : "Software", + "value" : "Adobe Photoshop 24.0 (Windows)" + } + ] }, { "minQtVersion" : "6.0.0", diff --git a/autotests/read/psd/mch-8bits.psd.json b/autotests/read/psd/mch-8bits.psd.json index 3a253cb..e4f488e 100644 --- a/autotests/read/psd/mch-8bits.psd.json +++ b/autotests/read/psd/mch-8bits.psd.json @@ -1,7 +1,21 @@ [ { "minQtVersion" : "6.8.0", - "fileName" : "mch-8bits_qt_6.8.tif" + "fileName" : "mch-8bits_qt_6.8.tif", + "resolution" : { + "dotsPerMeterX" : 4685, + "dotsPerMeterY" : 4685 + }, + "metadata" : [ + { + "key" : "CreationDate", + "value" : "2022-11-11T14:27:39" + }, + { + "key" : "Software", + "value" : "Adobe Photoshop 24.0 (Windows)" + } + ] }, { "minQtVersion" : "6.0.0", diff --git a/autotests/read/psd/metadata.png b/autotests/read/psd/metadata.png new file mode 100644 index 0000000..aaa8725 Binary files /dev/null and b/autotests/read/psd/metadata.png differ diff --git a/autotests/read/psd/metadata.psd b/autotests/read/psd/metadata.psd new file mode 100644 index 0000000..32fd8b3 Binary files /dev/null and b/autotests/read/psd/metadata.psd differ diff --git a/autotests/read/psd/metadata.psd.json b/autotests/read/psd/metadata.psd.json new file mode 100644 index 0000000..de4ccfe --- /dev/null +++ b/autotests/read/psd/metadata.psd.json @@ -0,0 +1,59 @@ +[ + { + "fileName" : "metadata.png", + "metadata" : [ + { + "key" : "CreationDate", + "value" : "2025-01-14T13:53:32+01:00" + }, + { + "key" : "Software" , + "value" : "Adobe Photoshop 26.2 (Windows)" + }, + { + "key" : "Altitude", + "value" : "34" + }, + { + "key" : "Author", + "value" : "KDE Project" + }, + { + "key" : "Copyright", + "value" : "@2025 KDE Project" + }, + { + "key" : "Description", + "value" : "TV broadcast test image." + }, + { + "key" : "Latitude", + "value" : "44.6478" + }, + { + "key" : "LensManufacturer", + "value" : "KDE Glasses" + }, + { + "key" : "LensModel", + "value" : "A1234" + }, + { + "key" : "Longitude", + "value" : "10.9254" + }, + { + "key" : "Manufacturer", + "value" : "KFramework" + }, + { + "key" : "Model", + "value" : "KImageFormats" + } + ], + "resolution" : { + "dotsPerMeterX" : 11811, + "dotsPerMeterY" : 11811 + } + } +] diff --git a/autotests/templateimage.cpp b/autotests/templateimage.cpp index afd0b2c..9182f70 100644 --- a/autotests/templateimage.cpp +++ b/autotests/templateimage.cpp @@ -114,8 +114,8 @@ bool TemplateImage::checkOptionaInfo(const QImage& image, QString& error) const auto meta = obj.value("metadata").toArray(); for (auto jv : meta) { auto obj = jv.toObject(); - auto key = obj.value("Key").toString(); - auto val = obj.value("Value").toString(); + auto key = obj.value("key").toString(); + auto val = obj.value("value").toString(); auto cur = image.text(key); if (cur != val) { error = QStringLiteral("Metadata '%1' mismatch (current: '%2', expected:'%3')!").arg(key, cur, val); diff --git a/src/imageformats/CMakeLists.txt b/src/imageformats/CMakeLists.txt index d0dd999..c062daf 100644 --- a/src/imageformats/CMakeLists.txt +++ b/src/imageformats/CMakeLists.txt @@ -106,7 +106,7 @@ kimageformats_add_plugin(kimg_pfm SOURCES pfm.cpp) ################################## -kimageformats_add_plugin(kimg_psd SOURCES psd.cpp scanlineconverter.cpp) +kimageformats_add_plugin(kimg_psd SOURCES psd.cpp microexif.cpp scanlineconverter.cpp) ################################## diff --git a/src/imageformats/psd.cpp b/src/imageformats/psd.cpp index 06c75f9..23b5013 100644 --- a/src/imageformats/psd.cpp +++ b/src/imageformats/psd.cpp @@ -3,7 +3,7 @@ SPDX-FileCopyrightText: 2003 Ignacio CastaƱo SPDX-FileCopyrightText: 2015 Alex Merry - SPDX-FileCopyrightText: 2022-2024 Mirco Miranda + SPDX-FileCopyrightText: 2022-2025 Mirco Miranda SPDX-License-Identifier: LGPL-2.0-or-later */ @@ -27,6 +27,7 @@ */ #include "fastmath_p.h" +#include "microexif_p.h" #include "psd_p.h" #include "scanlineconverter_p.h" #include "util_p.h" @@ -96,6 +97,8 @@ enum ImageResourceId : quint16 { IRI_ICCPROFILE = 0x040F, IRI_TRANSPARENCYINDEX = 0x0417, IRI_VERSIONINFO = 0x0421, + IRI_EXIFDATA1 = 0x0422, + IRI_EXIFDATA3 = 0x0423, // never seen IRI_XMPMETADATA = 0x0424 }; @@ -522,6 +525,25 @@ static bool setXmpData(QImage& img, const PSDImageResourceSection& irs) return true; } +/*! + * \brief setExifData + * Adds EXIF metadata to QImage. + * \param img The image. + * \param irs The image resource section. + * \return True on success, otherwise false. + */ +static bool setExifData(QImage& img, const PSDImageResourceSection& irs) +{ + if (!irs.contains(IRI_EXIFDATA1)) + return false; + auto irb = irs.value(IRI_EXIFDATA1); + auto exif = MicroExif::fromByteArray(irb.data); + if (exif.isEmpty()) + return false; + exif.toImageMetadata(img); + return true; +} + /*! * \brief hasMergedData * Checks if merged image data are available. @@ -1356,6 +1378,11 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img) // qDebug() << "No XMP data found!"; } + // EXIF data + if (!setExifData(img, irs)) { + // qDebug() << "No EXIF data found!"; + } + // Duotone images: color data contains the duotone specification (not documented). // Other applications that read Photoshop files can treat a duotone image as a gray image, // and just preserve the contents of the duotone information when reading and writing the file.