6 Commits
v1.1.0 ... main

Author SHA1 Message Date
relikd
8b0252f987 fix: jp2 image size bytes header 2026-02-28 22:28:06 +01:00
relikd
d5d77fa592 fix: update tests for pillow==11.3.0 2026-02-28 21:53:09 +01:00
relikd
eb7ce460b5 fix: Chrome cli 2025-10-15 18:37:20 +02:00
relikd
f80d7d79f6 feat: deprecation warning 2025-10-15 18:37:03 +02:00
relikd
bc6fe5a2b3 fix: file permissions 2025-10-15 18:36:52 +02:00
Nicholas Bollweg
028df18dbc ensure license is added to distributions (#2) 2024-09-24 15:57:01 +02:00
11 changed files with 44 additions and 17 deletions

1
MANIFEST.in Normal file
View File

@@ -0,0 +1 @@
include LICENSE

View File

@@ -172,6 +172,10 @@ class IcnsFile:
# Nested icns files must omit the icns header
if is_icns and data[:4] == b'icns':
data = data[8:]
if key in ('icp4', 'icp5'):
iType = IcnsType.get(key)
print('Warning: deprecated "{}"({}) use argb instead'.format(
str(key), iType.filename(size_only=True)), file=stderr)
self.media[key] = data
def remove_media(self, key: IcnsType.Media.KeyT) -> bool:

View File

@@ -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

0
icnsutil/autosize/ImageResizer.py Executable file → Normal file
View File

0
icnsutil/autosize/PixelResizer.py Executable file → Normal file
View File

6
icnsutil/autosize/SVGResizer.py Executable file → Normal file
View File

@@ -39,7 +39,7 @@ class ChromeSVG(SVGResizer):
def resize(self, size: int, fname_out: str) -> None:
run([self.exe, '--headless', '--disable-gpu', '--hide-scrollbars',
'--force-device-scale-factor=1', '--default-background-color=0',
'--window-size={0},{0}'.format(self.preferred_size),
'--screenshot="{}"'.format(self.fname), self.fname],
'--force-device-scale-factor=1', '--default-background-color=000000',
'--window-size={0},{0}'.format(size),
'--screenshot={}'.format(fname_out), self.fname],
stderr=DEVNULL)

0
icnsutil/autosize/helper.py Executable file → Normal file
View File

View File

@@ -2,7 +2,7 @@
from setuptools import setup
from icnsutil import __doc__, __version__
with open('README.md') as fp:
with open('README.md', encoding='utf-8') as fp:
longdesc = fp.read()
setup(
@@ -52,4 +52,5 @@ setup(
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Utilities',
],
include_package_data=True
)

BIN
tests/fixtures/32x32.jpf vendored Normal file

Binary file not shown.

View File

@@ -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')
@@ -258,10 +259,10 @@ class TestCLI_convert(unittest.TestCase):
def test_to_png(self):
for expected_size, fname in [
(103, '18x18.j2k'),
(4778, '256x256.jp2'),
(813, 'rgb.icns.png'),
(813, 'rgb.icns.rgb'),
(107, '18x18.j2k'),
(4496, '256x256.jp2'),
(818, 'rgb.icns.png'),
(818, 'rgb.icns.rgb'),
]:
size = self.assertConvert(fname, 'png')
self.assertEqual(size, expected_size)
@@ -292,7 +293,7 @@ class TestCLI_convert(unittest.TestCase):
def test_without_dest_name(self):
src = 'tmp_cli_out_convert.jp2'
shutil.copy('18x18.j2k', src)
for ext, size in [('png', 103), ('argb', 822), ('rgb', 812)]:
for ext, size in [('png', 107), ('argb', 822), ('rgb', 812)]:
run_cli(['img', ext, src]).stdout
self.assertTrue(os.path.exists(src + '.' + ext))
self.assertEqual(os.path.getsize(src + '.' + ext), size)

View File

@@ -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 (