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