/src/exiv2/src/rafimage.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | |
3 | | // included header files |
4 | | #include "rafimage.hpp" |
5 | | |
6 | | #include "basicio.hpp" |
7 | | #include "config.h" |
8 | | #include "enforce.hpp" |
9 | | #include "error.hpp" |
10 | | #include "futils.hpp" |
11 | | #include "image.hpp" |
12 | | #include "image_int.hpp" |
13 | | #include "jpgimage.hpp" |
14 | | #include "safe_op.hpp" |
15 | | #include "tags.hpp" |
16 | | #include "tiffimage.hpp" |
17 | | |
18 | | #include <array> |
19 | | #include <iostream> |
20 | | |
21 | | // ***************************************************************************** |
22 | | // class member definitions |
23 | | namespace Exiv2 { |
24 | | RafImage::RafImage(BasicIo::UniquePtr io, bool /*create*/) : |
25 | 168 | Image(ImageType::raf, mdExif | mdIptc | mdXmp, std::move(io)) { |
26 | 168 | } // RafImage::RafImage |
27 | | |
28 | 0 | std::string RafImage::mimeType() const { |
29 | 0 | return "image/x-fuji-raf"; |
30 | 0 | } |
31 | | |
32 | 9 | uint32_t RafImage::pixelWidth() const { |
33 | 9 | if (pixelWidth_ != 0) |
34 | 0 | return pixelWidth_; |
35 | | |
36 | 9 | auto widthIter = exifData_.findKey(Exiv2::ExifKey("Exif.Fujifilm.RawImageFullWidth")); |
37 | 9 | if (widthIter == exifData_.end() || widthIter->count() == 0) |
38 | 9 | return 0; |
39 | 0 | return widthIter->toUint32(); |
40 | 9 | } |
41 | | |
42 | 9 | uint32_t RafImage::pixelHeight() const { |
43 | 9 | if (pixelHeight_ != 0) |
44 | 0 | return pixelHeight_; |
45 | | |
46 | 9 | auto heightIter = exifData_.findKey(Exiv2::ExifKey("Exif.Fujifilm.RawImageFullHeight")); |
47 | 9 | if (heightIter == exifData_.end() || heightIter->count() == 0) |
48 | 9 | return 0; |
49 | 0 | return heightIter->toUint32(); |
50 | 9 | } |
51 | | |
52 | 0 | void RafImage::setExifData(const ExifData& /*exifData*/) { |
53 | | // Todo: implement me! |
54 | 0 | throw(Error(ErrorCode::kerInvalidSettingForImage, "Exif metadata", "RAF")); |
55 | 0 | } |
56 | | |
57 | 0 | void RafImage::setIptcData(const IptcData& /*iptcData*/) { |
58 | | // Todo: implement me! |
59 | 0 | throw(Error(ErrorCode::kerInvalidSettingForImage, "IPTC metadata", "RAF")); |
60 | 0 | } |
61 | | |
62 | 0 | void RafImage::setComment(const std::string&) { |
63 | | // not supported |
64 | 0 | throw(Error(ErrorCode::kerInvalidSettingForImage, "Image comment", "RAF")); |
65 | 0 | } |
66 | | |
67 | 0 | void RafImage::printStructure(std::ostream& out, PrintStructureOption option, size_t depth) { |
68 | 0 | if (io_->open() != 0) { |
69 | 0 | throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError()); |
70 | 0 | } |
71 | | // Ensure this is the correct image type |
72 | 0 | if (!isRafType(*io_, true)) { |
73 | 0 | if (io_->error() || io_->eof()) |
74 | 0 | throw Error(ErrorCode::kerFailedToReadImageData); |
75 | 0 | throw Error(ErrorCode::kerNotAnImage, "RAF"); |
76 | 0 | } |
77 | | |
78 | | // The following is based on https://libopenraw.freedesktop.org/formats/raf/ and |
79 | | // https://exiftool.org/TagNames/FujiFilm.html#RAFHeader |
80 | | |
81 | 0 | const bool bPrint = option == kpsBasic || option == kpsRecursive; |
82 | 0 | if (bPrint) { |
83 | 0 | io_->seek(0, BasicIo::beg); // rewind |
84 | 0 | size_t address = io_->tell(); |
85 | 0 | constexpr auto format = " {:9} | {:9} | "; |
86 | |
|
87 | 0 | { |
88 | 0 | out << Internal::indent(depth) << "STRUCTURE OF RAF FILE: " << io().path() << '\n'; |
89 | 0 | out << Internal::indent(depth) << " Address | Length | Payload" << '\n'; |
90 | 0 | } |
91 | |
|
92 | 0 | byte magicdata[17]; |
93 | 0 | io_->readOrThrow(magicdata, 16); |
94 | 0 | magicdata[16] = 0; |
95 | 0 | { |
96 | 0 | out << Internal::indent(depth) << stringFormat(format, address, 16) // 0 |
97 | 0 | << " magic : " << reinterpret_cast<const char*>(magicdata) << '\n'; |
98 | 0 | } |
99 | |
|
100 | 0 | address = io_->tell(); |
101 | 0 | byte data1[5]; |
102 | 0 | io_->read(data1, 4); |
103 | 0 | data1[4] = 0; |
104 | 0 | { |
105 | 0 | out << Internal::indent(depth) << stringFormat(format, address, 4) // 16 |
106 | 0 | << " data1 : " << reinterpret_cast<const char*>(&data1) << '\n'; |
107 | 0 | } |
108 | |
|
109 | 0 | address = io_->tell(); |
110 | 0 | byte data2[9]; |
111 | 0 | io_->read(data2, 8); |
112 | 0 | data2[8] = 0; |
113 | 0 | { |
114 | 0 | out << Internal::indent(depth) << stringFormat(format, address, 8) // 20 |
115 | 0 | << " data2 : " << reinterpret_cast<const char*>(&data2) << '\n'; |
116 | 0 | } |
117 | |
|
118 | 0 | address = io_->tell(); |
119 | 0 | byte camdata[33]; |
120 | 0 | io_->read(camdata, 32); |
121 | 0 | camdata[32] = 0; |
122 | 0 | { |
123 | 0 | out << Internal::indent(depth) << stringFormat(format, address, 32) // 28 |
124 | 0 | << " camera : " << reinterpret_cast<const char*>(&camdata) << '\n'; |
125 | 0 | } |
126 | |
|
127 | 0 | address = io_->tell(); |
128 | 0 | byte dir_version[5]; |
129 | 0 | io_->read(dir_version, 4); |
130 | 0 | dir_version[4] = 0; |
131 | 0 | { |
132 | 0 | out << Internal::indent(depth) << stringFormat(format, address, 4) // 60 |
133 | 0 | << " version : " << reinterpret_cast<const char*>(&dir_version) << '\n'; |
134 | 0 | } |
135 | |
|
136 | 0 | address = io_->tell(); |
137 | 0 | DataBuf unknown(20); |
138 | 0 | io_->readOrThrow(unknown.data(), unknown.size()); |
139 | 0 | { |
140 | 0 | out << Internal::indent(depth) << stringFormat(format, address, 20) |
141 | 0 | << " unknown : " << Internal::binaryToString(makeSlice(unknown, 0, unknown.size())) << '\n'; |
142 | 0 | } |
143 | |
|
144 | 0 | address = io_->tell(); |
145 | 0 | byte jpg_img_offset[4]; |
146 | 0 | io_->read(jpg_img_offset, 4); |
147 | 0 | byte jpg_img_length[4]; |
148 | 0 | size_t address2 = io_->tell(); |
149 | 0 | io_->read(jpg_img_length, 4); |
150 | |
|
151 | 0 | uint32_t jpg_img_off = Exiv2::getULong(jpg_img_offset, bigEndian); |
152 | 0 | uint32_t jpg_img_len = Exiv2::getULong(jpg_img_length, bigEndian); |
153 | 0 | { |
154 | 0 | out << Internal::indent(depth) << stringFormat(format, address, 4) << " JPEG offset : " << jpg_img_off << '\n'; |
155 | 0 | out << Internal::indent(depth) << stringFormat(format, address2, 4) << " JPEG length : " << jpg_img_len << '\n'; |
156 | 0 | } |
157 | | |
158 | | // RAFs can carry the payload in one or two parts |
159 | 0 | uint32_t meta_off[2], meta_len[2]; |
160 | 0 | uint32_t cfa_off[2], cfa_len[2], comp[2], cfa_size[2], cfa_data[2]; |
161 | 0 | for (size_t i = 0; i < 2; i++) { |
162 | 0 | address = io_->tell(); |
163 | 0 | byte data[4]; |
164 | 0 | io_->readOrThrow(data, 4); |
165 | 0 | meta_off[i] = Exiv2::getULong(data, bigEndian); |
166 | 0 | address2 = io_->tell(); |
167 | 0 | io_->readOrThrow(data, 4); |
168 | 0 | meta_len[i] = Exiv2::getULong(data, bigEndian); |
169 | 0 | { |
170 | 0 | out << Internal::indent(depth) << stringFormat(format, address, 4) |
171 | 0 | << stringFormat("meta offset{} : {}\n", i + 1, meta_off[i]); |
172 | 0 | out << Internal::indent(depth) << stringFormat(format, address2, 4) |
173 | 0 | << stringFormat("meta length{} : {}\n", i + 1, meta_len[i]); |
174 | 0 | } |
175 | |
|
176 | 0 | address = io_->tell(); |
177 | 0 | io_->readOrThrow(data, 4); |
178 | 0 | cfa_off[i] = Exiv2::getULong(data, bigEndian); |
179 | 0 | address2 = io_->tell(); |
180 | 0 | io_->readOrThrow(data, 4); |
181 | 0 | cfa_len[i] = Exiv2::getULong(data, bigEndian); |
182 | 0 | size_t address3 = io_->tell(); |
183 | 0 | io_->readOrThrow(data, 4); |
184 | 0 | comp[i] = Exiv2::getULong(data, bigEndian); |
185 | 0 | size_t address4 = io_->tell(); |
186 | 0 | io_->readOrThrow(data, 4); |
187 | 0 | cfa_size[i] = Exiv2::getULong(data, bigEndian); |
188 | 0 | size_t address5 = io_->tell(); |
189 | 0 | io_->readOrThrow(data, 4); |
190 | 0 | cfa_data[i] = Exiv2::getULong(data, bigEndian); |
191 | 0 | { |
192 | 0 | out << Internal::indent(depth) << stringFormat(format, address, 4U) |
193 | 0 | << stringFormat(" CFA offset{} : {}\n", i + 1, cfa_off[i]); |
194 | 0 | out << Internal::indent(depth) << stringFormat(format, address2, 4U) |
195 | 0 | << stringFormat(" CFA length{} : {}\n", i + 1, cfa_len[i]); |
196 | 0 | out << Internal::indent(depth) << stringFormat(format, address3, 4U) |
197 | 0 | << stringFormat("compression{} : {}\n", i + 1, comp[i]); |
198 | 0 | out << Internal::indent(depth) << stringFormat(format, address4, 4U) |
199 | 0 | << stringFormat(" CFA chunk{} : {}\n", i + 1, cfa_size[i]); |
200 | 0 | out << Internal::indent(depth) << stringFormat(format, address5, 4U) |
201 | 0 | << stringFormat(" unknown{} : {}\n", i + 1, cfa_data[i]); |
202 | 0 | } |
203 | 0 | } |
204 | |
|
205 | 0 | io_->seek(jpg_img_off, BasicIo::beg); // rewind |
206 | 0 | address = io_->tell(); |
207 | 0 | DataBuf payload(16); // header is different from chunks |
208 | 0 | io_->readOrThrow(payload.data(), payload.size()); |
209 | 0 | { |
210 | 0 | out << Internal::indent(depth) << stringFormat(format, address, jpg_img_len) |
211 | 0 | << " JPEG data : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << '\n'; |
212 | 0 | } |
213 | |
|
214 | 0 | io_->seek(meta_off[0], BasicIo::beg); // rewind |
215 | 0 | address = io_->tell(); |
216 | 0 | io_->readOrThrow(payload.data(), payload.size()); |
217 | 0 | { |
218 | 0 | out << Internal::indent(depth) << stringFormat(format, address, meta_len[0]) |
219 | 0 | << " meta data1 : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << '\n'; |
220 | 0 | } |
221 | |
|
222 | 0 | if (meta_off[1] && meta_len[1]) { |
223 | 0 | io_->seek(meta_off[1], BasicIo::beg); // rewind |
224 | 0 | address = io_->tell(); |
225 | 0 | io_->readOrThrow(payload.data(), payload.size()); |
226 | 0 | { |
227 | 0 | out << Internal::indent(depth) << stringFormat(format, address, meta_len[1]) |
228 | 0 | << " meta data2 : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << '\n'; |
229 | 0 | } |
230 | 0 | } |
231 | |
|
232 | 0 | io_->seek(cfa_off[0], BasicIo::beg); // rewind |
233 | 0 | address = io_->tell(); |
234 | 0 | io_->readOrThrow(payload.data(), payload.size()); |
235 | 0 | { |
236 | 0 | out << Internal::indent(depth) << stringFormat(format, address, cfa_len[0]) |
237 | 0 | << " CFA data1 : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << '\n'; |
238 | 0 | } |
239 | |
|
240 | 0 | if (cfa_off[1] && cfa_len[1]) { |
241 | 0 | io_->seek(cfa_off[1], BasicIo::beg); // rewind |
242 | 0 | address = io_->tell(); |
243 | 0 | io_->readOrThrow(payload.data(), payload.size()); |
244 | 0 | { |
245 | 0 | out << Internal::indent(depth) << stringFormat(format, address, cfa_len[1]) // cfa_off |
246 | 0 | << " CFA data2 : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << '\n'; |
247 | 0 | } |
248 | 0 | } |
249 | 0 | } |
250 | 0 | } // RafImage::printStructure |
251 | | |
252 | 168 | void RafImage::readMetadata() { |
253 | | #ifdef EXIV2_DEBUG_MESSAGES |
254 | | std::cerr << "Reading RAF file " << io_->path() << "\n"; |
255 | | #endif |
256 | 168 | if (io_->open() != 0) |
257 | 0 | throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError()); |
258 | 168 | IoCloser closer(*io_); |
259 | | // Ensure that this is the correct image type |
260 | 168 | if (!isRafType(*io_, false)) { |
261 | 0 | if (io_->error() || io_->eof()) |
262 | 0 | throw Error(ErrorCode::kerFailedToReadImageData); |
263 | 0 | throw Error(ErrorCode::kerNotAnImage, "RAF"); |
264 | 0 | } |
265 | | |
266 | 168 | clearMetadata(); |
267 | | |
268 | 168 | if (io_->seek(84, BasicIo::beg) != 0) |
269 | 19 | throw Error(ErrorCode::kerFailedToReadImageData); |
270 | 149 | byte jpg_img_offset[4]; |
271 | 149 | if (io_->read(jpg_img_offset, 4) != 4) |
272 | 10 | throw Error(ErrorCode::kerFailedToReadImageData); |
273 | 139 | byte jpg_img_length[4]; |
274 | 139 | if (io_->read(jpg_img_length, 4) != 4) |
275 | 18 | throw Error(ErrorCode::kerFailedToReadImageData); |
276 | 121 | uint32_t jpg_img_off_u32 = Exiv2::getULong(jpg_img_offset, bigEndian); |
277 | 121 | uint32_t jpg_img_len_u32 = Exiv2::getULong(jpg_img_length, bigEndian); |
278 | | |
279 | 121 | Internal::enforce(Safe::add(jpg_img_off_u32, jpg_img_len_u32) <= io_->size(), ErrorCode::kerCorruptedMetadata); |
280 | | |
281 | 121 | auto jpg_img_off = static_cast<long>(jpg_img_off_u32); |
282 | 121 | auto jpg_img_len = static_cast<long>(jpg_img_len_u32); |
283 | | |
284 | 121 | Internal::enforce(jpg_img_len >= 12, ErrorCode::kerCorruptedMetadata); |
285 | | |
286 | 121 | DataBuf jpg_buf(jpg_img_len); |
287 | 121 | if (io_->seek(jpg_img_off, BasicIo::beg) != 0) |
288 | 0 | throw Error(ErrorCode::kerFailedToReadImageData); |
289 | | |
290 | 121 | if (!jpg_buf.empty()) { |
291 | 89 | io_->read(jpg_buf.data(), jpg_buf.size()); |
292 | 89 | if (io_->error() || io_->eof()) |
293 | 0 | throw Error(ErrorCode::kerFailedToReadImageData); |
294 | 89 | } |
295 | | |
296 | | // Retrieve metadata from embedded JPEG preview image. |
297 | 121 | try { |
298 | 121 | auto jpg_io = std::make_unique<Exiv2::MemIo>(jpg_buf.data(), jpg_buf.size()); |
299 | 121 | auto jpg_img = JpegImage(std::move(jpg_io), false); |
300 | 121 | jpg_img.readMetadata(); |
301 | 121 | setByteOrder(jpg_img.byteOrder()); |
302 | 121 | xmpData_ = jpg_img.xmpData(); |
303 | 121 | exifData_ = jpg_img.exifData(); |
304 | 121 | iptcData_ = jpg_img.iptcData(); |
305 | 121 | comment_ = jpg_img.comment(); |
306 | 121 | } catch (const Exiv2::Error&) { |
307 | 76 | } |
308 | | |
309 | 121 | exifData_["Exif.Image2.JPEGInterchangeFormat"] = getULong(jpg_img_offset, bigEndian); |
310 | 89 | exifData_["Exif.Image2.JPEGInterchangeFormatLength"] = getULong(jpg_img_length, bigEndian); |
311 | | |
312 | | // Todo: parse the proprietary metadata structure |
313 | | // at offset 92 for pixelWidth_ & pixelHeight_ |
314 | | // See https://libopenraw.freedesktop.org/formats/raf/ |
315 | | // and https://exiftool.org/TagNames/FujiFilm.html#RAF |
316 | | |
317 | | // parse the tiff |
318 | 89 | std::array<byte, 4> readBuff; |
319 | 89 | if (io_->seek(100, BasicIo::beg) != 0) |
320 | 25 | throw Error(ErrorCode::kerFailedToReadImageData); |
321 | 64 | if (io_->read(readBuff.data(), 4) != 4) |
322 | 12 | throw Error(ErrorCode::kerFailedToReadImageData); |
323 | 52 | uint32_t tiffOffset = Exiv2::getULong(readBuff.data(), bigEndian); |
324 | | |
325 | 52 | if (io_->read(readBuff.data(), 4) != 4) |
326 | 5 | throw Error(ErrorCode::kerFailedToReadImageData); |
327 | 47 | uint32_t tiffLength = Exiv2::getULong(readBuff.data(), bigEndian); |
328 | | |
329 | | // sanity check. Does tiff lie inside the file? |
330 | 47 | Internal::enforce(Safe::add(tiffOffset, tiffLength) <= io_->size(), ErrorCode::kerCorruptedMetadata); |
331 | | |
332 | 47 | if (io_->seek(tiffOffset, BasicIo::beg) != 0) |
333 | 0 | throw Error(ErrorCode::kerFailedToReadImageData); |
334 | | |
335 | | // Check if this really is a tiff and then call the tiff parser. |
336 | | // Check is needed because some older models just embed a raw bitstream. |
337 | | // For those files we skip the parsing step. |
338 | 47 | if (io_->read(readBuff.data(), 4) != 4) { |
339 | 3 | throw Error(ErrorCode::kerFailedToReadImageData); |
340 | 3 | } |
341 | 44 | io_->seek(-4, BasicIo::cur); |
342 | 44 | const std::array<byte, 4> Id1{0x49, 0x49, 0x2A, 0x00}; |
343 | 44 | const std::array<byte, 4> Id2{0x4D, 0x4D, 0x00, 0x2A}; |
344 | 44 | if (readBuff == Id1 || readBuff == Id2) { |
345 | 2 | DataBuf tiff(tiffLength); |
346 | 2 | io_->read(tiff.data(), tiff.size()); |
347 | | |
348 | 2 | if (!io_->error() && !io_->eof()) { |
349 | 2 | TiffParser::decode(exifData_, iptcData_, xmpData_, tiff.c_data(), tiff.size()); |
350 | 2 | } |
351 | 2 | } |
352 | 44 | } |
353 | | |
354 | 10 | void RafImage::writeMetadata() { |
355 | | //! Todo: implement me! |
356 | 10 | throw(Error(ErrorCode::kerWritingImageFormatUnsupported, "RAF")); |
357 | 10 | } // RafImage::writeMetadata |
358 | | |
359 | | // ************************************************************************* |
360 | | // free functions |
361 | 168 | Image::UniquePtr newRafInstance(BasicIo::UniquePtr io, bool create) { |
362 | 168 | auto image = std::make_unique<RafImage>(std::move(io), create); |
363 | 168 | if (!image->good()) { |
364 | 0 | return nullptr; |
365 | 0 | } |
366 | 168 | return image; |
367 | 168 | } |
368 | | |
369 | 15.4k | bool isRafType(BasicIo& iIo, bool advance) { |
370 | 15.4k | const int32_t len = 8; |
371 | 15.4k | constexpr std::array<byte, len> RafId{'F', 'U', 'J', 'I', 'F', 'I', 'L', 'M'}; |
372 | 15.4k | std::array<byte, len> buf; |
373 | 15.4k | iIo.read(buf.data(), len); |
374 | 15.4k | if (iIo.error() || iIo.eof()) { |
375 | 0 | return false; |
376 | 0 | } |
377 | 15.4k | bool rc = buf == RafId; |
378 | 15.4k | if (!advance || !rc) { |
379 | 15.4k | iIo.seek(-len, BasicIo::cur); |
380 | 15.4k | } |
381 | 15.4k | return rc; |
382 | 15.4k | } |
383 | | |
384 | | } // namespace Exiv2 |