Coverage Report

Created: 2026-09-01 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/src/jp2image.cpp
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
3
// included header files
4
#include "jp2image.hpp"
5
6
#include "config.h"
7
8
#include "basicio.hpp"
9
#include "enforce.hpp"
10
#include "error.hpp"
11
#include "futils.hpp"
12
#include "image.hpp"
13
#include "image_int.hpp"
14
#include "jp2image_int.hpp"
15
#include "safe_op.hpp"
16
#include "tiffimage.hpp"
17
#include "types.hpp"
18
19
#include <algorithm>
20
#include <array>
21
#include <cstring>
22
#include <iostream>
23
24
#ifdef EXIV2_DEBUG_MESSAGES
25
#include <fstream>
26
#endif
27
28
// JPEG-2000 box types
29
enum kJp2BoxType : uint32_t {
30
  Signature = 0x6a502020,    // signature box, required,
31
  FileTypeBox = 0x66747970,  // File type box, required
32
  Header = 0x6a703268,       // Jp2 Header Box, required, Superbox
33
  ImageHeader = 0x69686472,  // Image Header Box ('ihdr'), required,
34
  ColorSpec = 0x636f6c72,    // Color Specification box ('colr'), required
35
  Uuid = 0x75756964,         // 'uuid'
36
  Close = 0x6a703263,        // 'jp2c'
37
};
38
39
namespace Exiv2 {
40
namespace {
41
// JPEG-2000 UUIDs for embedded metadata
42
//
43
// See http://www.jpeg.org/public/wg1n2600.doc for information about embedding IPTC-NAA data in JPEG-2000 files
44
// See http://www.adobe.com/devnet/xmp/pdfs/xmp_specification.pdf for information about embedding XMP data in JPEG-2000
45
// files
46
47
constexpr auto kJp2UuidExif = std::array<byte, 16>{
48
    0x4A, 0x70, 0x67, 0x54, 0x69, 0x66, 0x66, 0x45, 0x78, 0x69, 0x66, 0x2D, 0x3E, 0x4A, 0x50, 0x32,
49
};
50
constexpr auto kJp2UuidIptc = std::array<byte, 16>{
51
    0x33, 0xc7, 0xa4, 0xd2, 0xb8, 0x1d, 0x47, 0x23, 0xa0, 0xba, 0xf1, 0xa3, 0xe0, 0x97, 0xad, 0x38,
52
};
53
constexpr auto kJp2UuidXmp = std::array<byte, 16>{
54
    0xbe, 0x7a, 0xcf, 0xcb, 0x97, 0xa9, 0x42, 0xe8, 0x9c, 0x71, 0x99, 0x94, 0x91, 0xe3, 0xaf, 0xac,
55
};
56
57
// See section B.1.1 (JPEG 2000 Signature box) of JPEG-2000 specification
58
constexpr std::array<byte, 12> Jp2Signature{
59
    0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a,
60
};
61
62
constexpr std::array<byte, 249> Jp2Blank{
63
    0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x66, 0x74,
64
    0x79, 0x70, 0x6a, 0x70, 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x70, 0x32, 0x20, 0x00, 0x00, 0x00, 0x2d,
65
    0x6a, 0x70, 0x32, 0x68, 0x00, 0x00, 0x00, 0x16, 0x69, 0x68, 0x64, 0x72, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
66
    0x00, 0x01, 0x00, 0x01, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x63, 0x6f, 0x6c, 0x72, 0x01, 0x00,
67
    0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x70, 0x32, 0x63, 0xff, 0x4f, 0xff, 0x51, 0x00,
68
    0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69
    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70
    0x01, 0x07, 0x01, 0x01, 0xff, 0x64, 0x00, 0x23, 0x00, 0x01, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3a,
71
    0x20, 0x4a, 0x61, 0x73, 0x50, 0x65, 0x72, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x2e,
72
    0x39, 0x30, 0x30, 0x2e, 0x31, 0xff, 0x52, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x04, 0x04, 0x00,
73
    0x01, 0xff, 0x5c, 0x00, 0x13, 0x40, 0x40, 0x48, 0x48, 0x50, 0x48, 0x48, 0x50, 0x48, 0x48, 0x50, 0x48, 0x48,
74
    0x50, 0x48, 0x48, 0x50, 0xff, 0x90, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x01, 0xff, 0x5d,
75
    0x00, 0x14, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
76
    0x00, 0x00, 0xff, 0x93, 0xcf, 0xb4, 0x04, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0xd9,
77
};
78
79
const size_t boxHSize = sizeof(Internal::Jp2BoxHeader);
80
81
0
void lf(std::ostream& out, bool& bLF) {
82
0
  if (bLF) {
83
0
    out << '\n';
84
0
    out.flush();
85
0
    bLF = false;
86
0
  }
87
0
}
88
89
144k
void boxes_check(size_t b, size_t m) {
90
144k
  if (b > m) {
91
#ifdef EXIV2_DEBUG_MESSAGES
92
    std::cout << "Exiv2::Jp2Image::readMetadata box maximum exceeded" << '\n';
93
#endif
94
2
    throw Error(ErrorCode::kerCorruptedMetadata);
95
2
  }
96
144k
}
97
98
}  // namespace
99
100
Jp2Image::Jp2Image(BasicIo::UniquePtr io, const ImageCtorParams& params) :
101
1.62k
    Image(ImageType::jp2, mdExif | mdIptc | mdXmp, std::move(io), params) {
102
1.62k
  if (params.create() && io_->open() == 0) {
103
#ifdef EXIV2_DEBUG_MESSAGES
104
    std::cerr << "Exiv2::Jp2Image:: Creating JPEG2000 image to memory" << '\n';
105
#endif
106
0
    IoCloser closer(*io_);
107
0
    if (io_->write(Jp2Blank.data(), Jp2Blank.size()) != Jp2Blank.size()) {
108
#ifdef EXIV2_DEBUG_MESSAGES
109
      std::cerr << "Exiv2::Jp2Image:: Failed to create JPEG2000 image on memory" << '\n';
110
#endif
111
0
    }
112
0
  }
113
1.62k
}
114
115
// Obtains the ascii version from the box.type
116
0
std::string Jp2Image::toAscii(uint32_t n) {
117
0
  std::string result(sizeof(uint32_t), '\0');
118
0
  for (size_t i = 0; i < result.size(); ++i) {
119
0
    auto c = static_cast<unsigned char>(n >> (8 * (3 - i)));
120
0
    result[i] = static_cast<char>(c);
121
0
  }
122
0
  return result;
123
0
}
124
125
0
std::string Jp2Image::mimeType() const {
126
0
  if (brand_ == Internal::brandJph)
127
0
    return "image/jph";
128
0
  return "image/jp2";
129
0
}
130
131
0
void Jp2Image::setComment(const std::string&) {
132
0
  throw(Error(ErrorCode::kerInvalidSettingForImage, "Image comment", "JP2"));
133
0
}
134
135
1.62k
void Jp2Image::readMetadata() {
136
#ifdef EXIV2_DEBUG_MESSAGES
137
  std::cerr << "Exiv2::Jp2Image::readMetadata: Reading JPEG-2000 file " << io_->path() << '\n';
138
#endif
139
1.62k
  if (io_->open() != 0) {
140
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
141
0
  }
142
1.62k
  IoCloser closer(*io_);
143
1.62k
  if (!isJp2Type(*io_, false)) {
144
0
    throw Error(ErrorCode::kerNotAnImage, "JPEG-2000");
145
0
  }
146
147
1.62k
  Internal::Jp2BoxHeader box = {0, 0};
148
1.62k
  Internal::Jp2BoxHeader subBox = {0, 0};
149
1.62k
  Internal::Jp2ImageHeaderBox ihdr = {0, 0, 0, 0, 0, 0, 0};
150
1.62k
  Internal::Jp2UuidBox uuid = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
151
1.62k
  size_t boxesCount = 0;
152
1.62k
  const size_t boxem = 1000;  // boxes max
153
1.62k
  uint32_t lastBoxTypeRead = 0;
154
1.62k
  bool boxSignatureFound = false;
155
1.62k
  bool boxFileTypeFound = false;
156
157
24.3k
  while (io_->read(reinterpret_cast<byte*>(&box), boxHSize) == boxHSize) {
158
23.9k
    boxes_check(boxesCount++, boxem);
159
23.9k
    const size_t position = io_->tell();
160
23.9k
    box.length = getULong(reinterpret_cast<byte*>(&box.length), bigEndian);
161
23.9k
    box.type = getULong(reinterpret_cast<byte*>(&box.type), bigEndian);
162
#ifdef EXIV2_DEBUG_MESSAGES
163
    std::cout << "Exiv2::Jp2Image::readMetadata: Position: " << position << " box type: " << toAscii(box.type)
164
              << " length: " << box.length << '\n';
165
#endif
166
23.9k
    Internal::enforce(box.length <= boxHSize + io_->size() - io_->tell(), ErrorCode::kerCorruptedMetadata);
167
168
23.9k
    if (box.length == 0)
169
440
      return;
170
171
23.5k
    if (box.length == 1) {
172
      /// \todo In this case, the real box size is given in XLBox (bytes 8-15)
173
14
    }
174
175
23.5k
    switch (box.type) {
176
1.66k
      case kJp2BoxType::Signature: {
177
1.66k
        if (boxSignatureFound)  // Only one is allowed
178
49
          throw Error(ErrorCode::kerCorruptedMetadata);
179
1.62k
        boxSignatureFound = true;
180
1.62k
        break;
181
1.66k
      }
182
260
      case kJp2BoxType::FileTypeBox: {
183
        // This box shall immediately follow the JPEG 2000 Signature box
184
260
        if (boxFileTypeFound || lastBoxTypeRead != kJp2BoxType::Signature) {  // Only one is allowed
185
39
          throw Error(ErrorCode::kerCorruptedMetadata);
186
39
        }
187
221
        boxFileTypeFound = true;
188
221
        Internal::enforce(box.length >= boxHSize, ErrorCode::kerCorruptedMetadata);
189
221
        Blob boxData(box.length - boxHSize);
190
221
        io_->readOrThrow(boxData.data(), boxData.size(), ErrorCode::kerCorruptedMetadata);
191
221
        if (!Internal::isValidBoxFileType(boxData))
192
195
          throw Error(ErrorCode::kerCorruptedMetadata);
193
26
        brand_ = getULong(boxData.data(), bigEndian);
194
26
        break;
195
221
      }
196
6.57k
      case kJp2BoxType::Header: {
197
#ifdef EXIV2_DEBUG_MESSAGES
198
        std::cout << "Exiv2::Jp2Image::readMetadata: JP2Header box found\n";
199
#endif
200
6.57k
        size_t restore = io_->tell();
201
202
126k
        while (io_->read(reinterpret_cast<byte*>(&subBox), boxHSize) == boxHSize && subBox.length) {
203
120k
          boxes_check(boxesCount++, boxem);
204
120k
          subBox.length = getULong(reinterpret_cast<byte*>(&subBox.length), bigEndian);
205
120k
          subBox.type = getULong(reinterpret_cast<byte*>(&subBox.type), bigEndian);
206
120k
          if (subBox.length > io_->size()) {
207
82
            throw Error(ErrorCode::kerCorruptedMetadata);
208
82
          }
209
#ifdef EXIV2_DEBUG_MESSAGES
210
          std::cout << "Exiv2::Jp2Image::readMetadata: "
211
                    << "subBox = " << toAscii(subBox.type) << " length = " << subBox.length << '\n';
212
#endif
213
120k
          if (subBox.type == kJp2BoxType::ColorSpec && subBox.length != 15) {
214
#ifdef EXIV2_DEBUG_MESSAGES
215
            std::cout << "Exiv2::Jp2Image::readMetadata: "
216
                      << "Color data found" << '\n';
217
#endif
218
219
120
            const size_t pad = 3;  // 3 padding bytes 2 0 0
220
120
            const size_t data_length = Safe::add(subBox.length, 8u);
221
            // data_length makes no sense if it is larger than the rest of the file
222
120
            if (data_length > io_->size() - io_->tell()) {
223
7
              throw Error(ErrorCode::kerCorruptedMetadata);
224
7
            }
225
113
            DataBuf data(data_length);
226
113
            io_->read(data.data(), data.size());
227
113
            const size_t iccLength = data.read_uint32(pad, bigEndian);
228
            // subtracting pad from data.size() is safe:
229
            // data.size() is at least 8 and pad = 3
230
113
            if (iccLength > data.size() - pad) {
231
12
              throw Error(ErrorCode::kerCorruptedMetadata);
232
12
            }
233
101
            DataBuf icc(iccLength);
234
101
            std::copy_n(data.begin() + pad, icc.size(), icc.begin());
235
#ifdef EXIV2_DEBUG_MESSAGES
236
            const char* iccPath = "/tmp/libexiv2_jp2.icc";
237
            if (auto f = std::ofstream(iccPath, std::ios::binary)) {
238
              f.write(reinterpret_cast<const char*>(icc.c_data()), static_cast<std::streamsize>(icc.size()));
239
              f.close();
240
              std::cout << "Exiv2::Jp2Image::readMetadata: wrote iccProfile " << icc.size() << " bytes to " << iccPath
241
                        << '\n';
242
            }
243
#endif
244
101
            setIccProfile(std::move(icc));
245
101
          }
246
247
120k
          if (subBox.type == kJp2BoxType::ImageHeader) {
248
58
            io_->read(reinterpret_cast<byte*>(&ihdr), sizeof(ihdr));
249
#ifdef EXIV2_DEBUG_MESSAGES
250
            std::cout << "Exiv2::Jp2Image::readMetadata: Ihdr data found" << '\n';
251
#endif
252
58
            ihdr.imageHeight = getULong(reinterpret_cast<byte*>(&ihdr.imageHeight), bigEndian);
253
58
            ihdr.imageWidth = getULong(reinterpret_cast<byte*>(&ihdr.imageWidth), bigEndian);
254
58
            ihdr.componentCount = getShort(reinterpret_cast<byte*>(&ihdr.componentCount), bigEndian);
255
58
            Internal::enforce(ihdr.c == 7, ErrorCode::kerCorruptedMetadata);
256
257
58
            pixelWidth_ = ihdr.imageWidth;
258
58
            pixelHeight_ = ihdr.imageHeight;
259
58
          }
260
261
120k
          io_->seek(restore, BasicIo::beg);
262
120k
          if (io_->seek(subBox.length, BasicIo::cur) != 0) {
263
46
            throw Error(ErrorCode::kerCorruptedMetadata);
264
46
          }
265
120k
          restore = io_->tell();
266
120k
        }
267
6.42k
        break;
268
6.57k
      }
269
270
6.42k
      case kJp2BoxType::Uuid: {
271
#ifdef EXIV2_DEBUG_MESSAGES
272
        std::cout << "Exiv2::Jp2Image::readMetadata: UUID box found" << '\n';
273
#endif
274
275
4.56k
        if (io_->read(reinterpret_cast<byte*>(&uuid), sizeof(uuid)) == sizeof(uuid)) {
276
4.56k
          DataBuf rawData;
277
4.56k
          size_t bufRead;
278
4.56k
          bool bIsExif = uuid.uuid == kJp2UuidExif;
279
4.56k
          bool bIsIPTC = uuid.uuid == kJp2UuidIptc;
280
4.56k
          bool bIsXMP = uuid.uuid == kJp2UuidXmp;
281
282
4.56k
          if (bIsExif) {
283
#ifdef EXIV2_DEBUG_MESSAGES
284
            std::cout << "Exiv2::Jp2Image::readMetadata: Exif data found" << '\n';
285
#endif
286
1.04k
            Internal::enforce(box.length >= boxHSize + sizeof(uuid), ErrorCode::kerCorruptedMetadata);
287
1.04k
            rawData.alloc(box.length - (boxHSize + sizeof(uuid)));
288
1.04k
            bufRead = io_->read(rawData.data(), rawData.size());
289
1.04k
            if (io_->error())
290
0
              throw Error(ErrorCode::kerFailedToReadImageData);
291
1.04k
            if (bufRead != rawData.size())
292
0
              throw Error(ErrorCode::kerInputDataReadFailed);
293
294
1.04k
            if (rawData.size() > 8)  // "II*\0long"
295
882
            {
296
              // Find the position of Exif header in bytes array.
297
882
              const char a = rawData.read_uint8(0);
298
882
              const char b = rawData.read_uint8(1);
299
882
              const size_t notfound = std::numeric_limits<size_t>::max();
300
882
              size_t pos = (a == b && (a == 'I' || a == 'M')) ? 0 : notfound;
301
302
              // #1242  Forgive having Exif\0\0 in rawData.pData_
303
882
              std::array<byte, 6> exifHeader{0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
304
59.3k
              for (size_t i = 0; pos == notfound && i < (rawData.size() - exifHeader.size()); i++) {
305
58.5k
                if (rawData.cmpBytes(i, exifHeader.data(), exifHeader.size()) == 0) {
306
471
                  pos = i + sizeof(exifHeader);
307
471
#ifndef SUPPRESS_WARNINGS
308
471
                  EXV_WARNING << "Reading non-standard UUID-EXIF_bad box in " << io_->path() << '\n';
309
471
#endif
310
471
                }
311
58.5k
              }
312
313
              // If found it, store only these data at from this place.
314
882
              if (pos != notfound) {
315
#ifdef EXIV2_DEBUG_MESSAGES
316
                std::cout << "Exiv2::Jp2Image::readMetadata: Exif header found at position " << pos << '\n';
317
#endif
318
843
                const DecodeParams dp(max_recursion_depth_);
319
843
                ByteOrder bo = TiffParser::decode(exifData(), iptcData(), xmpData(), rawData.c_data(pos),
320
843
                                                  rawData.size() - pos, dp);
321
843
                setByteOrder(bo);
322
843
              }
323
882
            } else {
324
161
#ifndef SUPPRESS_WARNINGS
325
161
              EXV_WARNING << "Failed to decode Exif metadata." << '\n';
326
161
#endif
327
161
              exifData_.clear();
328
161
            }
329
1.04k
          }
330
331
4.56k
          if (bIsIPTC) {
332
#ifdef EXIV2_DEBUG_MESSAGES
333
            std::cout << "Exiv2::Jp2Image::readMetadata: Iptc data found" << '\n';
334
#endif
335
2.13k
            Internal::enforce(box.length >= boxHSize + sizeof(uuid), ErrorCode::kerCorruptedMetadata);
336
2.13k
            rawData.alloc(box.length - (boxHSize + sizeof(uuid)));
337
2.13k
            bufRead = io_->read(rawData.data(), rawData.size());
338
2.13k
            if (io_->error())
339
0
              throw Error(ErrorCode::kerFailedToReadImageData);
340
2.13k
            if (bufRead != rawData.size())
341
0
              throw Error(ErrorCode::kerInputDataReadFailed);
342
343
2.13k
            if (IptcParser::decode(iptcData_, rawData.c_data(), rawData.size())) {
344
783
#ifndef SUPPRESS_WARNINGS
345
783
              EXV_WARNING << "Failed to decode IPTC metadata." << '\n';
346
783
#endif
347
783
              iptcData_.clear();
348
783
            }
349
2.13k
          }
350
351
4.56k
          if (bIsXMP) {
352
#ifdef EXIV2_DEBUG_MESSAGES
353
            std::cout << "Exiv2::Jp2Image::readMetadata: Xmp data found" << '\n';
354
#endif
355
622
            Internal::enforce(box.length >= boxHSize + sizeof(uuid), ErrorCode::kerCorruptedMetadata);
356
622
            rawData.alloc(box.length - (boxHSize + sizeof(uuid)));
357
622
            bufRead = io_->read(rawData.data(), rawData.size());
358
622
            if (io_->error())
359
0
              throw Error(ErrorCode::kerFailedToReadImageData);
360
622
            if (bufRead != rawData.size())
361
0
              throw Error(ErrorCode::kerInputDataReadFailed);
362
622
            xmpPacket_.assign(rawData.c_str(), rawData.size());
363
364
622
            if (auto idx = xmpPacket_.find_first_of('<'); idx != std::string::npos && idx > 0) {
365
125
#ifndef SUPPRESS_WARNINGS
366
125
              EXV_WARNING << "Removing " << static_cast<uint32_t>(idx)
367
0
                          << " characters from the beginning of the XMP packet" << '\n';
368
125
#endif
369
125
              xmpPacket_ = xmpPacket_.substr(idx);
370
125
            }
371
372
622
            const DecodeParams dp(max_recursion_depth_);
373
622
            if (!xmpPacket_.empty() && XmpParser::decode(xmpData_, xmpPacket_, dp)) {
374
116
#ifndef SUPPRESS_WARNINGS
375
116
              EXV_WARNING << "Failed to decode XMP metadata." << '\n';
376
116
#endif
377
116
            }
378
622
          }
379
4.56k
        }
380
4.56k
        break;
381
4.56k
      }
382
383
10.2k
      default:
384
10.2k
        break;
385
23.5k
    }
386
22.7k
    lastBoxTypeRead = box.type;
387
388
    // Move to the next box.
389
22.7k
    io_->seek(static_cast<int64_t>(position - boxHSize + box.length), BasicIo::beg);
390
22.7k
    if (io_->error())
391
0
      throw Error(ErrorCode::kerFailedToReadImageData);
392
22.7k
  }
393
394
1.62k
}  // Jp2Image::readMetadata
395
396
0
void Jp2Image::printStructure(std::ostream& out, PrintStructureOption option, size_t depth) {
397
0
  if (io_->open() != 0)
398
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
399
400
0
  if (!isJp2Type(*io_, false)) {
401
0
    throw Error(ErrorCode::kerNotAJpeg);
402
0
  }
403
404
  // According to the JP2 standard: The start of the first box shall be the first byte of the file, and the
405
  // last byte of the last box shall be the last byte of the file.
406
407
0
  bool bPrint = option == kpsBasic || option == kpsRecursive;
408
0
  bool bRecursive = option == kpsRecursive;
409
0
  bool bICC = option == kpsIccProfile;
410
0
  bool bXMP = option == kpsXMP;
411
0
  bool bIPTCErase = option == kpsIptcErase;
412
0
  bool boxSignatureFound = false;
413
414
0
  if (bPrint) {
415
0
    out << "STRUCTURE OF JPEG2000 FILE: " << io_->path() << '\n';
416
0
    out << " address |   length | box       | data" << '\n';
417
0
  }
418
419
0
  if (bPrint || bXMP || bICC || bIPTCErase) {
420
0
    Internal::Jp2BoxHeader box = {1, 1};
421
0
    Internal::Jp2BoxHeader subBox = {1, 1};
422
0
    Internal::Jp2UuidBox uuid = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
423
0
    bool bLF = false;
424
425
0
    while (box.length && box.type != kJp2BoxType::Close &&
426
0
           io_->read(reinterpret_cast<byte*>(&box), boxHSize) == boxHSize) {
427
0
      const size_t position = io_->tell();
428
0
      box.length = getULong(reinterpret_cast<byte*>(&box.length), bigEndian);
429
0
      box.type = getULong(reinterpret_cast<byte*>(&box.type), bigEndian);
430
0
      Internal::enforce(box.length <= boxHSize + io_->size() - io_->tell(), ErrorCode::kerCorruptedMetadata);
431
432
0
      if (bPrint) {
433
0
        out << stringFormat("{:8} | {:8} | {}      | ", position - boxHSize, box.length, toAscii(box.type));
434
0
        bLF = true;
435
0
        if (box.type == kJp2BoxType::Close)
436
0
          lf(out, bLF);
437
0
      }
438
0
      if (box.type == kJp2BoxType::Close)
439
0
        break;
440
441
0
      switch (box.type) {
442
0
        case kJp2BoxType::Signature: {
443
0
          if (boxSignatureFound)  // Only one is allowed
444
0
            throw Error(ErrorCode::kerCorruptedMetadata);
445
0
          boxSignatureFound = true;
446
0
          break;
447
0
        }
448
0
        case kJp2BoxType::FileTypeBox: {
449
          // This box shall immediately follow the JPEG 2000 Signature box
450
          /// \todo  All files shall contain one and only one File Type box.
451
0
          Internal::enforce(box.length >= boxHSize, ErrorCode::kerCorruptedMetadata);
452
0
          Blob boxData(box.length - boxHSize);
453
0
          io_->readOrThrow(boxData.data(), boxData.size(), ErrorCode::kerCorruptedMetadata);
454
0
          if (!Internal::isValidBoxFileType(boxData))
455
0
            throw Error(ErrorCode::kerCorruptedMetadata);
456
0
          break;
457
0
        }
458
0
        case kJp2BoxType::Header: {
459
0
          lf(out, bLF);
460
          /// \todo  All files shall contain one and only one Header box.
461
462
0
          while (io_->read(reinterpret_cast<byte*>(&subBox), boxHSize) == boxHSize &&
463
0
                 io_->tell() < position + box.length)  // don't read beyond the box!
464
0
          {
465
0
            const size_t address = io_->tell() - boxHSize;
466
0
            subBox.length = getULong(reinterpret_cast<byte*>(&subBox.length), bigEndian);
467
0
            subBox.type = getULong(reinterpret_cast<byte*>(&subBox.type), bigEndian);
468
469
0
            if (subBox.length < boxHSize || subBox.length > io_->size() - io_->tell()) {
470
0
              throw Error(ErrorCode::kerCorruptedMetadata);
471
0
            }
472
473
0
            DataBuf data(subBox.length - boxHSize);
474
0
            io_->read(data.data(), data.size());
475
0
            if (bPrint) {
476
0
              out << stringFormat("{:8} | {:8} |  sub:{} | ", address, subBox.length, toAscii(subBox.type))
477
0
                  << Internal::binaryToString(makeSlice(data, 0, std::min<size_t>(30, data.size())));
478
0
              bLF = true;
479
0
            }
480
481
0
            if (subBox.type == kJp2BoxType::ImageHeader) {
482
0
              Internal::enforce(subBox.length == 22, ErrorCode::kerCorruptedMetadata);
483
              // height (4), width (4), componentsCount (2), bpc (1)
484
0
              auto compressionType = data.read_uint8(11);
485
0
              auto unkC = data.read_uint8(12);
486
0
              auto ipr = data.read_uint8(13);
487
0
              if (compressionType != 7 || unkC > 1 || ipr > 1) {
488
0
                throw Error(ErrorCode::kerCorruptedMetadata);
489
0
              }
490
0
            } else if (subBox.type == kJp2BoxType::ColorSpec) {
491
0
              const size_t pad = 3;  // don't know why there are 3 padding bytes
492
493
              // Bounds-check for the `getULong()` below, which reads 4 bytes, starting at `pad`.
494
0
              Internal::enforce(data.size() >= pad + 4, ErrorCode::kerCorruptedMetadata);
495
496
              /// \todo A conforming JP2 reader shall ignore all Colour Specification boxes after the first.
497
0
              auto METH = data.read_uint8(0);
498
              // auto PREC = data.read_uint8(1);
499
              // auto APPROX = data.read_uint8(2);
500
0
              if (METH == 1) {  // Enumerated Colourspace
501
0
                auto enumCS = data.read_uint32(3, bigEndian);
502
0
                if (enumCS != 16 && enumCS != 17) {
503
0
                  throw Error(ErrorCode::kerCorruptedMetadata);
504
0
                }
505
0
              } else {  // Restricted ICC Profile
506
                        // see the ICC Profile Format Specification, version ICC.1:1998-09
507
0
                const size_t iccLength = data.read_uint32(pad, bigEndian);
508
0
                if (bPrint) {
509
0
                  out << " | iccLength:" << iccLength;
510
0
                }
511
0
                Internal::enforce(iccLength <= data.size() - pad, ErrorCode::kerCorruptedMetadata);
512
0
                if (bICC) {
513
0
                  out.write(data.c_str(pad), iccLength);
514
0
                }
515
0
              }
516
0
            }
517
0
            lf(out, bLF);
518
0
          }
519
0
        } break;
520
521
0
        case kJp2BoxType::Uuid: {
522
0
          if (io_->read(reinterpret_cast<byte*>(&uuid), sizeof(uuid)) == sizeof(uuid)) {
523
0
            bool bIsExif = uuid.uuid == kJp2UuidExif;
524
0
            bool bIsIPTC = uuid.uuid == kJp2UuidIptc;
525
0
            bool bIsXMP = uuid.uuid == kJp2UuidXmp;
526
527
0
            bool bUnknown = !(bIsExif || bIsIPTC || bIsXMP);
528
529
0
            if (bPrint) {
530
0
              if (bIsExif)
531
0
                out << "Exif: ";
532
0
              if (bIsIPTC)
533
0
                out << "IPTC: ";
534
0
              if (bIsXMP)
535
0
                out << "XMP : ";
536
0
              if (bUnknown)
537
0
                out << "????: ";
538
0
            }
539
540
0
            DataBuf rawData;
541
0
            Internal::enforce(box.length >= sizeof(uuid) + boxHSize, ErrorCode::kerCorruptedMetadata);
542
0
            rawData.alloc(box.length - sizeof(uuid) - boxHSize);
543
0
            const size_t bufRead = io_->read(rawData.data(), rawData.size());
544
0
            if (io_->error())
545
0
              throw Error(ErrorCode::kerFailedToReadImageData);
546
0
            if (bufRead != rawData.size())
547
0
              throw Error(ErrorCode::kerInputDataReadFailed);
548
549
0
            if (bPrint) {
550
0
              out << Internal::binaryToString(makeSlice(rawData, 0, std::min<size_t>(40, rawData.size())));
551
0
              out.flush();
552
0
            }
553
0
            lf(out, bLF);
554
555
0
            if (bIsExif && bRecursive && rawData.size() > 8) {  // "II*\0long"
556
0
              const char a = rawData.read_uint8(0);
557
0
              const char b = rawData.read_uint8(1);
558
0
              if (a == b && (a == 'I' || a == 'M')) {
559
0
                MemIo p(rawData.c_data(), rawData.size());
560
0
                printTiffStructure(p, out, option, depth + 1);
561
0
              }
562
0
            }
563
564
0
            if (bIsIPTC && bRecursive) {
565
0
              IptcData::printStructure(out, makeSlice(rawData, 0, rawData.size()), depth);
566
0
            }
567
568
0
            if (bIsXMP && bXMP) {
569
0
              out.write(rawData.c_str(), rawData.size());
570
0
            }
571
0
          }
572
0
        } break;
573
574
0
        default:
575
0
          break;
576
0
      }
577
578
      // Move to the next box.
579
0
      io_->seek(static_cast<int64_t>(position - boxHSize + box.length), BasicIo::beg);
580
0
      if (io_->error())
581
0
        throw Error(ErrorCode::kerFailedToReadImageData);
582
0
      if (bPrint)
583
0
        lf(out, bLF);
584
0
    }
585
0
  }
586
0
}
587
588
782
void Jp2Image::writeMetadata() {
589
782
  if (io_->open() != 0) {
590
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
591
0
  }
592
782
  IoCloser closer(*io_);
593
782
  MemIo tempIo;
594
595
782
  doWriteMetadata(tempIo);  // may throw
596
782
  io_->close();
597
782
  io_->transfer(tempIo);  // may throw
598
599
782
}  // Jp2Image::writeMetadata
600
601
5.97k
void Jp2Image::encodeJp2Header(const DataBuf& boxBuf, DataBuf& outBuf) {
602
5.97k
  DataBuf output(boxBuf.size() + iccProfile_.size() + 100);  // allocate sufficient space
603
5.97k
  size_t outlen = boxHSize;                                  // now many bytes have we written to output?
604
5.97k
  size_t inlen = boxHSize;                                   // how many bytes have we read from boxBuf?
605
5.97k
  Internal::enforce(boxHSize <= output.size(), ErrorCode::kerCorruptedMetadata);
606
5.97k
  uint32_t length = getULong(boxBuf.c_data(0), bigEndian);
607
5.97k
  Internal::enforce(length <= output.size(), ErrorCode::kerCorruptedMetadata);
608
5.97k
  uint32_t count = boxHSize;
609
5.97k
  bool bWroteColor = false;
610
611
6.31k
  while (count < length && !bWroteColor) {
612
337
    Internal::enforce(boxHSize <= length - count, ErrorCode::kerCorruptedMetadata);
613
337
    Internal::Jp2BoxHeader subBox;
614
337
    std::memcpy(&subBox, boxBuf.c_data(count), boxHSize);
615
337
    Internal::Jp2BoxHeader newBox = subBox;
616
617
337
    if (count < length) {
618
335
      subBox.length = getULong(boxBuf.c_data(count), bigEndian);
619
335
      subBox.type = getULong(boxBuf.c_data(count + 4), bigEndian);
620
#ifdef EXIV2_DEBUG_MESSAGES
621
      std::cout << "Jp2Image::encodeJp2Header subbox: " << toAscii(subBox.type) << " length = " << subBox.length
622
                << '\n';
623
#endif
624
335
      Internal::enforce(subBox.length > 0, ErrorCode::kerCorruptedMetadata);
625
335
      Internal::enforce(subBox.length <= length - count, ErrorCode::kerCorruptedMetadata);
626
335
      count += subBox.length;
627
335
      newBox.type = subBox.type;
628
335
    } else {
629
2
      subBox.length = 0;
630
2
      newBox.type = kJp2BoxType::ColorSpec;
631
2
      count = length;
632
2
    }
633
634
337
    size_t newlen = subBox.length;
635
337
    if (newBox.type == kJp2BoxType::ColorSpec) {
636
29
      bWroteColor = true;
637
29
      if (!iccProfileDefined()) {
638
11
        const char* pad = "\x01\x00\x00\x00\x00\x00\x10\x00\x00\x05\x1cuuid";
639
11
        uint32_t psize = 15;
640
11
        Internal::enforce(newlen <= output.size() - outlen, ErrorCode::kerCorruptedMetadata);
641
11
        ul2Data(reinterpret_cast<byte*>(&newBox.length), psize, bigEndian);
642
11
        ul2Data(reinterpret_cast<byte*>(&newBox.type), newBox.type, bigEndian);
643
11
        std::memcpy(output.data(outlen), &newBox, sizeof(newBox));
644
11
        std::memcpy(output.data(outlen) + sizeof(newBox), pad, psize);
645
18
      } else {
646
18
        const char* pad = "\x02\x00\x00";
647
18
        uint32_t psize = 3;
648
18
        newlen = sizeof(newBox) + psize + iccProfile_.size();
649
18
        Internal::enforce(newlen <= output.size() - outlen, ErrorCode::kerCorruptedMetadata);
650
18
        ul2Data(reinterpret_cast<byte*>(&newBox.length), static_cast<uint32_t>(newlen), bigEndian);
651
18
        ul2Data(reinterpret_cast<byte*>(&newBox.type), newBox.type, bigEndian);
652
18
        std::memcpy(output.data(outlen), &newBox, sizeof(newBox));
653
18
        std::memcpy(output.data(outlen) + sizeof(newBox), pad, psize);
654
18
        std::copy(iccProfile_.begin(), iccProfile_.end(), output.begin() + outlen + sizeof(newBox) + psize);
655
18
      }
656
308
    } else {
657
308
      Internal::enforce(newlen <= output.size() - outlen, ErrorCode::kerCorruptedMetadata);
658
308
      std::copy_n(boxBuf.begin() + inlen, subBox.length, output.begin() + outlen);
659
308
    }
660
661
337
    outlen += newlen;
662
337
    inlen += subBox.length;
663
337
  }
664
665
  // allocate the correct number of bytes, copy the data and update the box header
666
5.97k
  outBuf.alloc(outlen);
667
5.97k
  std::copy_n(output.begin(), outlen, outBuf.begin());
668
5.97k
  ul2Data(outBuf.data(0), static_cast<uint32_t>(outlen), bigEndian);
669
5.97k
  ul2Data(outBuf.data(4), kJp2BoxType::Header, bigEndian);
670
5.97k
}
671
672
782
void Jp2Image::doWriteMetadata(BasicIo& outIo) {
673
782
  if (!io_->isopen())
674
0
    throw Error(ErrorCode::kerInputDataReadFailed);
675
782
  if (!outIo.isopen())
676
0
    throw Error(ErrorCode::kerImageWriteFailed);
677
678
#ifdef EXIV2_DEBUG_MESSAGES
679
  std::cout << "Exiv2::Jp2Image::doWriteMetadata: Writing JPEG-2000 file " << io_->path() << '\n';
680
  std::cout << "Exiv2::Jp2Image::doWriteMetadata: tmp file created " << outIo.path() << '\n';
681
#endif
682
683
  // Ensure that this is the correct image type
684
782
  if (!isJp2Type(*io_, true)) {
685
0
    throw Error(ErrorCode::kerNoImageInInputData);
686
0
  }
687
688
  // Write JPEG2000 Signature (This is the 1st box)
689
782
  if (outIo.write(Jp2Signature.data(), Jp2Signature.size()) != 12)
690
0
    throw Error(ErrorCode::kerImageWriteFailed);
691
692
#ifdef EXIV2_DEBUG_MESSAGES
693
  std::cout << "Jp2Image::doWriteMetadata: JPEG 2000 Signature box written" << '\n';
694
#endif
695
696
782
  Internal::Jp2BoxHeader box = {0, 0};
697
698
782
  std::array<byte, 4> boxDataSize;
699
782
  std::array<byte, 4> boxUUIDtype;
700
782
  DataBuf bheaderBuf(8);
701
702
17.6k
  while (io_->tell() < io_->size()) {
703
#ifdef EXIV2_DEBUG_MESSAGES
704
    std::cout << "Exiv2::Jp2Image::doWriteMetadata: Position: " << io_->tell() << " / " << io_->size() << '\n';
705
#endif
706
707
    // Read chunk header.
708
17.1k
    io_->readOrThrow(bheaderBuf.data(), bheaderBuf.size(), ErrorCode::kerInputDataReadFailed);
709
710
    // Decode box header.
711
17.1k
    box.length = bheaderBuf.read_uint32(0, bigEndian);
712
17.1k
    box.type = bheaderBuf.read_uint32(4, bigEndian);
713
714
#ifdef EXIV2_DEBUG_MESSAGES
715
    std::cout << "Exiv2::Jp2Image::doWriteMetadata: box type: " << toAscii(box.type) << " length: " << box.length
716
              << '\n';
717
#endif
718
719
17.1k
    if (box.length == 0) {
720
#ifdef EXIV2_DEBUG_MESSAGES
721
      std::cout << "Exiv2::Jp2Image::doWriteMetadata: Null Box size has been found. "
722
                   "This is the last box of file."
723
                << '\n';
724
#endif
725
373
      box.length = static_cast<uint32_t>(io_->size() - io_->tell() + 8);
726
373
    }
727
17.1k
    if (box.length < 8) {
728
      // box is broken, so there is nothing we can do here
729
3
      throw Error(ErrorCode::kerCorruptedMetadata);
730
3
    }
731
732
    // Prevent a malicious file from causing a large memory allocation.
733
17.1k
    Internal::enforce(box.length - 8 <= io_->size() - io_->tell(), ErrorCode::kerCorruptedMetadata);
734
735
    // Read whole box : Box header + Box data (not fixed size - can be null).
736
17.1k
    DataBuf boxBuf(box.length);                                       // Box header (8 bytes) + box data.
737
17.1k
    std::copy(bheaderBuf.begin(), bheaderBuf.end(), boxBuf.begin());  // Copy header.
738
17.1k
    io_->readOrThrow(boxBuf.data(8), box.length - 8, ErrorCode::kerInputDataReadFailed);  // Extract box data.
739
740
17.1k
    switch (box.type) {
741
5.97k
      case kJp2BoxType::Header: {
742
5.97k
        DataBuf newBuf;
743
5.97k
        encodeJp2Header(boxBuf, newBuf);
744
#ifdef EXIV2_DEBUG_MESSAGES
745
        std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write JP2Header box (length: " << box.length << ")\n";
746
#endif
747
5.97k
        if (outIo.write(newBuf.data(), newBuf.size()) != newBuf.size())
748
0
          throw Error(ErrorCode::kerImageWriteFailed);
749
750
        // Write all updated metadata here, just after JP2Header.
751
752
5.97k
        if (!exifData_.empty()) {
753
          // Update Exif data to a new UUID box
754
755
5.52k
          Blob blob;
756
5.52k
          ExifParser::encode(blob, littleEndian, exifData_);
757
5.52k
          if (!blob.empty()) {
758
5.49k
            DataBuf rawExif(blob.size());
759
5.49k
            std::copy(blob.begin(), blob.end(), rawExif.begin());
760
761
5.49k
            DataBuf boxData(8 + 16 + rawExif.size());
762
5.49k
            ul2Data(boxDataSize.data(), static_cast<uint32_t>(boxData.size()), bigEndian);
763
5.49k
            ul2Data(boxUUIDtype.data(), kJp2BoxType::Uuid, bigEndian);
764
5.49k
            std::copy(boxDataSize.begin(), boxDataSize.end(), boxData.begin());
765
5.49k
            std::copy(boxUUIDtype.begin(), boxUUIDtype.end(), boxData.begin() + 4);
766
5.49k
            std::copy(kJp2UuidExif.begin(), kJp2UuidExif.end(), boxData.begin() + 8);
767
5.49k
            std::copy(rawExif.begin(), rawExif.end(), boxData.begin() + 8 + 16);
768
769
#ifdef EXIV2_DEBUG_MESSAGES
770
            std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Exif metadata (length: " << boxData.size()
771
                      << '\n';
772
#endif
773
5.49k
            if (outIo.write(boxData.c_data(), boxData.size()) != boxData.size())
774
0
              throw Error(ErrorCode::kerImageWriteFailed);
775
5.49k
          }
776
5.52k
        }
777
778
5.97k
        if (!iptcData_.empty()) {
779
          // Update Iptc data to a new UUID box
780
781
393
          DataBuf rawIptc = IptcParser::encode(iptcData_);
782
393
          if (!rawIptc.empty()) {
783
393
            DataBuf boxData(8 + 16 + rawIptc.size());
784
393
            ul2Data(boxDataSize.data(), static_cast<uint32_t>(boxData.size()), bigEndian);
785
393
            ul2Data(boxUUIDtype.data(), kJp2BoxType::Uuid, bigEndian);
786
393
            std::copy(boxDataSize.begin(), boxDataSize.end(), boxData.begin());
787
393
            std::copy(boxUUIDtype.begin(), boxUUIDtype.end(), boxData.begin() + 4);
788
393
            std::copy(kJp2UuidIptc.begin(), kJp2UuidIptc.end(), boxData.begin() + 8);
789
393
            std::copy(rawIptc.begin(), rawIptc.end(), boxData.begin() + 8 + 16);
790
791
#ifdef EXIV2_DEBUG_MESSAGES
792
            std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Iptc metadata (length: " << boxData.size()
793
                      << '\n';
794
#endif
795
393
            if (outIo.write(boxData.c_data(), boxData.size()) != boxData.size())
796
0
              throw Error(ErrorCode::kerImageWriteFailed);
797
393
          }
798
393
        }
799
800
5.97k
        if (!writeXmpFromPacket() && XmpParser::encode(xmpPacket_, xmpData_) > 1) {
801
0
#ifndef SUPPRESS_WARNINGS
802
0
          EXV_ERROR << "Failed to encode XMP metadata." << '\n';
803
0
#endif
804
0
        }
805
5.97k
        if (!xmpPacket_.empty()) {
806
          // Update Xmp data to a new UUID box
807
808
133
          DataBuf xmp(reinterpret_cast<const byte*>(xmpPacket_.data()), xmpPacket_.size());
809
133
          DataBuf boxData(8 + 16 + xmp.size());
810
133
          ul2Data(boxDataSize.data(), static_cast<uint32_t>(boxData.size()), bigEndian);
811
133
          ul2Data(boxUUIDtype.data(), kJp2BoxType::Uuid, bigEndian);
812
133
          std::copy(boxDataSize.begin(), boxDataSize.end(), boxData.begin());
813
133
          std::copy(boxUUIDtype.begin(), boxUUIDtype.end(), boxData.begin() + 4);
814
133
          std::copy(kJp2UuidXmp.begin(), kJp2UuidXmp.end(), boxData.begin() + 8);
815
133
          std::copy(xmp.begin(), xmp.end(), boxData.begin() + 8 + 16);
816
817
#ifdef EXIV2_DEBUG_MESSAGES
818
          std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with XMP metadata (length: " << boxData.size()
819
                    << ")" << '\n';
820
#endif
821
133
          if (outIo.write(boxData.c_data(), boxData.size()) != boxData.size())
822
0
            throw Error(ErrorCode::kerImageWriteFailed);
823
133
        }
824
825
5.97k
        break;
826
5.97k
      }
827
828
5.97k
      case kJp2BoxType::Uuid: {
829
1.84k
        Internal::enforce(boxBuf.size() >= 24, ErrorCode::kerCorruptedMetadata);
830
1.84k
        if (boxBuf.cmpBytes(8, kJp2UuidExif.data(), 16) == 0) {
831
#ifdef EXIV2_DEBUG_MESSAGES
832
          std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Exif Uuid box" << '\n';
833
#endif
834
1.19k
        } else if (boxBuf.cmpBytes(8, kJp2UuidIptc.data(), 16) == 0) {
835
#ifdef EXIV2_DEBUG_MESSAGES
836
          std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Iptc Uuid box" << '\n';
837
#endif
838
757
        } else if (boxBuf.cmpBytes(8, kJp2UuidXmp.data(), 16) == 0) {
839
#ifdef EXIV2_DEBUG_MESSAGES
840
          std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Xmp Uuid box" << '\n';
841
#endif
842
432
        } else {
843
#ifdef EXIV2_DEBUG_MESSAGES
844
          std::cout << "Exiv2::Jp2Image::doWriteMetadata: write Uuid box (length: " << box.length << ")" << '\n';
845
#endif
846
432
          if (outIo.write(boxBuf.c_data(), boxBuf.size()) != boxBuf.size())
847
0
            throw Error(ErrorCode::kerImageWriteFailed);
848
432
        }
849
1.84k
        break;
850
1.84k
      }
851
852
9.08k
      default: {
853
#ifdef EXIV2_DEBUG_MESSAGES
854
        std::cout << "Exiv2::Jp2Image::doWriteMetadata: write box (length: " << box.length << ")" << '\n';
855
#endif
856
9.08k
        if (outIo.write(boxBuf.c_data(), boxBuf.size()) != boxBuf.size())
857
0
          throw Error(ErrorCode::kerImageWriteFailed);
858
859
9.08k
        break;
860
9.08k
      }
861
17.1k
    }
862
17.1k
  }
863
864
#ifdef EXIV2_DEBUG_MESSAGES
865
  std::cout << "Exiv2::Jp2Image::doWriteMetadata: EOF" << '\n';
866
#endif
867
868
782
}  // Jp2Image::doWriteMetadata
869
870
// *************************************************************************
871
// free functions
872
1.62k
Image::UniquePtr newJp2Instance(BasicIo::UniquePtr io, const ImageCtorParams& params) {
873
1.62k
  auto image = std::make_unique<Jp2Image>(std::move(io), params);
874
1.62k
  if (!image->good()) {
875
6
    return nullptr;
876
6
  }
877
1.62k
  return image;
878
1.62k
}
879
880
20.4k
bool isJp2Type(BasicIo& iIo, bool advance) {
881
20.4k
  std::array<byte, Jp2Signature.size()> buf;
882
20.4k
  const size_t bytesRead = iIo.read(buf.data(), Jp2Signature.size());
883
20.4k
  if (iIo.error() || iIo.eof() || bytesRead != Jp2Signature.size()) {
884
484
    return false;
885
484
  }
886
19.9k
  bool matched = buf == Jp2Signature;
887
19.9k
  if (!advance || !matched) {
888
19.1k
    iIo.seek(-static_cast<int64_t>(Jp2Signature.size()), BasicIo::cur);  // Return to original position
889
19.1k
  }
890
19.9k
  return matched;
891
20.4k
}
892
}  // namespace Exiv2