2023-12-21 14:13:31 +03:00
|
|
|
from __future__ import annotations
|
2024-01-20 14:23:03 +03:00
|
|
|
|
2017-09-01 13:36:51 +03:00
|
|
|
import io
|
2021-11-20 06:17:42 +03:00
|
|
|
import os
|
2022-02-21 05:49:01 +03:00
|
|
|
import warnings
|
2024-01-31 12:12:58 +03:00
|
|
|
from pathlib import Path
|
2015-04-12 05:58:46 +03:00
|
|
|
|
2020-02-03 12:11:32 +03:00
|
|
|
import pytest
|
2020-08-07 13:28:33 +03:00
|
|
|
|
2022-04-29 12:17:03 +03:00
|
|
|
from PIL import IcnsImagePlugin, Image, _binary
|
2019-07-06 23:40:53 +03:00
|
|
|
|
2022-04-29 12:17:03 +03:00
|
|
|
from .helper import assert_image_equal, assert_image_similar_tofile, skip_unless_feature
|
2019-07-06 23:40:53 +03:00
|
|
|
|
2014-03-26 14:49:39 +04:00
|
|
|
# sample icon file
|
2015-05-05 11:15:55 +03:00
|
|
|
TEST_FILE = "Tests/images/pillow.icns"
|
2014-03-26 14:49:39 +04:00
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_sanity() -> None:
|
2020-02-23 00:03:01 +03:00
|
|
|
# Loading this icon by default should result in the largest size
|
|
|
|
# (512x512@2x) being loaded
|
|
|
|
with Image.open(TEST_FILE) as im:
|
|
|
|
# Assert that there is no unclosed file warning
|
2022-02-21 05:49:01 +03:00
|
|
|
with warnings.catch_warnings():
|
2024-10-26 08:15:46 +03:00
|
|
|
warnings.simplefilter("error")
|
|
|
|
|
2021-02-10 15:37:55 +03:00
|
|
|
im.load()
|
2018-11-13 14:13:55 +03:00
|
|
|
|
2020-02-23 00:03:01 +03:00
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (1024, 1024)
|
|
|
|
assert im.format == "ICNS"
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_load() -> None:
|
2022-02-02 03:49:31 +03:00
|
|
|
with Image.open(TEST_FILE) as im:
|
|
|
|
assert im.load()[0, 0] == (0, 0, 0, 0)
|
|
|
|
|
|
|
|
# Test again now that it has already been loaded once
|
|
|
|
assert im.load()[0, 0] == (0, 0, 0, 0)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_save(tmp_path: Path) -> None:
|
2020-02-23 00:03:01 +03:00
|
|
|
temp_file = str(tmp_path / "temp.icns")
|
|
|
|
|
|
|
|
with Image.open(TEST_FILE) as im:
|
|
|
|
im.save(temp_file)
|
|
|
|
|
|
|
|
with Image.open(temp_file) as reread:
|
|
|
|
assert reread.mode == "RGBA"
|
|
|
|
assert reread.size == (1024, 1024)
|
|
|
|
assert reread.format == "ICNS"
|
2014-03-28 13:09:55 +04:00
|
|
|
|
2021-11-20 06:17:42 +03:00
|
|
|
file_length = os.path.getsize(temp_file)
|
|
|
|
with open(temp_file, "rb") as fp:
|
|
|
|
fp.seek(4)
|
|
|
|
assert _binary.i32be(fp.read(4)) == file_length
|
|
|
|
|
2015-04-24 02:26:52 +03:00
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_save_append_images(tmp_path: Path) -> None:
|
2020-02-23 00:03:01 +03:00
|
|
|
temp_file = str(tmp_path / "temp.icns")
|
|
|
|
provided_im = Image.new("RGBA", (32, 32), (255, 0, 0, 128))
|
|
|
|
|
|
|
|
with Image.open(TEST_FILE) as im:
|
|
|
|
im.save(temp_file, append_images=[provided_im])
|
2015-04-24 02:26:52 +03:00
|
|
|
|
2021-02-21 14:22:29 +03:00
|
|
|
assert_image_similar_tofile(im, temp_file, 1)
|
2017-09-01 13:36:51 +03:00
|
|
|
|
2020-02-23 00:03:01 +03:00
|
|
|
with Image.open(temp_file) as reread:
|
2024-09-06 04:37:12 +03:00
|
|
|
reread.size = (16, 16)
|
|
|
|
reread.load(2)
|
2020-02-23 00:03:01 +03:00
|
|
|
assert_image_equal(reread, provided_im)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_save_fp() -> None:
|
2020-06-28 10:24:27 +03:00
|
|
|
fp = io.BytesIO()
|
|
|
|
|
|
|
|
with Image.open(TEST_FILE) as im:
|
|
|
|
im.save(fp, format="ICNS")
|
|
|
|
|
|
|
|
with Image.open(fp) as reread:
|
|
|
|
assert reread.mode == "RGBA"
|
|
|
|
assert reread.size == (1024, 1024)
|
|
|
|
assert reread.format == "ICNS"
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_sizes() -> None:
|
2020-02-23 00:03:01 +03:00
|
|
|
# Check that we can load all of the sizes, and that the final pixel
|
|
|
|
# dimensions are as expected
|
|
|
|
with Image.open(TEST_FILE) as im:
|
|
|
|
for w, h, r in im.info["sizes"]:
|
|
|
|
wr = w * r
|
|
|
|
hr = h * r
|
2024-09-06 04:42:56 +03:00
|
|
|
with pytest.warns(DeprecationWarning):
|
|
|
|
im.size = (w, h, r)
|
2020-02-23 00:03:01 +03:00
|
|
|
im.load()
|
2020-02-22 16:06:21 +03:00
|
|
|
assert im.mode == "RGBA"
|
2020-02-23 00:03:01 +03:00
|
|
|
assert im.size == (wr, hr)
|
|
|
|
|
2024-09-06 04:37:12 +03:00
|
|
|
# Test using load() with scale
|
|
|
|
im.size = (w, h)
|
|
|
|
im.load(scale=r)
|
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (wr, hr)
|
|
|
|
|
2020-02-23 00:03:01 +03:00
|
|
|
# Check that we cannot load an incorrect size
|
|
|
|
with pytest.raises(ValueError):
|
2024-09-06 04:37:12 +03:00
|
|
|
im.size = (1, 2)
|
2020-02-23 00:03:01 +03:00
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_older_icon() -> None:
|
2020-02-23 00:03:01 +03:00
|
|
|
# This icon was made with Icon Composer rather than iconutil; it still
|
|
|
|
# uses PNG rather than JP2, however (since it was made on 10.9).
|
|
|
|
with Image.open("Tests/images/pillow2.icns") as im:
|
|
|
|
for w, h, r in im.info["sizes"]:
|
|
|
|
wr = w * r
|
|
|
|
hr = h * r
|
|
|
|
with Image.open("Tests/images/pillow2.icns") as im2:
|
2024-09-06 04:37:12 +03:00
|
|
|
im2.size = (w, h)
|
|
|
|
im2.load(r)
|
2020-02-23 00:03:01 +03:00
|
|
|
assert im2.mode == "RGBA"
|
|
|
|
assert im2.size == (wr, hr)
|
|
|
|
|
|
|
|
|
2022-04-29 12:17:03 +03:00
|
|
|
@skip_unless_feature("jpg_2000")
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_jp2_icon() -> None:
|
2022-02-17 10:55:48 +03:00
|
|
|
# This icon uses JPEG 2000 images instead of the PNG images.
|
|
|
|
# The advantage of doing this is that OS X 10.5 supports JPEG 2000
|
|
|
|
# but not PNG; some commercial software therefore does just this.
|
2020-02-23 00:03:01 +03:00
|
|
|
|
|
|
|
with Image.open("Tests/images/pillow3.icns") as im:
|
|
|
|
for w, h, r in im.info["sizes"]:
|
|
|
|
wr = w * r
|
|
|
|
hr = h * r
|
|
|
|
with Image.open("Tests/images/pillow3.icns") as im2:
|
2024-09-06 04:37:12 +03:00
|
|
|
im2.size = (w, h)
|
|
|
|
im2.load(r)
|
2020-02-23 00:03:01 +03:00
|
|
|
assert im2.mode == "RGBA"
|
|
|
|
assert im2.size == (wr, hr)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_getimage() -> None:
|
2020-02-23 00:03:01 +03:00
|
|
|
with open(TEST_FILE, "rb") as fp:
|
|
|
|
icns_file = IcnsImagePlugin.IcnsFile(fp)
|
|
|
|
|
|
|
|
im = icns_file.getimage()
|
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (1024, 1024)
|
|
|
|
|
|
|
|
im = icns_file.getimage((512, 512))
|
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (512, 512)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_not_an_icns_file() -> None:
|
2020-02-23 00:03:01 +03:00
|
|
|
with io.BytesIO(b"invalid\n") as fp:
|
|
|
|
with pytest.raises(SyntaxError):
|
|
|
|
IcnsImagePlugin.IcnsFile(fp)
|
2021-02-25 01:27:07 +03:00
|
|
|
|
|
|
|
|
2022-04-29 12:17:03 +03:00
|
|
|
@skip_unless_feature("jpg_2000")
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_icns_decompression_bomb() -> None:
|
2021-03-12 14:45:07 +03:00
|
|
|
with Image.open(
|
|
|
|
"Tests/images/oom-8ed3316a4109213ca96fb8a256a0bfefdece1461.icns"
|
|
|
|
) as im:
|
|
|
|
with pytest.raises(Image.DecompressionBombError):
|
|
|
|
im.load()
|