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