diff --git a/icnsutil/RawData.py b/icnsutil/RawData.py index 0f42ccc..7d31404 100644 --- a/icnsutil/RawData.py +++ b/icnsutil/RawData.py @@ -29,6 +29,32 @@ def determine_file_ext(data: bytes) -> Optional[str]: return None +def _determine_jp2_size(data: bytes) -> Optional[Tuple[int, int]]: + ''' Read raw bytes and extract JPEG2000 image size. ''' + if data[:4] == b'\xFF\x4F\xFF\x51': + w, h = struct.unpack('>II', data[8:16]) + return w, h + + # fixed "jP" file header 0000000C 6A502020 0D0A870A + off = 12 + filesize = len(data) + + while off < filesize: + box_size, box_type = struct.unpack('>I4s', data[off:off+8]) + # find JP2 Header box + if box_type == b'jp2h': + child = off + 8 # skip parent header + while child < (off + box_size): + # find Image Header box + if data[child+4:child+8] == b'ihdr': + h, w = struct.unpack('>II', data[child+8:child+16]) + return w, h + + child += struct.unpack('>I', data[child:child+4])[0] + off += box_size + return None + + def determine_image_size(data: bytes, ext: Optional[str] = None) \ -> Optional[Tuple[int, int]]: ''' Supports PNG, ARGB, and Jpeg 2000 image data. ''' @@ -45,14 +71,7 @@ def determine_image_size(data: bytes, ext: Optional[str] = None) \ data = data[4:] # without it32 header return IcnsType.match_maxsize(PackBytes.get_size(data), 'rgb').size elif ext == 'jp2': - if data[:4] == b'\xFF\x4F\xFF\x51': - w, h = struct.unpack('>II', data[8:16]) - return w, h - len_ftype = struct.unpack('>I', data[12:16])[0] - # file header + type box + header box (super box) + image header box - offset = 12 + len_ftype + 8 + 8 - h, w = struct.unpack('>II', data[offset:offset + 8]) - return w, h + return _determine_jp2_size(data) return None # icns does not support other image types except binary diff --git a/tests/fixtures/32x32.jpf b/tests/fixtures/32x32.jpf new file mode 100644 index 0000000..6d25a0e Binary files /dev/null and b/tests/fixtures/32x32.jpf differ diff --git a/tests/test_cli.py b/tests/test_cli.py index da7ae0b..b579ff3 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -146,6 +146,7 @@ class TestCLI_compose(unittest.TestCase): def test_jp2(self): self.assert_conv_file('256x256.jp2', 'ic08') self.assert_conv_file('18x18.j2k', 'icsb', arg=['-f']) + self.assert_conv_file('32x32.jpf', 'icp5', arg=['-f']) def test_argb(self): self.assert_conv_file('rgb.icns.argb', 'ic04') diff --git a/tests/test_icnsutil.py b/tests/test_icnsutil.py index 5dd0a0a..b09fce3 100644 --- a/tests/test_icnsutil.py +++ b/tests/test_icnsutil.py @@ -444,6 +444,7 @@ class TestRawData(unittest.TestCase): self.assertEqual(fn('rgb.icns.argb'), (16, 16)) self.assertEqual(fn('256x256.jp2'), (256, 256)) self.assertEqual(fn('18x18.j2k'), (18, 18)) + self.assertEqual(fn('32x32.jpf'), (32, 32)) def test_ext(self): for data, ext in (