Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | |
3 | | // included header files |
4 | | #include "image.hpp" |
5 | | |
6 | | #include "basicio.hpp" |
7 | | #include "config.h" |
8 | | #include "enforce.hpp" |
9 | | #include "error.hpp" |
10 | | #include "futils.hpp" |
11 | | #include "image_int.hpp" |
12 | | #include "safe_op.hpp" |
13 | | #include "slice.hpp" |
14 | | |
15 | | #ifdef EXV_ENABLE_BMFF |
16 | | #include "bmffimage.hpp" |
17 | | #endif // EXV_ENABLE_BMFF |
18 | | |
19 | | #include "cr2image.hpp" |
20 | | #include "crwimage.hpp" |
21 | | #include "epsimage.hpp" |
22 | | #include "jpgimage.hpp" |
23 | | #include "mrwimage.hpp" |
24 | | #ifdef EXV_HAVE_LIBZ |
25 | | #include "pngimage.hpp" |
26 | | #endif // EXV_HAVE_LIBZ |
27 | | #include "bmpimage.hpp" |
28 | | #include "gifimage.hpp" |
29 | | #include "jp2image.hpp" |
30 | | #include "nikonmn_int.hpp" |
31 | | #include "orfimage.hpp" |
32 | | #include "pgfimage.hpp" |
33 | | #include "psdimage.hpp" |
34 | | #include "rafimage.hpp" |
35 | | #include "rw2image.hpp" |
36 | | #include "tags.hpp" |
37 | | #include "tags_int.hpp" |
38 | | #include "tgaimage.hpp" |
39 | | #include "tiffimage.hpp" |
40 | | #include "webpimage.hpp" |
41 | | #include "xmpsidecar.hpp" |
42 | | #ifdef EXV_ENABLE_VIDEO |
43 | | #include "asfvideo.hpp" |
44 | | #include "matroskavideo.hpp" |
45 | | #include "quicktimevideo.hpp" |
46 | | #include "riffvideo.hpp" |
47 | | #endif // EXV_ENABLE_VIDEO |
48 | | |
49 | | // + standard includes |
50 | | #include <bit> |
51 | | #include <cstdio> |
52 | | #include <cstring> |
53 | | #include <set> |
54 | | |
55 | | #ifdef _WIN32 |
56 | | #include <windows.h> |
57 | | #endif |
58 | | |
59 | | // ***************************************************************************** |
60 | | namespace { |
61 | | using namespace Exiv2; |
62 | | |
63 | | //! Struct for storing image types and function pointers. |
64 | | struct Registry { |
65 | | //! Comparison operator to compare a Registry structure with an image type |
66 | 1.92M | bool operator==(const ImageType& imageType) const { |
67 | 1.92M | return imageType == imageType_; |
68 | 1.92M | } |
69 | | |
70 | | // DATA |
71 | | ImageType imageType_; |
72 | | NewInstanceFct newInstance_; |
73 | | IsThisTypeFct isThisType_; |
74 | | AccessMode exifSupport_; |
75 | | AccessMode iptcSupport_; |
76 | | AccessMode xmpSupport_; |
77 | | AccessMode commentSupport_; |
78 | | }; |
79 | | |
80 | | /// \todo Use std::unordered_map for implementing the registry. Avoid to use ImageType::none |
81 | | constexpr Registry registry[] = { |
82 | | // image type creation fct type check Exif mode IPTC mode XMP mode Comment mode |
83 | | //--------------- --------------- ---------- ----------- ----------- ----------- ------------ |
84 | | {ImageType::jpeg, newJpegInstance, isJpegType, amReadWrite, amReadWrite, amReadWrite, amReadWrite}, |
85 | | {ImageType::exv, newExvInstance, isExvType, amReadWrite, amReadWrite, amReadWrite, amReadWrite}, |
86 | | {ImageType::cr2, newCr2Instance, isCr2Type, amReadWrite, amReadWrite, amReadWrite, amNone}, |
87 | | {ImageType::crw, newCrwInstance, isCrwType, amReadWrite, amNone, amNone, amReadWrite}, |
88 | | {ImageType::mrw, newMrwInstance, isMrwType, amRead, amRead, amRead, amNone}, |
89 | | {ImageType::tiff, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone}, |
90 | | {ImageType::webp, newWebPInstance, isWebPType, amReadWrite, amNone, amReadWrite, amNone}, |
91 | | {ImageType::dng, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone}, |
92 | | {ImageType::nef, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone}, |
93 | | {ImageType::pef, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone}, |
94 | | {ImageType::arw, newTiffInstance, isTiffType, amRead, amRead, amRead, amNone}, |
95 | | {ImageType::rw2, newRw2Instance, isRw2Type, amRead, amRead, amRead, amNone}, |
96 | | {ImageType::sr2, newTiffInstance, isTiffType, amRead, amRead, amRead, amNone}, |
97 | | {ImageType::srw, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone}, |
98 | | {ImageType::orf, newOrfInstance, isOrfType, amReadWrite, amReadWrite, amReadWrite, amNone}, |
99 | | #ifdef EXV_HAVE_LIBZ |
100 | | {ImageType::png, newPngInstance, isPngType, amReadWrite, amReadWrite, amReadWrite, amReadWrite}, |
101 | | #endif // EXV_HAVE_LIBZ |
102 | | {ImageType::pgf, newPgfInstance, isPgfType, amReadWrite, amReadWrite, amReadWrite, amReadWrite}, |
103 | | {ImageType::raf, newRafInstance, isRafType, amRead, amRead, amRead, amNone}, |
104 | | {ImageType::eps, newEpsInstance, isEpsType, amNone, amNone, amReadWrite, amNone}, |
105 | | {ImageType::xmp, newXmpInstance, isXmpType, amReadWrite, amReadWrite, amReadWrite, amNone}, |
106 | | {ImageType::gif, newGifInstance, isGifType, amNone, amNone, amNone, amNone}, |
107 | | {ImageType::psd, newPsdInstance, isPsdType, amReadWrite, amReadWrite, amReadWrite, amNone}, |
108 | | {ImageType::tga, newTgaInstance, isTgaType, amNone, amNone, amNone, amNone}, |
109 | | {ImageType::bmp, newBmpInstance, isBmpType, amNone, amNone, amNone, amNone}, |
110 | | {ImageType::jp2, newJp2Instance, isJp2Type, amReadWrite, amReadWrite, amReadWrite, amNone}, |
111 | | // needs to be before bmff because some ftyp files are handled as qt and |
112 | | // the rest should fall through to bmff |
113 | | #ifdef EXV_ENABLE_VIDEO |
114 | | {ImageType::qtime, newQTimeInstance, isQTimeType, amRead, amNone, amRead, amNone}, |
115 | | {ImageType::asf, newAsfInstance, isAsfType, amRead, amNone, amRead, amNone}, |
116 | | {ImageType::riff, newRiffInstance, isRiffType, amRead, amNone, amRead, amNone}, |
117 | | {ImageType::mkv, newMkvInstance, isMkvType, amRead, amNone, amRead, amNone}, |
118 | | #endif // EXV_ENABLE_VIDEO |
119 | | #ifdef EXV_ENABLE_BMFF |
120 | | {ImageType::bmff, newBmffInstance, isBmffType, amRead, amRead, amRead, amNone}, |
121 | | #endif // EXV_ENABLE_BMFF |
122 | | }; |
123 | | |
124 | | #ifdef EXV_ENABLE_FILESYSTEM |
125 | 0 | std::string pathOfFileUrl(const std::string& url) { |
126 | 0 | std::string path = url.substr(7); |
127 | 0 | size_t found = path.find('/'); |
128 | 0 | if (found == std::string::npos) |
129 | 0 | return path; |
130 | 0 | return path.substr(found); |
131 | 0 | } |
132 | | #endif |
133 | | |
134 | | } // namespace |
135 | | |
136 | | // ***************************************************************************** |
137 | | // class member definitions |
138 | | namespace Exiv2 { |
139 | | |
140 | | ImageCtorParams::ImageCtorParams(bool create, size_t max_recursion_depth) : |
141 | 118k | create_(create), max_recursion_depth_(max_recursion_depth) { |
142 | 118k | } |
143 | | |
144 | | Image::Image(ImageType type, uint16_t supportedMetadata, BasicIo::UniquePtr io, const ImageCtorParams& params) : |
145 | 118k | io_(std::move(io)), |
146 | 118k | max_recursion_depth_(params.max_recursion_depth()), |
147 | 118k | imageType_(type), |
148 | 118k | supportedMetadata_(supportedMetadata) { |
149 | 118k | } |
150 | | |
151 | 118k | Image::~Image() = default; |
152 | | |
153 | 2.46k | void Image::printStructure(std::ostream&, PrintStructureOption, size_t /*depth*/) { |
154 | 2.46k | throw Error(ErrorCode::kerUnsupportedImageType, io_->path()); |
155 | 2.46k | } |
156 | | |
157 | 31.3k | bool Image::isStringType(uint16_t type) { |
158 | 31.3k | return type == Exiv2::asciiString || type == Exiv2::unsignedByte || type == Exiv2::signedByte || |
159 | 28.5k | type == Exiv2::undefined; |
160 | 31.3k | } |
161 | 17.5k | bool Image::isShortType(uint16_t type) { |
162 | 17.5k | return type == Exiv2::unsignedShort || type == Exiv2::signedShort; |
163 | 17.5k | } |
164 | 13.2k | bool Image::isLongType(uint16_t type) { |
165 | 13.2k | return type == Exiv2::unsignedLong || type == Exiv2::signedLong; |
166 | 13.2k | } |
167 | 20 | bool Image::isLongLongType(uint16_t type) { |
168 | 20 | return type == Exiv2::unsignedLongLong || type == Exiv2::signedLongLong; |
169 | 20 | } |
170 | 5.56k | bool Image::isRationalType(uint16_t type) { |
171 | 5.56k | return type == Exiv2::unsignedRational || type == Exiv2::signedRational; |
172 | 5.56k | } |
173 | 8.03k | bool Image::is2ByteType(uint16_t type) { |
174 | 8.03k | return isShortType(type); |
175 | 8.03k | } |
176 | 5.90k | bool Image::is4ByteType(uint16_t type) { |
177 | 5.90k | return isLongType(type) || type == Exiv2::tiffFloat || type == Exiv2::tiffIfd; |
178 | 5.90k | } |
179 | 1.48k | bool Image::is8ByteType(uint16_t type) { |
180 | 1.48k | return isRationalType(type) || isLongLongType(type) || type == Exiv2::tiffIfd8 || type == Exiv2::tiffDouble; |
181 | 1.48k | } |
182 | 19.0k | bool Image::isPrintXMP(uint16_t type, Exiv2::PrintStructureOption option) { |
183 | 19.0k | return type == 700 && option == kpsXMP; |
184 | 19.0k | } |
185 | 19.0k | bool Image::isPrintICC(uint16_t type, Exiv2::PrintStructureOption option) { |
186 | 19.0k | return type == 0x8773 && option == kpsIccProfile; |
187 | 19.0k | } |
188 | | |
189 | 6.59k | bool Image::isBigEndianPlatform() { |
190 | 6.59k | return std::endian::native == std::endian::big; |
191 | 6.59k | } |
192 | 15.9k | bool Image::isLittleEndianPlatform() { |
193 | 15.9k | return std::endian::native == std::endian::little; |
194 | 15.9k | } |
195 | | |
196 | 0 | uint64_t Image::byteSwap(uint64_t value, bool bSwap) { |
197 | | #ifdef __cpp_lib_byteswap |
198 | | return bSwap ? std::byteswap(value) : value; |
199 | | #elif defined(_MSC_VER) |
200 | | return bSwap ? _byteswap_uint64(value) : value; |
201 | | #else |
202 | 0 | uint64_t result = 0; |
203 | 0 | auto source_value = reinterpret_cast<const byte*>(&value); |
204 | 0 | auto destination_value = reinterpret_cast<byte*>(&result); |
205 | |
|
206 | 0 | for (int i = 0; i < 8; i++) |
207 | 0 | destination_value[i] = source_value[8 - i - 1]; |
208 | |
|
209 | 0 | return bSwap ? result : value; |
210 | 0 | #endif |
211 | 0 | } |
212 | | |
213 | 61.5k | uint32_t Image::byteSwap(uint32_t value, bool bSwap) { |
214 | | #ifdef __cpp_lib_byteswap |
215 | | return bSwap ? std::byteswap(value) : value; |
216 | | #elif defined(_MSC_VER) |
217 | | return bSwap ? _byteswap_ulong(value) : value; |
218 | | #else |
219 | 61.5k | uint32_t result = 0; |
220 | 61.5k | result |= (value & 0x000000FFU) << 24; |
221 | 61.5k | result |= (value & 0x0000FF00U) << 8; |
222 | 61.5k | result |= (value & 0x00FF0000U) >> 8; |
223 | 61.5k | result |= (value & 0xFF000000U) >> 24; |
224 | 61.5k | return bSwap ? result : value; |
225 | 61.5k | #endif |
226 | 61.5k | } |
227 | | |
228 | 44.5k | uint16_t Image::byteSwap(uint16_t value, bool bSwap) { |
229 | | #ifdef __cpp_lib_byteswap |
230 | | return bSwap ? std::byteswap(value) : value; |
231 | | #elif defined(_MSC_VER) |
232 | | return bSwap ? _byteswap_ushort(value) : value; |
233 | | #else |
234 | 44.5k | uint16_t result = 0; |
235 | 44.5k | result |= (value & 0x00FFU) << 8; |
236 | 44.5k | result |= (value & 0xFF00U) >> 8; |
237 | 44.5k | return bSwap ? result : value; |
238 | 44.5k | #endif |
239 | 44.5k | } |
240 | | |
241 | 44.5k | uint16_t Image::byteSwap2(const DataBuf& buf, size_t offset, bool bSwap) { |
242 | 44.5k | uint16_t v = 0; |
243 | 44.5k | auto p = reinterpret_cast<char*>(&v); |
244 | 44.5k | p[0] = buf.read_uint8(offset); |
245 | 44.5k | p[1] = buf.read_uint8(offset + 1); |
246 | 44.5k | return Image::byteSwap(v, bSwap); |
247 | 44.5k | } |
248 | | |
249 | 55.1k | uint32_t Image::byteSwap4(const DataBuf& buf, size_t offset, bool bSwap) { |
250 | 55.1k | uint32_t v = 0; |
251 | 55.1k | auto p = reinterpret_cast<char*>(&v); |
252 | 55.1k | p[0] = buf.read_uint8(offset); |
253 | 55.1k | p[1] = buf.read_uint8(offset + 1); |
254 | 55.1k | p[2] = buf.read_uint8(offset + 2); |
255 | 55.1k | p[3] = buf.read_uint8(offset + 3); |
256 | 55.1k | return Image::byteSwap(v, bSwap); |
257 | 55.1k | } |
258 | | |
259 | | /// \todo not used internally. At least we should test it |
260 | 0 | uint64_t Image::byteSwap8(const DataBuf& buf, size_t offset, bool bSwap) { |
261 | 0 | uint64_t v = 0; |
262 | 0 | auto p = reinterpret_cast<byte*>(&v); |
263 | |
|
264 | 0 | for (int i = 0; i < 8; i++) |
265 | 0 | p[i] = buf.read_uint8(offset + i); |
266 | |
|
267 | 0 | return Image::byteSwap(v, bSwap); |
268 | 0 | } |
269 | | |
270 | 9.50k | const char* Image::typeName(uint16_t tag) { |
271 | | //! List of TIFF image tags |
272 | 9.50k | const char* result = nullptr; |
273 | 9.50k | switch (tag) { |
274 | 129 | case Exiv2::unsignedByte: |
275 | 129 | result = "BYTE"; |
276 | 129 | break; |
277 | 190 | case Exiv2::asciiString: |
278 | 190 | result = "ASCII"; |
279 | 190 | break; |
280 | 890 | case Exiv2::unsignedShort: |
281 | 890 | result = "SHORT"; |
282 | 890 | break; |
283 | 2.57k | case Exiv2::unsignedLong: |
284 | 2.57k | result = "LONG"; |
285 | 2.57k | break; |
286 | 557 | case Exiv2::unsignedRational: |
287 | 557 | result = "RATIONAL"; |
288 | 557 | break; |
289 | 352 | case Exiv2::signedByte: |
290 | 352 | result = "SBYTE"; |
291 | 352 | break; |
292 | 842 | case Exiv2::undefined: |
293 | 842 | result = "UNDEFINED"; |
294 | 842 | break; |
295 | 1.22k | case Exiv2::signedShort: |
296 | 1.22k | result = "SSHORT"; |
297 | 1.22k | break; |
298 | 731 | case Exiv2::signedLong: |
299 | 731 | result = "SLONG"; |
300 | 731 | break; |
301 | 901 | case Exiv2::signedRational: |
302 | 901 | result = "SRATIONAL"; |
303 | 901 | break; |
304 | 305 | case Exiv2::tiffFloat: |
305 | 305 | result = "FLOAT"; |
306 | 305 | break; |
307 | 15 | case Exiv2::tiffDouble: |
308 | 15 | result = "DOUBLE"; |
309 | 15 | break; |
310 | 789 | case Exiv2::tiffIfd: |
311 | 789 | result = "IFD"; |
312 | 789 | break; |
313 | 0 | default: |
314 | 0 | result = "unknown"; |
315 | 0 | break; |
316 | 9.50k | } |
317 | 9.50k | return result; |
318 | 9.50k | } |
319 | | |
320 | 9.80k | static bool typeValid(uint16_t type) { |
321 | 9.80k | return type >= 1 && type <= 13; |
322 | 9.80k | } |
323 | | |
324 | | static std::set<size_t> visits; // #547 |
325 | | |
326 | | void Image::printIFDStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option, size_t start, |
327 | 20.9k | bool bSwap, char c, size_t depth) { |
328 | 20.9k | if (depth == 1) |
329 | 4.83k | visits.clear(); |
330 | 20.9k | bool bFirst = true; |
331 | | |
332 | | // buffer |
333 | 20.9k | const size_t dirSize = 32; |
334 | 20.9k | DataBuf dir(dirSize); |
335 | 20.9k | const bool bPrint = option == kpsBasic || option == kpsRecursive; |
336 | | |
337 | 21.0k | do { |
338 | | // Read top of directory |
339 | 21.0k | io.seekOrThrow(start, BasicIo::beg, ErrorCode::kerCorruptedMetadata); |
340 | 21.0k | io.readOrThrow(dir.data(), 2, ErrorCode::kerCorruptedMetadata); |
341 | 21.0k | const uint16_t dirLength = byteSwap2(dir, 0, bSwap); |
342 | | // Prevent infinite loops. (GHSA-m479-7frc-gqqg) |
343 | 21.0k | Internal::enforce(dirLength > 0, ErrorCode::kerCorruptedMetadata); |
344 | | |
345 | 21.0k | if (dirLength > 500) // tooBig |
346 | 15 | throw Error(ErrorCode::kerTiffDirectoryTooLarge); |
347 | | |
348 | 20.9k | if (bFirst && bPrint) { |
349 | 20.8k | out << Internal::indent(depth) << stringFormat("STRUCTURE OF TIFF FILE ({}{}): {}\n", c, c, io.path()); |
350 | 20.8k | } |
351 | | |
352 | | // Read the dictionary |
353 | 30.5k | for (int i = 0; i < dirLength; i++) { |
354 | 25.5k | if (visits.contains(io.tell())) { // #547 |
355 | 15.7k | throw Error(ErrorCode::kerCorruptedMetadata); |
356 | 15.7k | } |
357 | 9.83k | visits.insert(io.tell()); |
358 | | |
359 | 9.83k | if (bFirst && bPrint) { |
360 | 5.12k | out << Internal::indent(depth) << " address | tag | " |
361 | 5.12k | << " type | count | offset | value\n"; |
362 | 5.12k | } |
363 | 9.83k | bFirst = false; |
364 | | |
365 | 9.83k | io.readOrThrow(dir.data(), 12, ErrorCode::kerCorruptedMetadata); |
366 | 9.83k | const uint16_t tag = byteSwap2(dir, 0, bSwap); |
367 | 9.83k | const uint16_t type = byteSwap2(dir, 2, bSwap); |
368 | 9.83k | const uint32_t count = byteSwap4(dir, 4, bSwap); |
369 | 9.83k | const uint32_t offset = byteSwap4(dir, 8, bSwap); |
370 | | |
371 | | // Break for unknown tag types else we may segfault. |
372 | 9.83k | if (!typeValid(type)) { |
373 | 219 | EXV_ERROR << "invalid type in tiff structure" << type << '\n'; |
374 | 219 | throw Error(ErrorCode::kerInvalidTypeValue); |
375 | 219 | } |
376 | | |
377 | | // prepare to print the value |
378 | 9.61k | const uint32_t kount = [=] { |
379 | | // haul in all the data |
380 | 9.58k | if (isPrintXMP(tag, option)) |
381 | 0 | return count; |
382 | | // ditto |
383 | 9.58k | if (isPrintICC(tag, option)) |
384 | 0 | return count; |
385 | | // restrict long arrays |
386 | 9.58k | if (isStringType(type)) { |
387 | 1.55k | return std::min(count, 32u); |
388 | 1.55k | } |
389 | 8.03k | return std::min(count, 5u); |
390 | 9.58k | }(); |
391 | 9.61k | const uint32_t pad = isStringType(type) ? 1 : 0; |
392 | 9.61k | const uint32_t size = [=] { |
393 | 9.58k | if (isStringType(type)) |
394 | 1.55k | return 1; |
395 | 8.03k | if (is2ByteType(type)) |
396 | 2.12k | return 2; |
397 | 5.90k | if (is4ByteType(type)) |
398 | 4.41k | return 4; |
399 | 1.48k | if (is8ByteType(type)) |
400 | 1.48k | return 8; |
401 | 0 | return 1; |
402 | 1.48k | }(); |
403 | | |
404 | | // if ( offset > io.size() ) offset = 0; // Denial of service? |
405 | | |
406 | | // #55 and #56 memory allocation crash test/data/POC8 |
407 | 9.61k | const uint64_t allocate64 = (size * static_cast<uint64_t>(count)) + pad + 20; |
408 | 9.61k | if (allocate64 > io.size()) { |
409 | 73 | throw Error(ErrorCode::kerInvalidMalloc); |
410 | 73 | } |
411 | 9.54k | DataBuf buf(allocate64); // allocate a buffer |
412 | 9.54k | std::copy_n(dir.begin() + 8, 4, buf.begin()); // copy dir[8:11] into buffer (short strings) |
413 | | |
414 | | // We have already checked that this multiplication cannot overflow. |
415 | 9.54k | const size_t count_x_size = static_cast<size_t>(count) * size; |
416 | 9.54k | const bool bOffsetIsPointer = count_x_size > 4; |
417 | | |
418 | 9.54k | if (bOffsetIsPointer) { // read into buffer |
419 | 4.79k | const size_t restore = io.tell(); // save |
420 | 4.79k | io.seekOrThrow(offset, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // position |
421 | 4.79k | io.readOrThrow(buf.data(), count_x_size, ErrorCode::kerCorruptedMetadata); // read |
422 | 4.79k | io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // restore |
423 | 4.79k | } |
424 | | |
425 | 9.54k | if (bPrint) { |
426 | 9.50k | const size_t address = start + 2 + (i * 12); |
427 | 9.50k | const std::string offsetString = bOffsetIsPointer ? stringFormat("{:9}", offset) : ""; |
428 | 9.50k | std::string sp; // output spacer |
429 | | |
430 | 9.50k | out << Internal::indent(depth) |
431 | 9.50k | << stringFormat("{:8} | {:#06x} {:<28} | {:>9} | {:>8} | {:9} | ", address, tag, tagName(tag), |
432 | 9.50k | typeName(type), count, offsetString); |
433 | 9.50k | if (isShortType(type)) { |
434 | 6.03k | for (size_t k = 0; k < kount; k++) { |
435 | 3.92k | out << sp << byteSwap2(buf, k * size, bSwap); |
436 | 3.92k | sp = " "; |
437 | 3.92k | } |
438 | 7.38k | } else if (isLongType(type)) { |
439 | 9.97k | for (size_t k = 0; k < kount; k++) { |
440 | 6.66k | out << sp << byteSwap4(buf, k * size, bSwap); |
441 | 6.66k | sp = " "; |
442 | 6.66k | } |
443 | | |
444 | 4.08k | } else if (isRationalType(type)) { |
445 | 3.01k | for (size_t k = 0; k < kount; k++) { |
446 | 1.56k | const uint32_t a = byteSwap4(buf, (k * size) + 0, bSwap); |
447 | 1.56k | const uint32_t b = byteSwap4(buf, (k * size) + 4, bSwap); |
448 | 1.56k | out << sp << a << "/" << b; |
449 | 1.56k | sp = " "; |
450 | 1.56k | } |
451 | 2.62k | } else if (isStringType(type)) { |
452 | 1.51k | out << sp << Internal::binaryToString(makeSlice(buf, 0, kount)); |
453 | 1.51k | } |
454 | | |
455 | 9.50k | sp = kount == count ? "" : " ..."; |
456 | 9.50k | out << sp << '\n'; |
457 | | |
458 | 9.50k | if (option == kpsRecursive && (tag == 0x8769 /* ExifTag */ || tag == 0x014a /*SubIFDs*/ || type == tiffIfd)) { |
459 | 1.26k | for (size_t k = 0; k < count; k++) { |
460 | 16 | const size_t restore = io.tell(); |
461 | 16 | const uint32_t offset_inner = byteSwap4(buf, k * size, bSwap); |
462 | 16 | printIFDStructure(io, out, option, offset_inner, bSwap, c, depth + 1); |
463 | 16 | io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata); |
464 | 16 | } |
465 | 8.25k | } else if (option == kpsRecursive && tag == 0x83bb /* IPTCNAA */) { |
466 | 3.21k | if (count > 0) { |
467 | 1.97k | if (static_cast<size_t>(Safe::add(count, offset)) > io.size()) { |
468 | 4 | throw Error(ErrorCode::kerCorruptedMetadata); |
469 | 4 | } |
470 | | |
471 | 1.97k | const size_t restore = io.tell(); |
472 | 1.97k | io.seekOrThrow(offset, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // position |
473 | 1.97k | auto bytes = std::make_unique<byte[]>(count); // allocate memory |
474 | 1.97k | io.readOrThrow(bytes.get(), count, ErrorCode::kerCorruptedMetadata); |
475 | 1.97k | io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata); |
476 | 1.97k | IptcData::printStructure(out, makeSliceUntil(bytes.get(), count), depth); |
477 | 1.97k | } |
478 | 5.03k | } else if (option == kpsRecursive && tag == 0x927c /* MakerNote */ && count > 10) { |
479 | 1 | const size_t restore = io.tell(); // save |
480 | | |
481 | 1 | const uint32_t jump = 10; |
482 | 1 | byte bytes[20]; |
483 | 1 | const auto chars = reinterpret_cast<const char*>(&bytes[0]); |
484 | 1 | io.seekOrThrow(offset, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // position |
485 | 1 | io.readOrThrow(bytes, jump, ErrorCode::kerCorruptedMetadata); // read |
486 | 1 | bytes[jump] = 0; |
487 | | |
488 | 1 | bool bNikon = ::strcmp("Nikon", chars) == 0; |
489 | 1 | bool bSony = ::strcmp("SONY DSC ", chars) == 0; |
490 | | |
491 | 1 | if (bNikon) { |
492 | | // tag is an embedded tiff |
493 | 0 | const uint32_t byteslen = count - jump; |
494 | 0 | auto b = DataBuf(byteslen); // allocate a buffer |
495 | 0 | io.readOrThrow(b.data(), byteslen, ErrorCode::kerCorruptedMetadata); // read |
496 | 0 | MemIo memIo(b.c_data(), byteslen); // create a file |
497 | 0 | printTiffStructure(memIo, out, option, depth + 1); |
498 | 1 | } else { |
499 | | // tag is an IFD |
500 | 1 | const uint32_t punt = bSony ? 12 : 0; |
501 | 1 | io.seekOrThrow(0, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // position |
502 | 1 | printIFDStructure(io, out, option, offset + punt, bSwap, c, depth + 1); |
503 | 1 | } |
504 | | |
505 | 1 | io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // restore |
506 | 1 | } |
507 | 9.50k | } |
508 | | |
509 | 9.53k | if (isPrintXMP(tag, option)) { |
510 | 0 | buf.write_uint8(count, 0); |
511 | 0 | out << buf.c_str(); |
512 | 0 | } |
513 | 9.53k | if (isPrintICC(tag, option)) { |
514 | 0 | out.write(buf.c_str(), count); |
515 | 0 | } |
516 | 9.53k | } |
517 | 4.99k | if (start) { |
518 | 4.78k | io.readOrThrow(dir.data(), 4, ErrorCode::kerCorruptedMetadata); |
519 | 4.78k | start = byteSwap4(dir, 0, bSwap); |
520 | 4.78k | } |
521 | 4.99k | } while (start); |
522 | | |
523 | 4.97k | if (bPrint) { |
524 | 4.76k | out << Internal::indent(depth) << "END " << io.path() << '\n'; |
525 | 4.76k | } |
526 | 4.97k | out.flush(); |
527 | 4.97k | } |
528 | | |
529 | | void Image::printTiffStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option, size_t depth, |
530 | 37.1k | size_t offset /*=0*/) { |
531 | 37.1k | if (option == kpsBasic || option == kpsXMP || option == kpsRecursive || option == kpsIccProfile) { |
532 | | // buffer |
533 | 20.9k | const size_t dirSize = 32; |
534 | 20.9k | DataBuf dir(dirSize); |
535 | | |
536 | | // read header (we already know for certain that we have a Tiff file) |
537 | 20.9k | io.readOrThrow(dir.data(), 8, ErrorCode::kerCorruptedMetadata); |
538 | 20.9k | auto c = dir.read_uint8(0); |
539 | 20.9k | bool bSwap = (c == 'M' && isLittleEndianPlatform()) || (c == 'I' && isBigEndianPlatform()); |
540 | 20.9k | size_t start = byteSwap4(dir, 4, bSwap); |
541 | 20.9k | printIFDStructure(io, out, option, start + offset, bSwap, c, depth); |
542 | 20.9k | } |
543 | 37.1k | } |
544 | | |
545 | 105k | void Image::clearMetadata() { |
546 | 105k | clearExifData(); |
547 | 105k | clearIptcData(); |
548 | 105k | clearXmpPacket(); |
549 | 105k | clearXmpData(); |
550 | 105k | clearComment(); |
551 | 105k | clearIccProfile(); |
552 | 105k | } |
553 | | |
554 | 691k | ExifData& Image::exifData() { |
555 | 691k | return exifData_; |
556 | 691k | } |
557 | | |
558 | 57.9k | IptcData& Image::iptcData() { |
559 | 57.9k | return iptcData_; |
560 | 57.9k | } |
561 | | |
562 | 96.8k | XmpData& Image::xmpData() { |
563 | 96.8k | return xmpData_; |
564 | 96.8k | } |
565 | | |
566 | 3.49k | std::string& Image::xmpPacket() { |
567 | | // Serialize the current XMP |
568 | 3.49k | if (!xmpData_.empty() && !writeXmpFromPacket()) { |
569 | 0 | XmpParser::encode(xmpPacket_, xmpData_, XmpParser::useCompactFormat | XmpParser::omitAllFormatting); |
570 | 0 | } |
571 | 3.49k | return xmpPacket_; |
572 | 3.49k | } |
573 | | |
574 | | /// \todo not used internally. At least we should test it |
575 | 0 | void Image::setMetadata(const Image& image) { |
576 | 0 | if (checkMode(mdExif) & amWrite) { |
577 | 0 | setExifData(image.exifData()); |
578 | 0 | } |
579 | 0 | if (checkMode(mdIptc) & amWrite) { |
580 | 0 | setIptcData(image.iptcData()); |
581 | 0 | } |
582 | 0 | if (checkMode(mdIccProfile) & amWrite) { |
583 | 0 | setIccProfile(image.iccProfile().clone()); |
584 | 0 | } |
585 | 0 | if (checkMode(mdXmp) & amWrite) { |
586 | 0 | setXmpPacket(image.xmpPacket()); |
587 | 0 | setXmpData(image.xmpData()); |
588 | 0 | } |
589 | 0 | if (checkMode(mdComment) & amWrite) { |
590 | 0 | setComment(image.comment()); |
591 | 0 | } |
592 | 0 | } |
593 | | |
594 | 105k | void Image::clearExifData() { |
595 | 105k | exifData_.clear(); |
596 | 105k | } |
597 | | |
598 | 449 | void Image::setExifData(const ExifData& exifData) { |
599 | 449 | exifData_ = exifData; |
600 | 449 | } |
601 | | |
602 | 105k | void Image::clearIptcData() { |
603 | 105k | iptcData_.clear(); |
604 | 105k | } |
605 | | |
606 | 449 | void Image::setIptcData(const IptcData& iptcData) { |
607 | 449 | iptcData_ = iptcData; |
608 | 449 | } |
609 | | |
610 | 105k | void Image::clearXmpPacket() { |
611 | 105k | xmpPacket_.clear(); |
612 | | // Also clear the packet cached inside xmpData_. Some formats (e.g. TIFF, where |
613 | | // XMP is stored in the Exif.Image.XMLPacket tag) write XMP from xmpData_'s |
614 | | // packet when writeXmpFromPacket() is set. Without this, a stale packet would |
615 | | // survive and the XMP would not be erased (e.g. "exiv2 -dx" on a TIFF). |
616 | 105k | xmpData_.setPacket(std::string()); |
617 | 105k | writeXmpFromPacket(true); |
618 | 105k | } |
619 | | |
620 | 0 | void Image::setXmpPacket(const std::string& xmpPacket) { |
621 | 0 | const DecodeParams dp(max_recursion_depth_); |
622 | 0 | if (XmpParser::decode(xmpData_, xmpPacket, dp)) { |
623 | 0 | throw Error(ErrorCode::kerInvalidXMP); |
624 | 0 | } |
625 | 0 | xmpPacket_ = xmpPacket; |
626 | 0 | } |
627 | | |
628 | 105k | void Image::clearXmpData() { |
629 | 105k | xmpData_.clear(); |
630 | 105k | writeXmpFromPacket(false); |
631 | 105k | } |
632 | | |
633 | 449 | void Image::setXmpData(const XmpData& xmpData) { |
634 | 449 | xmpData_ = xmpData; |
635 | 449 | writeXmpFromPacket(false); |
636 | 449 | } |
637 | | |
638 | | #ifdef EXV_HAVE_XMP_TOOLKIT |
639 | 210k | void Image::writeXmpFromPacket(bool flag) { |
640 | 210k | writeXmpFromPacket_ = flag; |
641 | 210k | } |
642 | | #else |
643 | | void Image::writeXmpFromPacket(bool) { |
644 | | } |
645 | | #endif |
646 | | |
647 | 105k | void Image::clearComment() { |
648 | 105k | comment_.erase(); |
649 | 105k | } |
650 | | |
651 | 294 | void Image::setComment(const std::string& comment) { |
652 | 294 | comment_ = comment; |
653 | 294 | } |
654 | | |
655 | 809 | void Image::setIccProfile(Exiv2::DataBuf&& iccProfile, bool bTestValid) { |
656 | 809 | iccProfile_ = std::move(iccProfile); |
657 | 809 | if (bTestValid) { |
658 | 809 | checkIccProfile(); |
659 | 809 | } |
660 | 809 | } |
661 | | |
662 | 16.2k | void Image::appendIccProfile(const uint8_t* bytes, size_t size, bool bTestValid) { |
663 | 16.2k | if (size == 0) { |
664 | 3.58k | return; |
665 | 3.58k | } |
666 | 12.6k | const size_t start = iccProfile_.size(); |
667 | 12.6k | iccProfile_.resize(Safe::add(start, size)); |
668 | 12.6k | memcpy(iccProfile_.data(start), bytes, size); |
669 | 12.6k | if (bTestValid) { |
670 | 101 | checkIccProfile(); |
671 | 101 | } |
672 | 12.6k | } |
673 | | |
674 | 910 | void Image::checkIccProfile() const { |
675 | 910 | if (iccProfile_.size() < sizeof(long)) { |
676 | 46 | throw Error(ErrorCode::kerInvalidIccProfile); |
677 | 46 | } |
678 | 864 | const size_t size = iccProfile_.read_uint32(0, bigEndian); |
679 | 864 | if (size != iccProfile_.size()) { |
680 | 181 | throw Error(ErrorCode::kerInvalidIccProfile); |
681 | 181 | } |
682 | 864 | } |
683 | | |
684 | 105k | void Image::clearIccProfile() { |
685 | 105k | iccProfile_.reset(); |
686 | 105k | } |
687 | | |
688 | 56.1k | void Image::setByteOrder(ByteOrder byteOrder) { |
689 | 56.1k | byteOrder_ = byteOrder; |
690 | 56.1k | } |
691 | | |
692 | 7.82k | ByteOrder Image::byteOrder() const { |
693 | 7.82k | return byteOrder_; |
694 | 7.82k | } |
695 | | |
696 | 5.28k | uint32_t Image::pixelWidth() const { |
697 | 5.28k | return pixelWidth_; |
698 | 5.28k | } |
699 | | |
700 | 5.28k | uint32_t Image::pixelHeight() const { |
701 | 5.28k | return pixelHeight_; |
702 | 5.28k | } |
703 | | |
704 | 1.15M | const ExifData& Image::exifData() const { |
705 | 1.15M | return exifData_; |
706 | 1.15M | } |
707 | | |
708 | 0 | const IptcData& Image::iptcData() const { |
709 | 0 | return iptcData_; |
710 | 0 | } |
711 | | |
712 | 29.4k | const XmpData& Image::xmpData() const { |
713 | 29.4k | return xmpData_; |
714 | 29.4k | } |
715 | | |
716 | 2.10k | std::string Image::comment() const { |
717 | 2.10k | return comment_; |
718 | 2.10k | } |
719 | | |
720 | 0 | const std::string& Image::xmpPacket() const { |
721 | 0 | return xmpPacket_; |
722 | 0 | } |
723 | | |
724 | 126k | BasicIo& Image::io() const { |
725 | 126k | return *io_; |
726 | 126k | } |
727 | | |
728 | 26.7k | bool Image::writeXmpFromPacket() const { |
729 | 26.7k | return writeXmpFromPacket_; |
730 | 26.7k | } |
731 | | |
732 | 122k | const NativePreviewList& Image::nativePreviews() const { |
733 | 122k | return nativePreviews_; |
734 | 122k | } |
735 | | |
736 | 117k | bool Image::good() const { |
737 | 117k | if (io_->open() != 0) |
738 | 0 | return false; |
739 | 117k | IoCloser closer(*io_); |
740 | 117k | return ImageFactory::checkType(imageType_, *io_, false); |
741 | 117k | } |
742 | | |
743 | | /// \todo not used internally. At least we should test it |
744 | 0 | bool Image::supportsMetadata(MetadataId metadataId) const { |
745 | 0 | return (supportedMetadata_ & metadataId) != 0; |
746 | 0 | } |
747 | | |
748 | 0 | AccessMode Image::checkMode(MetadataId metadataId) const { |
749 | 0 | return ImageFactory::checkMode(imageType_, metadataId); |
750 | 0 | } |
751 | | |
752 | 9.50k | const std::string& Image::tagName(uint16_t tag) { |
753 | 9.50k | if (init_) { |
754 | 236 | int idx; |
755 | 236 | const TagInfo* ti; |
756 | 708 | for (ti = Internal::mnTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) |
757 | 472 | tags_[ti[idx].tag_] = ti[idx].name_; |
758 | 1.41k | for (ti = Internal::iopTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) |
759 | 1.18k | tags_[ti[idx].tag_] = ti[idx].name_; |
760 | 7.78k | for (ti = Internal::gpsTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) |
761 | 7.55k | tags_[ti[idx].tag_] = ti[idx].name_; |
762 | 60.6k | for (ti = Internal::ifdTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) |
763 | 60.4k | tags_[ti[idx].tag_] = ti[idx].name_; |
764 | 22.8k | for (ti = Internal::exifTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) |
765 | 22.6k | tags_[ti[idx].tag_] = ti[idx].name_; |
766 | 4.72k | for (ti = Internal::mpfTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) |
767 | 4.48k | tags_[ti[idx].tag_] = ti[idx].name_; |
768 | 4.01k | for (ti = Internal::Nikon1MakerNote::tagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) |
769 | 3.77k | tags_[ti[idx].tag_] = ti[idx].name_; |
770 | 236 | } |
771 | 9.50k | init_ = false; |
772 | | |
773 | 9.50k | return tags_[tag]; |
774 | 9.50k | } |
775 | | |
776 | 0 | AccessMode ImageFactory::checkMode(ImageType type, MetadataId metadataId) { |
777 | 0 | auto r = Exiv2::find(registry, type); |
778 | 0 | if (!r) |
779 | 0 | throw Error(ErrorCode::kerUnsupportedImageType, static_cast<int>(type)); |
780 | 0 | if (metadataId == mdExif) |
781 | 0 | return r->exifSupport_; |
782 | 0 | if (metadataId == mdIptc) |
783 | 0 | return r->iptcSupport_; |
784 | 0 | if (metadataId == mdXmp) |
785 | 0 | return r->xmpSupport_; |
786 | 0 | if (metadataId == mdComment) |
787 | 0 | return r->commentSupport_; |
788 | 0 | return amNone; |
789 | 0 | } |
790 | | |
791 | 117k | bool ImageFactory::checkType(ImageType type, BasicIo& io, bool advance) { |
792 | 117k | if (auto r = Exiv2::find(registry, type)) |
793 | 117k | return r->isThisType_(io, advance); |
794 | 0 | return false; |
795 | 117k | } |
796 | | |
797 | 0 | ImageType ImageFactory::getType([[maybe_unused]] const std::string& path) { |
798 | 0 | #ifdef EXV_ENABLE_FILESYSTEM |
799 | 0 | FileIo fileIo(path); |
800 | 0 | return getType(fileIo); |
801 | | #else |
802 | | return ImageType::none; |
803 | | #endif |
804 | 0 | } |
805 | | |
806 | 0 | ImageType ImageFactory::getType(const byte* data, size_t size) { |
807 | 0 | MemIo memIo(data, size); |
808 | 0 | return getType(memIo); |
809 | 0 | } |
810 | | |
811 | 0 | ImageType ImageFactory::getType(BasicIo& io) { |
812 | 0 | if (io.open() != 0) |
813 | 0 | return ImageType::none; |
814 | 0 | IoCloser closer(io); |
815 | 0 | for (const auto& r : registry) { |
816 | 0 | if (r.isThisType_(io, false)) { |
817 | 0 | return r.imageType_; |
818 | 0 | } |
819 | 0 | } |
820 | 0 | return ImageType::none; |
821 | 0 | } |
822 | | |
823 | 0 | BasicIo::UniquePtr ImageFactory::createIo(const std::string& path, [[maybe_unused]] bool useCurl) { |
824 | 0 | [[maybe_unused]] Protocol fProt = fileProtocol(path); |
825 | |
|
826 | | #ifdef EXV_USE_CURL |
827 | | if (useCurl && (fProt == pHttp || fProt == pHttps || fProt == pFtp)) { |
828 | | return std::make_unique<CurlIo>(path); // may throw |
829 | | } |
830 | | #endif |
831 | |
|
832 | 0 | #ifdef EXV_ENABLE_WEBREADY |
833 | 0 | if (fProt == pHttp) |
834 | 0 | return std::make_unique<HttpIo>(path); // may throw |
835 | 0 | #endif |
836 | 0 | #ifdef EXV_ENABLE_FILESYSTEM |
837 | 0 | if (fProt == pFileUri) |
838 | 0 | return std::make_unique<FileIo>(pathOfFileUrl(path)); |
839 | 0 | if (fProt == pStdin || fProt == pDataUri) |
840 | 0 | return std::make_unique<XPathIo>(path); // may throw |
841 | | |
842 | 0 | return std::make_unique<FileIo>(path); |
843 | | #else |
844 | | throw Error(ErrorCode::kerFileAccessDisabled, path); |
845 | | #endif |
846 | 0 | } // ImageFactory::createIo |
847 | | |
848 | | #ifdef _WIN32 |
849 | | BasicIo::UniquePtr ImageFactory::createIo(const std::wstring& path) { |
850 | | #ifdef EXV_ENABLE_FILESYSTEM |
851 | | return std::make_unique<FileIo>(path); |
852 | | #else |
853 | | return nullptr; |
854 | | #endif |
855 | | } |
856 | | #endif |
857 | | |
858 | 0 | Image::UniquePtr ImageFactory::open(const std::string& path, bool useCurl) { |
859 | 0 | auto image = open(ImageFactory::createIo(path, useCurl)); // may throw |
860 | 0 | if (!image) |
861 | 0 | throw Error(ErrorCode::kerFileContainsUnknownImageType, path); |
862 | 0 | return image; |
863 | 0 | } |
864 | | |
865 | | #ifdef _WIN32 |
866 | | Image::UniquePtr ImageFactory::open(const std::wstring& path) { |
867 | | auto image = open(ImageFactory::createIo(path)); // may throw |
868 | | if (!image) { |
869 | | char t[1024]; |
870 | | WideCharToMultiByte(CP_UTF8, 0, path.c_str(), -1, t, 1024, nullptr, nullptr); |
871 | | throw Error(ErrorCode::kerFileContainsUnknownImageType, t); |
872 | | } |
873 | | return image; |
874 | | } |
875 | | #endif |
876 | | |
877 | 120k | Image::UniquePtr ImageFactory::open(const byte* data, size_t size) { |
878 | 120k | auto image = open(std::make_unique<MemIo>(data, size)); // may throw |
879 | 120k | if (!image) |
880 | 2.80k | throw Error(ErrorCode::kerMemoryContainsUnknownImageType); |
881 | 117k | return image; |
882 | 120k | } |
883 | | |
884 | 120k | Image::UniquePtr ImageFactory::open(BasicIo::UniquePtr io) { |
885 | 120k | if (io->open() != 0) { |
886 | 0 | throw Error(ErrorCode::kerDataSourceOpenFailed, io->path(), strError()); |
887 | 0 | } |
888 | 1.99M | for (const auto& r : registry) { |
889 | 1.99M | if (r.isThisType_(*io, false)) { |
890 | 117k | return r.newInstance_(std::move(io), ImageCtorParams(false, 1000)); |
891 | 117k | } |
892 | 1.99M | } |
893 | 3.33k | return nullptr; |
894 | 120k | } |
895 | | |
896 | | #ifdef EXV_ENABLE_FILESYSTEM |
897 | 0 | Image::UniquePtr ImageFactory::create(ImageType type, const std::string& path) { |
898 | 0 | auto fileIo = std::make_unique<FileIo>(path); |
899 | | // Create or overwrite the file, then close it |
900 | 0 | if (fileIo->open("w+b") != 0) { |
901 | 0 | throw Error(ErrorCode::kerFileOpenFailed, path, "w+b", strError()); |
902 | 0 | } |
903 | 0 | fileIo->close(); |
904 | |
|
905 | 0 | BasicIo::UniquePtr io(std::move(fileIo)); |
906 | 0 | auto image = create(type, std::move(io)); |
907 | 0 | if (!image) |
908 | 0 | throw Error(ErrorCode::kerUnsupportedImageType, static_cast<int>(type)); |
909 | 0 | return image; |
910 | 0 | } |
911 | | #endif |
912 | | |
913 | 449 | Image::UniquePtr ImageFactory::create(ImageType type) { |
914 | 449 | auto image = create(type, std::make_unique<MemIo>()); |
915 | 449 | if (!image) |
916 | 0 | throw Error(ErrorCode::kerUnsupportedImageType, static_cast<int>(type)); |
917 | 449 | return image; |
918 | 449 | } |
919 | | |
920 | 449 | Image::UniquePtr ImageFactory::create(ImageType type, BasicIo::UniquePtr io) { |
921 | | // BasicIo instance does not need to be open |
922 | 449 | if (type == ImageType::none) |
923 | 0 | return {}; |
924 | 449 | if (auto r = Exiv2::find(registry, type)) |
925 | 449 | return r->newInstance_(std::move(io), ImageCtorParams(true, 1000)); |
926 | 0 | return {}; |
927 | 449 | } |
928 | | |
929 | | // ***************************************************************************** |
930 | | // template, inline and free functions |
931 | | |
932 | 409k | void append(Blob& blob, const byte* buf, size_t len) { |
933 | 409k | if (len != 0) { |
934 | 344k | Blob::size_type size = blob.size(); |
935 | 344k | if (blob.capacity() - size < len) { |
936 | 12.2k | blob.reserve(size + 65536); |
937 | 12.2k | } |
938 | 344k | blob.resize(size + len); |
939 | 344k | std::copy_n(buf, len, blob.begin() + size); |
940 | 344k | } |
941 | 409k | } // append |
942 | | |
943 | | } // namespace Exiv2 |