Skip to content

Commit 86c25e2

Browse files
authored
Merge pull request #2153 from Exiv2/027_FixJp2
0.27 - Fix JP2 write/read metadata
2 parents 060de55 + d03e56e commit 86c25e2

File tree

11 files changed

+608
-406
lines changed

11 files changed

+608
-406
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_library( exiv2lib_int OBJECT
1919
fujimn_int.cpp fujimn_int.hpp
2020
helper_functions.cpp helper_functions.hpp
2121
image_int.cpp image_int.hpp
22+
jp2image_int.cpp jp2image_int.hpp
2223
makernote_int.cpp makernote_int.hpp
2324
minoltamn_int.cpp minoltamn_int.hpp
2425
nikonmn_int.cpp nikonmn_int.hpp

src/jp2image.cpp

Lines changed: 373 additions & 398 deletions
Large diffs are not rendered by default.

src/jp2image_int.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include "jp2image_int.hpp"
2+
3+
#include <cassert>
4+
5+
#include "error.hpp"
6+
#include "types.hpp"
7+
8+
namespace Exiv2 {
9+
namespace Internal {
10+
bool isValidBoxFileType(const std::vector<uint8_t> &boxData)
11+
{
12+
// BR & MinV are obligatory (4 + 4 bytes). Afterwards we have N compatibility
13+
// lists (of size 4)
14+
if ((boxData.size() - 8u) % 4u != 0) {
15+
return false;
16+
}
17+
18+
const size_t N = (boxData.size() - 8u) / 4u;
19+
const uint32_t brand = getULong(boxData.data(), bigEndian);
20+
const uint32_t minorVersion = getULong(boxData.data() + 4, bigEndian);
21+
22+
bool clWithRightBrand = false;
23+
for (size_t i = 0; i < N; i++) {
24+
uint32_t compatibilityList = getULong(boxData.data() + 8 + i * 4, bigEndian);
25+
if (compatibilityList == brandJp2) {
26+
clWithRightBrand = true;
27+
break;
28+
}
29+
}
30+
return (brand == brandJp2 && minorVersion == 0 && clWithRightBrand);
31+
}
32+
}
33+
}

src/jp2image_int.hpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#ifndef JP2IMAGE_INT_HPP
2+
#define JP2IMAGE_INT_HPP
3+
4+
#include <stdint.h>
5+
#include <vector>
6+
7+
namespace Exiv2
8+
{
9+
namespace Internal
10+
{
11+
struct Jp2BoxHeader
12+
{
13+
uint32_t length;
14+
uint32_t type;
15+
};
16+
17+
struct Jp2ImageHeaderBox
18+
{
19+
uint32_t imageHeight;
20+
uint32_t imageWidth;
21+
uint16_t componentCount;
22+
uint8_t bpc; //<! Bits per component
23+
uint8_t c; //<! Compression type
24+
uint8_t unkC; //<! Colourspace unknown
25+
uint8_t ipr; //<! Intellectual property
26+
};
27+
28+
struct Jp2UuidBox
29+
{
30+
uint8_t uuid[16];
31+
};
32+
33+
const uint32_t brandJp2 = 0x6a703220;
34+
35+
/// @brief Determines if the File Type box is valid
36+
bool isValidBoxFileType(const std::vector<uint8_t>& boxData);
37+
} // namespace Internal
38+
} // namespace Exiv2
39+
40+
#endif // JP2IMAGE_INT_HPP

test/data/icc-test.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -735,23 +735,23 @@ STRUCTURE OF JPEG2000 FILE: Reagan2.jp2
735735
12 | 20 | ftyp |
736736
32 | 3185 | jp2h |
737737
40 | 22 | sub:ihdr | .............
738-
62 | 3155 | sub:colr | ......HLino....mntrRGB XYZ .. | pad: 2 0 0 | iccLength:3144
738+
62 | 3155 | sub:colr | ......HLino....mntrRGB XYZ .. | iccLength:3144
739739
3217 | 0 | jp2c |
740740
STRUCTURE OF JPEG2000 FILE: Reagan2.jp2
741741
address | length | box | data
742742
0 | 12 | jP |
743743
12 | 20 | ftyp |
744744
32 | 1613641 | jp2h |
745745
40 | 22 | sub:ihdr | .............
746-
62 | 1613611 | sub:colr | ...... APPL....prtrRGB Lab .. | pad: 2 0 0 | iccLength:1613600
746+
62 | 1613611 | sub:colr | ...... APPL....prtrRGB Lab .. | iccLength:1613600
747747
1613673 | 0 | jp2c |
748748
STRUCTURE OF JPEG2000 FILE: Reagan2.jp2
749749
address | length | box | data
750750
0 | 12 | jP |
751751
12 | 20 | ftyp |
752752
32 | 601 | jp2h |
753753
40 | 22 | sub:ihdr | .............
754-
62 | 571 | sub:colr | ......0ADBE....mntrRGB XYZ .. | pad: 2 0 0 | iccLength:560
754+
62 | 571 | sub:colr | ......0ADBE....mntrRGB XYZ .. | iccLength:560
755755
633 | 0 | jp2c |
756756
1d3fda2edb4a89ab60a23c5f7c7d81dd
757757
1d3fda2edb4a89ab60a23c5f7c7d81dd

tests/bugfixes/github/test_issue_1845.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ class TiffDirectoryWriteDirEntryAssert(metaclass=CaseMeta):
1212

1313
filename = path("$tmp_path/issue_1845_poc.jp2")
1414
commands = ["$exiv2 -q -D +1 ad $filename"]
15-
stderr = [""]
15+
stderr = [
16+
"""$exception_in_adjust """ + filename + """:
17+
$kerCorruptedMetadata
18+
"""]
1619
stdout = [""]
17-
retval = [0]
20+
retval = [1]

tests/bugfixes/github/test_issue_ghsa_mxw9_qx4c_6m8v.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class Jp2ImageEncodeJp2HeaderOutOfBoundsRead2(metaclass=CaseMeta):
1313
filename = path("$tmp_path/issue_ghsa_mxw9_qx4c_6m8v_poc.jp2")
1414
commands = ["$exiv2 rm $filename"]
1515
stdout = [""]
16-
retval = [0]
17-
18-
compare_stderr = check_no_ASAN_UBSAN_errors
16+
stderr = [
17+
"""$exception_in_erase """ + filename + """:
18+
$kerCorruptedMetadata
19+
"""]
20+
retval = [1]

tests/suite.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,7 @@ addition_overflow_message: Overflow in addition
4848
exiv2_exception_message: Exiv2 exception in print action for file
4949
exiv2_overflow_exception_message: std::overflow_error exception in print action for file
5050
exception_in_extract: Exiv2 exception in extract action for file
51+
exception_in_adjust: Exiv2 exception in adjust action for file
52+
exception_in_erase: Exiv2 exception in erase action for file
5153
uncaught_exception: Uncaught exception:
5254
no_exif_data_found_retval: 253

unitTests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_executable(unit_tests
1212
test_futils.cpp
1313
test_helper_functions.cpp
1414
test_image_int.cpp
15+
test_jp2image.cpp
1516
test_safe_op.cpp
1617
test_slice.cpp
1718
test_tiffheader.cpp

unitTests/test_basicio.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,55 @@
2222
#include <gtest/gtest.h>
2323
using namespace Exiv2;
2424

25+
TEST(MemIo_Default, readEReturns0)
26+
{
27+
std::vector<byte> buf(10);
28+
MemIo io;
29+
ASSERT_EQ(0, io.read(buf.data(), (long)buf.size()));
30+
}
31+
32+
TEST(MemIo_Default, isNotAtEof)
33+
{
34+
MemIo io;
35+
ASSERT_FALSE(io.eof());
36+
}
37+
38+
TEST(MemIo_Default, seekBeyondBufferSizeReturns1AndSetsEofToTrue)
39+
{
40+
MemIo io;
41+
ASSERT_EQ(1, io.seek(1, BasicIo::beg));
42+
ASSERT_TRUE(io.eof());
43+
}
44+
45+
TEST(MemIo_Default, seekBefore0Returns1ButItDoesNotSetEofToTrue)
46+
{
47+
MemIo io;
48+
ASSERT_EQ(1, io.seek(-1, BasicIo::beg));
49+
ASSERT_FALSE(io.eof());
50+
}
51+
52+
TEST(MemIo_Default, seekToEndPosition_doesNotTriggerEof)
53+
{
54+
MemIo io;
55+
ASSERT_EQ(0, io.tell());
56+
ASSERT_EQ(0, io.seek(0, BasicIo::end));
57+
ASSERT_EQ(0, io.tell());
58+
ASSERT_FALSE(io.eof());
59+
}
60+
61+
TEST(MemIo_Default, seekToEndPositionAndReadTriggersEof)
62+
{
63+
MemIo io;
64+
ASSERT_EQ(0, io.seek(0, BasicIo::end));
65+
ASSERT_EQ(0, io.tell());
66+
67+
std::vector<byte> buf2(64, 0);
68+
ASSERT_EQ(0, io.read(buf2.data(), 1)); // Note that we cannot even read 1 byte being at the end
69+
ASSERT_TRUE(io.eof());
70+
}
71+
72+
// -------------------------
73+
2574
TEST(MemIo, seek_out_of_bounds_00)
2675
{
2776
byte buf[1024];

0 commit comments

Comments
 (0)