From 6c4b6d9d7f0958be0922ae1d40124876200f9431 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Wed, 10 May 2023 22:17:07 +0200 Subject: [PATCH 1/4] Extract XMP data from embedded JPEG preview inside RAF files The Fujifilm X-T5 camera stores in-camera rating for RAF images by using XMP. But the XMP data is not directly encoded into the RAF structure - instead it is attached as a second APP1 segment to the embedded JPEG preview file. This patch extracts the JPEG preview and parses it like a standalone JPEG file. --- src/rafimage.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/rafimage.cpp b/src/rafimage.cpp index 0fcd7e11..b8a909ef 100644 --- a/src/rafimage.cpp +++ b/src/rafimage.cpp @@ -10,6 +10,7 @@ #include "futils.hpp" #include "image.hpp" #include "image_int.hpp" +#include "jpgimage.hpp" #include "safe_op.hpp" #include "tiffimage.hpp" @@ -248,17 +249,31 @@ void RafImage::readMetadata() { Internal::enforce(jpg_img_len >= 12, ErrorCode::kerCorruptedMetadata); - DataBuf buf(jpg_img_len - 12); - if (io_->seek(jpg_img_off + 12, BasicIo::beg) != 0) + DataBuf jpg_buf(jpg_img_len); + if (io_->seek(jpg_img_off, BasicIo::beg) != 0) throw Error(ErrorCode::kerFailedToReadImageData); - if (!buf.empty()) { - io_->read(buf.data(), buf.size()); + if (!jpg_buf.empty()) { + io_->read(jpg_buf.data(), jpg_buf.size()); if (io_->error() || io_->eof()) throw Error(ErrorCode::kerFailedToReadImageData); } - ByteOrder bo = TiffParser::decode(exifData_, iptcData_, xmpData_, buf.c_data(), buf.size()); + // Extracting metadata from first APP1 container + constexpr byte tiff_offset = 12; + ByteOrder bo = TiffParser::decode(exifData_, iptcData_, xmpData_, jpg_buf.c_data() + tiff_offset, jpg_buf.size() - tiff_offset); + + // If there is no XMP data in first APP1 EXIF structure, there is maybe + // another APP1 container for XMP. Just use the JpegImage metadata parser for that. + if(xmpData_.empty()) { + auto jpg_io = std::make_unique(jpg_buf.data(), jpg_buf.size()); + auto jpg_img = JpegImage(std::move(jpg_io), false); + jpg_img.readMetadata(); + const auto jpg_xmp_data = jpg_img.xmpData(); + for(auto& datum : jpg_xmp_data) { + xmpData_.add(datum); + } + } exifData_["Exif.Image2.JPEGInterchangeFormat"] = getULong(jpg_img_offset, bigEndian); exifData_["Exif.Image2.JPEGInterchangeFormatLength"] = getULong(jpg_img_length, bigEndian); From d27c5ac8386e6771d96820ad37e9226bf09da7d9 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Mon, 15 May 2023 19:25:35 +0200 Subject: [PATCH 2/4] RAF: Simplify metadata extraction Remove manual TiffParser usage for reading APP1 segement, instead use JpegImage for proper parsing. --- src/rafimage.cpp | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/rafimage.cpp b/src/rafimage.cpp index b8a909ef..9e79fa78 100644 --- a/src/rafimage.cpp +++ b/src/rafimage.cpp @@ -259,27 +259,18 @@ void RafImage::readMetadata() { throw Error(ErrorCode::kerFailedToReadImageData); } - // Extracting metadata from first APP1 container - constexpr byte tiff_offset = 12; - ByteOrder bo = TiffParser::decode(exifData_, iptcData_, xmpData_, jpg_buf.c_data() + tiff_offset, jpg_buf.size() - tiff_offset); - - // If there is no XMP data in first APP1 EXIF structure, there is maybe - // another APP1 container for XMP. Just use the JpegImage metadata parser for that. - if(xmpData_.empty()) { - auto jpg_io = std::make_unique(jpg_buf.data(), jpg_buf.size()); - auto jpg_img = JpegImage(std::move(jpg_io), false); - jpg_img.readMetadata(); - const auto jpg_xmp_data = jpg_img.xmpData(); - for(auto& datum : jpg_xmp_data) { - xmpData_.add(datum); - } - } + // Retreive metadata from embedded JPEG preview image. + auto jpg_io = std::make_unique(jpg_buf.data(), jpg_buf.size()); + auto jpg_img = JpegImage(std::move(jpg_io), false); + jpg_img.readMetadata(); + setByteOrder(jpg_img.byteOrder()); + xmpData_ = jpg_img.xmpData(); + exifData_ = jpg_img.exifData(); + iptcData_ = jpg_img.iptcData(); exifData_["Exif.Image2.JPEGInterchangeFormat"] = getULong(jpg_img_offset, bigEndian); exifData_["Exif.Image2.JPEGInterchangeFormatLength"] = getULong(jpg_img_length, bigEndian); - setByteOrder(bo); - // parse the tiff byte readBuff[4]; if (io_->seek(100, BasicIo::beg) != 0) From f5f1f30f5b4c9415200ded8f7db41a1466125c52 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Wed, 17 May 2023 18:26:24 +0200 Subject: [PATCH 3/4] RAF: Ignore JPEG decoder errors if the JPEG offsets are invalid --- src/rafimage.cpp | 17 ++++++++++------- tests/bugfixes/github/test_issue_857.py | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/rafimage.cpp b/src/rafimage.cpp index 9e79fa78..2750ee70 100644 --- a/src/rafimage.cpp +++ b/src/rafimage.cpp @@ -260,13 +260,16 @@ void RafImage::readMetadata() { } // Retreive metadata from embedded JPEG preview image. - auto jpg_io = std::make_unique(jpg_buf.data(), jpg_buf.size()); - auto jpg_img = JpegImage(std::move(jpg_io), false); - jpg_img.readMetadata(); - setByteOrder(jpg_img.byteOrder()); - xmpData_ = jpg_img.xmpData(); - exifData_ = jpg_img.exifData(); - iptcData_ = jpg_img.iptcData(); + try { + auto jpg_io = std::make_unique(jpg_buf.data(), jpg_buf.size()); + auto jpg_img = JpegImage(std::move(jpg_io), false); + jpg_img.readMetadata(); + setByteOrder(jpg_img.byteOrder()); + xmpData_ = jpg_img.xmpData(); + exifData_ = jpg_img.exifData(); + iptcData_ = jpg_img.iptcData(); + } catch (const Exiv2::Error&) { + } exifData_["Exif.Image2.JPEGInterchangeFormat"] = getULong(jpg_img_offset, bigEndian); exifData_["Exif.Image2.JPEGInterchangeFormatLength"] = getULong(jpg_img_length, bigEndian); diff --git a/tests/bugfixes/github/test_issue_857.py b/tests/bugfixes/github/test_issue_857.py index 266bebcd..d0472df6 100644 --- a/tests/bugfixes/github/test_issue_857.py +++ b/tests/bugfixes/github/test_issue_857.py @@ -22,7 +22,7 @@ class OutOfMemoryInRafImageReadMetadata(metaclass=CaseMeta): $kerCorruptedMetadata """, """Exiv2 exception in print action for file $filename2: -This does not look like a TIFF image +$kerCorruptedMetadata """ ] retval = [1,1] From c493e38d5a2b5041279c584c0d3b32cc37190163 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Fri, 19 May 2023 08:45:35 +0200 Subject: [PATCH 4/4] RAF: Copy comment from embedded JPEG image --- src/rafimage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rafimage.cpp b/src/rafimage.cpp index 2750ee70..6cbb7da6 100644 --- a/src/rafimage.cpp +++ b/src/rafimage.cpp @@ -268,6 +268,7 @@ void RafImage::readMetadata() { xmpData_ = jpg_img.xmpData(); exifData_ = jpg_img.exifData(); iptcData_ = jpg_img.iptcData(); + comment_ = jpg_img.comment(); } catch (const Exiv2::Error&) { }