/src/exiv2/src/tiffvisitor_int.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | |
3 | | // included header files |
4 | | #include "tiffvisitor_int.hpp" // see bug #487 |
5 | | |
6 | | #include "config.h" |
7 | | #include "enforce.hpp" |
8 | | #include "exif.hpp" |
9 | | #include "image_int.hpp" |
10 | | #include "iptc.hpp" |
11 | | #include "makernote_int.hpp" |
12 | | #include "photoshop.hpp" |
13 | | #include "safe_op.hpp" |
14 | | #include "sonymn_int.hpp" |
15 | | #include "tags.hpp" |
16 | | #include "tags_int.hpp" |
17 | | #include "tiffcomposite_int.hpp" |
18 | | #include "tiffimage_int.hpp" |
19 | | #include "value.hpp" |
20 | | #include "xmp_exiv2.hpp" |
21 | | |
22 | | #include <functional> |
23 | | #include <iomanip> |
24 | | |
25 | | #ifdef EXIV2_DEBUG_MESSAGES |
26 | | #include <iostream> |
27 | | #endif |
28 | | |
29 | | // ***************************************************************************** |
30 | | namespace { |
31 | | //! Unary predicate that matches an Exifdatum with a given group and index. |
32 | | class FindExifdatum2 { |
33 | | public: |
34 | | //! Constructor, initializes the object with the group and index to look for. |
35 | 0 | FindExifdatum2(Exiv2::IfdId group, int idx) : groupName_(Exiv2::Internal::groupName(group)), idx_(idx) { |
36 | 0 | } |
37 | | //! Returns true if group and index match. |
38 | 0 | bool operator()(const Exiv2::Exifdatum& md) const { |
39 | 0 | return idx_ == md.idx() && md.groupName() == groupName_; |
40 | 0 | } |
41 | | |
42 | | private: |
43 | | const char* groupName_; |
44 | | int idx_; |
45 | | |
46 | | }; // class FindExifdatum2 |
47 | | |
48 | 0 | Exiv2::ByteOrder stringToByteOrder(std::string_view val) { |
49 | 0 | if (val == "II") |
50 | 0 | return Exiv2::littleEndian; |
51 | 0 | if (val == "MM") |
52 | 0 | return Exiv2::bigEndian; |
53 | | |
54 | 0 | return Exiv2::invalidByteOrder; |
55 | 0 | } |
56 | | } // namespace |
57 | | |
58 | | // ***************************************************************************** |
59 | | // class member definitions |
60 | | namespace Exiv2::Internal { |
61 | 28.9k | void TiffVisitor::setGo(GoEvent event, bool go) { |
62 | 28.9k | go_[event] = go; |
63 | 28.9k | } |
64 | | |
65 | 16.1M | bool TiffVisitor::go(GoEvent event) const { |
66 | 16.1M | return go_[event]; |
67 | 16.1M | } |
68 | | |
69 | 136k | void TiffVisitor::visitDirectoryNext(TiffDirectory* /*object*/) { |
70 | 136k | } |
71 | | |
72 | 130k | void TiffVisitor::visitDirectoryEnd(TiffDirectory* /*object*/) { |
73 | 130k | } |
74 | | |
75 | 1.09k | void TiffVisitor::visitIfdMakernoteEnd(TiffIfdMakernote* /*object*/) { |
76 | 1.09k | } |
77 | | |
78 | 78.5k | void TiffVisitor::visitBinaryArrayEnd(TiffBinaryArray* /*object*/) { |
79 | 78.5k | } |
80 | | |
81 | 0 | void TiffFinder::init(uint16_t tag, IfdId group) { |
82 | 0 | tag_ = tag; |
83 | 0 | group_ = group; |
84 | 0 | tiffComponent_ = nullptr; |
85 | 0 | setGo(geTraverse, true); |
86 | 0 | } |
87 | | |
88 | 4.59M | void TiffFinder::findObject(TiffComponent* object) { |
89 | 4.59M | if (object->tag() == tag_ && object->group() == group_) { |
90 | 28.9k | tiffComponent_ = object; |
91 | 28.9k | setGo(geTraverse, false); |
92 | 28.9k | } |
93 | 4.59M | } |
94 | | |
95 | 2.61M | void TiffFinder::visitEntry(TiffEntry* object) { |
96 | 2.61M | findObject(object); |
97 | 2.61M | } |
98 | | |
99 | 8.69k | void TiffFinder::visitDataEntry(TiffDataEntry* object) { |
100 | 8.69k | findObject(object); |
101 | 8.69k | } |
102 | | |
103 | 26.9k | void TiffFinder::visitImageEntry(TiffImageEntry* object) { |
104 | 26.9k | findObject(object); |
105 | 26.9k | } |
106 | | |
107 | 115k | void TiffFinder::visitSizeEntry(TiffSizeEntry* object) { |
108 | 115k | findObject(object); |
109 | 115k | } |
110 | | |
111 | 100k | void TiffFinder::visitDirectory(TiffDirectory* object) { |
112 | 100k | findObject(object); |
113 | 100k | } |
114 | | |
115 | 19.9k | void TiffFinder::visitSubIfd(TiffSubIfd* object) { |
116 | 19.9k | findObject(object); |
117 | 19.9k | } |
118 | | |
119 | 11.0k | void TiffFinder::visitMnEntry(TiffMnEntry* object) { |
120 | 11.0k | findObject(object); |
121 | 11.0k | } |
122 | | |
123 | 10.2k | void TiffFinder::visitIfdMakernote(TiffIfdMakernote* object) { |
124 | 10.2k | findObject(object); |
125 | 10.2k | } |
126 | | |
127 | 58.3k | void TiffFinder::visitBinaryArray(TiffBinaryArray* object) { |
128 | 58.3k | findObject(object); |
129 | 58.3k | } |
130 | | |
131 | 1.63M | void TiffFinder::visitBinaryElement(TiffBinaryElement* object) { |
132 | 1.63M | findObject(object); |
133 | 1.63M | } |
134 | | |
135 | | TiffCopier::TiffCopier(TiffComponent* pRoot, uint32_t root, const TiffHeaderBase* pHeader, |
136 | | PrimaryGroups pPrimaryGroups) : |
137 | 0 | pRoot_(pRoot), root_(root), pHeader_(pHeader), pPrimaryGroups_(std::move(pPrimaryGroups)) { |
138 | 0 | } |
139 | | |
140 | 0 | void TiffCopier::copyObject(const TiffComponent* object) { |
141 | 0 | if (pHeader_->isImageTag(object->tag(), object->group(), pPrimaryGroups_)) { |
142 | 0 | auto clone = object->clone(); |
143 | | // Assumption is that the corresponding TIFF entry doesn't exist |
144 | 0 | auto tiffPath = TiffCreator::getPath(object->tag(), object->group(), root_); |
145 | 0 | pRoot_->addPath(object->tag(), tiffPath, pRoot_, std::move(clone)); |
146 | | #ifdef EXIV2_DEBUG_MESSAGES |
147 | | ExifKey key(object->tag(), groupName(object->group())); |
148 | | std::cerr << "Copied " << key << "\n"; |
149 | | #endif |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | 0 | void TiffCopier::visitEntry(TiffEntry* object) { |
154 | 0 | copyObject(object); |
155 | 0 | } |
156 | | |
157 | 0 | void TiffCopier::visitDataEntry(TiffDataEntry* object) { |
158 | 0 | copyObject(object); |
159 | 0 | } |
160 | | |
161 | 0 | void TiffCopier::visitImageEntry(TiffImageEntry* object) { |
162 | 0 | copyObject(object); |
163 | 0 | } |
164 | | |
165 | 0 | void TiffCopier::visitSizeEntry(TiffSizeEntry* object) { |
166 | 0 | copyObject(object); |
167 | 0 | } |
168 | | |
169 | 0 | void TiffCopier::visitDirectory(TiffDirectory* /*object*/) { |
170 | | // Do not copy directories (avoids problems with SubIfds) |
171 | 0 | } |
172 | | |
173 | 0 | void TiffCopier::visitSubIfd(TiffSubIfd* object) { |
174 | 0 | copyObject(object); |
175 | 0 | } |
176 | | |
177 | 0 | void TiffCopier::visitMnEntry(TiffMnEntry* object) { |
178 | 0 | copyObject(object); |
179 | 0 | } |
180 | | |
181 | 0 | void TiffCopier::visitIfdMakernote(TiffIfdMakernote* object) { |
182 | 0 | copyObject(object); |
183 | 0 | } |
184 | | |
185 | 0 | void TiffCopier::visitBinaryArray(TiffBinaryArray* object) { |
186 | 0 | copyObject(object); |
187 | 0 | } |
188 | | |
189 | 0 | void TiffCopier::visitBinaryElement(TiffBinaryElement* object) { |
190 | 0 | copyObject(object); |
191 | 0 | } |
192 | | |
193 | | TiffDecoder::TiffDecoder(ExifData& exifData, IptcData& iptcData, XmpData& xmpData, TiffComponent* pRoot, |
194 | | FindDecoderFct findDecoderFct, const DecodeParams& dp) : |
195 | 26.2k | exifData_(exifData), |
196 | 26.2k | iptcData_(iptcData), |
197 | 26.2k | xmpData_(xmpData), |
198 | 26.2k | pRoot_(pRoot), |
199 | 26.2k | findDecoderFct_(findDecoderFct), |
200 | 26.2k | max_recursion_depth_(dp.max_recursion_depth()) { |
201 | | // #1402 Fujifilm RAF. Search for the make |
202 | | // Find camera make in existing metadata (read from the JPEG) |
203 | 26.2k | ExifKey key("Exif.Image.Make"); |
204 | 26.2k | if (exifData_.findKey(key) != exifData_.end()) { |
205 | 11 | make_ = exifData_.findKey(key)->toString(); |
206 | 26.2k | } else { |
207 | | // Find camera make by looking for tag 0x010f in IFD0 |
208 | 26.2k | TiffFinder finder(0x010f, IfdId::ifd0Id); |
209 | 26.2k | pRoot_->accept(finder); |
210 | 26.2k | auto te = dynamic_cast<const TiffEntryBase*>(finder.result()); |
211 | 26.2k | if (te && te->pValue()) { |
212 | 7.79k | make_ = te->pValue()->toString(); |
213 | 7.79k | } |
214 | 26.2k | } |
215 | 26.2k | } |
216 | | |
217 | 1.29M | void TiffDecoder::visitEntry(TiffEntry* object) { |
218 | 1.29M | decodeTiffEntry(object); |
219 | 1.29M | } |
220 | | |
221 | 1.14k | void TiffDecoder::visitDataEntry(TiffDataEntry* object) { |
222 | 1.14k | decodeTiffEntry(object); |
223 | 1.14k | } |
224 | | |
225 | 4.57k | void TiffDecoder::visitImageEntry(TiffImageEntry* object) { |
226 | 4.57k | decodeTiffEntry(object); |
227 | 4.57k | } |
228 | | |
229 | 9.37k | void TiffDecoder::visitSizeEntry(TiffSizeEntry* object) { |
230 | 9.37k | decodeTiffEntry(object); |
231 | 9.37k | } |
232 | | |
233 | 43.0k | void TiffDecoder::visitDirectory(TiffDirectory* /* object */) { |
234 | | // Nothing to do |
235 | 43.0k | } |
236 | | |
237 | 10.4k | void TiffDecoder::visitSubIfd(TiffSubIfd* object) { |
238 | 10.4k | decodeTiffEntry(object); |
239 | 10.4k | } |
240 | | |
241 | 1.43k | void TiffDecoder::visitMnEntry(TiffMnEntry* object) { |
242 | | // Always decode binary makernote tag |
243 | 1.43k | decodeTiffEntry(object); |
244 | 1.43k | } |
245 | | |
246 | 991 | void TiffDecoder::visitIfdMakernote(TiffIfdMakernote* object) { |
247 | 991 | exifData_["Exif.MakerNote.Offset"] = static_cast<uint32_t>(object->mnOffset()); |
248 | 991 | switch (object->byteOrder()) { |
249 | 991 | case littleEndian: |
250 | 991 | exifData_["Exif.MakerNote.ByteOrder"] = "II"; |
251 | 991 | break; |
252 | 0 | case bigEndian: |
253 | 0 | exifData_["Exif.MakerNote.ByteOrder"] = "MM"; |
254 | 0 | break; |
255 | 0 | case invalidByteOrder: |
256 | 0 | break; |
257 | 991 | } |
258 | 991 | } |
259 | | |
260 | 84.1k | void TiffDecoder::getObjData(const byte*& pData, size_t& size, uint16_t tag, IfdId group, const TiffEntryBase* object) { |
261 | 84.1k | if (object && object->tag() == tag && object->group() == group) { |
262 | 84.0k | pData = object->pData(); |
263 | 84.0k | size = object->size(); |
264 | 84.0k | return; |
265 | 84.0k | } |
266 | 40 | TiffFinder finder(tag, group); |
267 | 40 | pRoot_->accept(finder); |
268 | 40 | if (auto te = dynamic_cast<const TiffEntryBase*>(finder.result())) { |
269 | 0 | pData = te->pData(); |
270 | 0 | size = te->size(); |
271 | 0 | return; |
272 | 0 | } |
273 | 40 | } |
274 | | |
275 | 84.0k | void TiffDecoder::decodeXmp(const TiffEntryBase* object) { |
276 | | // add Exif tag anyway |
277 | 84.0k | decodeStdTiffEntry(object); |
278 | | |
279 | 84.0k | const byte* pData = nullptr; |
280 | 84.0k | size_t size = 0; |
281 | 84.0k | getObjData(pData, size, 0x02bc, IfdId::ifd0Id, object); |
282 | 84.0k | if (pData) { |
283 | 84.0k | std::string xmpPacket; |
284 | 84.0k | xmpPacket.assign(reinterpret_cast<const char*>(pData), size); |
285 | 84.0k | std::string::size_type idx = xmpPacket.find_first_of('<'); |
286 | 84.0k | if (idx != std::string::npos && idx > 0) { |
287 | 57.7k | #ifndef SUPPRESS_WARNINGS |
288 | 57.7k | EXV_WARNING << "Removing " << idx << " characters from the beginning of the XMP packet\n"; |
289 | 57.7k | #endif |
290 | 57.7k | xmpPacket = xmpPacket.substr(idx); |
291 | 57.7k | } |
292 | 84.0k | const DecodeParams dp(max_recursion_depth_); |
293 | 84.0k | if (XmpParser::decode(xmpData_, xmpPacket, dp)) { |
294 | 79.0k | #ifndef SUPPRESS_WARNINGS |
295 | 79.0k | EXV_WARNING << "Failed to decode XMP metadata.\n"; |
296 | 79.0k | #endif |
297 | 79.0k | } |
298 | 84.0k | } |
299 | 84.0k | } // TiffDecoder::decodeXmp |
300 | | |
301 | 85 | void TiffDecoder::decodeIptc(const TiffEntryBase* object) { |
302 | | // add Exif tag anyway |
303 | 85 | decodeStdTiffEntry(object); |
304 | | |
305 | | // All tags are read at this point, so the first time we come here, |
306 | | // find the relevant IPTC tag and decode IPTC if found |
307 | 85 | if (decodedIptc_) { |
308 | 30 | return; |
309 | 30 | } |
310 | 55 | decodedIptc_ = true; |
311 | | // 1st choice: IPTCNAA |
312 | 55 | const byte* pData = nullptr; |
313 | 55 | size_t size = 0; |
314 | 55 | getObjData(pData, size, 0x83bb, IfdId::ifd0Id, object); |
315 | 55 | if (pData) { |
316 | 33 | if (0 == IptcParser::decode(iptcData_, pData, size)) { |
317 | 15 | return; |
318 | 15 | } |
319 | 18 | #ifndef SUPPRESS_WARNINGS |
320 | 18 | EXV_WARNING << "Failed to decode IPTC block found in " << "Directory Image, entry 0x83bb\n"; |
321 | | |
322 | 18 | #endif |
323 | 18 | } |
324 | | |
325 | | // 2nd choice if no IPTCNAA record found or failed to decode it: |
326 | | // ImageResources |
327 | 40 | pData = nullptr; |
328 | 40 | size = 0; |
329 | 40 | getObjData(pData, size, 0x8649, IfdId::ifd0Id, object); |
330 | 40 | if (pData) { |
331 | 22 | const byte* record = nullptr; |
332 | 22 | uint32_t sizeHdr = 0; |
333 | 22 | uint32_t sizeData = 0; |
334 | 22 | if (0 != Photoshop::locateIptcIrb(pData, size, &record, sizeHdr, sizeData)) { |
335 | 22 | return; |
336 | 22 | } |
337 | 0 | if (0 == IptcParser::decode(iptcData_, record + sizeHdr, sizeData)) { |
338 | 0 | return; |
339 | 0 | } |
340 | 0 | #ifndef SUPPRESS_WARNINGS |
341 | 0 | EXV_WARNING << "Failed to decode IPTC block found in " << "Directory Image, entry 0x8649\n"; |
342 | |
|
343 | 0 | #endif |
344 | 0 | } |
345 | 40 | } // TiffMetadataDecoder::decodeIptc |
346 | | |
347 | 975 | static const TagInfo* findTag(const TagInfo* pList, uint16_t tag) { |
348 | 68.2k | while (pList->tag_ != 0xffff && pList->tag_ != tag) |
349 | 67.2k | pList++; |
350 | 975 | return pList->tag_ != 0xffff ? pList : nullptr; |
351 | 975 | } |
352 | | |
353 | 510 | void TiffDecoder::decodeCanonAFInfo(const TiffEntryBase* object) { |
354 | | // report Exif.Canon.AFInfo as usual |
355 | 510 | TiffDecoder::decodeStdTiffEntry(object); |
356 | 510 | if (object->pValue()->count() < 3 || object->pValue()->typeId() != unsignedShort) |
357 | 43 | return; // insufficient data |
358 | | |
359 | | // create vector of signedShorts from unsignedShorts in Exif.Canon.AFInfo |
360 | 467 | std::vector<int16_t> ints; |
361 | 467 | std::vector<uint16_t> uint; |
362 | 68.1k | for (size_t i = 0; i < object->pValue()->count(); i++) { |
363 | 67.6k | ints.push_back(object->pValue()->toInt64(i)); |
364 | 67.6k | uint.push_back(object->pValue()->toUint32(i)); |
365 | 67.6k | } |
366 | | // Check this is AFInfo2 (ints[0] = bytes in object) |
367 | 467 | if (ints.front() != static_cast<int16_t>(object->pValue()->count()) * 2) |
368 | 391 | return; |
369 | | |
370 | 76 | std::string familyGroup(std::string("Exif.") + groupName(object->group()) + "."); |
371 | | |
372 | 76 | const uint16_t nPoints = uint.at(2); |
373 | 76 | const uint16_t nMasks = (nPoints + 15) / (sizeof(uint16_t) * 8); |
374 | 76 | int nStart = 0; |
375 | | |
376 | 76 | const std::tuple<uint16_t, uint16_t, bool> records[] = { |
377 | 76 | {0x2600, 1, true}, // AFInfoSize |
378 | 76 | {0x2601, 1, true}, // AFAreaMode |
379 | 76 | {0x2602, 1, true}, // AFNumPoints |
380 | 76 | {0x2603, 1, true}, // AFValidPoints |
381 | 76 | {0x2604, 1, true}, // AFCanonImageWidth |
382 | 76 | {0x2605, 1, true}, // AFCanonImageHeight |
383 | 76 | {0x2606, 1, true}, // AFImageWidth" |
384 | 76 | {0x2607, 1, true}, // AFImageHeight |
385 | 76 | {0x2608, nPoints, true}, // AFAreaWidths |
386 | 76 | {0x2609, nPoints, true}, // AFAreaHeights |
387 | 76 | {0x260a, nPoints, true}, // AFXPositions |
388 | 76 | {0x260b, nPoints, true}, // AFYPositions |
389 | 76 | {0x260c, nMasks, false}, // AFPointsInFocus |
390 | 76 | {0x260d, nMasks, false}, // AFPointsSelected |
391 | 76 | {0x260e, nMasks, false}, // AFPointsUnusable |
392 | 76 | }; |
393 | | // check we have enough data! |
394 | 76 | uint16_t count = 0; |
395 | 1.09k | for (const auto& [tag, size, bSigned] : records) { |
396 | 1.09k | count += size; |
397 | 1.09k | if (count > ints.size()) |
398 | 11 | return; |
399 | 1.09k | } |
400 | | |
401 | 975 | for (const auto& [tag, size, bSigned] : records) { |
402 | 975 | auto pTags = ExifTags::tagList("Canon"); |
403 | 975 | if (auto pTag = findTag(pTags, tag)) { |
404 | 975 | auto v = Exiv2::Value::create(bSigned ? Exiv2::signedShort : Exiv2::unsignedShort); |
405 | 975 | std::string s; |
406 | 975 | if (bSigned) { |
407 | 7.74k | for (uint16_t k = 0; k < size; k++) |
408 | 6.96k | s += stringFormat(" {}", ints.at(nStart++)); |
409 | 780 | } else { |
410 | 507 | for (uint16_t k = 0; k < size; k++) |
411 | 312 | s += stringFormat(" {}", uint.at(nStart++)); |
412 | 195 | } |
413 | | |
414 | 975 | v->read(s); |
415 | 975 | exifData_[familyGroup + pTag->name_] = *v; |
416 | 975 | } |
417 | 975 | } |
418 | 65 | } |
419 | | |
420 | 1.58M | void TiffDecoder::decodeTiffEntry(const TiffEntryBase* object) { |
421 | | // Don't decode the entry if value is not set |
422 | 1.58M | if (!object->pValue()) |
423 | 568k | return; |
424 | | |
425 | | // skip decoding if decoderFct == 0 |
426 | 1.01M | if (auto decoderFct = findDecoderFct_(make_, object->tag(), object->group())) |
427 | 1.01M | std::invoke(decoderFct, *this, object); |
428 | 1.01M | } // TiffDecoder::decodeTiffEntry |
429 | | |
430 | 1.01M | void TiffDecoder::decodeStdTiffEntry(const TiffEntryBase* object) { |
431 | 1.01M | ExifKey key(object->tag(), groupName(object->group())); |
432 | 1.01M | key.setIdx(object->idx()); |
433 | 1.01M | exifData_.add(key, object->pValue()); |
434 | | |
435 | 1.01M | } // TiffDecoder::decodeTiffEntry |
436 | | |
437 | 10.1k | void TiffDecoder::visitBinaryArray(TiffBinaryArray* object) { |
438 | 10.1k | if (!object->cfg() || !object->decoded()) { |
439 | 5.89k | decodeTiffEntry(object); |
440 | 5.89k | } |
441 | 10.1k | } |
442 | | |
443 | 254k | void TiffDecoder::visitBinaryElement(TiffBinaryElement* object) { |
444 | 254k | decodeTiffEntry(object); |
445 | 254k | } |
446 | | |
447 | | TiffEncoder::TiffEncoder(ExifData exifData, const IptcData& iptcData, const XmpData& xmpData, TiffComponent* pRoot, |
448 | | bool isNewImage, PrimaryGroups pPrimaryGroups, const TiffHeaderBase* pHeader, |
449 | | FindEncoderFct findEncoderFct) : |
450 | 0 | exifData_(std::move(exifData)), |
451 | 0 | iptcData_(iptcData), |
452 | 0 | xmpData_(xmpData), |
453 | 0 | pHeader_(pHeader), |
454 | 0 | pRoot_(pRoot), |
455 | 0 | isNewImage_(isNewImage), |
456 | 0 | pPrimaryGroups_(std::move(pPrimaryGroups)), |
457 | 0 | byteOrder_(pHeader->byteOrder()), |
458 | 0 | origByteOrder_(byteOrder_), |
459 | 0 | findEncoderFct_(findEncoderFct) { |
460 | 0 | encodeIptc(); |
461 | 0 | encodeXmp(); |
462 | | |
463 | | // Find camera make |
464 | 0 | ExifKey key("Exif.Image.Make"); |
465 | 0 | if (auto pos = exifData_.findKey(key); pos != exifData_.end()) { |
466 | 0 | make_ = pos->toString(); |
467 | 0 | } |
468 | 0 | if (make_.empty() && pRoot_) { |
469 | 0 | TiffFinder finder(0x010f, IfdId::ifd0Id); |
470 | 0 | pRoot_->accept(finder); |
471 | 0 | auto te = dynamic_cast<const TiffEntryBase*>(finder.result()); |
472 | 0 | if (te && te->pValue()) { |
473 | 0 | make_ = te->pValue()->toString(); |
474 | 0 | } |
475 | 0 | } |
476 | 0 | } |
477 | | |
478 | 0 | void TiffEncoder::encodeIptc() { |
479 | | // Update IPTCNAA Exif tag, if it exists. Delete the tag if there |
480 | | // is no IPTC data anymore. |
481 | | // If there is new IPTC data and Exif.Image.ImageResources does |
482 | | // not exist, create a new IPTCNAA Exif tag. |
483 | 0 | bool del = false; |
484 | 0 | ExifKey iptcNaaKey("Exif.Image.IPTCNAA"); |
485 | 0 | auto pos = exifData_.findKey(iptcNaaKey); |
486 | 0 | if (pos != exifData_.end()) { |
487 | 0 | iptcNaaKey.setIdx(pos->idx()); |
488 | 0 | exifData_.erase(pos); |
489 | 0 | del = true; |
490 | 0 | } |
491 | 0 | DataBuf rawIptc = IptcParser::encode(iptcData_); |
492 | 0 | ExifKey irbKey("Exif.Image.ImageResources"); |
493 | 0 | pos = exifData_.findKey(irbKey); |
494 | 0 | if (pos != exifData_.end()) { |
495 | 0 | irbKey.setIdx(pos->idx()); |
496 | 0 | } |
497 | 0 | if (!rawIptc.empty() && (del || pos == exifData_.end())) { |
498 | 0 | auto value = Value::create(unsignedLong); |
499 | 0 | DataBuf buf; |
500 | 0 | if (rawIptc.size() % 4 != 0) { |
501 | | // Pad the last unsignedLong value with 0s |
502 | 0 | buf.alloc(((rawIptc.size() / 4) * 4) + 4); |
503 | 0 | std::move(rawIptc.begin(), rawIptc.end(), buf.begin()); |
504 | 0 | } else { |
505 | 0 | buf = std::move(rawIptc); // Note: This resets rawIptc |
506 | 0 | } |
507 | 0 | value->read(buf.data(), buf.size(), byteOrder_); |
508 | 0 | Exifdatum iptcDatum(iptcNaaKey, value.get()); |
509 | 0 | exifData_.add(iptcDatum); |
510 | 0 | pos = exifData_.findKey(irbKey); // needed after add() |
511 | 0 | } |
512 | | // Also update IPTC IRB in Exif.Image.ImageResources if it exists, |
513 | | // but don't create it if not. |
514 | 0 | if (pos != exifData_.end()) { |
515 | 0 | DataBuf irbBuf(pos->value().size()); |
516 | 0 | pos->value().copy(irbBuf.data(), invalidByteOrder); |
517 | 0 | irbBuf = Photoshop::setIptcIrb(irbBuf.c_data(), irbBuf.size(), iptcData_); |
518 | 0 | exifData_.erase(pos); |
519 | 0 | if (!irbBuf.empty()) { |
520 | 0 | auto value = Value::create(unsignedByte); |
521 | 0 | value->read(irbBuf.data(), irbBuf.size(), invalidByteOrder); |
522 | 0 | Exifdatum iptcDatum(irbKey, value.get()); |
523 | 0 | exifData_.add(iptcDatum); |
524 | 0 | } |
525 | 0 | } |
526 | 0 | } // TiffEncoder::encodeIptc |
527 | | |
528 | 0 | void TiffEncoder::encodeXmp() { |
529 | 0 | #ifdef EXV_HAVE_XMP_TOOLKIT |
530 | 0 | ExifKey xmpKey("Exif.Image.XMLPacket"); |
531 | | // Remove any existing XMP Exif tag |
532 | 0 | if (auto pos = exifData_.findKey(xmpKey); pos != exifData_.end()) { |
533 | 0 | xmpKey.setIdx(pos->idx()); |
534 | 0 | exifData_.erase(pos); |
535 | 0 | } |
536 | 0 | std::string xmpPacket; |
537 | 0 | if (xmpData_.usePacket()) { |
538 | 0 | xmpPacket = xmpData_.xmpPacket(); |
539 | 0 | } else { |
540 | 0 | if (XmpParser::encode(xmpPacket, xmpData_) > 1) { |
541 | 0 | #ifndef SUPPRESS_WARNINGS |
542 | 0 | EXV_ERROR << "Failed to encode XMP metadata.\n"; |
543 | 0 | #endif |
544 | 0 | } |
545 | 0 | } |
546 | 0 | if (!xmpPacket.empty()) { |
547 | | // Set the XMP Exif tag to the new value |
548 | 0 | auto value = Value::create(unsignedByte); |
549 | 0 | value->read(reinterpret_cast<const byte*>(xmpPacket.data()), xmpPacket.size(), invalidByteOrder); |
550 | 0 | Exifdatum xmpDatum(xmpKey, value.get()); |
551 | 0 | exifData_.add(xmpDatum); |
552 | 0 | } |
553 | 0 | #endif |
554 | 0 | } // TiffEncoder::encodeXmp |
555 | | |
556 | 0 | void TiffEncoder::setDirty(bool flag) { |
557 | 0 | dirty_ = flag; |
558 | 0 | setGo(geTraverse, !flag); |
559 | 0 | } |
560 | | |
561 | 0 | bool TiffEncoder::dirty() const { |
562 | 0 | return dirty_ || !exifData_.empty(); |
563 | 0 | } |
564 | | |
565 | 0 | void TiffEncoder::visitEntry(TiffEntry* object) { |
566 | 0 | encodeTiffComponent(object); |
567 | 0 | } |
568 | | |
569 | 0 | void TiffEncoder::visitDataEntry(TiffDataEntry* object) { |
570 | 0 | encodeTiffComponent(object); |
571 | 0 | } |
572 | | |
573 | 0 | void TiffEncoder::visitImageEntry(TiffImageEntry* object) { |
574 | 0 | encodeTiffComponent(object); |
575 | 0 | } |
576 | | |
577 | 0 | void TiffEncoder::visitSizeEntry(TiffSizeEntry* object) { |
578 | 0 | encodeTiffComponent(object); |
579 | 0 | } |
580 | | |
581 | 0 | void TiffEncoder::visitDirectory(TiffDirectory* /*object*/) { |
582 | | // Nothing to do |
583 | 0 | } |
584 | | |
585 | 0 | void TiffEncoder::visitDirectoryNext(TiffDirectory* object) { |
586 | | // Update type and count in IFD entries, in case they changed |
587 | 0 | byte* p = object->start() + 2; |
588 | 0 | for (const auto& component : object->components_) { |
589 | 0 | p += updateDirEntry(p, byteOrder(), component); |
590 | 0 | } |
591 | 0 | } |
592 | | |
593 | 0 | uint32_t TiffEncoder::updateDirEntry(byte* buf, ByteOrder byteOrder, const TiffComponent::SharedPtr& tiffComponent) { |
594 | 0 | auto pTiffEntry = std::dynamic_pointer_cast<TiffEntryBase>(tiffComponent); |
595 | 0 | if (!pTiffEntry) |
596 | 0 | return 0; |
597 | 0 | us2Data(buf + 2, pTiffEntry->tiffType(), byteOrder); |
598 | 0 | ul2Data(buf + 4, static_cast<uint32_t>(pTiffEntry->count()), byteOrder); |
599 | | // Move data to offset field, if it fits and is not yet there. |
600 | 0 | if (pTiffEntry->size() <= 4 && buf + 8 != pTiffEntry->pData()) { |
601 | | #ifdef EXIV2_DEBUG_MESSAGES |
602 | | std::cerr << "Copying data for tag " << pTiffEntry->tag() << " to offset area.\n"; |
603 | | #endif |
604 | 0 | memset(buf + 8, 0x0, 4); |
605 | 0 | if (pTiffEntry->size() > 0) { |
606 | 0 | std::copy_n(pTiffEntry->pData(), pTiffEntry->size(), buf + 8); |
607 | 0 | memset(const_cast<byte*>(pTiffEntry->pData()), 0x0, pTiffEntry->size()); |
608 | 0 | } |
609 | 0 | } |
610 | 0 | return 12; |
611 | 0 | } |
612 | | |
613 | 0 | void TiffEncoder::visitSubIfd(TiffSubIfd* object) { |
614 | 0 | encodeTiffComponent(object); |
615 | 0 | } |
616 | | |
617 | 0 | void TiffEncoder::visitMnEntry(TiffMnEntry* object) { |
618 | | // Test is required here as well as in the callback encoder function |
619 | 0 | if (!object->mn_) { |
620 | 0 | encodeTiffComponent(object); |
621 | 0 | } else if (del_) { |
622 | | // The makernote is made up of decoded tags, delete binary tag |
623 | 0 | ExifKey key(object->tag(), groupName(object->group())); |
624 | 0 | auto pos = exifData_.findKey(key); |
625 | 0 | if (pos != exifData_.end()) |
626 | 0 | exifData_.erase(pos); |
627 | 0 | } |
628 | 0 | } |
629 | | |
630 | 0 | void TiffEncoder::visitIfdMakernote(TiffIfdMakernote* object) { |
631 | 0 | auto pos = exifData_.findKey(ExifKey("Exif.MakerNote.ByteOrder")); |
632 | 0 | if (pos != exifData_.end()) { |
633 | | // Set Makernote byte order |
634 | 0 | ByteOrder bo = stringToByteOrder(pos->toString()); |
635 | 0 | if (bo != invalidByteOrder && bo != object->byteOrder()) { |
636 | 0 | object->setByteOrder(bo); |
637 | 0 | setDirty(); |
638 | 0 | } |
639 | 0 | if (del_) |
640 | 0 | exifData_.erase(pos); |
641 | 0 | } |
642 | 0 | if (del_) { |
643 | | // Remove remaining synthesized tags |
644 | 0 | for (auto synthesizedTag : {"Exif.MakerNote.Offset"}) { |
645 | 0 | pos = exifData_.findKey(ExifKey(synthesizedTag)); |
646 | 0 | if (pos != exifData_.end()) |
647 | 0 | exifData_.erase(pos); |
648 | 0 | } |
649 | 0 | } |
650 | | // Modify encoder for Makernote peculiarities, byte order |
651 | 0 | byteOrder_ = object->byteOrder(); |
652 | |
|
653 | 0 | } // TiffEncoder::visitIfdMakernote |
654 | | |
655 | 0 | void TiffEncoder::visitIfdMakernoteEnd(TiffIfdMakernote* /*object*/) { |
656 | | // Reset byte order back to that from the c'tor |
657 | 0 | byteOrder_ = origByteOrder_; |
658 | |
|
659 | 0 | } // TiffEncoder::visitIfdMakernoteEnd |
660 | | |
661 | 0 | void TiffEncoder::visitBinaryArray(TiffBinaryArray* object) { |
662 | 0 | if (!object->cfg() || !object->decoded()) { |
663 | 0 | encodeTiffComponent(object); |
664 | 0 | } |
665 | 0 | } |
666 | | |
667 | 0 | void TiffEncoder::visitBinaryArrayEnd(TiffBinaryArray* object) { |
668 | 0 | if (!object->cfg() || !object->decoded()) |
669 | 0 | return; |
670 | 0 | size_t size = object->TiffEntryBase::doSize(); |
671 | 0 | if (size == 0) |
672 | 0 | return; |
673 | 0 | if (!object->initialize(pRoot_)) |
674 | 0 | return; |
675 | | |
676 | | // Re-encrypt buffer if necessary |
677 | 0 | CryptFct cryptFct = object->cfg()->cryptFct_; |
678 | 0 | if (cryptFct == &sonyTagDecipher) { |
679 | 0 | cryptFct = sonyTagEncipher; |
680 | 0 | } |
681 | 0 | if (cryptFct) { |
682 | 0 | const byte* pData = object->pData(); |
683 | 0 | DataBuf buf = cryptFct(object->tag(), pData, size, pRoot_); |
684 | 0 | if (!buf.empty()) { |
685 | 0 | pData = buf.c_data(); |
686 | 0 | size = buf.size(); |
687 | 0 | } |
688 | 0 | if (!object->updOrigDataBuf(pData, size)) { |
689 | 0 | setDirty(); |
690 | 0 | } |
691 | 0 | } |
692 | 0 | } |
693 | | |
694 | 0 | void TiffEncoder::visitBinaryElement(TiffBinaryElement* object) { |
695 | | // Temporarily overwrite byte order according to that of the binary element |
696 | 0 | ByteOrder boOrig = byteOrder_; |
697 | 0 | if (object->elByteOrder() != invalidByteOrder) |
698 | 0 | byteOrder_ = object->elByteOrder(); |
699 | 0 | encodeTiffComponent(object); |
700 | 0 | byteOrder_ = boOrig; |
701 | 0 | } |
702 | | |
703 | 0 | bool TiffEncoder::isImageTag(uint16_t tag, IfdId group) const { |
704 | 0 | return !isNewImage_ && pHeader_->isImageTag(tag, group, pPrimaryGroups_); |
705 | 0 | } |
706 | | |
707 | 0 | void TiffEncoder::encodeTiffComponent(TiffEntryBase* object, const Exifdatum* datum) { |
708 | 0 | auto pos = exifData_.end(); |
709 | 0 | const Exifdatum* ed = datum; |
710 | 0 | if (!ed) { |
711 | | // Non-intrusive writing: find matching tag |
712 | 0 | ExifKey key(object->tag(), groupName(object->group())); |
713 | 0 | pos = exifData_.findKey(key); |
714 | 0 | if (pos != exifData_.end()) { |
715 | 0 | ed = &(*pos); |
716 | 0 | if (object->idx() != pos->idx()) { |
717 | | // Try to find exact match (in case of duplicate tags) |
718 | 0 | auto pos2 = std::find_if(exifData_.begin(), exifData_.end(), FindExifdatum2(object->group(), object->idx())); |
719 | 0 | if (pos2 != exifData_.end() && pos2->key() == key.key()) { |
720 | 0 | ed = &(*pos2); |
721 | 0 | pos = pos2; // make sure we delete the correct tag below |
722 | 0 | } |
723 | 0 | } |
724 | 0 | } else { |
725 | 0 | setDirty(); |
726 | | #ifdef EXIV2_DEBUG_MESSAGES |
727 | | std::cerr << "DELETING " << key << ", idx = " << object->idx() << "\n"; |
728 | | #endif |
729 | 0 | } |
730 | 0 | } else { |
731 | | // For intrusive writing, the index is used to preserve the order of |
732 | | // duplicate tags |
733 | 0 | object->idx_ = ed->idx(); |
734 | 0 | } |
735 | | // Skip encoding image tags of existing TIFF image - they were copied earlier - |
736 | | // but encode image tags of new images (creation) |
737 | 0 | if (ed && !isImageTag(object->tag(), object->group())) { |
738 | 0 | if (auto fct = findEncoderFct_(make_, object->tag(), object->group())) { |
739 | | // If an encoding function is registered for the tag, use it |
740 | 0 | std::invoke(fct, *this, object, ed); |
741 | 0 | } else { |
742 | | // Else use the encode function at the object (results in a double-dispatch |
743 | | // to the appropriate encoding function of the encoder. |
744 | 0 | object->encode(*this, ed); |
745 | 0 | } |
746 | 0 | } |
747 | 0 | if (del_ && pos != exifData_.end()) { |
748 | 0 | exifData_.erase(pos); |
749 | 0 | } |
750 | | #ifdef EXIV2_DEBUG_MESSAGES |
751 | | std::cerr << "\n"; |
752 | | #endif |
753 | 0 | } // TiffEncoder::encodeTiffComponent |
754 | | |
755 | 0 | void TiffEncoder::encodeBinaryArray(TiffBinaryArray* object, const Exifdatum* datum) { |
756 | 0 | encodeOffsetEntry(object, datum); |
757 | 0 | } // TiffEncoder::encodeBinaryArray |
758 | | |
759 | 0 | void TiffEncoder::encodeBinaryElement(TiffBinaryElement* object, const Exifdatum* datum) { |
760 | 0 | encodeTiffEntryBase(object, datum); |
761 | 0 | } // TiffEncoder::encodeArrayElement |
762 | | |
763 | 0 | void TiffEncoder::encodeDataEntry(TiffDataEntry* object, const Exifdatum* datum) { |
764 | 0 | encodeOffsetEntry(object, datum); |
765 | |
|
766 | 0 | if (!dirty_ && writeMethod() == wmNonIntrusive) { |
767 | 0 | if (object->sizeDataArea_ < object->pValue()->sizeDataArea()) { |
768 | | #ifdef EXIV2_DEBUG_MESSAGES |
769 | | ExifKey key(object->tag(), groupName(object->group())); |
770 | | std::cerr << "DATAAREA GREW " << key << "\n"; |
771 | | #endif |
772 | 0 | setDirty(); |
773 | 0 | } else { |
774 | | // Write the new dataarea, fill with 0x0 |
775 | | #ifdef EXIV2_DEBUG_MESSAGES |
776 | | ExifKey key(object->tag(), groupName(object->group())); |
777 | | std::cerr << "Writing data area for " << key << "\n"; |
778 | | #endif |
779 | 0 | DataBuf buf = object->pValue()->dataArea(); |
780 | 0 | if (!buf.empty()) { |
781 | 0 | std::copy(buf.begin(), buf.end(), object->pDataArea_); |
782 | 0 | if (object->sizeDataArea_ > buf.size()) { |
783 | 0 | memset(object->pDataArea_ + buf.size(), 0x0, object->sizeDataArea_ - buf.size()); |
784 | 0 | } |
785 | 0 | } |
786 | 0 | } |
787 | 0 | } |
788 | |
|
789 | 0 | } // TiffEncoder::encodeDataEntry |
790 | | |
791 | 0 | void TiffEncoder::encodeTiffEntry(TiffEntry* object, const Exifdatum* datum) { |
792 | 0 | encodeTiffEntryBase(object, datum); |
793 | 0 | } // TiffEncoder::encodeTiffEntry |
794 | | |
795 | 0 | void TiffEncoder::encodeImageEntry(TiffImageEntry* object, const Exifdatum* datum) { |
796 | 0 | encodeOffsetEntry(object, datum); |
797 | |
|
798 | 0 | size_t sizeDataArea = object->pValue()->sizeDataArea(); |
799 | |
|
800 | 0 | if (sizeDataArea > 0 && writeMethod() == wmNonIntrusive) { |
801 | | #ifdef EXIV2_DEBUG_MESSAGES |
802 | | std::cerr << "\t DATAAREA IS SET (NON-INTRUSIVE WRITING)"; |
803 | | #endif |
804 | 0 | setDirty(); |
805 | 0 | } |
806 | |
|
807 | 0 | if (sizeDataArea > 0 && writeMethod() == wmIntrusive) { |
808 | | #ifdef EXIV2_DEBUG_MESSAGES |
809 | | std::cerr << "\t DATAAREA IS SET (INTRUSIVE WRITING)"; |
810 | | #endif |
811 | | // Set pseudo strips (without a data pointer) from the size tag |
812 | 0 | ExifKey key(object->szTag(), groupName(object->szGroup())); |
813 | 0 | auto pos = exifData_.findKey(key); |
814 | 0 | const byte* zero = nullptr; |
815 | 0 | if (pos == exifData_.end()) { |
816 | 0 | #ifndef SUPPRESS_WARNINGS |
817 | 0 | EXV_ERROR << "Size tag " << key << " not found. Writing only one strip.\n"; |
818 | 0 | #endif |
819 | 0 | object->strips_.clear(); |
820 | 0 | object->strips_.emplace_back(zero, sizeDataArea); |
821 | 0 | } else { |
822 | 0 | size_t sizeTotal = 0; |
823 | 0 | object->strips_.clear(); |
824 | 0 | for (size_t i = 0; i < pos->count(); ++i) { |
825 | 0 | uint32_t len = pos->toUint32(i); |
826 | 0 | object->strips_.emplace_back(zero, len); |
827 | 0 | sizeTotal += len; |
828 | 0 | } |
829 | 0 | if (sizeTotal != sizeDataArea) { |
830 | 0 | #ifndef SUPPRESS_WARNINGS |
831 | 0 | ExifKey key2(object->tag(), groupName(object->group())); |
832 | 0 | EXV_ERROR << "Sum of all sizes of " << key << " != data size of " << key2 << ". " |
833 | 0 | << "This results in an invalid image.\n"; |
834 | 0 | #endif |
835 | | // Todo: How to fix? Write only one strip? |
836 | 0 | } |
837 | 0 | } |
838 | 0 | } |
839 | |
|
840 | 0 | if (sizeDataArea == 0 && writeMethod() == wmIntrusive) { |
841 | | #ifdef EXIV2_DEBUG_MESSAGES |
842 | | std::cerr << "\t USE STRIPS FROM SOURCE TREE IMAGE ENTRY"; |
843 | | #endif |
844 | | // Set strips from source tree |
845 | 0 | if (pSourceTree_) { |
846 | 0 | TiffFinder finder(object->tag(), object->group()); |
847 | 0 | pSourceTree_->accept(finder); |
848 | 0 | if (auto ti = dynamic_cast<const TiffImageEntry*>(finder.result())) { |
849 | 0 | object->strips_ = ti->strips_; |
850 | 0 | } |
851 | 0 | } |
852 | 0 | #ifndef SUPPRESS_WARNINGS |
853 | 0 | else { |
854 | 0 | ExifKey key2(object->tag(), groupName(object->group())); |
855 | 0 | EXV_WARNING << "No image data to encode " << key2 << ".\n"; |
856 | 0 | } |
857 | 0 | #endif |
858 | 0 | } |
859 | |
|
860 | 0 | } // TiffEncoder::encodeImageEntry |
861 | | |
862 | 0 | void TiffEncoder::encodeMnEntry(TiffMnEntry* object, const Exifdatum* datum) { |
863 | | // Test is required here as well as in the visit function |
864 | 0 | if (!object->mn_) |
865 | 0 | encodeTiffEntryBase(object, datum); |
866 | 0 | } // TiffEncoder::encodeMnEntry |
867 | | |
868 | 0 | void TiffEncoder::encodeSizeEntry(TiffSizeEntry* object, const Exifdatum* datum) { |
869 | 0 | encodeTiffEntryBase(object, datum); |
870 | 0 | } // TiffEncoder::encodeSizeEntry |
871 | | |
872 | 0 | void TiffEncoder::encodeSubIfd(TiffSubIfd* object, const Exifdatum* datum) { |
873 | 0 | encodeOffsetEntry(object, datum); |
874 | 0 | } // TiffEncoder::encodeSubIfd |
875 | | |
876 | 0 | void TiffEncoder::encodeTiffEntryBase(TiffEntryBase* object, const Exifdatum* datum) { |
877 | | #ifdef EXIV2_DEBUG_MESSAGES |
878 | | bool tooLarge = false; |
879 | | #endif |
880 | 0 | if (datum->size() > object->size_) { // value doesn't fit, encode for intrusive writing |
881 | 0 | setDirty(); |
882 | | #ifdef EXIV2_DEBUG_MESSAGES |
883 | | tooLarge = true; |
884 | | #endif |
885 | 0 | } |
886 | 0 | object->updateValue(datum->getValue(), byteOrder()); // clones the value |
887 | | #ifdef EXIV2_DEBUG_MESSAGES |
888 | | ExifKey key(object->tag(), groupName(object->group())); |
889 | | std::cerr << "UPDATING DATA " << key; |
890 | | if (tooLarge) { |
891 | | std::cerr << "\t\t\t ALLOCATED " << std::dec << object->size_ << " BYTES"; |
892 | | } |
893 | | #endif |
894 | 0 | } |
895 | | |
896 | 0 | void TiffEncoder::encodeOffsetEntry(TiffEntryBase* object, const Exifdatum* datum) { |
897 | 0 | size_t newSize = datum->size(); |
898 | 0 | if (newSize > object->size_) { // value doesn't fit, encode for intrusive writing |
899 | 0 | setDirty(); |
900 | 0 | object->updateValue(datum->getValue(), byteOrder()); // clones the value |
901 | | #ifdef EXIV2_DEBUG_MESSAGES |
902 | | ExifKey key(object->tag(), groupName(object->group())); |
903 | | std::cerr << "UPDATING DATA " << key; |
904 | | std::cerr << "\t\t\t ALLOCATED " << object->size() << " BYTES"; |
905 | | #endif |
906 | 0 | } else { |
907 | 0 | object->setValue(datum->getValue()); // clones the value |
908 | | #ifdef EXIV2_DEBUG_MESSAGES |
909 | | ExifKey key(object->tag(), groupName(object->group())); |
910 | | std::cerr << "NOT UPDATING " << key; |
911 | | std::cerr << "\t\t\t PRESERVE VALUE DATA"; |
912 | | #endif |
913 | 0 | } |
914 | 0 | } |
915 | | |
916 | 0 | void TiffEncoder::add(TiffComponent* pRootDir, TiffComponent::UniquePtr pSourceDir, uint32_t root) { |
917 | 0 | writeMethod_ = wmIntrusive; |
918 | 0 | pSourceTree_ = std::move(pSourceDir); |
919 | | |
920 | | // Ensure that the exifData_ entries are not deleted, to be able to |
921 | | // iterate over all remaining entries. |
922 | 0 | del_ = false; |
923 | |
|
924 | 0 | auto posBo = exifData_.end(); |
925 | 0 | for (auto i = exifData_.begin(); i != exifData_.end(); ++i) { |
926 | 0 | IfdId group = groupId(i->groupName()); |
927 | | // Skip synthesized info tags |
928 | 0 | if (group == IfdId::mnId) { |
929 | 0 | if (i->tag() == 0x0002) { |
930 | 0 | posBo = i; |
931 | 0 | } |
932 | 0 | continue; |
933 | 0 | } |
934 | | |
935 | | // Skip image tags of existing TIFF image - they were copied earlier - |
936 | | // but add and encode image tags of new images (creation) |
937 | 0 | if (isImageTag(i->tag(), group)) |
938 | 0 | continue; |
939 | | |
940 | | // Assumption is that the corresponding TIFF entry doesn't exist |
941 | 0 | auto tiffPath = TiffCreator::getPath(i->tag(), group, root); |
942 | 0 | TiffComponent* tc = pRootDir->addPath(i->tag(), tiffPath, pRootDir); |
943 | 0 | auto object = dynamic_cast<TiffEntryBase*>(tc); |
944 | | #ifdef EXIV2_DEBUG_MESSAGES |
945 | | if (!object) { |
946 | | std::cerr << "Warning: addPath() didn't add an entry for " << i->groupName() << " tag 0x" << std::setw(4) |
947 | | << std::setfill('0') << std::hex << i->tag() << "\n"; |
948 | | } |
949 | | #endif |
950 | 0 | if (object) { |
951 | 0 | encodeTiffComponent(object, &(*i)); |
952 | 0 | } |
953 | 0 | } |
954 | | |
955 | | /* |
956 | | What follows is a hack. I can't think of a better way to set |
957 | | the makernote byte order (and other properties maybe) in the |
958 | | makernote header during intrusive writing. The thing is that |
959 | | visit/encodeIfdMakernote is not called in this case and there |
960 | | can't be an Exif tag which corresponds to this component. |
961 | | */ |
962 | 0 | if (posBo == exifData_.end()) |
963 | 0 | return; |
964 | | |
965 | 0 | TiffFinder finder(0x927c, IfdId::exifId); |
966 | 0 | pRootDir->accept(finder); |
967 | 0 | if (auto te = dynamic_cast<const TiffMnEntry*>(finder.result())) { |
968 | 0 | if (const auto& mn = te->mn_) { |
969 | | // Set Makernote byte order |
970 | 0 | ByteOrder bo = stringToByteOrder(posBo->toString()); |
971 | 0 | if (bo != invalidByteOrder) |
972 | 0 | mn->setByteOrder(bo); |
973 | 0 | } |
974 | 0 | } |
975 | |
|
976 | 0 | } // TiffEncoder::add |
977 | | |
978 | | TiffReader::TiffReader(const byte* pData, size_t size, TiffComponent* pRoot, TiffRwState state) : |
979 | 26.2k | pData_(pData), size_(size), pLast_(pData + size), pRoot_(pRoot), origState_(state), mnState_(state) { |
980 | 26.2k | pState_ = &origState_; |
981 | | |
982 | 26.2k | } // TiffReader::TiffReader |
983 | | |
984 | 27.2k | void TiffReader::setOrigState() { |
985 | 27.2k | pState_ = &origState_; |
986 | 27.2k | } |
987 | | |
988 | 27.2k | void TiffReader::setMnState(const TiffRwState* state) { |
989 | 27.2k | if (state) { |
990 | | // invalidByteOrder indicates 'no change' |
991 | 991 | if (state->byteOrder() == invalidByteOrder) { |
992 | 0 | mnState_ = TiffRwState{origState_.byteOrder(), state->baseOffset()}; |
993 | 991 | } else { |
994 | 991 | mnState_ = *state; |
995 | 991 | } |
996 | 991 | } |
997 | 27.2k | pState_ = &mnState_; |
998 | 27.2k | } |
999 | | |
1000 | 5.87M | ByteOrder TiffReader::byteOrder() const { |
1001 | 5.87M | return pState_->byteOrder(); |
1002 | 5.87M | } |
1003 | | |
1004 | 1.27M | size_t TiffReader::baseOffset() const { |
1005 | 1.27M | return pState_->baseOffset(); |
1006 | 1.27M | } |
1007 | | |
1008 | 5.71k | void TiffReader::readDataEntryBase(TiffDataEntryBase* object) { |
1009 | 5.71k | readTiffEntry(object); |
1010 | 5.71k | TiffFinder finder(object->szTag(), object->szGroup()); |
1011 | 5.71k | pRoot_->accept(finder); |
1012 | 5.71k | auto te = dynamic_cast<const TiffEntryBase*>(finder.result()); |
1013 | 5.71k | if (te && te->pValue()) { |
1014 | 1.23k | object->setStrips(te->pValue(), pData_, size_, baseOffset()); |
1015 | 1.23k | } |
1016 | 5.71k | } |
1017 | | |
1018 | 1.29M | void TiffReader::visitEntry(TiffEntry* object) { |
1019 | 1.29M | readTiffEntry(object); |
1020 | 1.29M | } |
1021 | | |
1022 | 1.14k | void TiffReader::visitDataEntry(TiffDataEntry* object) { |
1023 | 1.14k | readDataEntryBase(object); |
1024 | 1.14k | } |
1025 | | |
1026 | 4.57k | void TiffReader::visitImageEntry(TiffImageEntry* object) { |
1027 | 4.57k | readDataEntryBase(object); |
1028 | 4.57k | } |
1029 | | |
1030 | 9.37k | void TiffReader::visitSizeEntry(TiffSizeEntry* object) { |
1031 | 9.37k | readTiffEntry(object); |
1032 | 9.37k | TiffFinder finder(object->dtTag(), object->dtGroup()); |
1033 | 9.37k | pRoot_->accept(finder); |
1034 | 9.37k | auto te = dynamic_cast<TiffDataEntryBase*>(finder.result()); |
1035 | 9.37k | if (te && te->pValue()) { |
1036 | 4.90k | te->setStrips(object->pValue(), pData_, size_, baseOffset()); |
1037 | 4.90k | } |
1038 | 9.37k | } |
1039 | | |
1040 | 43.0k | bool TiffReader::circularReference(const byte* start, IfdId group) { |
1041 | 43.0k | if (auto pos = dirList_.find(start); pos != dirList_.end()) { |
1042 | 2.35k | #ifndef SUPPRESS_WARNINGS |
1043 | 2.35k | EXV_ERROR << groupName(group) << " pointer references previously read " << groupName(pos->second) |
1044 | 2.35k | << " directory; ignored.\n"; |
1045 | 2.35k | #endif |
1046 | 2.35k | return true; |
1047 | 2.35k | } |
1048 | 40.7k | dirList_[start] = group; |
1049 | 40.7k | return false; |
1050 | 43.0k | } |
1051 | | |
1052 | 1.02M | int TiffReader::nextIdx(IfdId group) { |
1053 | 1.02M | return ++idxSeq_[group]; |
1054 | 1.02M | } |
1055 | | |
1056 | 26.2k | void TiffReader::postProcess() { |
1057 | 26.2k | setMnState(); // All components to be post-processed must be from the Makernote |
1058 | 26.2k | postProc_ = true; |
1059 | 26.2k | for (auto pos : postList_) { |
1060 | 10.1k | pos->accept(*this); |
1061 | 10.1k | } |
1062 | 26.2k | postProc_ = false; |
1063 | 26.2k | setOrigState(); |
1064 | 26.2k | } |
1065 | | |
1066 | 43.0k | void TiffReader::visitDirectory(TiffDirectory* object) { |
1067 | 43.0k | const byte* p = object->start(); |
1068 | | |
1069 | 43.0k | if (circularReference(object->start(), object->group())) |
1070 | 2.35k | return; |
1071 | | |
1072 | 40.7k | if (p + 2 > pLast_) { |
1073 | 3 | #ifndef SUPPRESS_WARNINGS |
1074 | 3 | EXV_ERROR << "Directory " << groupName(object->group()) << ": IFD exceeds data buffer, cannot read entry count.\n"; |
1075 | 3 | #endif |
1076 | 3 | return; |
1077 | 3 | } |
1078 | 40.7k | const uint16_t n = getUShort(p, byteOrder()); |
1079 | 40.7k | p += 2; |
1080 | | // Sanity check with an "unreasonably" large number |
1081 | 40.7k | if (n > 256) { |
1082 | 4.20k | #ifndef SUPPRESS_WARNINGS |
1083 | 4.20k | EXV_ERROR << "Directory " << groupName(object->group()) << " with " << n |
1084 | 4.20k | << " entries considered invalid; not read.\n"; |
1085 | 4.20k | #endif |
1086 | 4.20k | return; |
1087 | 4.20k | } |
1088 | 1.37M | for (uint16_t i = 0; i < n; ++i) { |
1089 | 1.34M | if (p + 12 > pLast_) { |
1090 | 8.71k | #ifndef SUPPRESS_WARNINGS |
1091 | 8.71k | EXV_ERROR << "Directory " << groupName(object->group()) << ": IFD entry " << i |
1092 | 8.71k | << " lies outside of the data buffer.\n"; |
1093 | 8.71k | #endif |
1094 | 8.71k | return; |
1095 | 8.71k | } |
1096 | 1.33M | uint16_t tag = getUShort(p, byteOrder()); |
1097 | 1.33M | if (auto tc = TiffCreator::create(tag, object->group())) { |
1098 | 1.33M | tc->setStart(p); |
1099 | 1.33M | object->addChild(std::move(tc)); |
1100 | 1.33M | } else { |
1101 | 347 | #ifndef SUPPRESS_WARNINGS |
1102 | 347 | EXV_WARNING << "Unable to handle tag " << tag << ".\n"; |
1103 | 347 | #endif |
1104 | 347 | } |
1105 | 1.33M | p += 12; |
1106 | 1.33M | } |
1107 | | |
1108 | 27.7k | if (object->hasNext()) { |
1109 | 27.7k | if (p + 4 > pLast_) { |
1110 | 12 | #ifndef SUPPRESS_WARNINGS |
1111 | 12 | EXV_ERROR << "Directory " << groupName(object->group()) |
1112 | 12 | << ": IFD exceeds data buffer, cannot read next pointer.\n"; |
1113 | 12 | #endif |
1114 | 12 | return; |
1115 | 12 | } |
1116 | 27.7k | TiffComponent::UniquePtr tc; |
1117 | 27.7k | uint32_t next = getULong(p, byteOrder()); |
1118 | 27.7k | if (next) { |
1119 | 18.8k | tc = TiffCreator::create(Tag::next, object->group()); |
1120 | 18.8k | #ifndef SUPPRESS_WARNINGS |
1121 | 18.8k | if (!tc) { |
1122 | 3.69k | EXV_WARNING << "Directory " << groupName(object->group()) << " has an unexpected next pointer; ignored.\n"; |
1123 | 3.69k | } |
1124 | 18.8k | #endif |
1125 | 18.8k | } |
1126 | 27.7k | if (tc) { |
1127 | 15.1k | if (next > size_ || baseOffset() > size_ - next) { |
1128 | 10.8k | #ifndef SUPPRESS_WARNINGS |
1129 | 10.8k | EXV_ERROR << "Directory " << groupName(object->group()) << ": Next pointer is out of bounds; ignored.\n"; |
1130 | 10.8k | #endif |
1131 | 10.8k | return; |
1132 | 10.8k | } |
1133 | 4.33k | tc->setStart(pData_ + baseOffset() + next); |
1134 | 4.33k | object->addNext(std::move(tc)); |
1135 | 4.33k | } |
1136 | 27.7k | } // object->hasNext() |
1137 | | |
1138 | 27.7k | } // TiffReader::visitDirectory |
1139 | | |
1140 | 10.4k | void TiffReader::visitSubIfd(TiffSubIfd* object) { |
1141 | 10.4k | readTiffEntry(object); |
1142 | 10.4k | if ((object->tiffType() == ttUnsignedLong || object->tiffType() == ttSignedLong || object->tiffType() == ttTiffIfd) && |
1143 | 10.1k | object->count() >= 1) { |
1144 | | // Todo: Fix hack |
1145 | 9.78k | uint32_t maxi = 9; |
1146 | 9.78k | if (object->group() == IfdId::ifd1Id) |
1147 | 0 | maxi = 1; |
1148 | 21.2k | for (uint32_t i = 0; i < object->count(); ++i) { |
1149 | 12.6k | const uint32_t offset = getULong(object->pData() + (4 * i), byteOrder()); |
1150 | 12.6k | if (offset > size_ || baseOffset() > size_ - offset) { |
1151 | 1.00k | #ifndef SUPPRESS_WARNINGS |
1152 | 1.00k | EXV_ERROR << "Directory " << groupName(object->group()) << ", entry 0x" << std::setw(4) << std::setfill('0') |
1153 | 1.00k | << std::hex << object->tag() << " Sub-IFD pointer " << i << " is out of bounds; ignoring it.\n"; |
1154 | 1.00k | #endif |
1155 | 1.00k | return; |
1156 | 1.00k | } |
1157 | 11.6k | if (i >= maxi) { |
1158 | 231 | #ifndef SUPPRESS_WARNINGS |
1159 | 231 | EXV_WARNING << "Directory " << groupName(object->group()) << ", entry 0x" << std::setw(4) << std::setfill('0') |
1160 | 231 | << std::hex << object->tag() << ": Skipping sub-IFDs beyond the first " << i << ".\n"; |
1161 | 231 | #endif |
1162 | 231 | break; |
1163 | 231 | } |
1164 | | // If there are multiple dirs, group is incremented for each |
1165 | 11.4k | auto td = std::make_unique<TiffDirectory>(object->tag(), |
1166 | 11.4k | static_cast<IfdId>(static_cast<uint32_t>(object->newGroup_) + i)); |
1167 | 11.4k | td->setStart(pData_ + baseOffset() + offset); |
1168 | 11.4k | object->addChild(std::move(td)); |
1169 | 11.4k | } |
1170 | 9.78k | } |
1171 | 678 | #ifndef SUPPRESS_WARNINGS |
1172 | 678 | else { |
1173 | 678 | EXV_WARNING << "Directory " << groupName(object->group()) << ", entry 0x" << std::setw(4) << std::setfill('0') |
1174 | 678 | << std::hex << object->tag() << " doesn't look like a sub-IFD.\n"; |
1175 | 678 | } |
1176 | 10.4k | #endif |
1177 | | |
1178 | 10.4k | } // TiffReader::visitSubIfd |
1179 | | |
1180 | 1.43k | void TiffReader::visitMnEntry(TiffMnEntry* object) { |
1181 | 1.43k | readTiffEntry(object); |
1182 | | // Find camera make |
1183 | 1.43k | TiffFinder finder(0x010f, IfdId::ifd0Id); |
1184 | 1.43k | pRoot_->accept(finder); |
1185 | 1.43k | auto te = dynamic_cast<const TiffEntryBase*>(finder.result()); |
1186 | 1.43k | if (te && te->pValue()) { |
1187 | 1.30k | auto make = te->pValue()->toString(); |
1188 | | // create concrete makernote, based on make and makernote contents |
1189 | 1.30k | object->mn_ = |
1190 | 1.30k | TiffMnCreator::create(object->tag(), object->mnGroup_, make, object->pData_, object->size_, byteOrder()); |
1191 | 1.30k | } |
1192 | 1.43k | if (object->mn_) |
1193 | 999 | object->mn_->setStart(object->pData()); |
1194 | | |
1195 | 1.43k | } // TiffReader::visitMnEntry |
1196 | | |
1197 | 999 | void TiffReader::visitIfdMakernote(TiffIfdMakernote* object) { |
1198 | 999 | object->setImageByteOrder(byteOrder()); // set the byte order for the image |
1199 | | |
1200 | 999 | if (!object->readHeader(object->start(), pLast_ - object->start(), byteOrder())) { |
1201 | 8 | #ifndef SUPPRESS_WARNINGS |
1202 | 8 | EXV_ERROR << "Failed to read " << groupName(object->ifd_.group()) << " IFD Makernote header.\n"; |
1203 | | #ifdef EXIV2_DEBUG_MESSAGES |
1204 | | if (pLast_ - object->start() >= 16u) { |
1205 | | hexdump(std::cerr, object->start(), 16u); |
1206 | | } |
1207 | | #endif // EXIV2_DEBUG_MESSAGES |
1208 | 8 | #endif // SUPPRESS_WARNINGS |
1209 | 8 | setGo(geKnownMakernote, false); |
1210 | 8 | return; |
1211 | 8 | } |
1212 | | |
1213 | 991 | auto start = object->start(); |
1214 | 991 | auto offset = object->ifdOffset(); |
1215 | 991 | enforce(pData_ <= start, ErrorCode::kerCorruptedMetadata); |
1216 | 991 | enforce(start <= pLast_, ErrorCode::kerCorruptedMetadata); |
1217 | 991 | enforce(offset <= static_cast<size_t>(pLast_ - start), ErrorCode::kerCorruptedMetadata); |
1218 | 991 | object->ifd_.setStart(start + offset); |
1219 | | |
1220 | | // Modify reader for Makernote peculiarities, byte order and offset |
1221 | 991 | object->mnOffset_ = start - pData_; |
1222 | 991 | auto state = TiffRwState{object->byteOrder(), object->baseOffset()}; |
1223 | 991 | setMnState(&state); |
1224 | | |
1225 | 991 | } // TiffReader::visitIfdMakernote |
1226 | | |
1227 | 991 | void TiffReader::visitIfdMakernoteEnd(TiffIfdMakernote* /*object*/) { |
1228 | | // Reset state (byte order, create function, offset) back to that for the image |
1229 | 991 | setOrigState(); |
1230 | 991 | } // TiffReader::visitIfdMakernoteEnd |
1231 | | |
1232 | 1.33M | void TiffReader::readTiffEntry(TiffEntryBase* object) { |
1233 | 1.33M | try { |
1234 | 1.33M | byte* p = object->start(); |
1235 | | |
1236 | 1.33M | if (p + 12 > pLast_) { |
1237 | 0 | #ifndef SUPPRESS_WARNINGS |
1238 | 0 | EXV_ERROR << "Entry in directory " << groupName(object->group()) |
1239 | 0 | << "requests access to memory beyond the data buffer. " << "Skipping entry.\n"; |
1240 | 0 | #endif |
1241 | 0 | return; |
1242 | 0 | } |
1243 | | // Component already has tag |
1244 | 1.33M | p += 2; |
1245 | 1.33M | auto tiffType = static_cast<TiffType>(getUShort(p, byteOrder())); |
1246 | 1.33M | TypeId typeId = toTypeId(tiffType, object->tag(), object->group()); |
1247 | 1.33M | size_t typeSize = TypeInfo::typeSize(typeId); |
1248 | 1.33M | if (0 == typeSize) { |
1249 | 904k | #ifndef SUPPRESS_WARNINGS |
1250 | 904k | EXV_WARNING << stringFormat( |
1251 | 904k | "Directory {}, entry 0x{:04x} has unknown Exif (TIFF) type {}; setting type size 1.\n", |
1252 | 904k | groupName(object->group()), object->tag(), static_cast<uint16_t>(tiffType)); |
1253 | 904k | #endif |
1254 | 904k | typeSize = 1; |
1255 | 904k | } |
1256 | 1.33M | p += 2; |
1257 | 1.33M | uint32_t count = getULong(p, byteOrder()); |
1258 | 1.33M | if (count >= 0x10000000) { |
1259 | 568k | #ifndef SUPPRESS_WARNINGS |
1260 | 568k | EXV_ERROR << stringFormat("Directory {}, entry 0x{:04x} has invalid size {}*{}; skipping entry.\n", |
1261 | 568k | groupName(object->group()), object->tag(), count, typeSize); |
1262 | 568k | #endif |
1263 | 568k | return; |
1264 | 568k | } |
1265 | 766k | p += 4; |
1266 | | |
1267 | 766k | if (count > std::numeric_limits<size_t>::max() / typeSize) { |
1268 | 0 | throw Error(ErrorCode::kerArithmeticOverflow); |
1269 | 0 | } |
1270 | 766k | size_t size = typeSize * count; |
1271 | 766k | size_t offset = getULong(p, byteOrder()); |
1272 | 766k | byte* pData = p; |
1273 | 766k | if (size > 4 && Safe::add<size_t>(baseOffset(), offset) >= size_) { |
1274 | | // #1143 |
1275 | 226k | if (object->tag() == 0x2001 && std::string(groupName(object->group())) == "Sony1") { |
1276 | | // This tag is Exif.Sony1.PreviewImage, which refers to a preview image which is |
1277 | | // not stored in the metadata. Instead it is stored at the end of the file, after |
1278 | | // the main image. The value of `size` refers to the size of the preview image, not |
1279 | | // the size of the tag's payload, so we set it to zero here so that we don't attempt |
1280 | | // to read those bytes from the metadata. We currently leave this tag as "undefined", |
1281 | | // although we may attempt to handle it better in the future. More discussion of |
1282 | | // this issue can be found here: |
1283 | | // |
1284 | | // https://github.com/Exiv2/exiv2/issues/2001 |
1285 | | // https://github.com/Exiv2/exiv2/pull/2008 |
1286 | | // https://github.com/Exiv2/exiv2/pull/2013 |
1287 | 0 | typeId = undefined; |
1288 | 0 | size = 0; |
1289 | 226k | } else { |
1290 | 226k | #ifndef SUPPRESS_WARNINGS |
1291 | 226k | EXV_ERROR << "Offset of directory " << groupName(object->group()) << ", entry 0x" << std::setw(4) |
1292 | 226k | << std::setfill('0') << std::hex << object->tag() << " is out of bounds: " << "Offset = 0x" |
1293 | 226k | << std::setw(8) << std::setfill('0') << std::hex << offset << "; truncating the entry\n"; |
1294 | 226k | #endif |
1295 | 226k | } |
1296 | 226k | size = 0; |
1297 | 226k | } |
1298 | 766k | if (size > 4) { |
1299 | | // setting pData to pData_ + baseOffset() + offset can result in pData pointing to invalid memory, |
1300 | | // as offset can be arbitrarily large |
1301 | 336k | if (Safe::add<size_t>(baseOffset(), offset) > static_cast<size_t>(pLast_ - pData_)) { |
1302 | 0 | throw Error(ErrorCode::kerCorruptedMetadata); |
1303 | 0 | } |
1304 | 336k | pData = const_cast<byte*>(pData_) + baseOffset() + offset; |
1305 | | |
1306 | | // check for size being invalid |
1307 | 336k | if (size > static_cast<size_t>(pLast_ - pData)) { |
1308 | 71.3k | #ifndef SUPPRESS_WARNINGS |
1309 | 71.3k | EXV_ERROR << "Upper boundary of data for " << "directory " << groupName(object->group()) << ", entry 0x" |
1310 | 71.3k | << std::setw(4) << std::setfill('0') << std::hex << object->tag() |
1311 | 71.3k | << " is out of bounds: " << "Offset = 0x" << std::setw(8) << std::setfill('0') << std::hex << offset |
1312 | 71.3k | << ", size = " << std::dec << size |
1313 | 71.3k | << ", exceeds buffer size by " |
1314 | | // cast to make MSVC happy |
1315 | 71.3k | << size - static_cast<size_t>(pLast_ - pData) << " Bytes; truncating the entry\n"; |
1316 | 71.3k | #endif |
1317 | 71.3k | size = 0; |
1318 | 71.3k | } |
1319 | 336k | } |
1320 | 766k | auto v = Value::create(typeId); |
1321 | 766k | enforce(v != nullptr, ErrorCode::kerCorruptedMetadata); |
1322 | 766k | v->read(pData, size, byteOrder()); |
1323 | | |
1324 | 766k | object->setValue(std::move(v)); |
1325 | 766k | auto d = std::make_shared<DataBuf>(); |
1326 | 766k | object->setData(pData, size, std::move(d)); |
1327 | 766k | object->setOffset(offset); |
1328 | 766k | object->setIdx(nextIdx(object->group())); |
1329 | 766k | } catch (std::overflow_error&) { |
1330 | 0 | throw Error(ErrorCode::kerCorruptedMetadata); // #562 don't throw std::overflow_error |
1331 | 0 | } |
1332 | 1.33M | } // TiffReader::readTiffEntry |
1333 | | |
1334 | 20.2k | void TiffReader::visitBinaryArray(TiffBinaryArray* object) { |
1335 | 20.2k | if (!postProc_) { |
1336 | | // Defer reading children until after all other components are read, but |
1337 | | // since state (offset) is not set during post-processing, read entry here |
1338 | 10.1k | readTiffEntry(object); |
1339 | 10.1k | object->iniOrigDataBuf(); |
1340 | 10.1k | postList_.push_back(object); |
1341 | 10.1k | return; |
1342 | 10.1k | } |
1343 | | // Check duplicates |
1344 | 10.1k | TiffFinder finder(object->tag(), object->group()); |
1345 | 10.1k | pRoot_->accept(finder); |
1346 | 10.1k | if (auto te = dynamic_cast<const TiffEntryBase*>(finder.result())) { |
1347 | 10.1k | if (te->idx() != object->idx()) { |
1348 | 4.63k | #ifndef SUPPRESS_WARNINGS |
1349 | 4.63k | EXV_WARNING << "Not decoding duplicate binary array tag 0x" << std::setw(4) << std::setfill('0') << std::hex |
1350 | 4.63k | << object->tag() << std::dec << ", group " << groupName(object->group()) << ", idx " << object->idx() |
1351 | 4.63k | << "\n"; |
1352 | 4.63k | #endif |
1353 | 4.63k | object->setDecoded(false); |
1354 | 4.63k | return; |
1355 | 4.63k | } |
1356 | 10.1k | } |
1357 | | |
1358 | 5.47k | if (object->TiffEntryBase::doSize() == 0) |
1359 | 1.26k | return; |
1360 | 4.20k | if (!object->initialize(pRoot_)) |
1361 | 0 | return; |
1362 | 4.20k | const ArrayCfg* cfg = object->cfg(); |
1363 | 4.20k | if (!cfg) |
1364 | 0 | return; |
1365 | | |
1366 | 4.20k | if (auto cryptFct = cfg->cryptFct_) { |
1367 | 0 | const byte* pData = object->pData(); |
1368 | 0 | size_t size = object->TiffEntryBase::doSize(); |
1369 | 0 | auto buf = std::make_shared<DataBuf>(cryptFct(object->tag(), pData, size, pRoot_)); |
1370 | 0 | if (!buf->empty()) |
1371 | 0 | object->setData(std::move(buf)); |
1372 | 0 | } |
1373 | | |
1374 | 4.20k | const ArrayDef* defs = object->def(); |
1375 | 4.20k | const ArrayDef* defsEnd = defs + object->defSize(); |
1376 | 4.20k | const ArrayDef* def = &cfg->elDefaultDef_; |
1377 | 4.20k | ArrayDef gap = *def; |
1378 | | |
1379 | 258k | for (size_t idx = 0; idx < object->TiffEntryBase::doSize();) { |
1380 | 254k | if (defs) { |
1381 | 54.3k | def = std::find(defs, defsEnd, idx); |
1382 | 54.3k | if (def == defsEnd) { |
1383 | 52.9k | if (cfg->concat_) { |
1384 | | // Determine gap-size |
1385 | 0 | const ArrayDef* xdef = defs; |
1386 | 0 | for (; xdef != defsEnd && xdef->idx_ <= idx; ++xdef) { |
1387 | 0 | } |
1388 | 0 | size_t gapSize = 0; |
1389 | 0 | if (xdef != defsEnd && xdef->idx_ > idx) { |
1390 | 0 | gapSize = xdef->idx_ - idx; |
1391 | 0 | } else { |
1392 | 0 | gapSize = object->TiffEntryBase::doSize() - idx; |
1393 | 0 | } |
1394 | 0 | gap.idx_ = idx; |
1395 | 0 | gap.tiffType_ = cfg->elDefaultDef_.tiffType_; |
1396 | 0 | gap.count_ = gapSize / cfg->tagStep(); |
1397 | 0 | if (gap.count_ * cfg->tagStep() != gapSize) { |
1398 | 0 | gap.tiffType_ = ttUndefined; |
1399 | 0 | gap.count_ = gapSize; |
1400 | 0 | } |
1401 | 0 | def = ⪆ |
1402 | 52.9k | } else { |
1403 | 52.9k | def = &cfg->elDefaultDef_; |
1404 | 52.9k | } |
1405 | 52.9k | } |
1406 | 54.3k | } |
1407 | 254k | idx += object->addElement(idx, *def); // idx may be different from def->idx_ |
1408 | 254k | } |
1409 | | |
1410 | 4.20k | } // TiffReader::visitBinaryArray |
1411 | | |
1412 | 254k | void TiffReader::visitBinaryElement(TiffBinaryElement* object) { |
1413 | 254k | auto pData = object->start(); |
1414 | 254k | size_t size = object->TiffEntryBase::doSize(); |
1415 | 254k | ByteOrder bo = object->elByteOrder(); |
1416 | 254k | if (bo == invalidByteOrder) |
1417 | 252k | bo = byteOrder(); |
1418 | 254k | TypeId typeId = toTypeId(object->elDef()->tiffType_, object->tag(), object->group()); |
1419 | 254k | auto v = Value::create(typeId); |
1420 | 254k | enforce(v != nullptr, ErrorCode::kerCorruptedMetadata); |
1421 | 254k | v->read(pData, size, bo); |
1422 | | |
1423 | 254k | object->setValue(std::move(v)); |
1424 | 254k | object->setOffset(0); |
1425 | 254k | object->setIdx(nextIdx(object->group())); |
1426 | 254k | } |
1427 | | |
1428 | | } // namespace Exiv2::Internal |