mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-27 10:26:19 +03:00
Added type hints
This commit is contained in:
parent
be01d536c6
commit
e005bcf8f2
|
@ -98,6 +98,7 @@ def assert_image_equal(a: Image.Image, b: Image.Image, msg: str | None = None) -
|
|||
def assert_image_equal_tofile(
|
||||
a: Image.Image, filename: str, msg: str | None = None, mode: str | None = None
|
||||
) -> None:
|
||||
img: Image.Image
|
||||
with Image.open(filename) as img:
|
||||
if mode:
|
||||
img = img.convert(mode)
|
||||
|
@ -142,6 +143,7 @@ def assert_image_similar_tofile(
|
|||
msg: str | None = None,
|
||||
mode: str | None = None,
|
||||
) -> None:
|
||||
img: Image.Image
|
||||
with Image.open(filename) as img:
|
||||
if mode:
|
||||
img = img.convert(mode)
|
||||
|
|
|
@ -92,8 +92,11 @@ def test_good() -> None:
|
|||
|
||||
for f in get_files("g"):
|
||||
try:
|
||||
im: Image.Image
|
||||
with Image.open(f) as im:
|
||||
im.load()
|
||||
|
||||
compare: Image.Image
|
||||
with Image.open(get_compare(f)) as compare:
|
||||
compare.load()
|
||||
if im.mode == "P":
|
||||
|
|
|
@ -12,6 +12,7 @@ from PIL import Image, ImageSequence, PngImagePlugin
|
|||
# (referenced from https://wiki.mozilla.org/APNG_Specification)
|
||||
def test_apng_basic() -> None:
|
||||
with Image.open("Tests/images/apng/single_frame.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert not im.is_animated
|
||||
assert im.n_frames == 1
|
||||
assert im.get_format_mimetype() == "image/apng"
|
||||
|
@ -20,6 +21,7 @@ def test_apng_basic() -> None:
|
|||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
||||
with Image.open("Tests/images/apng/single_frame_default.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.is_animated
|
||||
assert im.n_frames == 2
|
||||
assert im.get_format_mimetype() == "image/apng"
|
||||
|
@ -49,6 +51,7 @@ def test_apng_basic() -> None:
|
|||
)
|
||||
def test_apng_fdat(filename: str) -> None:
|
||||
with Image.open(filename) as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
@ -56,31 +59,37 @@ def test_apng_fdat(filename: str) -> None:
|
|||
|
||||
def test_apng_dispose() -> None:
|
||||
with Image.open("Tests/images/apng/dispose_op_none.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
||||
with Image.open("Tests/images/apng/dispose_op_background.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 0, 0, 0)
|
||||
assert im.getpixel((64, 32)) == (0, 0, 0, 0)
|
||||
|
||||
with Image.open("Tests/images/apng/dispose_op_background_final.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
||||
with Image.open("Tests/images/apng/dispose_op_previous.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
||||
with Image.open("Tests/images/apng/dispose_op_previous_final.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
||||
with Image.open("Tests/images/apng/dispose_op_previous_first.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 0, 0, 0)
|
||||
assert im.getpixel((64, 32)) == (0, 0, 0, 0)
|
||||
|
@ -88,21 +97,25 @@ def test_apng_dispose() -> None:
|
|||
|
||||
def test_apng_dispose_region() -> None:
|
||||
with Image.open("Tests/images/apng/dispose_op_none_region.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
||||
with Image.open("Tests/images/apng/dispose_op_background_before_region.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 0, 0, 0)
|
||||
assert im.getpixel((64, 32)) == (0, 0, 0, 0)
|
||||
|
||||
with Image.open("Tests/images/apng/dispose_op_background_region.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 0, 255, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 0, 0, 0)
|
||||
|
||||
with Image.open("Tests/images/apng/dispose_op_previous_region.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
@ -129,6 +142,7 @@ def test_apng_dispose_op_previous_frame() -> None:
|
|||
# ],
|
||||
# )
|
||||
with Image.open("Tests/images/apng/dispose_op_previous_frame.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (255, 0, 0, 255)
|
||||
|
||||
|
@ -142,26 +156,31 @@ def test_apng_dispose_op_background_p_mode() -> None:
|
|||
|
||||
def test_apng_blend() -> None:
|
||||
with Image.open("Tests/images/apng/blend_op_source_solid.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
||||
with Image.open("Tests/images/apng/blend_op_source_transparent.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 0, 0, 0)
|
||||
assert im.getpixel((64, 32)) == (0, 0, 0, 0)
|
||||
|
||||
with Image.open("Tests/images/apng/blend_op_source_near_transparent.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 2)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 2)
|
||||
|
||||
with Image.open("Tests/images/apng/blend_op_over.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
||||
with Image.open("Tests/images/apng/blend_op_over_near_transparent.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 97)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
@ -175,6 +194,7 @@ def test_apng_blend_transparency() -> None:
|
|||
|
||||
def test_apng_chunk_order() -> None:
|
||||
with Image.open("Tests/images/apng/fctl_actl.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
|
@ -230,66 +250,78 @@ def test_apng_num_plays() -> None:
|
|||
|
||||
def test_apng_mode() -> None:
|
||||
with Image.open("Tests/images/apng/mode_16bit.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.mode == "RGBA"
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (0, 0, 128, 191)
|
||||
assert im.getpixel((64, 32)) == (0, 0, 128, 191)
|
||||
|
||||
with Image.open("Tests/images/apng/mode_grayscale.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.mode == "L"
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == 128
|
||||
assert im.getpixel((64, 32)) == 255
|
||||
|
||||
with Image.open("Tests/images/apng/mode_grayscale_alpha.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.mode == "LA"
|
||||
im.seek(im.n_frames - 1)
|
||||
assert im.getpixel((0, 0)) == (128, 191)
|
||||
assert im.getpixel((64, 32)) == (128, 191)
|
||||
|
||||
with Image.open("Tests/images/apng/mode_palette.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.mode == "P"
|
||||
im.seek(im.n_frames - 1)
|
||||
im = im.convert("RGB")
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0)
|
||||
rgb_im = im.convert("RGB")
|
||||
assert rgb_im.getpixel((0, 0)) == (0, 255, 0)
|
||||
assert rgb_im.getpixel((64, 32)) == (0, 255, 0)
|
||||
|
||||
with Image.open("Tests/images/apng/mode_palette_alpha.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.mode == "P"
|
||||
im.seek(im.n_frames - 1)
|
||||
im = im.convert("RGBA")
|
||||
assert im.getpixel((0, 0)) == (255, 0, 0, 0)
|
||||
assert im.getpixel((64, 32)) == (255, 0, 0, 0)
|
||||
rgb_im = im.convert("RGBA")
|
||||
assert rgb_im.getpixel((0, 0)) == (255, 0, 0, 0)
|
||||
assert rgb_im.getpixel((64, 32)) == (255, 0, 0, 0)
|
||||
|
||||
with Image.open("Tests/images/apng/mode_palette_1bit_alpha.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.mode == "P"
|
||||
im.seek(im.n_frames - 1)
|
||||
im = im.convert("RGBA")
|
||||
assert im.getpixel((0, 0)) == (0, 0, 255, 128)
|
||||
assert im.getpixel((64, 32)) == (0, 0, 255, 128)
|
||||
rgb_im = im.convert("RGBA")
|
||||
assert rgb_im.getpixel((0, 0)) == (0, 0, 255, 128)
|
||||
assert rgb_im.getpixel((64, 32)) == (0, 0, 255, 128)
|
||||
|
||||
|
||||
def test_apng_chunk_errors() -> None:
|
||||
with Image.open("Tests/images/apng/chunk_no_actl.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert not im.is_animated
|
||||
|
||||
with pytest.warns(UserWarning):
|
||||
with Image.open("Tests/images/apng/chunk_multi_actl.png") as im:
|
||||
im.load()
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert not im.is_animated
|
||||
|
||||
with Image.open("Tests/images/apng/chunk_actl_after_idat.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert not im.is_animated
|
||||
|
||||
with Image.open("Tests/images/apng/chunk_no_fctl.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
with pytest.raises(SyntaxError):
|
||||
im.seek(im.n_frames - 1)
|
||||
|
||||
with Image.open("Tests/images/apng/chunk_repeat_fctl.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
with pytest.raises(SyntaxError):
|
||||
im.seek(im.n_frames - 1)
|
||||
|
||||
with Image.open("Tests/images/apng/chunk_no_fdat.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
with pytest.raises(SyntaxError):
|
||||
im.seek(im.n_frames - 1)
|
||||
|
||||
|
@ -297,18 +329,21 @@ def test_apng_chunk_errors() -> None:
|
|||
def test_apng_syntax_errors() -> None:
|
||||
with pytest.warns(UserWarning):
|
||||
with Image.open("Tests/images/apng/syntax_num_frames_zero.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert not im.is_animated
|
||||
with pytest.raises(OSError):
|
||||
im.load()
|
||||
|
||||
with pytest.warns(UserWarning):
|
||||
with Image.open("Tests/images/apng/syntax_num_frames_zero_default.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert not im.is_animated
|
||||
im.load()
|
||||
|
||||
# we can handle this case gracefully
|
||||
exception = None
|
||||
with Image.open("Tests/images/apng/syntax_num_frames_low.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
try:
|
||||
im.seek(im.n_frames - 1)
|
||||
except Exception as e:
|
||||
|
@ -317,11 +352,13 @@ def test_apng_syntax_errors() -> None:
|
|||
|
||||
with pytest.raises(OSError):
|
||||
with Image.open("Tests/images/apng/syntax_num_frames_high.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
im.load()
|
||||
|
||||
with pytest.warns(UserWarning):
|
||||
with Image.open("Tests/images/apng/syntax_num_frames_invalid.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert not im.is_animated
|
||||
im.load()
|
||||
|
||||
|
@ -341,6 +378,7 @@ def test_apng_syntax_errors() -> None:
|
|||
def test_apng_sequence_errors(test_file: str) -> None:
|
||||
with pytest.raises(SyntaxError):
|
||||
with Image.open(f"Tests/images/apng/{test_file}") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
im.seek(im.n_frames - 1)
|
||||
im.load()
|
||||
|
||||
|
@ -351,6 +389,8 @@ def test_apng_save(tmp_path: Path) -> None:
|
|||
im.save(test_file, save_all=True)
|
||||
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
|
||||
im.load()
|
||||
assert not im.is_animated
|
||||
assert im.n_frames == 1
|
||||
|
@ -366,6 +406,8 @@ def test_apng_save(tmp_path: Path) -> None:
|
|||
)
|
||||
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
|
||||
im.load()
|
||||
assert im.is_animated
|
||||
assert im.n_frames == 2
|
||||
|
@ -405,6 +447,8 @@ def test_apng_save_split_fdat(tmp_path: Path) -> None:
|
|||
append_images=frames,
|
||||
)
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
|
||||
exception = None
|
||||
try:
|
||||
im.seek(im.n_frames - 1)
|
||||
|
@ -452,6 +496,7 @@ def test_apng_save_duration_loop(tmp_path: Path) -> None:
|
|||
test_file, save_all=True, append_images=[frame, frame], duration=[500, 100, 150]
|
||||
)
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.n_frames == 1
|
||||
assert "duration" not in im.info
|
||||
|
||||
|
@ -463,6 +508,7 @@ def test_apng_save_duration_loop(tmp_path: Path) -> None:
|
|||
duration=[500, 100, 150],
|
||||
)
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.n_frames == 2
|
||||
assert im.info["duration"] == 600
|
||||
|
||||
|
@ -473,6 +519,7 @@ def test_apng_save_duration_loop(tmp_path: Path) -> None:
|
|||
frame.info["duration"] = 300
|
||||
frame.save(test_file, save_all=True, append_images=[frame, different_frame])
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.n_frames == 2
|
||||
assert im.info["duration"] == 600
|
||||
|
||||
|
|
|
@ -166,9 +166,9 @@ def test_save_dib(tmp_path: Path) -> None:
|
|||
def test_rgba_bitfields() -> None:
|
||||
# This test image has been manually hexedited
|
||||
# to change the bitfield compression in the header from XBGR to RGBA
|
||||
with Image.open("Tests/images/rgb32bf-rgba.bmp") as im:
|
||||
with Image.open("Tests/images/rgb32bf-rgba.bmp") as bmp_im:
|
||||
# So before the comparing the image, swap the channels
|
||||
b, g, r = im.split()[1:]
|
||||
b, g, r = bmp_im.split()[1:]
|
||||
im = Image.merge("RGB", (r, g, b))
|
||||
|
||||
assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp")
|
||||
|
|
|
@ -61,6 +61,7 @@ def test_handler(tmp_path: Path) -> None:
|
|||
|
||||
def load(self, im: ImageFile.StubImageFile) -> Image.Image:
|
||||
self.loaded = True
|
||||
assert im.fp is not None
|
||||
im.fp.close()
|
||||
return Image.new("RGB", (1, 1))
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ import pytest
|
|||
|
||||
from PIL import ContainerIO, Image
|
||||
|
||||
from .helper import hopper
|
||||
|
||||
TEST_FILE = "Tests/images/dummy.container"
|
||||
|
||||
|
||||
|
@ -15,15 +13,15 @@ def test_sanity() -> None:
|
|||
|
||||
|
||||
def test_isatty() -> None:
|
||||
with hopper() as im:
|
||||
container = ContainerIO.ContainerIO(im, 0, 0)
|
||||
with open(TEST_FILE, "rb") as fh:
|
||||
container = ContainerIO.ContainerIO(fh, 0, 0)
|
||||
|
||||
assert container.isatty() is False
|
||||
|
||||
|
||||
def test_seekable() -> None:
|
||||
with hopper() as im:
|
||||
container = ContainerIO.ContainerIO(im, 0, 0)
|
||||
with open(TEST_FILE, "rb") as fh:
|
||||
container = ContainerIO.ContainerIO(fh, 0, 0)
|
||||
|
||||
assert container.seekable() is True
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ def test_invalid_file() -> None:
|
|||
no_cursors_file = "Tests/images/no_cursors.cur"
|
||||
|
||||
cur = CurImagePlugin.CurImageFile(TEST_FILE)
|
||||
assert cur.fp is not None
|
||||
cur.fp.close()
|
||||
with open(no_cursors_file, "rb") as cur.fp:
|
||||
with pytest.raises(TypeError):
|
||||
|
|
|
@ -65,12 +65,14 @@ def test_tell() -> None:
|
|||
|
||||
def test_n_frames() -> None:
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert isinstance(im, DcxImagePlugin.DcxImageFile)
|
||||
assert im.n_frames == 1
|
||||
assert not im.is_animated
|
||||
|
||||
|
||||
def test_eoferror() -> None:
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert isinstance(im, DcxImagePlugin.DcxImageFile)
|
||||
n_frames = im.n_frames
|
||||
|
||||
# Test seeking past the last frame
|
||||
|
|
|
@ -50,6 +50,7 @@ TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA = "Tests/images/uncompressed_rgb.dds"
|
|||
)
|
||||
def test_sanity_dxt1_bc1(image_path: str) -> None:
|
||||
"""Check DXT1 and BC1 images can be opened"""
|
||||
target: Image.Image
|
||||
with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target:
|
||||
target = target.convert("RGBA")
|
||||
with Image.open(image_path) as im:
|
||||
|
@ -331,11 +332,13 @@ def test_dxt5_colorblock_alpha_issue_4142() -> None:
|
|||
|
||||
with Image.open("Tests/images/dxt5-colorblock-alpha-issue-4142.dds") as im:
|
||||
px = im.getpixel((0, 0))
|
||||
assert isinstance(px, tuple)
|
||||
assert px[0] != 0
|
||||
assert px[1] != 0
|
||||
assert px[2] != 0
|
||||
|
||||
px = im.getpixel((1, 0))
|
||||
assert isinstance(px, tuple)
|
||||
assert px[0] != 0
|
||||
assert px[1] != 0
|
||||
assert px[2] != 0
|
||||
|
|
|
@ -85,6 +85,8 @@ simple_eps_file_with_long_binary_data = (
|
|||
def test_sanity(filename: str, size: tuple[int, int], scale: int) -> None:
|
||||
expected_size = tuple(s * scale for s in size)
|
||||
with Image.open(filename) as image:
|
||||
assert isinstance(image, EpsImagePlugin.EpsImageFile)
|
||||
|
||||
image.load(scale=scale)
|
||||
assert image.mode == "RGB"
|
||||
assert image.size == expected_size
|
||||
|
@ -94,10 +96,12 @@ def test_sanity(filename: str, size: tuple[int, int], scale: int) -> None:
|
|||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_load() -> None:
|
||||
with Image.open(FILE1) as im:
|
||||
assert im.load()[0, 0] == (255, 255, 255)
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[0, 0] == (255, 255, 255)
|
||||
|
||||
# Test again now that it has already been loaded once
|
||||
assert im.load()[0, 0] == (255, 255, 255)
|
||||
assert px[0, 0] == (255, 255, 255)
|
||||
|
||||
|
||||
def test_binary() -> None:
|
||||
|
@ -215,6 +219,8 @@ def test_showpage() -> None:
|
|||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_transparency() -> None:
|
||||
with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
|
||||
assert isinstance(plot_image, EpsImagePlugin.EpsImageFile)
|
||||
|
||||
plot_image.load(transparency=True)
|
||||
assert plot_image.mode == "RGBA"
|
||||
|
||||
|
@ -239,8 +245,8 @@ def test_bytesio_object() -> None:
|
|||
with Image.open(img_bytes) as img:
|
||||
img.load()
|
||||
|
||||
with Image.open(FILE1_COMPARE) as image1_scale1_compare:
|
||||
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
||||
with Image.open(FILE1_COMPARE) as im:
|
||||
image1_scale1_compare = im.convert("RGB")
|
||||
image1_scale1_compare.load()
|
||||
assert_image_similar(img, image1_scale1_compare, 5)
|
||||
|
||||
|
@ -265,16 +271,16 @@ def test_render_scale1() -> None:
|
|||
# Zero bounding box
|
||||
with Image.open(FILE1) as image1_scale1:
|
||||
image1_scale1.load()
|
||||
with Image.open(FILE1_COMPARE) as image1_scale1_compare:
|
||||
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
||||
with Image.open(FILE1_COMPARE) as im:
|
||||
image1_scale1_compare = im.convert("RGB")
|
||||
image1_scale1_compare.load()
|
||||
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
||||
|
||||
# Non-zero bounding box
|
||||
with Image.open(FILE2) as image2_scale1:
|
||||
image2_scale1.load()
|
||||
with Image.open(FILE2_COMPARE) as image2_scale1_compare:
|
||||
image2_scale1_compare = image2_scale1_compare.convert("RGB")
|
||||
with Image.open(FILE2_COMPARE) as im:
|
||||
image2_scale1_compare = im.convert("RGB")
|
||||
image2_scale1_compare.load()
|
||||
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||
|
||||
|
@ -286,17 +292,19 @@ def test_render_scale2() -> None:
|
|||
|
||||
# Zero bounding box
|
||||
with Image.open(FILE1) as image1_scale2:
|
||||
assert isinstance(image1_scale2, EpsImagePlugin.EpsImageFile)
|
||||
image1_scale2.load(scale=2)
|
||||
with Image.open(FILE1_COMPARE_SCALE2) as image1_scale2_compare:
|
||||
image1_scale2_compare = image1_scale2_compare.convert("RGB")
|
||||
with Image.open(FILE1_COMPARE_SCALE2) as im:
|
||||
image1_scale2_compare = im.convert("RGB")
|
||||
image1_scale2_compare.load()
|
||||
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
||||
|
||||
# Non-zero bounding box
|
||||
with Image.open(FILE2) as image2_scale2:
|
||||
assert isinstance(image2_scale2, EpsImagePlugin.EpsImageFile)
|
||||
image2_scale2.load(scale=2)
|
||||
with Image.open(FILE2_COMPARE_SCALE2) as image2_scale2_compare:
|
||||
image2_scale2_compare = image2_scale2_compare.convert("RGB")
|
||||
with Image.open(FILE2_COMPARE_SCALE2) as im:
|
||||
image2_scale2_compare = im.convert("RGB")
|
||||
image2_scale2_compare.load()
|
||||
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
||||
|
||||
|
@ -304,9 +312,9 @@ def test_render_scale2() -> None:
|
|||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
@pytest.mark.parametrize("filename", (FILE1, FILE2, "Tests/images/illu10_preview.eps"))
|
||||
def test_resize(filename: str) -> None:
|
||||
with Image.open(filename) as im:
|
||||
with Image.open(filename) as img:
|
||||
new_size = (100, 100)
|
||||
im = im.resize(new_size)
|
||||
im = img.resize(new_size)
|
||||
assert im.size == new_size
|
||||
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ animated_test_file_with_prefix_chunk = "Tests/images/2422.flc"
|
|||
|
||||
def test_sanity() -> None:
|
||||
with Image.open(static_test_file) as im:
|
||||
assert isinstance(im, FliImagePlugin.FliImageFile)
|
||||
|
||||
im.load()
|
||||
assert im.mode == "P"
|
||||
assert im.size == (128, 128)
|
||||
|
@ -28,6 +30,8 @@ def test_sanity() -> None:
|
|||
assert not im.is_animated
|
||||
|
||||
with Image.open(animated_test_file) as im:
|
||||
assert isinstance(im, FliImagePlugin.FliImageFile)
|
||||
|
||||
assert im.mode == "P"
|
||||
assert im.size == (320, 200)
|
||||
assert im.format == "FLI"
|
||||
|
@ -39,6 +43,8 @@ def test_prefix_chunk() -> None:
|
|||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
try:
|
||||
with Image.open(animated_test_file_with_prefix_chunk) as im:
|
||||
assert isinstance(im, FliImagePlugin.FliImageFile)
|
||||
|
||||
assert im.mode == "P"
|
||||
assert im.size == (320, 200)
|
||||
assert im.format == "FLI"
|
||||
|
@ -46,6 +52,7 @@ def test_prefix_chunk() -> None:
|
|||
assert im.is_animated
|
||||
|
||||
palette = im.getpalette()
|
||||
assert palette is not None
|
||||
assert palette[3:6] == [255, 255, 255]
|
||||
assert palette[381:384] == [204, 204, 12]
|
||||
assert palette[765:] == [252, 0, 0]
|
||||
|
@ -110,16 +117,19 @@ def test_palette_chunk_second() -> None:
|
|||
|
||||
def test_n_frames() -> None:
|
||||
with Image.open(static_test_file) as im:
|
||||
assert isinstance(im, FliImagePlugin.FliImageFile)
|
||||
assert im.n_frames == 1
|
||||
assert not im.is_animated
|
||||
|
||||
with Image.open(animated_test_file) as im:
|
||||
assert isinstance(im, FliImagePlugin.FliImageFile)
|
||||
assert im.n_frames == 384
|
||||
assert im.is_animated
|
||||
|
||||
|
||||
def test_eoferror() -> None:
|
||||
with Image.open(animated_test_file) as im:
|
||||
assert isinstance(im, FliImagePlugin.FliImageFile)
|
||||
n_frames = im.n_frames
|
||||
|
||||
# Test seeking past the last frame
|
||||
|
|
|
@ -22,10 +22,11 @@ def test_sanity() -> None:
|
|||
|
||||
def test_close() -> None:
|
||||
with Image.open("Tests/images/input_bw_one_band.fpx") as im:
|
||||
pass
|
||||
assert isinstance(im, FpxImagePlugin.FpxImageFile)
|
||||
assert im.ole.fp.closed
|
||||
|
||||
im = Image.open("Tests/images/input_bw_one_band.fpx")
|
||||
assert isinstance(im, FpxImagePlugin.FpxImageFile)
|
||||
im.close()
|
||||
assert im.ole.fp.closed
|
||||
|
||||
|
|
|
@ -14,10 +14,12 @@ def test_gbr_file() -> None:
|
|||
|
||||
def test_load() -> None:
|
||||
with Image.open("Tests/images/gbr.gbr") as im:
|
||||
assert im.load()[0, 0] == (0, 0, 0, 0)
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[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)
|
||||
assert px[0, 0] == (0, 0, 0, 0)
|
||||
|
||||
|
||||
def test_multiple_load_operations() -> None:
|
||||
|
|
|
@ -81,12 +81,16 @@ def test_invalid_file() -> None:
|
|||
def test_l_mode_transparency() -> None:
|
||||
with Image.open("Tests/images/no_palette_with_transparency.gif") as im:
|
||||
assert im.mode == "L"
|
||||
assert im.load()[0, 0] == 128
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[0, 0] == 128
|
||||
assert im.info["transparency"] == 255
|
||||
|
||||
im.seek(1)
|
||||
assert im.mode == "L"
|
||||
assert im.load()[0, 0] == 128
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[0, 0] == 128
|
||||
|
||||
|
||||
def test_l_mode_after_rgb() -> None:
|
||||
|
@ -221,6 +225,7 @@ def test_optimize_if_palette_can_be_reduced_by_half() -> None:
|
|||
out = BytesIO()
|
||||
im.save(out, "GIF", optimize=optimize)
|
||||
with Image.open(out) as reloaded:
|
||||
assert reloaded.palette is not None
|
||||
assert len(reloaded.palette.palette) // 3 == colors
|
||||
|
||||
|
||||
|
@ -277,6 +282,7 @@ def test_roundtrip_save_all(tmp_path: Path) -> None:
|
|||
im.save(out, save_all=True)
|
||||
|
||||
with Image.open(out) as reread:
|
||||
assert isinstance(reread, GifImagePlugin.GifImageFile)
|
||||
assert reread.n_frames == 5
|
||||
|
||||
|
||||
|
@ -302,10 +308,14 @@ def test_roundtrip_save_all_1(tmp_path: Path) -> None:
|
|||
),
|
||||
)
|
||||
def test_loading_multiple_palettes(path: str, mode: str) -> None:
|
||||
im: Image.Image
|
||||
with Image.open(path) as im:
|
||||
assert im.mode == "P"
|
||||
assert im.palette is not None
|
||||
first_frame_colors = im.palette.colors.keys()
|
||||
original_color = im.convert("RGB").load()[0, 0]
|
||||
px = im.convert("RGB").load()
|
||||
assert px is not None
|
||||
original_color = px[0, 0]
|
||||
|
||||
im.seek(1)
|
||||
assert im.mode == mode
|
||||
|
@ -313,10 +323,14 @@ def test_loading_multiple_palettes(path: str, mode: str) -> None:
|
|||
im = im.convert("RGB")
|
||||
|
||||
# Check a color only from the old palette
|
||||
assert im.load()[0, 0] == original_color
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[0, 0] == original_color
|
||||
|
||||
# Check a color from the new palette
|
||||
assert im.load()[24, 24] not in first_frame_colors
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[24, 24] not in first_frame_colors
|
||||
|
||||
|
||||
def test_headers_saving_for_animated_gifs(tmp_path: Path) -> None:
|
||||
|
@ -334,7 +348,7 @@ def test_headers_saving_for_animated_gifs(tmp_path: Path) -> None:
|
|||
|
||||
def test_palette_handling(tmp_path: Path) -> None:
|
||||
# see https://github.com/python-pillow/Pillow/issues/513
|
||||
|
||||
im: Image.Image
|
||||
with Image.open(TEST_GIF) as im:
|
||||
im = im.convert("RGB")
|
||||
|
||||
|
@ -358,8 +372,8 @@ def test_palette_434(tmp_path: Path) -> None:
|
|||
|
||||
return reloaded
|
||||
|
||||
orig = "Tests/images/test.colors.gif"
|
||||
with Image.open(orig) as im:
|
||||
im: Image.Image
|
||||
with Image.open("Tests/images/test.colors.gif") as im:
|
||||
with roundtrip(im) as reloaded:
|
||||
assert_image_similar(im, reloaded, 1)
|
||||
with roundtrip(im, optimize=True) as reloaded:
|
||||
|
@ -374,6 +388,7 @@ def test_palette_434(tmp_path: Path) -> None:
|
|||
|
||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||
def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
|
||||
img: Image.Image
|
||||
with Image.open(TEST_GIF) as img:
|
||||
img = img.convert("RGB")
|
||||
|
||||
|
@ -386,6 +401,7 @@ def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
|
|||
|
||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||
def test_save_netpbm_l_mode(tmp_path: Path) -> None:
|
||||
img: Image.Image
|
||||
with Image.open(TEST_GIF) as img:
|
||||
img = img.convert("L")
|
||||
|
||||
|
@ -438,10 +454,12 @@ def test_seek_rewind() -> None:
|
|||
def test_n_frames(path: str, n_frames: int) -> None:
|
||||
# Test is_animated before n_frames
|
||||
with Image.open(path) as im:
|
||||
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||
assert im.is_animated == (n_frames != 1)
|
||||
|
||||
# Test is_animated after n_frames
|
||||
with Image.open(path) as im:
|
||||
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||
assert im.n_frames == n_frames
|
||||
assert im.is_animated == (n_frames != 1)
|
||||
|
||||
|
@ -451,6 +469,7 @@ def test_no_change() -> None:
|
|||
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||
im.seek(1)
|
||||
expected = im.copy()
|
||||
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||
assert im.n_frames == 5
|
||||
assert_image_equal(im, expected)
|
||||
|
||||
|
@ -458,17 +477,20 @@ def test_no_change() -> None:
|
|||
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||
im.seek(3)
|
||||
expected = im.copy()
|
||||
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||
assert im.is_animated
|
||||
assert_image_equal(im, expected)
|
||||
|
||||
with Image.open("Tests/images/comment_after_only_frame.gif") as im:
|
||||
expected = Image.new("P", (1, 1))
|
||||
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||
assert not im.is_animated
|
||||
assert_image_equal(im, expected)
|
||||
|
||||
|
||||
def test_eoferror() -> None:
|
||||
with Image.open(TEST_GIF) as im:
|
||||
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||
n_frames = im.n_frames
|
||||
|
||||
# Test seeking past the last frame
|
||||
|
@ -483,11 +505,13 @@ def test_eoferror() -> None:
|
|||
def test_first_frame_transparency() -> None:
|
||||
with Image.open("Tests/images/first_frame_transparency.gif") as im:
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[0, 0] == im.info["transparency"]
|
||||
|
||||
|
||||
def test_dispose_none() -> None:
|
||||
with Image.open("Tests/images/dispose_none.gif") as img:
|
||||
assert isinstance(img, GifImagePlugin.GifImageFile)
|
||||
try:
|
||||
while True:
|
||||
img.seek(img.tell() + 1)
|
||||
|
@ -511,6 +535,7 @@ def test_dispose_none_load_end() -> None:
|
|||
|
||||
def test_dispose_background() -> None:
|
||||
with Image.open("Tests/images/dispose_bgnd.gif") as img:
|
||||
assert isinstance(img, GifImagePlugin.GifImageFile)
|
||||
try:
|
||||
while True:
|
||||
img.seek(img.tell() + 1)
|
||||
|
@ -523,7 +548,10 @@ def test_dispose_background_transparency() -> None:
|
|||
with Image.open("Tests/images/dispose_bgnd_transparency.gif") as img:
|
||||
img.seek(2)
|
||||
px = img.load()
|
||||
assert px[35, 30][3] == 0
|
||||
assert px is not None
|
||||
value = px[35, 30]
|
||||
assert isinstance(value, tuple)
|
||||
assert value[3] == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -565,6 +593,7 @@ def test_transparent_dispose(
|
|||
|
||||
def test_dispose_previous() -> None:
|
||||
with Image.open("Tests/images/dispose_prev.gif") as img:
|
||||
assert isinstance(img, GifImagePlugin.GifImageFile)
|
||||
try:
|
||||
while True:
|
||||
img.seek(img.tell() + 1)
|
||||
|
@ -602,6 +631,7 @@ def test_save_dispose(tmp_path: Path) -> None:
|
|||
for method in range(0, 4):
|
||||
im_list[0].save(out, save_all=True, append_images=im_list[1:], disposal=method)
|
||||
with Image.open(out) as img:
|
||||
assert isinstance(img, GifImagePlugin.GifImageFile)
|
||||
for _ in range(2):
|
||||
img.seek(img.tell() + 1)
|
||||
assert img.disposal_method == method
|
||||
|
@ -615,6 +645,7 @@ def test_save_dispose(tmp_path: Path) -> None:
|
|||
)
|
||||
|
||||
with Image.open(out) as img:
|
||||
assert isinstance(img, GifImagePlugin.GifImageFile)
|
||||
for i in range(2):
|
||||
img.seek(img.tell() + 1)
|
||||
assert img.disposal_method == i + 1
|
||||
|
@ -737,6 +768,7 @@ def test_dispose2_background_frame(tmp_path: Path) -> None:
|
|||
im_list[0].save(out, save_all=True, append_images=im_list[1:], disposal=2)
|
||||
|
||||
with Image.open(out) as im:
|
||||
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||
assert im.n_frames == 3
|
||||
|
||||
|
||||
|
@ -903,6 +935,8 @@ def test_identical_frames(tmp_path: Path) -> None:
|
|||
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
||||
)
|
||||
with Image.open(out) as reread:
|
||||
assert isinstance(reread, GifImagePlugin.GifImageFile)
|
||||
|
||||
# Assert that the first three frames were combined
|
||||
assert reread.n_frames == 2
|
||||
|
||||
|
@ -932,6 +966,8 @@ def test_identical_frames_to_single_frame(
|
|||
|
||||
im_list[0].save(out, save_all=True, append_images=im_list[1:], duration=duration)
|
||||
with Image.open(out) as reread:
|
||||
assert isinstance(reread, GifImagePlugin.GifImageFile)
|
||||
|
||||
# Assert that all frames were combined
|
||||
assert reread.n_frames == 1
|
||||
|
||||
|
@ -979,9 +1015,9 @@ def test_webp_background(tmp_path: Path) -> None:
|
|||
|
||||
# Test opaque WebP background
|
||||
if features.check("webp"):
|
||||
with Image.open("Tests/images/hopper.webp") as im:
|
||||
assert im.info["background"] == (255, 255, 255, 255)
|
||||
im.save(out)
|
||||
with Image.open("Tests/images/hopper.webp") as img:
|
||||
assert img.info["background"] == (255, 255, 255, 255)
|
||||
img.save(out)
|
||||
|
||||
# Test non-opaque WebP background
|
||||
im = Image.new("L", (100, 100), "#000")
|
||||
|
@ -990,6 +1026,7 @@ def test_webp_background(tmp_path: Path) -> None:
|
|||
|
||||
|
||||
def test_comment(tmp_path: Path) -> None:
|
||||
im: Image.Image
|
||||
with Image.open(TEST_GIF) as im:
|
||||
assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0"
|
||||
|
||||
|
@ -1118,6 +1155,7 @@ def test_append_images(tmp_path: Path) -> None:
|
|||
im.copy().save(out, save_all=True, append_images=ims)
|
||||
|
||||
with Image.open(out) as reread:
|
||||
assert isinstance(reread, GifImagePlugin.GifImageFile)
|
||||
assert reread.n_frames == 3
|
||||
|
||||
# Tests appending using a generator
|
||||
|
@ -1127,6 +1165,7 @@ def test_append_images(tmp_path: Path) -> None:
|
|||
im.save(out, save_all=True, append_images=im_generator(ims))
|
||||
|
||||
with Image.open(out) as reread:
|
||||
assert isinstance(reread, GifImagePlugin.GifImageFile)
|
||||
assert reread.n_frames == 3
|
||||
|
||||
# Tests appending single and multiple frame images
|
||||
|
@ -1135,6 +1174,7 @@ def test_append_images(tmp_path: Path) -> None:
|
|||
im.save(out, save_all=True, append_images=[im2])
|
||||
|
||||
with Image.open(out) as reread:
|
||||
assert isinstance(reread, GifImagePlugin.GifImageFile)
|
||||
assert reread.n_frames == 10
|
||||
|
||||
|
||||
|
@ -1235,6 +1275,7 @@ def test_bbox(tmp_path: Path) -> None:
|
|||
im.save(out, save_all=True, append_images=ims)
|
||||
|
||||
with Image.open(out) as reread:
|
||||
assert isinstance(reread, GifImagePlugin.GifImageFile)
|
||||
assert reread.n_frames == 2
|
||||
|
||||
|
||||
|
@ -1247,6 +1288,7 @@ def test_bbox_alpha(tmp_path: Path) -> None:
|
|||
im.save(out, save_all=True, append_images=[im2])
|
||||
|
||||
with Image.open(out) as reread:
|
||||
assert isinstance(reread, GifImagePlugin.GifImageFile)
|
||||
assert reread.n_frames == 2
|
||||
|
||||
|
||||
|
@ -1308,6 +1350,9 @@ def test_palette_save_all_P(tmp_path: Path) -> None:
|
|||
with Image.open(out) as im:
|
||||
# Assert that the frames are correct, and each frame has the same palette
|
||||
assert_image_equal(im.convert("RGB"), frames[0].convert("RGB"))
|
||||
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||
assert im.palette is not None
|
||||
assert im.global_palette is not None
|
||||
assert im.palette.palette == im.global_palette.palette
|
||||
|
||||
im.seek(1)
|
||||
|
@ -1373,7 +1418,9 @@ def test_getdata() -> None:
|
|||
def test_lzw_bits() -> None:
|
||||
# see https://github.com/python-pillow/Pillow/issues/2811
|
||||
with Image.open("Tests/images/issue_2811.gif") as im:
|
||||
assert im.tile[0][3][0] == 11 # LZW bits
|
||||
args = im.tile[0][3]
|
||||
assert isinstance(args, tuple)
|
||||
assert args[0] == 11 # LZW bits
|
||||
# codec error prepatch
|
||||
im.load()
|
||||
|
||||
|
@ -1398,6 +1445,7 @@ def test_extents(
|
|||
GifImagePlugin.LOADING_STRATEGY = loading_strategy
|
||||
try:
|
||||
with Image.open("Tests/images/" + test_file) as im:
|
||||
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||
assert im.size == (100, 100)
|
||||
|
||||
# Check that n_frames does not change the size
|
||||
|
@ -1428,4 +1476,8 @@ def test_saving_rgba(tmp_path: Path) -> None:
|
|||
|
||||
with Image.open(out) as reloaded:
|
||||
reloaded_rgba = reloaded.convert("RGBA")
|
||||
assert reloaded_rgba.load()[0, 0][3] == 0
|
||||
px = reloaded_rgba.load()
|
||||
assert px is not None
|
||||
value = px[0, 0]
|
||||
assert isinstance(value, tuple)
|
||||
assert value[3] == 0
|
||||
|
|
|
@ -59,8 +59,9 @@ def test_handler(tmp_path: Path) -> None:
|
|||
def open(self, im: Image.Image) -> None:
|
||||
self.opened = True
|
||||
|
||||
def load(self, im: Image.Image) -> Image.Image:
|
||||
def load(self, im: ImageFile.ImageFile) -> Image.Image:
|
||||
self.loaded = True
|
||||
assert im.fp is not None
|
||||
im.fp.close()
|
||||
return Image.new("RGB", (1, 1))
|
||||
|
||||
|
|
|
@ -61,8 +61,9 @@ def test_handler(tmp_path: Path) -> None:
|
|||
def open(self, im: Image.Image) -> None:
|
||||
self.opened = True
|
||||
|
||||
def load(self, im: Image.Image) -> Image.Image:
|
||||
def load(self, im: ImageFile.ImageFile) -> Image.Image:
|
||||
self.loaded = True
|
||||
assert im.fp is not None
|
||||
im.fp.close()
|
||||
return Image.new("RGB", (1, 1))
|
||||
|
||||
|
|
|
@ -30,10 +30,14 @@ def test_sanity() -> None:
|
|||
|
||||
def test_load() -> None:
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert im.load()[0, 0] == (0, 0, 0, 0)
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[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)
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[0, 0] == (0, 0, 0, 0)
|
||||
|
||||
|
||||
def test_save(tmp_path: Path) -> None:
|
||||
|
@ -63,6 +67,7 @@ def test_save_append_images(tmp_path: Path) -> None:
|
|||
assert_image_similar_tofile(im, temp_file, 1)
|
||||
|
||||
with Image.open(temp_file) as reread:
|
||||
assert isinstance(reread, IcnsImagePlugin.IcnsImageFile)
|
||||
reread.size = (16, 16, 2)
|
||||
reread.load()
|
||||
assert_image_equal(reread, provided_im)
|
||||
|
@ -84,6 +89,7 @@ def test_sizes() -> None:
|
|||
# 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:
|
||||
assert isinstance(im, IcnsImagePlugin.IcnsImageFile)
|
||||
for w, h, r in im.info["sizes"]:
|
||||
wr = w * r
|
||||
hr = h * r
|
||||
|
@ -105,6 +111,7 @@ def test_older_icon() -> None:
|
|||
wr = w * r
|
||||
hr = h * r
|
||||
with Image.open("Tests/images/pillow2.icns") as im2:
|
||||
assert isinstance(im2, IcnsImagePlugin.IcnsImageFile)
|
||||
im2.size = (w, h, r)
|
||||
im2.load()
|
||||
assert im2.mode == "RGBA"
|
||||
|
@ -122,6 +129,7 @@ def test_jp2_icon() -> None:
|
|||
wr = w * r
|
||||
hr = h * r
|
||||
with Image.open("Tests/images/pillow3.icns") as im2:
|
||||
assert isinstance(im2, IcnsImagePlugin.IcnsImageFile)
|
||||
im2.size = (w, h, r)
|
||||
im2.load()
|
||||
assert im2.mode == "RGBA"
|
||||
|
|
|
@ -24,7 +24,9 @@ def test_sanity() -> None:
|
|||
|
||||
def test_load() -> None:
|
||||
with Image.open(TEST_ICO_FILE) as im:
|
||||
assert im.load()[0, 0] == (1, 1, 9, 255)
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[0, 0] == (1, 1, 9, 255)
|
||||
|
||||
|
||||
def test_mask() -> None:
|
||||
|
@ -75,6 +77,7 @@ def test_save_to_bytes() -> None:
|
|||
# The other one
|
||||
output.seek(0)
|
||||
with Image.open(output) as reloaded:
|
||||
assert isinstance(reloaded, IcoImagePlugin.IcoImageFile)
|
||||
reloaded.size = (32, 32)
|
||||
|
||||
assert im.mode == reloaded.mode
|
||||
|
@ -92,6 +95,7 @@ def test_getpixel(tmp_path: Path) -> None:
|
|||
im.save(temp_file, "ico", sizes=[(32, 32), (64, 64)])
|
||||
|
||||
with Image.open(temp_file) as reloaded:
|
||||
assert isinstance(reloaded, IcoImagePlugin.IcoImageFile)
|
||||
reloaded.load()
|
||||
reloaded.size = (32, 32)
|
||||
|
||||
|
@ -165,6 +169,7 @@ def test_save_to_bytes_bmp(mode: str) -> None:
|
|||
# The other one
|
||||
output.seek(0)
|
||||
with Image.open(output) as reloaded:
|
||||
assert isinstance(reloaded, IcoImagePlugin.IcoImageFile)
|
||||
reloaded.size = (32, 32)
|
||||
|
||||
assert "RGBA" == reloaded.mode
|
||||
|
@ -176,6 +181,7 @@ def test_save_to_bytes_bmp(mode: str) -> None:
|
|||
|
||||
def test_incorrect_size() -> None:
|
||||
with Image.open(TEST_ICO_FILE) as im:
|
||||
assert isinstance(im, IcoImagePlugin.IcoImageFile)
|
||||
with pytest.raises(ValueError):
|
||||
im.size = (1, 1)
|
||||
|
||||
|
@ -217,6 +223,7 @@ def test_save_append_images(tmp_path: Path) -> None:
|
|||
im.save(outfile, sizes=[(32, 32), (128, 128)], append_images=[provided_im])
|
||||
|
||||
with Image.open(outfile) as reread:
|
||||
assert isinstance(reread, IcoImagePlugin.IcoImageFile)
|
||||
assert_image_equal(reread, hopper("RGBA"))
|
||||
|
||||
reread.size = (32, 32)
|
||||
|
@ -253,8 +260,7 @@ def test_truncated_mask() -> None:
|
|||
|
||||
try:
|
||||
with Image.open(io.BytesIO(data)) as im:
|
||||
with Image.open("Tests/images/hopper_mask.png") as expected:
|
||||
assert im.mode == "1"
|
||||
assert im.mode == "1"
|
||||
|
||||
# 32 bpp
|
||||
output = io.BytesIO()
|
||||
|
|
|
@ -64,12 +64,14 @@ def test_tell() -> None:
|
|||
|
||||
def test_n_frames() -> None:
|
||||
with Image.open(TEST_IM) as im:
|
||||
assert isinstance(im, ImImagePlugin.ImImageFile)
|
||||
assert im.n_frames == 1
|
||||
assert not im.is_animated
|
||||
|
||||
|
||||
def test_eoferror() -> None:
|
||||
with Image.open(TEST_IM) as im:
|
||||
assert isinstance(im, ImImagePlugin.ImImageFile)
|
||||
n_frames = im.n_frames
|
||||
|
||||
# Test seeking past the last frame
|
||||
|
|
|
@ -91,6 +91,7 @@ class TestFileJpeg:
|
|||
def test_app(self) -> None:
|
||||
# Test APP/COM reader (@PIL135)
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
assert im.applist[0] == ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00")
|
||||
assert im.applist[1] == (
|
||||
"COM",
|
||||
|
@ -131,26 +132,30 @@ class TestFileJpeg:
|
|||
f = "Tests/images/pil_sample_cmyk.jpg"
|
||||
with Image.open(f) as im:
|
||||
# the source image has red pixels in the upper left corner.
|
||||
c, m, y, k = (x / 255.0 for x in im.getpixel((0, 0)))
|
||||
value = im.getpixel((0, 0))
|
||||
assert isinstance(value, tuple)
|
||||
c, m, y, k = (x / 255.0 for x in value)
|
||||
assert c == 0.0
|
||||
assert m > 0.8
|
||||
assert y > 0.8
|
||||
assert k == 0.0
|
||||
# the opposite corner is black
|
||||
c, m, y, k = (
|
||||
x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||
)
|
||||
value = im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||
assert isinstance(value, tuple)
|
||||
c, m, y, k = (x / 255.0 for x in value)
|
||||
assert k > 0.9
|
||||
# roundtrip, and check again
|
||||
im = self.roundtrip(im)
|
||||
c, m, y, k = (x / 255.0 for x in im.getpixel((0, 0)))
|
||||
value = im.getpixel((0, 0))
|
||||
assert isinstance(value, tuple)
|
||||
c, m, y, k = (x / 255.0 for x in value)
|
||||
assert c == 0.0
|
||||
assert m > 0.8
|
||||
assert y > 0.8
|
||||
assert k == 0.0
|
||||
c, m, y, k = (
|
||||
x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||
)
|
||||
value = im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||
assert isinstance(value, tuple)
|
||||
c, m, y, k = (x / 255.0 for x in value)
|
||||
assert k > 0.9
|
||||
|
||||
def test_rgb(self) -> None:
|
||||
|
@ -309,6 +314,8 @@ class TestFileJpeg:
|
|||
|
||||
def test_exif_typeerror(self) -> None:
|
||||
with Image.open("Tests/images/exif_typeerror.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
|
||||
# Should not raise a TypeError
|
||||
im._getexif()
|
||||
|
||||
|
@ -324,8 +331,10 @@ class TestFileJpeg:
|
|||
|
||||
# Reading
|
||||
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||
exif = im._getexif()
|
||||
assert exif[gps_index] == expected_exif_gps
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
exif_data = im._getexif()
|
||||
assert exif_data is not None
|
||||
assert exif_data[gps_index] == expected_exif_gps
|
||||
|
||||
# Writing
|
||||
f = str(tmp_path / "temp.jpg")
|
||||
|
@ -334,8 +343,10 @@ class TestFileJpeg:
|
|||
hopper().save(f, exif=exif)
|
||||
|
||||
with Image.open(f) as reloaded:
|
||||
exif = reloaded._getexif()
|
||||
assert exif[gps_index] == expected_exif_gps
|
||||
assert isinstance(reloaded, JpegImagePlugin.JpegImageFile)
|
||||
exif_data = reloaded._getexif()
|
||||
assert exif_data is not None
|
||||
assert exif_data[gps_index] == expected_exif_gps
|
||||
|
||||
def test_empty_exif_gps(self) -> None:
|
||||
with Image.open("Tests/images/empty_gps_ifd.jpg") as im:
|
||||
|
@ -363,6 +374,7 @@ class TestFileJpeg:
|
|||
exifs = []
|
||||
for i in range(2):
|
||||
with Image.open("Tests/images/exif-200dpcm.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
exifs.append(im._getexif())
|
||||
assert exifs[0] == exifs[1]
|
||||
|
||||
|
@ -396,13 +408,18 @@ class TestFileJpeg:
|
|||
}
|
||||
|
||||
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
|
||||
exif = im._getexif()
|
||||
assert exif is not None
|
||||
|
||||
for tag, value in expected_exif.items():
|
||||
assert value == exif[tag]
|
||||
|
||||
def test_exif_gps_typeerror(self) -> None:
|
||||
with Image.open("Tests/images/exif_gps_typeerror.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
|
||||
# Should not raise a TypeError
|
||||
im._getexif()
|
||||
|
||||
|
@ -478,7 +495,9 @@ class TestFileJpeg:
|
|||
|
||||
def test_exif(self) -> None:
|
||||
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
info = im._getexif()
|
||||
assert info is not None
|
||||
assert info[305] == "Adobe Photoshop CS Macintosh"
|
||||
|
||||
def test_get_child_images(self) -> None:
|
||||
|
@ -490,6 +509,7 @@ class TestFileJpeg:
|
|||
|
||||
def test_mp(self) -> None:
|
||||
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
assert im._getmp() is None
|
||||
|
||||
def test_quality_keep(self, tmp_path: Path) -> None:
|
||||
|
@ -547,12 +567,14 @@ class TestFileJpeg:
|
|||
f = str(tmp_path / "temp.jpg")
|
||||
im.save(f, qtables=[[n] * 64] * n)
|
||||
with Image.open(f) as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
assert len(im.quantization) == n
|
||||
reloaded = self.roundtrip(im, qtables="keep")
|
||||
assert im.quantization == reloaded.quantization
|
||||
assert max(reloaded.quantization[0]) <= 255
|
||||
|
||||
with Image.open("Tests/images/hopper.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
qtables = im.quantization
|
||||
reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)
|
||||
assert im.quantization == reloaded.quantization
|
||||
|
@ -652,6 +674,7 @@ class TestFileJpeg:
|
|||
|
||||
def test_load_16bit_qtables(self) -> None:
|
||||
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
assert len(im.quantization) == 2
|
||||
assert len(im.quantization[0]) == 64
|
||||
assert max(im.quantization[0]) > 255
|
||||
|
@ -659,10 +682,12 @@ class TestFileJpeg:
|
|||
def test_save_multiple_16bit_qtables(self) -> None:
|
||||
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
|
||||
im2 = self.roundtrip(im, qtables="keep")
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
assert im.quantization == im2.quantization
|
||||
|
||||
def test_save_single_16bit_qtable(self) -> None:
|
||||
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
im2 = self.roundtrip(im, qtables={0: im.quantization[0]})
|
||||
assert len(im2.quantization) == 1
|
||||
assert im2.quantization[0] == im.quantization[0]
|
||||
|
@ -693,9 +718,10 @@ class TestFileJpeg:
|
|||
|
||||
@pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
|
||||
def test_load_djpeg(self) -> None:
|
||||
with Image.open(TEST_FILE) as img:
|
||||
img.load_djpeg()
|
||||
assert_image_similar_tofile(img, TEST_FILE, 5)
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
im.load_djpeg()
|
||||
assert_image_similar_tofile(im, TEST_FILE, 5)
|
||||
|
||||
@pytest.mark.skipif(not cjpeg_available(), reason="cjpeg not available")
|
||||
def test_save_cjpeg(self, tmp_path: Path) -> None:
|
||||
|
@ -868,7 +894,10 @@ class TestFileJpeg:
|
|||
# in contrast to normal 8
|
||||
with Image.open("Tests/images/exif-ifd-offset.jpg") as im:
|
||||
# Act / Assert
|
||||
assert im._getexif()[306] == "2017:03:13 23:03:09"
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
exif = im._getexif()
|
||||
assert exif is not None
|
||||
assert exif[306] == "2017:03:13 23:03:09"
|
||||
|
||||
def test_multiple_exif(self) -> None:
|
||||
with Image.open("Tests/images/multiple_exif.jpg") as im:
|
||||
|
@ -896,6 +925,7 @@ class TestFileJpeg:
|
|||
|
||||
def test_photoshop_malformed_and_multiple(self) -> None:
|
||||
with Image.open("Tests/images/app13-multiple.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
assert "photoshop" in im.info
|
||||
assert 24 == len(im.info["photoshop"])
|
||||
apps_13_lengths = [len(v) for k, v in im.applist if k == "APP13"]
|
||||
|
@ -1020,7 +1050,7 @@ class TestFileJpeg:
|
|||
|
||||
with Image.open(TEST_FILE) as im:
|
||||
im.tile = [
|
||||
("INFINITE", (0, 0, 128, 128), 0, ("RGB", 0, 1)),
|
||||
ImageFile._Tile("INFINITE", (0, 0, 128, 128), 0, ("RGB", 0, 1)),
|
||||
]
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
im.load()
|
||||
|
@ -1067,6 +1097,7 @@ class TestFileJpeg:
|
|||
|
||||
def test_deprecation(self) -> None:
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
with pytest.warns(DeprecationWarning):
|
||||
assert im.huffman_ac == {}
|
||||
with pytest.warns(DeprecationWarning):
|
||||
|
@ -1083,8 +1114,9 @@ class TestFileCloseW32:
|
|||
im.save(tmpfile)
|
||||
|
||||
im = Image.open(tmpfile)
|
||||
assert im.fp is not None
|
||||
assert not im.fp.closed
|
||||
fp = im.fp
|
||||
assert not fp.closed
|
||||
with pytest.raises(OSError):
|
||||
os.remove(tmpfile)
|
||||
im.load()
|
||||
|
|
|
@ -54,6 +54,7 @@ def test_sanity() -> None:
|
|||
|
||||
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[0, 0] == (0, 0, 0)
|
||||
assert im.mode == "RGB"
|
||||
assert im.size == (640, 480)
|
||||
|
@ -154,7 +155,7 @@ def test_reduce() -> None:
|
|||
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||
assert callable(im.reduce)
|
||||
|
||||
im.reduce = 2
|
||||
im.reduce = 2 # type: ignore[method-assign, assignment]
|
||||
assert im.reduce == 2
|
||||
|
||||
im.load()
|
||||
|
@ -221,12 +222,14 @@ def test_layers() -> None:
|
|||
out.seek(0)
|
||||
|
||||
with Image.open(out) as im:
|
||||
assert isinstance(im, Jpeg2KImagePlugin.Jpeg2KImageFile)
|
||||
im.layers = 1
|
||||
im.load()
|
||||
assert_image_similar(im, test_card, 13)
|
||||
|
||||
out.seek(0)
|
||||
with Image.open(out) as im:
|
||||
assert isinstance(im, Jpeg2KImagePlugin.Jpeg2KImageFile)
|
||||
im.layers = 3
|
||||
im.load()
|
||||
assert_image_similar(im, test_card, 0.4)
|
||||
|
@ -397,6 +400,7 @@ def test_subsampling_decode(name: str) -> None:
|
|||
def test_pclr() -> None:
|
||||
with Image.open(f"{EXTRA_DIR}/issue104_jpxstream.jp2") as im:
|
||||
assert im.mode == "P"
|
||||
assert im.palette is not None
|
||||
assert len(im.palette.colors) == 256
|
||||
assert im.palette.colors[(255, 255, 255)] == 0
|
||||
|
||||
|
@ -404,6 +408,7 @@ def test_pclr() -> None:
|
|||
f"{EXTRA_DIR}/147af3f1083de4393666b7d99b01b58b_signal_sigsegv_130c531_6155_5136.jp2"
|
||||
) as im:
|
||||
assert im.mode == "P"
|
||||
assert im.palette is not None
|
||||
assert len(im.palette.colors) == 139
|
||||
assert im.palette.colors[(0, 0, 0, 0)] == 0
|
||||
|
||||
|
|
|
@ -11,7 +11,15 @@ from typing import Any, NamedTuple
|
|||
|
||||
import pytest
|
||||
|
||||
from PIL import Image, ImageFilter, ImageOps, TiffImagePlugin, TiffTags, features
|
||||
from PIL import (
|
||||
Image,
|
||||
ImageFile,
|
||||
ImageFilter,
|
||||
ImageOps,
|
||||
TiffImagePlugin,
|
||||
TiffTags,
|
||||
features,
|
||||
)
|
||||
from PIL.TiffImagePlugin import OSUBFILETYPE, SAMPLEFORMAT, STRIPOFFSETS, SUBIFD
|
||||
|
||||
from .helper import (
|
||||
|
@ -27,7 +35,7 @@ from .helper import (
|
|||
|
||||
@skip_unless_feature("libtiff")
|
||||
class LibTiffTestCase:
|
||||
def _assert_noerr(self, tmp_path: Path, im: TiffImagePlugin.TiffImageFile) -> None:
|
||||
def _assert_noerr(self, tmp_path: Path, im: ImageFile.ImageFile) -> None:
|
||||
"""Helper tests that assert basic sanity about the g4 tiff reading"""
|
||||
# 1 bit
|
||||
assert im.mode == "1"
|
||||
|
@ -36,6 +44,7 @@ class LibTiffTestCase:
|
|||
im.load()
|
||||
im.getdata()
|
||||
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
try:
|
||||
assert im._compression == "group4"
|
||||
except AttributeError:
|
||||
|
@ -157,6 +166,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
"""Test metadata writing through libtiff"""
|
||||
f = str(tmp_path / "temp.tiff")
|
||||
with Image.open("Tests/images/hopper_g4.tif") as img:
|
||||
assert isinstance(img, TiffImagePlugin.TiffImageFile)
|
||||
img.save(f, tiffinfo=img.tag)
|
||||
|
||||
if legacy_api:
|
||||
|
@ -174,6 +184,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
]
|
||||
|
||||
with Image.open(f) as loaded:
|
||||
assert isinstance(loaded, TiffImagePlugin.TiffImageFile)
|
||||
if legacy_api:
|
||||
reloaded = loaded.tag.named()
|
||||
else:
|
||||
|
@ -216,6 +227,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
# Exclude ones that have special meaning
|
||||
# that we're already testing them
|
||||
with Image.open("Tests/images/hopper_g4.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
for tag in im.tag_v2:
|
||||
try:
|
||||
del core_items[tag]
|
||||
|
@ -321,6 +333,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im.save(out, tiffinfo=tiffinfo)
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
for tag, value in tiffinfo.items():
|
||||
reloaded_value = reloaded.tag_v2[tag]
|
||||
if (
|
||||
|
@ -353,12 +366,14 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
def test_osubfiletype(self, tmp_path: Path) -> None:
|
||||
outfile = str(tmp_path / "temp.tif")
|
||||
with Image.open("Tests/images/g4_orientation_6.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
im.tag_v2[OSUBFILETYPE] = 1
|
||||
im.save(outfile)
|
||||
|
||||
def test_subifd(self, tmp_path: Path) -> None:
|
||||
outfile = str(tmp_path / "temp.tif")
|
||||
with Image.open("Tests/images/g4_orientation_6.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
im.tag_v2[SUBIFD] = 10000
|
||||
|
||||
# Should not segfault
|
||||
|
@ -373,6 +388,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
hopper().save(out, tiffinfo={700: b"xmlpacket tag"})
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
if 700 in reloaded.tag_v2:
|
||||
assert reloaded.tag_v2[700] == b"xmlpacket tag"
|
||||
|
||||
|
@ -434,12 +450,14 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
"""Tests String data in info directory"""
|
||||
test_file = "Tests/images/hopper_g4_500.tif"
|
||||
with Image.open(test_file) as orig:
|
||||
out = str(tmp_path / "temp.tif")
|
||||
assert isinstance(orig, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
out = str(tmp_path / "temp.tif")
|
||||
orig.tag[269] = "temp.tif"
|
||||
orig.save(out)
|
||||
|
||||
with Image.open(out) as reread:
|
||||
assert isinstance(reread, TiffImagePlugin.TiffImageFile)
|
||||
assert "temp.tif" == reread.tag_v2[269]
|
||||
assert "temp.tif" == reread.tag[269][0]
|
||||
|
||||
|
@ -462,8 +480,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
# test case from irc, how to do blur on b/w image
|
||||
# and save to compressed tif.
|
||||
out = str(tmp_path / "temp.tif")
|
||||
with Image.open("Tests/images/pport_g4.tif") as im:
|
||||
im = im.convert("L")
|
||||
with Image.open("Tests/images/pport_g4.tif") as img:
|
||||
im = img.convert("L")
|
||||
|
||||
im = im.filter(ImageFilter.GaussianBlur(4))
|
||||
im.save(out, compression="tiff_adobe_deflate")
|
||||
|
@ -545,6 +563,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
|
||||
with Image.open(out) as reloaded:
|
||||
# colormap/palette tag
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert len(reloaded.tag_v2[320]) == 768
|
||||
|
||||
@pytest.mark.parametrize("compression", ("tiff_ccitt", "group3", "group4"))
|
||||
|
@ -556,8 +575,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im.save(out, compression=compression)
|
||||
|
||||
def test_fp_leak(self) -> None:
|
||||
im: Image.Image | None = Image.open("Tests/images/hopper_g4_500.tif")
|
||||
im: ImageFile.ImageFile | None = Image.open("Tests/images/hopper_g4_500.tif")
|
||||
assert im is not None
|
||||
assert im.fp is not None
|
||||
fn = im.fp.fileno()
|
||||
|
||||
os.fstat(fn)
|
||||
|
@ -576,6 +596,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
with Image.open("Tests/images/multipage.tiff") as im:
|
||||
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
|
||||
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
im.seek(0)
|
||||
assert im.size == (10, 10)
|
||||
assert im.convert("RGB").getpixel((0, 0)) == (0, 128, 0)
|
||||
|
@ -595,6 +616,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
# issue #862
|
||||
monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True)
|
||||
with Image.open("Tests/images/multipage.tiff") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
frames = im.n_frames
|
||||
assert frames == 3
|
||||
for _ in range(frames):
|
||||
|
@ -614,6 +636,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
def test__next(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True)
|
||||
with Image.open("Tests/images/hopper.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert not im.tag.next
|
||||
im.load()
|
||||
assert not im.tag.next
|
||||
|
@ -694,21 +717,25 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im.save(outfile, compression="jpeg")
|
||||
|
||||
with Image.open(outfile) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert reloaded.tag_v2[530] == (1, 1)
|
||||
assert reloaded.tag_v2[532] == (0, 255, 128, 255, 128, 255)
|
||||
|
||||
def test_exif_ifd(self) -> None:
|
||||
out = io.BytesIO()
|
||||
with Image.open("Tests/images/tiff_adobe_deflate.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im.tag_v2[34665] == 125456
|
||||
im.save(out, "TIFF")
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert 34665 not in reloaded.tag_v2
|
||||
|
||||
im.save(out, "TIFF", tiffinfo={34665: 125456})
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
if Image.core.libtiff_support_custom_tags:
|
||||
assert reloaded.tag_v2[34665] == 125456
|
||||
|
||||
|
@ -790,6 +817,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
|
||||
def test_multipage_compression(self) -> None:
|
||||
with Image.open("Tests/images/compression.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
im.seek(0)
|
||||
assert im._compression == "tiff_ccitt"
|
||||
assert im.size == (10, 10)
|
||||
|
@ -1083,6 +1112,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
with Image.open("Tests/images/g4_orientation_1.tif") as base_im:
|
||||
for i in range(2, 9):
|
||||
with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert 274 in im.tag_v2
|
||||
|
||||
im.load()
|
||||
|
@ -1094,9 +1124,10 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
with Image.open("Tests/images/g4_orientation_1.tif") as base_im:
|
||||
for i in range(2, 9):
|
||||
with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im:
|
||||
im = ImageOps.exif_transpose(im)
|
||||
transposed_im = ImageOps.exif_transpose(im)
|
||||
assert transposed_im is not None
|
||||
|
||||
assert_image_similar(base_im, im, 0.7)
|
||||
assert_image_similar(base_im, transposed_im, 0.7)
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Backtrace in Python Core")
|
||||
def test_sampleformat_not_corrupted(self) -> None:
|
||||
|
|
|
@ -22,19 +22,21 @@ def test_sanity() -> None:
|
|||
|
||||
# Adjust for the gamma of 2.2 encoded into the file
|
||||
lut = ImagePalette.make_gamma_lut(1 / 2.2)
|
||||
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
|
||||
im1 = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
|
||||
|
||||
im2 = hopper("RGBA")
|
||||
assert_image_similar(im, im2, 10)
|
||||
assert_image_similar(im1, im2, 10)
|
||||
|
||||
|
||||
def test_n_frames() -> None:
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert isinstance(im, MicImagePlugin.MicImageFile)
|
||||
assert im.n_frames == 1
|
||||
|
||||
|
||||
def test_is_animated() -> None:
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert isinstance(im, MicImagePlugin.MicImageFile)
|
||||
assert not im.is_animated
|
||||
|
||||
|
||||
|
@ -55,10 +57,11 @@ def test_seek() -> None:
|
|||
|
||||
def test_close() -> None:
|
||||
with Image.open(TEST_FILE) as im:
|
||||
pass
|
||||
assert isinstance(im, MicImagePlugin.MicImageFile)
|
||||
assert im.ole.fp.closed
|
||||
|
||||
im = Image.open(TEST_FILE)
|
||||
assert isinstance(im, MicImagePlugin.MicImageFile)
|
||||
im.close()
|
||||
assert im.ole.fp.closed
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from typing import Any
|
|||
|
||||
import pytest
|
||||
|
||||
from PIL import Image, ImageFile, MpoImagePlugin
|
||||
from PIL import Image, ImageFile, JpegImagePlugin, MpoImagePlugin
|
||||
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
|
@ -71,6 +71,7 @@ def test_context_manager() -> None:
|
|||
def test_app(test_file: str) -> None:
|
||||
# Test APP/COM reader (@PIL135)
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
assert im.applist[0][0] == "APP1"
|
||||
assert im.applist[1][0] == "APP2"
|
||||
assert (
|
||||
|
@ -110,9 +111,11 @@ def test_ignore_frame_size() -> None:
|
|||
# Ignore the different size of the second frame
|
||||
# since this is not a "Large Thumbnail" image
|
||||
with Image.open("Tests/images/ignore_frame_size.mpo") as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
assert im.size == (64, 64)
|
||||
|
||||
im.seek(1)
|
||||
assert im.mpinfo is not None
|
||||
assert (
|
||||
im.mpinfo[0xB002][1]["Attribute"]["MPType"]
|
||||
== "Multi-Frame Image: (Disparity)"
|
||||
|
@ -145,7 +148,9 @@ def test_reload_exif_after_seek() -> None:
|
|||
@pytest.mark.parametrize("test_file", test_files)
|
||||
def test_mp(test_file: str) -> None:
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
mpinfo = im._getmp()
|
||||
assert mpinfo is not None
|
||||
assert mpinfo[45056] == b"0100"
|
||||
assert mpinfo[45057] == 2
|
||||
|
||||
|
@ -154,7 +159,9 @@ def test_mp_offset() -> None:
|
|||
# This image has been manually hexedited to have an IFD offset of 10
|
||||
# in APP2 data, in contrast to normal 8
|
||||
with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
mpinfo = im._getmp()
|
||||
assert mpinfo is not None
|
||||
assert mpinfo[45056] == b"0100"
|
||||
assert mpinfo[45057] == 2
|
||||
|
||||
|
@ -170,7 +177,9 @@ def test_mp_no_data() -> None:
|
|||
@pytest.mark.parametrize("test_file", test_files)
|
||||
def test_mp_attribute(test_file: str) -> None:
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
mpinfo = im._getmp()
|
||||
assert mpinfo is not None
|
||||
for frame_number, mpentry in enumerate(mpinfo[0xB002]):
|
||||
mpattr = mpentry["Attribute"]
|
||||
if frame_number:
|
||||
|
@ -211,12 +220,14 @@ def test_seek(test_file: str) -> None:
|
|||
|
||||
def test_n_frames() -> None:
|
||||
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
assert im.n_frames == 2
|
||||
assert im.is_animated
|
||||
|
||||
|
||||
def test_eoferror() -> None:
|
||||
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
n_frames = im.n_frames
|
||||
|
||||
# Test seeking past the last frame
|
||||
|
@ -230,6 +241,8 @@ def test_eoferror() -> None:
|
|||
|
||||
def test_adopt_jpeg() -> None:
|
||||
with Image.open("Tests/images/hopper.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
MpoImagePlugin.MpoImageFile.adopt(im)
|
||||
|
||||
|
@ -267,6 +280,7 @@ def test_save(test_file: str) -> None:
|
|||
|
||||
|
||||
def test_save_all() -> None:
|
||||
im: Image.Image
|
||||
for test_file in test_files:
|
||||
with Image.open(test_file) as im:
|
||||
im_reloaded = roundtrip(im, save_all=True)
|
||||
|
|
|
@ -4,7 +4,7 @@ import re
|
|||
import sys
|
||||
import warnings
|
||||
import zlib
|
||||
from io import BytesIO
|
||||
from io import BytesIO, TextIOWrapper
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
from typing import Any, cast
|
||||
|
@ -93,6 +93,7 @@ class TestFilePng:
|
|||
|
||||
hopper("RGB").save(test_file)
|
||||
|
||||
im: Image.Image
|
||||
with Image.open(test_file) as im:
|
||||
im.load()
|
||||
assert im.mode == "RGB"
|
||||
|
@ -103,6 +104,8 @@ class TestFilePng:
|
|||
for mode in ["1", "L", "P", "RGB", "I", "I;16", "I;16B"]:
|
||||
im = hopper(mode)
|
||||
im.save(test_file)
|
||||
|
||||
reloaded: Image.Image
|
||||
with Image.open(test_file) as reloaded:
|
||||
if mode in ("I", "I;16B"):
|
||||
reloaded = reloaded.convert(mode)
|
||||
|
@ -225,11 +228,13 @@ class TestFilePng:
|
|||
test_file = "Tests/images/pil123p.png"
|
||||
with Image.open(test_file) as im:
|
||||
assert_image(im, "P", (162, 150))
|
||||
im = im.convert("RGBA")
|
||||
assert_image(im, "RGBA", (162, 150))
|
||||
rgba_im = im.convert("RGBA")
|
||||
assert_image(rgba_im, "RGBA", (162, 150))
|
||||
|
||||
# image has 124 unique alpha values
|
||||
assert len(im.getchannel("A").getcolors()) == 124
|
||||
colors = rgba_im.getchannel("A").getcolors()
|
||||
assert colors is not None
|
||||
assert len(colors) == 124
|
||||
|
||||
def test_load_transparent_rgb(self) -> None:
|
||||
test_file = "Tests/images/rgb_trns.png"
|
||||
|
@ -237,11 +242,13 @@ class TestFilePng:
|
|||
assert im.info["transparency"] == (0, 255, 52)
|
||||
|
||||
assert_image(im, "RGB", (64, 64))
|
||||
im = im.convert("RGBA")
|
||||
assert_image(im, "RGBA", (64, 64))
|
||||
rgba_im = im.convert("RGBA")
|
||||
assert_image(rgba_im, "RGBA", (64, 64))
|
||||
|
||||
# image has 876 transparent pixels
|
||||
assert im.getchannel("A").getcolors()[0][0] == 876
|
||||
colors = rgba_im.getchannel("A").getcolors()
|
||||
assert colors is not None
|
||||
assert colors[0][0] == 876
|
||||
|
||||
def test_save_p_transparent_palette(self, tmp_path: Path) -> None:
|
||||
in_file = "Tests/images/pil123p.png"
|
||||
|
@ -258,11 +265,13 @@ class TestFilePng:
|
|||
assert len(im.info["transparency"]) == 256
|
||||
|
||||
assert_image(im, "P", (162, 150))
|
||||
im = im.convert("RGBA")
|
||||
assert_image(im, "RGBA", (162, 150))
|
||||
rgba_im = im.convert("RGBA")
|
||||
assert_image(rgba_im, "RGBA", (162, 150))
|
||||
|
||||
# image has 124 unique alpha values
|
||||
assert len(im.getchannel("A").getcolors()) == 124
|
||||
colors = rgba_im.getchannel("A").getcolors()
|
||||
assert colors is not None
|
||||
assert len(colors) == 124
|
||||
|
||||
def test_save_p_single_transparency(self, tmp_path: Path) -> None:
|
||||
in_file = "Tests/images/p_trns_single.png"
|
||||
|
@ -279,13 +288,15 @@ class TestFilePng:
|
|||
assert im.info["transparency"] == 164
|
||||
assert im.getpixel((31, 31)) == 164
|
||||
assert_image(im, "P", (64, 64))
|
||||
im = im.convert("RGBA")
|
||||
assert_image(im, "RGBA", (64, 64))
|
||||
rgba_im = im.convert("RGBA")
|
||||
assert_image(rgba_im, "RGBA", (64, 64))
|
||||
|
||||
assert im.getpixel((31, 31)) == (0, 255, 52, 0)
|
||||
assert rgba_im.getpixel((31, 31)) == (0, 255, 52, 0)
|
||||
|
||||
# image has 876 transparent pixels
|
||||
assert im.getchannel("A").getcolors()[0][0] == 876
|
||||
colors = rgba_im.getchannel("A").getcolors()
|
||||
assert colors is not None
|
||||
assert colors[0][0] == 876
|
||||
|
||||
def test_save_p_transparent_black(self, tmp_path: Path) -> None:
|
||||
# check if solid black image with full transparency
|
||||
|
@ -313,7 +324,9 @@ class TestFilePng:
|
|||
assert im.info["transparency"] == 255
|
||||
|
||||
im_rgba = im.convert("RGBA")
|
||||
assert im_rgba.getchannel("A").getcolors()[0][0] == num_transparent
|
||||
colors = im_rgba.getchannel("A").getcolors()
|
||||
assert colors is not None
|
||||
assert colors[0][0] == num_transparent
|
||||
|
||||
test_file = str(tmp_path / "temp.png")
|
||||
im.save(test_file)
|
||||
|
@ -324,7 +337,9 @@ class TestFilePng:
|
|||
assert_image_equal(im, test_im)
|
||||
|
||||
test_im_rgba = test_im.convert("RGBA")
|
||||
assert test_im_rgba.getchannel("A").getcolors()[0][0] == num_transparent
|
||||
colors = test_im_rgba.getchannel("A").getcolors()
|
||||
assert colors is not None
|
||||
assert colors[0][0] == num_transparent
|
||||
|
||||
def test_save_rgb_single_transparency(self, tmp_path: Path) -> None:
|
||||
in_file = "Tests/images/caption_6_33_22.png"
|
||||
|
@ -578,6 +593,7 @@ class TestFilePng:
|
|||
|
||||
def test_read_private_chunks(self) -> None:
|
||||
with Image.open("Tests/images/exif.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.private_chunks == [(b"orNT", b"\x01")]
|
||||
|
||||
def test_roundtrip_private_chunk(self) -> None:
|
||||
|
@ -600,6 +616,7 @@ class TestFilePng:
|
|||
|
||||
def test_textual_chunks_after_idat(self) -> None:
|
||||
with Image.open("Tests/images/hopper.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert "comment" in im.text
|
||||
for k, v in {
|
||||
"date:create": "2014-09-04T09:37:08+03:00",
|
||||
|
@ -609,20 +626,24 @@ class TestFilePng:
|
|||
|
||||
# Raises a SyntaxError in load_end
|
||||
with Image.open("Tests/images/broken_data_stream.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
with pytest.raises(OSError):
|
||||
assert isinstance(im.text, dict)
|
||||
|
||||
# Raises a UnicodeDecodeError in load_end
|
||||
with Image.open("Tests/images/truncated_image.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
|
||||
# The file is truncated
|
||||
with pytest.raises(OSError):
|
||||
im.text()
|
||||
im.text
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
assert isinstance(im.text, dict)
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||
|
||||
# Raises an EOFError in load_end
|
||||
with Image.open("Tests/images/hopper_idat_after_image_end.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.text == {"TXT": "VALUE", "ZIP": "VALUE"}
|
||||
|
||||
def test_unknown_compression_method(self) -> None:
|
||||
|
@ -667,6 +688,9 @@ class TestFilePng:
|
|||
im.save(out, bits=4, save_all=save_all)
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||
assert reloaded.png is not None
|
||||
assert reloaded.png.im_palette is not None
|
||||
assert len(reloaded.png.im_palette[1]) == 48
|
||||
|
||||
def test_plte_length(self, tmp_path: Path) -> None:
|
||||
|
@ -677,6 +701,9 @@ class TestFilePng:
|
|||
im.save(str(tmp_path / "temp.png"))
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||
assert reloaded.png is not None
|
||||
assert reloaded.png.im_palette is not None
|
||||
assert len(reloaded.png.im_palette[1]) == 3
|
||||
|
||||
def test_getxmp(self) -> None:
|
||||
|
@ -698,13 +725,17 @@ class TestFilePng:
|
|||
def test_exif(self) -> None:
|
||||
# With an EXIF chunk
|
||||
with Image.open("Tests/images/exif.png") as im:
|
||||
exif = im._getexif()
|
||||
assert exif[274] == 1
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
exif_data = im._getexif()
|
||||
assert exif_data is not None
|
||||
assert exif_data[274] == 1
|
||||
|
||||
# With an ImageMagick zTXt chunk
|
||||
with Image.open("Tests/images/exif_imagemagick.png") as im:
|
||||
exif = im._getexif()
|
||||
assert exif[274] == 1
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
exif_data = im._getexif()
|
||||
assert exif_data is not None
|
||||
assert exif_data[274] == 1
|
||||
|
||||
# Assert that info still can be extracted
|
||||
# when the image is no longer a PngImageFile instance
|
||||
|
@ -713,8 +744,10 @@ class TestFilePng:
|
|||
|
||||
# With a tEXt chunk
|
||||
with Image.open("Tests/images/exif_text.png") as im:
|
||||
exif = im._getexif()
|
||||
assert exif[274] == 1
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
exif_data = im._getexif()
|
||||
assert exif_data is not None
|
||||
assert exif_data[274] == 1
|
||||
|
||||
# With XMP tags
|
||||
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
||||
|
@ -728,6 +761,7 @@ class TestFilePng:
|
|||
im.save(test_file)
|
||||
|
||||
with Image.open(test_file) as reloaded:
|
||||
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||
assert reloaded._getexif() is None
|
||||
|
||||
# Test passing in exif
|
||||
|
@ -735,7 +769,9 @@ class TestFilePng:
|
|||
im.save(test_file, exif=im.getexif())
|
||||
|
||||
with Image.open(test_file) as reloaded:
|
||||
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||
exif = reloaded._getexif()
|
||||
assert exif is not None
|
||||
assert exif[274] == 1
|
||||
|
||||
@mark_if_feature_version(
|
||||
|
@ -747,7 +783,9 @@ class TestFilePng:
|
|||
im.save(test_file, exif=im.getexif())
|
||||
|
||||
with Image.open(test_file) as reloaded:
|
||||
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||
exif = reloaded._getexif()
|
||||
assert exif is not None
|
||||
assert exif[305] == "Adobe Photoshop CS Macintosh"
|
||||
|
||||
def test_exif_argument(self, tmp_path: Path) -> None:
|
||||
|
@ -773,10 +811,8 @@ class TestFilePng:
|
|||
def test_save_stdout(self, buffer: bool) -> None:
|
||||
old_stdout = sys.stdout
|
||||
|
||||
class MyStdOut:
|
||||
buffer = BytesIO()
|
||||
|
||||
mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO()
|
||||
b = BytesIO()
|
||||
mystdout: TextIOWrapper | BytesIO = TextIOWrapper(b) if buffer else b
|
||||
|
||||
sys.stdout = mystdout
|
||||
|
||||
|
@ -786,9 +822,7 @@ class TestFilePng:
|
|||
# Reset stdout
|
||||
sys.stdout = old_stdout
|
||||
|
||||
if isinstance(mystdout, MyStdOut):
|
||||
mystdout = mystdout.buffer
|
||||
with Image.open(mystdout) as reloaded:
|
||||
with Image.open(b) as reloaded:
|
||||
assert_image_equal_tofile(reloaded, TEST_PNG_FILE)
|
||||
|
||||
def test_truncated_end_chunk(self) -> None:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from io import BytesIO
|
||||
from io import BytesIO, TextIOWrapper
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
@ -79,6 +79,7 @@ def test_arbitrary_maxval(
|
|||
assert im.mode == mode
|
||||
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert tuple(px[x, 0] for x in range(3)) == pixels
|
||||
|
||||
|
||||
|
@ -370,10 +371,8 @@ def test_mimetypes(tmp_path: Path) -> None:
|
|||
def test_save_stdout(buffer: bool) -> None:
|
||||
old_stdout = sys.stdout
|
||||
|
||||
class MyStdOut:
|
||||
buffer = BytesIO()
|
||||
|
||||
mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO()
|
||||
b = BytesIO()
|
||||
mystdout: TextIOWrapper | BytesIO = TextIOWrapper(b) if buffer else b
|
||||
|
||||
sys.stdout = mystdout
|
||||
|
||||
|
@ -383,7 +382,5 @@ def test_save_stdout(buffer: bool) -> None:
|
|||
# Reset stdout
|
||||
sys.stdout = old_stdout
|
||||
|
||||
if isinstance(mystdout, MyStdOut):
|
||||
mystdout = mystdout.buffer
|
||||
with Image.open(mystdout) as reloaded:
|
||||
with Image.open(b) as reloaded:
|
||||
assert_image_equal_tofile(reloaded, TEST_FILE)
|
||||
|
|
|
@ -55,17 +55,21 @@ def test_invalid_file() -> None:
|
|||
|
||||
def test_n_frames() -> None:
|
||||
with Image.open("Tests/images/hopper_merged.psd") as im:
|
||||
assert isinstance(im, PsdImagePlugin.PsdImageFile)
|
||||
assert im.n_frames == 1
|
||||
assert not im.is_animated
|
||||
|
||||
for path in [test_file, "Tests/images/negative_layer_count.psd"]:
|
||||
with Image.open(path) as im:
|
||||
assert isinstance(im, PsdImagePlugin.PsdImageFile)
|
||||
assert im.n_frames == 2
|
||||
assert im.is_animated
|
||||
|
||||
|
||||
def test_eoferror() -> None:
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, PsdImagePlugin.PsdImageFile)
|
||||
|
||||
# PSD seek index starts at 1 rather than 0
|
||||
n_frames = im.n_frames + 1
|
||||
|
||||
|
@ -115,11 +119,13 @@ def test_rgba() -> None:
|
|||
|
||||
def test_negative_top_left_layer() -> None:
|
||||
with Image.open("Tests/images/negative_top_left_layer.psd") as im:
|
||||
assert isinstance(im, PsdImagePlugin.PsdImageFile)
|
||||
assert im.layers[0][2] == (-50, -50, 50, 50)
|
||||
|
||||
|
||||
def test_layer_skip() -> None:
|
||||
with Image.open("Tests/images/five_channels.psd") as im:
|
||||
assert isinstance(im, PsdImagePlugin.PsdImageFile)
|
||||
assert im.n_frames == 1
|
||||
|
||||
|
||||
|
@ -171,5 +177,6 @@ def test_crashes(test_file: str, raises: type[Exception]) -> None:
|
|||
def test_layer_crashes(test_file: str) -> None:
|
||||
with open(test_file, "rb") as f:
|
||||
with Image.open(f) as im:
|
||||
assert isinstance(im, PsdImagePlugin.PsdImageFile)
|
||||
with pytest.raises(SyntaxError):
|
||||
im.layers
|
||||
|
|
|
@ -92,6 +92,7 @@ def test_tell() -> None:
|
|||
|
||||
def test_n_frames() -> None:
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert isinstance(im, SpiderImagePlugin.SpiderImageFile)
|
||||
assert im.n_frames == 1
|
||||
assert not im.is_animated
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ def test_palette_depth_8(tmp_path: Path) -> None:
|
|||
|
||||
def test_palette_depth_16(tmp_path: Path) -> None:
|
||||
with Image.open("Tests/images/p_16.tga") as im:
|
||||
assert im.palette is not None
|
||||
assert im.palette.mode == "RGBA"
|
||||
assert_image_equal_tofile(im.convert("RGBA"), "Tests/images/p_16.png")
|
||||
|
||||
|
@ -213,10 +214,18 @@ def test_save_orientation(tmp_path: Path) -> None:
|
|||
def test_horizontal_orientations() -> None:
|
||||
# These images have been manually hexedited to have the relevant orientations
|
||||
with Image.open("Tests/images/rgb32rle_top_right.tga") as im:
|
||||
assert im.load()[90, 90][:3] == (0, 0, 0)
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
value = px[90, 90]
|
||||
assert isinstance(value, tuple)
|
||||
assert value[:3] == (0, 0, 0)
|
||||
|
||||
with Image.open("Tests/images/rgb32rle_bottom_right.tga") as im:
|
||||
assert im.load()[90, 90][:3] == (0, 255, 0)
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
value = px[90, 90]
|
||||
assert isinstance(value, tuple)
|
||||
assert value[:3] == (0, 255, 0)
|
||||
|
||||
|
||||
def test_save_rle(tmp_path: Path) -> None:
|
||||
|
@ -259,13 +268,17 @@ def test_save_l_transparency(tmp_path: Path) -> None:
|
|||
in_file = "Tests/images/la.tga"
|
||||
with Image.open(in_file) as im:
|
||||
assert im.mode == "LA"
|
||||
assert im.getchannel("A").getcolors()[0][0] == num_transparent
|
||||
colors = im.getchannel("A").getcolors()
|
||||
assert colors is not None
|
||||
assert colors[0][0] == num_transparent
|
||||
|
||||
out = str(tmp_path / "temp.tga")
|
||||
im.save(out)
|
||||
|
||||
with Image.open(out) as test_im:
|
||||
assert test_im.mode == "LA"
|
||||
assert test_im.getchannel("A").getcolors()[0][0] == num_transparent
|
||||
colors = test_im.getchannel("A").getcolors()
|
||||
assert colors is not None
|
||||
assert colors[0][0] == num_transparent
|
||||
|
||||
assert_image_equal(im, test_im)
|
||||
|
|
|
@ -9,7 +9,13 @@ from types import ModuleType
|
|||
|
||||
import pytest
|
||||
|
||||
from PIL import Image, ImageFile, TiffImagePlugin, UnidentifiedImageError
|
||||
from PIL import (
|
||||
Image,
|
||||
ImageFile,
|
||||
JpegImagePlugin,
|
||||
TiffImagePlugin,
|
||||
UnidentifiedImageError,
|
||||
)
|
||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
||||
|
||||
from .helper import (
|
||||
|
@ -108,6 +114,8 @@ class TestFileTiff:
|
|||
assert_image_equal_tofile(im, "Tests/images/hopper.tif")
|
||||
|
||||
with Image.open("Tests/images/hopper_bigtiff.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
# multistrip support not yet implemented
|
||||
del im.tag_v2[273]
|
||||
|
||||
|
@ -127,6 +135,8 @@ class TestFileTiff:
|
|||
def test_xyres_tiff(self) -> None:
|
||||
filename = "Tests/images/pil168.tif"
|
||||
with Image.open(filename) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
# legacy api
|
||||
assert isinstance(im.tag[X_RESOLUTION][0], tuple)
|
||||
assert isinstance(im.tag[Y_RESOLUTION][0], tuple)
|
||||
|
@ -140,6 +150,8 @@ class TestFileTiff:
|
|||
def test_xyres_fallback_tiff(self) -> None:
|
||||
filename = "Tests/images/compression.tif"
|
||||
with Image.open(filename) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
# v2 api
|
||||
assert isinstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||
assert isinstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||
|
@ -154,6 +166,8 @@ class TestFileTiff:
|
|||
def test_int_resolution(self) -> None:
|
||||
filename = "Tests/images/pil168.tif"
|
||||
with Image.open(filename) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
# Try to read a file where X,Y_RESOLUTION are ints
|
||||
im.tag_v2[X_RESOLUTION] = 71
|
||||
im.tag_v2[Y_RESOLUTION] = 71
|
||||
|
@ -168,6 +182,7 @@ class TestFileTiff:
|
|||
with Image.open(
|
||||
"Tests/images/hopper_float_dpi_" + str(resolution_unit) + ".tif"
|
||||
) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im.tag_v2.get(RESOLUTION_UNIT) == resolution_unit
|
||||
assert im.info["dpi"] == (dpi, dpi)
|
||||
|
||||
|
@ -185,6 +200,7 @@ class TestFileTiff:
|
|||
with Image.open("Tests/images/10ct_32bit_128.tiff") as im:
|
||||
im.save(b, format="tiff", resolution=123.45)
|
||||
with Image.open(b) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im.tag_v2[X_RESOLUTION] == 123.45
|
||||
assert im.tag_v2[Y_RESOLUTION] == 123.45
|
||||
|
||||
|
@ -200,10 +216,12 @@ class TestFileTiff:
|
|||
TiffImagePlugin.PREFIXES.pop()
|
||||
|
||||
def test_bad_exif(self) -> None:
|
||||
with Image.open("Tests/images/hopper_bad_exif.jpg") as i:
|
||||
with Image.open("Tests/images/hopper_bad_exif.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
|
||||
# Should not raise struct.error.
|
||||
with pytest.warns(UserWarning):
|
||||
i._getexif()
|
||||
im._getexif()
|
||||
|
||||
def test_save_rgba(self, tmp_path: Path) -> None:
|
||||
im = hopper("RGBA")
|
||||
|
@ -294,11 +312,13 @@ class TestFileTiff:
|
|||
)
|
||||
def test_n_frames(self, path: str, n_frames: int) -> None:
|
||||
with Image.open(path) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im.n_frames == n_frames
|
||||
assert im.is_animated == (n_frames != 1)
|
||||
|
||||
def test_eoferror(self) -> None:
|
||||
with Image.open("Tests/images/multipage-lastframe.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
n_frames = im.n_frames
|
||||
|
||||
# Test seeking past the last frame
|
||||
|
@ -342,20 +362,24 @@ class TestFileTiff:
|
|||
def test_frame_order(self) -> None:
|
||||
# A frame can't progress to itself after reading
|
||||
with Image.open("Tests/images/multipage_single_frame_loop.tiff") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im.n_frames == 1
|
||||
|
||||
# A frame can't progress to a frame that has already been read
|
||||
with Image.open("Tests/images/multipage_multiple_frame_loop.tiff") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im.n_frames == 2
|
||||
|
||||
# Frames don't have to be in sequence
|
||||
with Image.open("Tests/images/multipage_out_of_order.tiff") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im.n_frames == 3
|
||||
|
||||
def test___str__(self) -> None:
|
||||
filename = "Tests/images/pil136.tiff"
|
||||
with Image.open(filename) as im:
|
||||
# Act
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
ret = str(im.ifd)
|
||||
|
||||
# Assert
|
||||
|
@ -365,6 +389,8 @@ class TestFileTiff:
|
|||
# Arrange
|
||||
filename = "Tests/images/pil136.tiff"
|
||||
with Image.open(filename) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
# v2 interface
|
||||
v2_tags = {
|
||||
256: 55,
|
||||
|
@ -404,6 +430,7 @@ class TestFileTiff:
|
|||
def test__delitem__(self) -> None:
|
||||
filename = "Tests/images/pil136.tiff"
|
||||
with Image.open(filename) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
len_before = len(dict(im.ifd))
|
||||
del im.ifd[256]
|
||||
len_after = len(dict(im.ifd))
|
||||
|
@ -436,6 +463,7 @@ class TestFileTiff:
|
|||
|
||||
def test_ifd_tag_type(self) -> None:
|
||||
with Image.open("Tests/images/ifd_tag_type.tiff") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert 0x8825 in im.tag_v2
|
||||
|
||||
def test_exif(self, tmp_path: Path) -> None:
|
||||
|
@ -524,6 +552,7 @@ class TestFileTiff:
|
|||
im = hopper(mode)
|
||||
im.save(filename, tiffinfo={262: 0})
|
||||
with Image.open(filename) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert reloaded.tag_v2[262] == 0
|
||||
assert_image_equal(im, reloaded)
|
||||
|
||||
|
@ -602,6 +631,8 @@ class TestFileTiff:
|
|||
filename = str(tmp_path / "temp.tif")
|
||||
hopper("RGB").save(filename, "TIFF", **kwargs)
|
||||
with Image.open(filename) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
# legacy interface
|
||||
assert im.tag[X_RESOLUTION][0][0] == 72
|
||||
assert im.tag[Y_RESOLUTION][0][0] == 36
|
||||
|
@ -676,6 +707,7 @@ class TestFileTiff:
|
|||
def test_planar_configuration_save(self, tmp_path: Path) -> None:
|
||||
infile = "Tests/images/tiff_tiled_planar_raw.tif"
|
||||
with Image.open(infile) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im._planar_configuration == 2
|
||||
|
||||
outfile = str(tmp_path / "temp.tif")
|
||||
|
@ -703,12 +735,13 @@ class TestFileTiff:
|
|||
|
||||
def test_tiff_save_all(self) -> None:
|
||||
mp = BytesIO()
|
||||
with Image.open("Tests/images/multipage.tiff") as im:
|
||||
im.save(mp, format="tiff", save_all=True)
|
||||
with Image.open("Tests/images/multipage.tiff") as img:
|
||||
img.save(mp, format="tiff", save_all=True)
|
||||
|
||||
mp.seek(0, os.SEEK_SET)
|
||||
with Image.open(mp) as im:
|
||||
assert im.n_frames == 3
|
||||
with Image.open(mp) as img:
|
||||
assert isinstance(img, TiffImagePlugin.TiffImageFile)
|
||||
assert img.n_frames == 3
|
||||
|
||||
# Test appending images
|
||||
mp = BytesIO()
|
||||
|
@ -718,6 +751,7 @@ class TestFileTiff:
|
|||
|
||||
mp.seek(0, os.SEEK_SET)
|
||||
with Image.open(mp) as reread:
|
||||
assert isinstance(reread, TiffImagePlugin.TiffImageFile)
|
||||
assert reread.n_frames == 3
|
||||
|
||||
# Test appending using a generator
|
||||
|
@ -729,6 +763,7 @@ class TestFileTiff:
|
|||
|
||||
mp.seek(0, os.SEEK_SET)
|
||||
with Image.open(mp) as reread:
|
||||
assert isinstance(reread, TiffImagePlugin.TiffImageFile)
|
||||
assert reread.n_frames == 3
|
||||
|
||||
def test_saving_icc_profile(self, tmp_path: Path) -> None:
|
||||
|
@ -792,6 +827,7 @@ class TestFileTiff:
|
|||
|
||||
def test_get_photoshop_blocks(self) -> None:
|
||||
with Image.open("Tests/images/lab.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert list(im.get_photoshop_blocks().keys()) == [
|
||||
1061,
|
||||
1002,
|
||||
|
@ -846,6 +882,7 @@ class TestFileTiff:
|
|||
|
||||
im = Image.open(tmpfile)
|
||||
fp = im.fp
|
||||
assert fp is not None
|
||||
assert not fp.closed
|
||||
im.load()
|
||||
assert fp.closed
|
||||
|
@ -859,6 +896,7 @@ class TestFileTiff:
|
|||
with open(tmpfile, "rb") as f:
|
||||
im = Image.open(f)
|
||||
fp = im.fp
|
||||
assert fp is not None
|
||||
assert not fp.closed
|
||||
im.load()
|
||||
assert not fp.closed
|
||||
|
@ -910,8 +948,9 @@ class TestFileTiffW32:
|
|||
im.save(tmpfile)
|
||||
|
||||
im = Image.open(tmpfile)
|
||||
assert im.fp is not None
|
||||
assert not im.fp.closed
|
||||
fp = im.fp
|
||||
assert not fp.closed
|
||||
with pytest.raises(OSError):
|
||||
os.remove(tmpfile)
|
||||
im.load()
|
||||
|
|
|
@ -61,6 +61,7 @@ def test_rt_metadata(tmp_path: Path) -> None:
|
|||
img.save(f, tiffinfo=info)
|
||||
|
||||
with Image.open(f) as loaded:
|
||||
assert isinstance(loaded, TiffImagePlugin.TiffImageFile)
|
||||
assert loaded.tag[ImageJMetaDataByteCounts] == (len(bin_data),)
|
||||
assert loaded.tag_v2[ImageJMetaDataByteCounts] == (len(bin_data),)
|
||||
|
||||
|
@ -80,12 +81,14 @@ def test_rt_metadata(tmp_path: Path) -> None:
|
|||
info[ImageJMetaDataByteCounts] = (8, len(bin_data) - 8)
|
||||
img.save(f, tiffinfo=info)
|
||||
with Image.open(f) as loaded:
|
||||
assert isinstance(loaded, TiffImagePlugin.TiffImageFile)
|
||||
assert loaded.tag[ImageJMetaDataByteCounts] == (8, len(bin_data) - 8)
|
||||
assert loaded.tag_v2[ImageJMetaDataByteCounts] == (8, len(bin_data) - 8)
|
||||
|
||||
|
||||
def test_read_metadata() -> None:
|
||||
with Image.open("Tests/images/hopper_g4.tif") as img:
|
||||
assert isinstance(img, TiffImagePlugin.TiffImageFile)
|
||||
assert {
|
||||
"YResolution": IFDRational(4294967295, 113653537),
|
||||
"PlanarConfiguration": 1,
|
||||
|
@ -128,6 +131,8 @@ def test_read_metadata() -> None:
|
|||
def test_write_metadata(tmp_path: Path) -> None:
|
||||
"""Test metadata writing through the python code"""
|
||||
with Image.open("Tests/images/hopper.tif") as img:
|
||||
assert isinstance(img, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
f = str(tmp_path / "temp.tiff")
|
||||
del img.tag[278]
|
||||
img.save(f, tiffinfo=img.tag)
|
||||
|
@ -135,6 +140,7 @@ def test_write_metadata(tmp_path: Path) -> None:
|
|||
original = img.tag_v2.named()
|
||||
|
||||
with Image.open(f) as loaded:
|
||||
assert isinstance(loaded, TiffImagePlugin.TiffImageFile)
|
||||
reloaded = loaded.tag_v2.named()
|
||||
|
||||
ignored = ["StripByteCounts", "RowsPerStrip", "PageNumber", "StripOffsets"]
|
||||
|
@ -165,19 +171,21 @@ def test_write_metadata(tmp_path: Path) -> None:
|
|||
def test_change_stripbytecounts_tag_type(tmp_path: Path) -> None:
|
||||
out = str(tmp_path / "temp.tiff")
|
||||
with Image.open("Tests/images/hopper.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
info = im.tag_v2
|
||||
del info[278]
|
||||
|
||||
# Resize the image so that STRIPBYTECOUNTS will be larger than a SHORT
|
||||
im = im.resize((500, 500))
|
||||
info[TiffImagePlugin.IMAGEWIDTH] = im.width
|
||||
resized_im = im.resize((500, 500))
|
||||
info[TiffImagePlugin.IMAGEWIDTH] = resized_im.width
|
||||
|
||||
# STRIPBYTECOUNTS can be a SHORT or a LONG
|
||||
info.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] = TiffTags.SHORT
|
||||
|
||||
im.save(out, tiffinfo=info)
|
||||
resized_im.save(out, tiffinfo=info)
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert reloaded.tag_v2.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] == TiffTags.LONG
|
||||
|
||||
|
||||
|
@ -208,6 +216,7 @@ def test_writing_other_types_to_ascii(
|
|||
im.save(out, tiffinfo=info)
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert reloaded.tag_v2[271] == expected
|
||||
|
||||
|
||||
|
@ -225,6 +234,7 @@ def test_writing_other_types_to_bytes(value: int | IFDRational, tmp_path: Path)
|
|||
im.save(out, tiffinfo=info)
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert reloaded.tag_v2[700] == b"\x01"
|
||||
|
||||
|
||||
|
@ -244,6 +254,7 @@ def test_writing_other_types_to_undefined(
|
|||
im.save(out, tiffinfo=info)
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert reloaded.tag_v2[33723] == b"1"
|
||||
|
||||
|
||||
|
@ -288,6 +299,7 @@ def test_iccprofile_binary() -> None:
|
|||
# but probably won't be able to save it.
|
||||
|
||||
with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im.tag_v2.tagtype[34675] == 1
|
||||
assert im.info["icc_profile"]
|
||||
|
||||
|
@ -313,6 +325,7 @@ def test_exif_div_zero(tmp_path: Path) -> None:
|
|||
im.save(out, tiffinfo=info, compression="raw")
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert 0 == reloaded.tag_v2[41988].numerator
|
||||
assert 0 == reloaded.tag_v2[41988].denominator
|
||||
|
||||
|
@ -332,6 +345,7 @@ def test_ifd_unsigned_rational(tmp_path: Path) -> None:
|
|||
im.save(out, tiffinfo=info, compression="raw")
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert max_long == reloaded.tag_v2[41493].numerator
|
||||
assert 1 == reloaded.tag_v2[41493].denominator
|
||||
|
||||
|
@ -344,6 +358,7 @@ def test_ifd_unsigned_rational(tmp_path: Path) -> None:
|
|||
im.save(out, tiffinfo=info, compression="raw")
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert max_long == reloaded.tag_v2[41493].numerator
|
||||
assert 1 == reloaded.tag_v2[41493].denominator
|
||||
|
||||
|
@ -362,6 +377,7 @@ def test_ifd_signed_rational(tmp_path: Path) -> None:
|
|||
im.save(out, tiffinfo=info, compression="raw")
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert numerator == reloaded.tag_v2[37380].numerator
|
||||
assert denominator == reloaded.tag_v2[37380].denominator
|
||||
|
||||
|
@ -374,6 +390,7 @@ def test_ifd_signed_rational(tmp_path: Path) -> None:
|
|||
im.save(out, tiffinfo=info, compression="raw")
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert numerator == reloaded.tag_v2[37380].numerator
|
||||
assert denominator == reloaded.tag_v2[37380].denominator
|
||||
|
||||
|
@ -387,6 +404,7 @@ def test_ifd_signed_rational(tmp_path: Path) -> None:
|
|||
im.save(out, tiffinfo=info, compression="raw")
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert 2**31 - 1 == reloaded.tag_v2[37380].numerator
|
||||
assert -1 == reloaded.tag_v2[37380].denominator
|
||||
|
||||
|
@ -401,6 +419,7 @@ def test_ifd_signed_long(tmp_path: Path) -> None:
|
|||
im.save(out, tiffinfo=info, compression="raw")
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert reloaded.tag_v2[37000] == -60000
|
||||
|
||||
|
||||
|
@ -421,11 +440,13 @@ def test_empty_values() -> None:
|
|||
|
||||
def test_photoshop_info(tmp_path: Path) -> None:
|
||||
with Image.open("Tests/images/issue_2278.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert len(im.tag_v2[34377]) == 70
|
||||
assert isinstance(im.tag_v2[34377], bytes)
|
||||
out = str(tmp_path / "temp.tiff")
|
||||
im.save(out)
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert len(reloaded.tag_v2[34377]) == 70
|
||||
assert isinstance(reloaded.tag_v2[34377], bytes)
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@ def test_open() -> None:
|
|||
|
||||
def test_load() -> None:
|
||||
with WalImageFile.open(TEST_FILE) as im:
|
||||
assert im.load()[0, 0] == 122
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[0, 0] == 122
|
||||
|
||||
# Test again now that it has already been loaded once
|
||||
assert im.load()[0, 0] == 122
|
||||
assert px[0, 0] == 122
|
||||
|
|
|
@ -218,6 +218,7 @@ class TestFileWebp:
|
|||
# Save P mode GIF with background
|
||||
with Image.open("Tests/images/chi.gif") as im:
|
||||
original_value = im.convert("RGB").getpixel((1, 1))
|
||||
assert isinstance(original_value, tuple)
|
||||
|
||||
# Save as WEBP
|
||||
out_webp = str(tmp_path / "temp.webp")
|
||||
|
@ -230,6 +231,7 @@ class TestFileWebp:
|
|||
|
||||
with Image.open(out_gif) as reread:
|
||||
reread_value = reread.convert("RGB").getpixel((1, 1))
|
||||
assert isinstance(reread_value, tuple)
|
||||
difference = sum(abs(original_value[i] - reread_value[i]) for i in range(0, 3))
|
||||
assert difference < 5
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from pathlib import Path
|
|||
import pytest
|
||||
from packaging.version import parse as parse_version
|
||||
|
||||
from PIL import Image, features
|
||||
from PIL import GifImagePlugin, Image, WebPImagePlugin, features
|
||||
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
|
@ -22,10 +22,12 @@ def test_n_frames() -> None:
|
|||
"""Ensure that WebP format sets n_frames and is_animated attributes correctly."""
|
||||
|
||||
with Image.open("Tests/images/hopper.webp") as im:
|
||||
assert isinstance(im, WebPImagePlugin.WebPImageFile)
|
||||
assert im.n_frames == 1
|
||||
assert not im.is_animated
|
||||
|
||||
with Image.open("Tests/images/iss634.webp") as im:
|
||||
assert isinstance(im, WebPImagePlugin.WebPImageFile)
|
||||
assert im.n_frames == 42
|
||||
assert im.is_animated
|
||||
|
||||
|
@ -37,11 +39,13 @@ def test_write_animation_L(tmp_path: Path) -> None:
|
|||
"""
|
||||
|
||||
with Image.open("Tests/images/iss634.gif") as orig:
|
||||
assert isinstance(orig, GifImagePlugin.GifImageFile)
|
||||
assert orig.n_frames > 1
|
||||
|
||||
temp_file = str(tmp_path / "temp.webp")
|
||||
orig.save(temp_file, save_all=True)
|
||||
with Image.open(temp_file) as im:
|
||||
assert isinstance(im, WebPImagePlugin.WebPImageFile)
|
||||
assert im.n_frames == orig.n_frames
|
||||
|
||||
# Compare first and last frames to the original animated GIF
|
||||
|
@ -69,6 +73,7 @@ def test_write_animation_RGB(tmp_path: Path) -> None:
|
|||
|
||||
def check(temp_file: str) -> None:
|
||||
with Image.open(temp_file) as im:
|
||||
assert isinstance(im, WebPImagePlugin.WebPImageFile)
|
||||
assert im.n_frames == 2
|
||||
|
||||
# Compare first frame to original
|
||||
|
@ -127,6 +132,7 @@ def test_timestamp_and_duration(tmp_path: Path) -> None:
|
|||
)
|
||||
|
||||
with Image.open(temp_file) as im:
|
||||
assert isinstance(im, WebPImagePlugin.WebPImageFile)
|
||||
assert im.n_frames == 5
|
||||
assert im.is_animated
|
||||
|
||||
|
@ -170,6 +176,7 @@ def test_seeking(tmp_path: Path) -> None:
|
|||
)
|
||||
|
||||
with Image.open(temp_file) as im:
|
||||
assert isinstance(im, WebPImagePlugin.WebPImageFile)
|
||||
assert im.n_frames == 5
|
||||
assert im.is_animated
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from types import ModuleType
|
|||
|
||||
import pytest
|
||||
|
||||
from PIL import Image
|
||||
from PIL import Image, WebPImagePlugin
|
||||
|
||||
from .helper import mark_if_feature_version, skip_unless_feature
|
||||
|
||||
|
@ -22,11 +22,13 @@ except ImportError:
|
|||
def test_read_exif_metadata() -> None:
|
||||
file_path = "Tests/images/flower.webp"
|
||||
with Image.open(file_path) as image:
|
||||
assert isinstance(image, WebPImagePlugin.WebPImageFile)
|
||||
assert image.format == "WEBP"
|
||||
exif_data = image.info.get("exif", None)
|
||||
assert exif_data
|
||||
|
||||
exif = image._getexif()
|
||||
assert exif is not None
|
||||
|
||||
# Camera make
|
||||
assert exif[271] == "Canon"
|
||||
|
@ -110,6 +112,7 @@ def test_read_no_exif() -> None:
|
|||
|
||||
test_buffer.seek(0)
|
||||
with Image.open(test_buffer) as webp_image:
|
||||
assert isinstance(webp_image, WebPImagePlugin.WebPImageFile)
|
||||
assert not webp_image._getexif()
|
||||
|
||||
|
||||
|
|
|
@ -31,7 +31,9 @@ def test_load_raw() -> None:
|
|||
def test_load() -> None:
|
||||
with Image.open("Tests/images/drawing.emf") as im:
|
||||
if hasattr(Image.core, "drawwmf"):
|
||||
assert im.load()[0, 0] == (255, 255, 255)
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[0, 0] == (255, 255, 255)
|
||||
|
||||
|
||||
def test_register_handler(tmp_path: Path) -> None:
|
||||
|
@ -64,6 +66,7 @@ def test_load_float_dpi() -> None:
|
|||
|
||||
def test_load_set_dpi() -> None:
|
||||
with Image.open("Tests/images/drawing.wmf") as im:
|
||||
assert isinstance(im, WmfImagePlugin.WmfStubImageFile)
|
||||
assert im.size == (82, 82)
|
||||
|
||||
if hasattr(Image.core, "drawwmf"):
|
||||
|
|
|
@ -30,6 +30,7 @@ def test_invalid_file() -> None:
|
|||
def test_load_read() -> None:
|
||||
# Arrange
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert isinstance(im, XpmImagePlugin.XpmImageFile)
|
||||
dummy_bytes = 1
|
||||
|
||||
# Act
|
||||
|
|
|
@ -605,8 +605,8 @@ class TestImage:
|
|||
assert im.mode == mode
|
||||
assert im.getpixel((0, 0)) == 0
|
||||
assert im.getpixel((255, 255)) == 255
|
||||
with Image.open(target_file) as target:
|
||||
target = target.convert(mode)
|
||||
with Image.open(target_file) as img:
|
||||
target = img.convert(mode)
|
||||
assert_image_equal(im, target)
|
||||
|
||||
def test_radial_gradient_wrong_mode(self) -> None:
|
||||
|
@ -630,8 +630,8 @@ class TestImage:
|
|||
assert im.mode == mode
|
||||
assert im.getpixel((0, 0)) == 255
|
||||
assert im.getpixel((128, 128)) == 0
|
||||
with Image.open(target_file) as target:
|
||||
target = target.convert(mode)
|
||||
with Image.open(target_file) as img:
|
||||
target = img.convert(mode)
|
||||
assert_image_equal(im, target)
|
||||
|
||||
def test_register_extensions(self) -> None:
|
||||
|
@ -652,8 +652,8 @@ class TestImage:
|
|||
|
||||
def test_remap_palette(self) -> None:
|
||||
# Test identity transform
|
||||
with Image.open("Tests/images/hopper.gif") as im:
|
||||
assert_image_equal(im, im.remap_palette(list(range(256))))
|
||||
with Image.open("Tests/images/hopper.gif") as img:
|
||||
assert_image_equal(img, img.remap_palette(list(range(256))))
|
||||
|
||||
# Test identity transform with an RGBA palette
|
||||
im = Image.new("P", (256, 1))
|
||||
|
@ -662,12 +662,14 @@ class TestImage:
|
|||
im.putpalette(list(range(256)) * 4, "RGBA")
|
||||
im_remapped = im.remap_palette(list(range(256)))
|
||||
assert_image_equal(im, im_remapped)
|
||||
assert im.palette is not None
|
||||
assert im_remapped.palette is not None
|
||||
assert im.palette.palette == im_remapped.palette.palette
|
||||
|
||||
# Test illegal image mode
|
||||
with hopper() as im:
|
||||
with pytest.raises(ValueError):
|
||||
im.remap_palette(None)
|
||||
im.remap_palette([])
|
||||
|
||||
def test_remap_palette_transparency(self) -> None:
|
||||
im = Image.new("P", (1, 2), (0, 0, 0))
|
||||
|
@ -768,7 +770,7 @@ class TestImage:
|
|||
assert dict(exif)
|
||||
|
||||
# Test that exif data is cleared after another load
|
||||
exif.load(None)
|
||||
exif.load(b"")
|
||||
assert not dict(exif)
|
||||
|
||||
# Test loading just the EXIF header
|
||||
|
|
|
@ -76,8 +76,8 @@ def test_8bit() -> None:
|
|||
|
||||
|
||||
def test_16bit() -> None:
|
||||
with Image.open("Tests/images/16bit.cropped.tif") as im:
|
||||
_test_float_conversion(im)
|
||||
with Image.open("Tests/images/16bit.cropped.tif") as img:
|
||||
_test_float_conversion(img)
|
||||
|
||||
for color in (65535, 65536):
|
||||
im = Image.new("I", (1, 1), color)
|
||||
|
@ -236,6 +236,7 @@ def test_gif_with_rgba_palette_to_p() -> None:
|
|||
with Image.open("Tests/images/hopper.gif") as im:
|
||||
im.info["transparency"] = 255
|
||||
im.load()
|
||||
assert im.palette is not None
|
||||
assert im.palette.mode == "RGB"
|
||||
im_p = im.convert("P")
|
||||
|
||||
|
|
|
@ -78,13 +78,13 @@ def test_crop_crash() -> None:
|
|||
extents = (1, 1, 10, 10)
|
||||
# works prepatch
|
||||
with Image.open(test_img) as img:
|
||||
img2 = img.crop(extents)
|
||||
img2.load()
|
||||
img1 = img.crop(extents)
|
||||
img1.load()
|
||||
|
||||
# fail prepatch
|
||||
with Image.open(test_img) as img:
|
||||
img = img.crop(extents)
|
||||
img.load()
|
||||
img2 = img.crop(extents)
|
||||
img2.load()
|
||||
|
||||
|
||||
def test_crop_zero() -> None:
|
||||
|
|
|
@ -38,6 +38,7 @@ def test_close_after_load(caplog: pytest.LogCaptureFixture) -> None:
|
|||
def test_contextmanager() -> None:
|
||||
fn = None
|
||||
with Image.open("Tests/images/hopper.gif") as im:
|
||||
assert im.fp is not None
|
||||
fn = im.fp.fileno()
|
||||
os.fstat(fn)
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ def test_putpalette_with_alpha_values() -> None:
|
|||
expected = im.convert("RGBA")
|
||||
|
||||
palette = im.getpalette()
|
||||
assert palette is not None
|
||||
transparency = im.info.pop("transparency")
|
||||
|
||||
palette_with_alpha_values = []
|
||||
|
|
|
@ -56,20 +56,21 @@ def test_rgba_quantize() -> None:
|
|||
|
||||
def test_quantize() -> None:
|
||||
with Image.open("Tests/images/caption_6_33_22.png") as image:
|
||||
image = image.convert("RGB")
|
||||
converted = image.quantize()
|
||||
converted = image.convert("RGB")
|
||||
converted = converted.quantize()
|
||||
assert converted.mode == "P"
|
||||
assert_image_similar(converted.convert("RGB"), image, 1)
|
||||
|
||||
|
||||
def test_quantize_no_dither() -> None:
|
||||
image = hopper()
|
||||
palette: Image.Image
|
||||
with Image.open("Tests/images/caption_6_33_22.png") as palette:
|
||||
palette = palette.convert("P")
|
||||
|
||||
converted = image.quantize(dither=Image.Dither.NONE, palette=palette)
|
||||
converted = hopper().quantize(dither=Image.Dither.NONE, palette=palette)
|
||||
assert converted.mode == "P"
|
||||
assert converted.palette is not None
|
||||
assert palette.palette is not None
|
||||
assert converted.palette.palette == palette.palette.palette
|
||||
|
||||
|
||||
|
@ -93,8 +94,8 @@ def test_quantize_no_dither2() -> None:
|
|||
|
||||
def test_quantize_dither_diff() -> None:
|
||||
image = hopper()
|
||||
with Image.open("Tests/images/caption_6_33_22.png") as palette:
|
||||
palette = palette.convert("P")
|
||||
with Image.open("Tests/images/caption_6_33_22.png") as im:
|
||||
palette = im.convert("P")
|
||||
|
||||
dither = image.quantize(dither=Image.Dither.FLOYDSTEINBERG, palette=palette)
|
||||
nodither = image.quantize(dither=Image.Dither.NONE, palette=palette)
|
||||
|
|
|
@ -297,14 +297,14 @@ class TestImageResize:
|
|||
# Test unknown resampling filter
|
||||
with hopper() as im:
|
||||
with pytest.raises(ValueError):
|
||||
im.resize((10, 10), "unknown")
|
||||
im.resize((10, 10), -1)
|
||||
|
||||
@skip_unless_feature("libtiff")
|
||||
def test_load_first(self) -> None:
|
||||
# load() may change the size of the image
|
||||
# Test that resize() is calling it before getting the size
|
||||
with Image.open("Tests/images/g4_orientation_5.tif") as im:
|
||||
im = im.resize((64, 64))
|
||||
with Image.open("Tests/images/g4_orientation_5.tif") as img:
|
||||
im = img.resize((64, 64))
|
||||
assert im.size == (64, 64)
|
||||
|
||||
@pytest.mark.parametrize("mode", ("L", "RGB", "I", "F"))
|
||||
|
|
|
@ -40,8 +40,8 @@ def test_mode(mode: str) -> None:
|
|||
|
||||
@pytest.mark.parametrize("angle", (0, 90, 180, 270))
|
||||
def test_angle(angle: int) -> None:
|
||||
with Image.open("Tests/images/test-card.png") as im:
|
||||
rotate(im, im.mode, angle)
|
||||
with Image.open("Tests/images/test-card.png") as img:
|
||||
rotate(img, img.mode, angle)
|
||||
|
||||
im = hopper()
|
||||
assert_image_equal(im.rotate(angle), im.rotate(angle, expand=1))
|
||||
|
@ -74,6 +74,7 @@ def test_center_0() -> None:
|
|||
im = hopper()
|
||||
im = im.rotate(45, center=(0, 0), resample=Image.Resampling.BICUBIC)
|
||||
|
||||
target: Image.Image
|
||||
with Image.open("Tests/images/hopper_45.png") as target:
|
||||
target_origin = target.size[1] / 2
|
||||
target = target.crop((0, target_origin, 128, target_origin + 128))
|
||||
|
@ -85,6 +86,7 @@ def test_center_14() -> None:
|
|||
im = hopper()
|
||||
im = im.rotate(45, center=(14, 14), resample=Image.Resampling.BICUBIC)
|
||||
|
||||
target: Image.Image
|
||||
with Image.open("Tests/images/hopper_45.png") as target:
|
||||
target_origin = target.size[1] / 2 - 14
|
||||
target = target.crop((6, target_origin, 128 + 6, target_origin + 128))
|
||||
|
@ -94,6 +96,8 @@ def test_center_14() -> None:
|
|||
|
||||
def test_translate() -> None:
|
||||
im = hopper()
|
||||
|
||||
target: Image.Image
|
||||
with Image.open("Tests/images/hopper_45.png") as target:
|
||||
target_origin = (target.size[1] / 2 - 64) - 5
|
||||
target = target.crop(
|
||||
|
|
|
@ -106,20 +106,20 @@ def test_load_first() -> None:
|
|||
assert im.size == (590, 88)
|
||||
|
||||
|
||||
def test_load_first_unless_jpeg() -> None:
|
||||
def test_load_first_unless_jpeg(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
# Test that thumbnail() still uses draft() for JPEG
|
||||
with Image.open("Tests/images/hopper.jpg") as im:
|
||||
draft = im.draft
|
||||
orig_draft = im.draft
|
||||
|
||||
def im_draft(
|
||||
mode: str, size: tuple[int, int]
|
||||
) -> tuple[str, tuple[int, int, float, float]] | None:
|
||||
result = draft(mode, size)
|
||||
result = orig_draft(mode, size)
|
||||
assert result is not None
|
||||
|
||||
return result
|
||||
|
||||
im.draft = im_draft
|
||||
monkeypatch.setattr(im, "draft", im_draft)
|
||||
|
||||
im.thumbnail((64, 64))
|
||||
|
||||
|
@ -158,6 +158,7 @@ def test_reducing_gap_values() -> None:
|
|||
|
||||
|
||||
def test_reducing_gap_for_DCT_scaling() -> None:
|
||||
ref: Image.Image
|
||||
with Image.open("Tests/images/hopper.jpg") as ref:
|
||||
# thumbnail should call draft with reducing_gap scale
|
||||
ref.draft(None, (18 * 3, 18 * 3))
|
||||
|
|
|
@ -47,6 +47,8 @@ class TestImageTransform:
|
|||
transformed = im.transform(
|
||||
im.size, Image.Transform.AFFINE, [1, 0, 0, 0, 1, 0]
|
||||
)
|
||||
assert im.palette is not None
|
||||
assert transformed.palette is not None
|
||||
assert im.palette.palette == transformed.palette.palette
|
||||
|
||||
def test_extent(self) -> None:
|
||||
|
@ -245,14 +247,14 @@ class TestImageTransform:
|
|||
def test_missing_method_data(self) -> None:
|
||||
with hopper() as im:
|
||||
with pytest.raises(ValueError):
|
||||
im.transform((100, 100), None)
|
||||
im.transform((100, 100), None) # type: ignore[arg-type]
|
||||
|
||||
@pytest.mark.parametrize("resample", (Image.Resampling.BOX, "unknown"))
|
||||
def test_unknown_resampling_filter(self, resample: Image.Resampling | str) -> None:
|
||||
with hopper() as im:
|
||||
(w, h) = im.size
|
||||
with pytest.raises(ValueError):
|
||||
im.transform((100, 100), Image.Transform.EXTENT, (0, 0, w, h), resample)
|
||||
im.transform((100, 100), Image.Transform.EXTENT, (0, 0, w, h), resample) # type: ignore[arg-type]
|
||||
|
||||
|
||||
class TestImageTransformAffine:
|
||||
|
|
|
@ -190,6 +190,7 @@ def test_bitmap() -> None:
|
|||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
small: Image.Image
|
||||
with Image.open("Tests/images/pil123rgba.png") as small:
|
||||
small = small.resize((50, 50), Image.Resampling.NEAREST)
|
||||
|
||||
|
|
|
@ -249,8 +249,8 @@ def test_colorize_2color() -> None:
|
|||
# Test the colorizing function with 2-color functionality
|
||||
|
||||
# Open test image (256px by 10px, black to white)
|
||||
with Image.open("Tests/images/bw_gradient.png") as im:
|
||||
im = im.convert("L")
|
||||
with Image.open("Tests/images/bw_gradient.png") as img:
|
||||
im = img.convert("L")
|
||||
|
||||
# Create image with original 2-color functionality
|
||||
im_test = ImageOps.colorize(im, "red", "green")
|
||||
|
@ -289,8 +289,8 @@ def test_colorize_2color_offset() -> None:
|
|||
# Test the colorizing function with 2-color functionality and offset
|
||||
|
||||
# Open test image (256px by 10px, black to white)
|
||||
with Image.open("Tests/images/bw_gradient.png") as im:
|
||||
im = im.convert("L")
|
||||
with Image.open("Tests/images/bw_gradient.png") as img:
|
||||
im = img.convert("L")
|
||||
|
||||
# Create image with original 2-color functionality with offsets
|
||||
im_test = ImageOps.colorize(
|
||||
|
@ -331,8 +331,8 @@ def test_colorize_3color_offset() -> None:
|
|||
# Test the colorizing function with 3-color functionality and offset
|
||||
|
||||
# Open test image (256px by 10px, black to white)
|
||||
with Image.open("Tests/images/bw_gradient.png") as im:
|
||||
im = im.convert("L")
|
||||
with Image.open("Tests/images/bw_gradient.png") as img:
|
||||
im = img.convert("L")
|
||||
|
||||
# Create image with new three color functionality with offsets
|
||||
im_test = ImageOps.colorize(
|
||||
|
@ -428,6 +428,7 @@ def test_exif_transpose() -> None:
|
|||
check(orientation_im)
|
||||
|
||||
# Orientation from "XML:com.adobe.xmp" info key
|
||||
im: Image.Image
|
||||
for suffix in ("", "_exiftool"):
|
||||
with Image.open("Tests/images/xmp_tags_orientation" + suffix + ".png") as im:
|
||||
assert im.getexif()[0x0112] == 3
|
||||
|
@ -442,10 +443,10 @@ def test_exif_transpose() -> None:
|
|||
# Orientation from "Raw profile type exif" info key
|
||||
# This test image has been manually hexedited from exif_imagemagick.png
|
||||
# to have a different orientation
|
||||
with Image.open("Tests/images/exif_imagemagick_orientation.png") as im:
|
||||
assert im.getexif()[0x0112] == 3
|
||||
with Image.open("Tests/images/exif_imagemagick_orientation.png") as img:
|
||||
assert img.getexif()[0x0112] == 3
|
||||
|
||||
transposed_im = ImageOps.exif_transpose(im)
|
||||
transposed_im = ImageOps.exif_transpose(img)
|
||||
assert transposed_im is not None
|
||||
assert 0x0112 not in transposed_im.getexif()
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ def test_sanity() -> None:
|
|||
def test_reload() -> None:
|
||||
with Image.open("Tests/images/hopper.gif") as im:
|
||||
original = im.copy()
|
||||
assert im.palette is not None
|
||||
im.palette.dirty = 1
|
||||
assert_image_equal(im.convert("RGB"), original.convert("RGB"))
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
|
||||
from PIL import Image, ImageSequence, TiffImagePlugin
|
||||
from PIL import Image, ImageSequence, PsdImagePlugin, TiffImagePlugin
|
||||
|
||||
from .helper import assert_image_equal, hopper, skip_unless_feature
|
||||
|
||||
|
@ -31,6 +31,8 @@ def test_sanity(tmp_path: Path) -> None:
|
|||
|
||||
def test_iterator() -> None:
|
||||
with Image.open("Tests/images/multipage.tiff") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
i = ImageSequence.Iterator(im)
|
||||
for index in range(0, im.n_frames):
|
||||
assert i[index] == next(i)
|
||||
|
@ -42,6 +44,8 @@ def test_iterator() -> None:
|
|||
|
||||
def test_iterator_min_frame() -> None:
|
||||
with Image.open("Tests/images/hopper.psd") as im:
|
||||
assert isinstance(im, PsdImagePlugin.PsdImageFile)
|
||||
|
||||
i = ImageSequence.Iterator(im)
|
||||
for index in range(1, im.n_frames):
|
||||
assert i[index] == next(i)
|
||||
|
@ -74,9 +78,14 @@ def test_consecutive() -> None:
|
|||
def test_palette_mmap() -> None:
|
||||
# Using mmap in ImageFile can require to reload the palette.
|
||||
with Image.open("Tests/images/multipage-mmap.tiff") as im:
|
||||
color1 = im.getpalette()[:3]
|
||||
palette = im.getpalette()
|
||||
assert palette is not None
|
||||
color1 = palette[:3]
|
||||
im.seek(0)
|
||||
color2 = im.getpalette()[:3]
|
||||
|
||||
palette = im.getpalette()
|
||||
assert palette is not None
|
||||
color2 = palette[:3]
|
||||
assert color1 == color2
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ def helper_pickle_file(
|
|||
tmp_path: Path, protocol: int, test_file: str, mode: str | None
|
||||
) -> None:
|
||||
# Arrange
|
||||
im: Image.Image
|
||||
with Image.open(test_file) as im:
|
||||
filename = str(tmp_path / "temp.pkl")
|
||||
if mode:
|
||||
|
@ -33,6 +34,7 @@ def helper_pickle_file(
|
|||
|
||||
|
||||
def helper_pickle_string(protocol: int, test_file: str, mode: str | None) -> None:
|
||||
im: Image.Image
|
||||
with Image.open(test_file) as im:
|
||||
if mode:
|
||||
im = im.convert(mode)
|
||||
|
@ -77,8 +79,8 @@ def test_pickle_image(
|
|||
def test_pickle_la_mode_with_palette(tmp_path: Path) -> None:
|
||||
# Arrange
|
||||
filename = str(tmp_path / "temp.pkl")
|
||||
with Image.open("Tests/images/hopper.jpg") as im:
|
||||
im = im.convert("PA")
|
||||
with Image.open("Tests/images/hopper.jpg") as img:
|
||||
im = img.convert("PA")
|
||||
|
||||
# Act / Assert
|
||||
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
|
||||
|
|
|
@ -39,6 +39,7 @@ class TestShellInjection:
|
|||
shutil.copy(TEST_JPG, src_file)
|
||||
|
||||
with Image.open(src_file) as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
im.load_djpeg()
|
||||
|
||||
@pytest.mark.skipif(not cjpeg_available(), reason="cjpeg not available")
|
||||
|
@ -49,11 +50,13 @@ class TestShellInjection:
|
|||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||
def test_save_netpbm_filename_bmp_mode(self, tmp_path: Path) -> None:
|
||||
with Image.open(TEST_GIF) as im:
|
||||
im = im.convert("RGB")
|
||||
self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm)
|
||||
im_rgb = im.convert("RGB")
|
||||
self.assert_save_filename_check(
|
||||
tmp_path, im_rgb, GifImagePlugin._save_netpbm
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||
def test_save_netpbm_filename_l_mode(self, tmp_path: Path) -> None:
|
||||
with Image.open(TEST_GIF) as im:
|
||||
im = im.convert("L")
|
||||
self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm)
|
||||
im_l = im.convert("L")
|
||||
self.assert_save_filename_check(tmp_path, im_l, GifImagePlugin._save_netpbm)
|
||||
|
|
|
@ -72,4 +72,5 @@ def test_ifd_rational_save(
|
|||
im.save(out, dpi=(res, res), compression="raw")
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282])
|
||||
|
|
|
@ -213,6 +213,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
|||
format_description = "DirectDraw Surface"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(4)):
|
||||
msg = "not a DDS file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -258,6 +258,7 @@ class BlpImageFile(ImageFile.ImageFile):
|
|||
format_description = "Blizzard Mipmap Format"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
self.magic = self.fp.read(4)
|
||||
|
||||
self.fp.seek(5, os.SEEK_CUR)
|
||||
|
|
|
@ -74,6 +74,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _bitmap(self, header: int = 0, offset: int = 0) -> None:
|
||||
"""Read relevant info about the BMP"""
|
||||
assert self.fp is not None
|
||||
read, seek = self.fp.read, self.fp.seek
|
||||
if header:
|
||||
seek(header)
|
||||
|
@ -307,6 +308,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
def _open(self) -> None:
|
||||
"""Open file, check magic number and read header"""
|
||||
# read 14 bytes: magic number, filesize, reserved, header final offset
|
||||
assert self.fp is not None
|
||||
head_data = self.fp.read(14)
|
||||
# choke if the file does not have the required magic bytes
|
||||
if not _accept(head_data):
|
||||
|
|
|
@ -40,6 +40,7 @@ class BufrStubImageFile(ImageFile.StubImageFile):
|
|||
format_description = "BUFR"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
offset = self.fp.tell()
|
||||
|
||||
if not _accept(self.fp.read(4)):
|
||||
|
|
|
@ -38,6 +38,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
|
|||
format_description = "Windows Cursor"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
offset = self.fp.tell()
|
||||
|
||||
# check magic
|
||||
|
|
|
@ -24,6 +24,7 @@ from __future__ import annotations
|
|||
|
||||
from . import Image
|
||||
from ._binary import i32le as i32
|
||||
from ._util import DeferredError
|
||||
from .PcxImagePlugin import PcxImageFile
|
||||
|
||||
MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?
|
||||
|
@ -44,6 +45,7 @@ class DcxImageFile(PcxImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# Header
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(4)
|
||||
if not _accept(s):
|
||||
msg = "not a DCX file"
|
||||
|
@ -66,6 +68,8 @@ class DcxImageFile(PcxImageFile):
|
|||
def seek(self, frame: int) -> None:
|
||||
if not self._seek_check(frame):
|
||||
return
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self.frame = frame
|
||||
self.fp = self._fp
|
||||
self.fp.seek(self._offset[frame])
|
||||
|
|
|
@ -333,6 +333,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
|||
format_description = "DirectDraw Surface"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(4)):
|
||||
msg = "not a DDS file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -184,6 +184,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"}
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
(length, offset) = self._find_offset(self.fp)
|
||||
|
||||
# go to offset - start of "%!PS"
|
||||
|
@ -377,6 +378,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
) -> Image.core.PixelAccess | None:
|
||||
# Load EPS via Ghostscript
|
||||
if self.tile:
|
||||
assert self.fp is not None
|
||||
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
|
||||
self._mode = self.im.mode
|
||||
self._size = self.im.size
|
||||
|
|
|
@ -22,6 +22,7 @@ from . import Image, ImageFile, ImagePalette
|
|||
from ._binary import i16le as i16
|
||||
from ._binary import i32le as i32
|
||||
from ._binary import o8
|
||||
from ._util import DeferredError
|
||||
|
||||
#
|
||||
# decoder
|
||||
|
@ -47,6 +48,7 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# HEAD
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(128)
|
||||
if not (_accept(s) and s[20:22] == b"\x00\x00"):
|
||||
msg = "not an FLI/FLC file"
|
||||
|
@ -110,6 +112,7 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
# load palette
|
||||
|
||||
i = 0
|
||||
assert self.fp is not None
|
||||
for e in range(i16(self.fp.read(2))):
|
||||
s = self.fp.read(2)
|
||||
i = i + s[0]
|
||||
|
@ -134,6 +137,8 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
self._seek(f)
|
||||
|
||||
def _seek(self, frame: int) -> None:
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
if frame == 0:
|
||||
self.__frame = -1
|
||||
self._fp.seek(self.__rewind)
|
||||
|
|
|
@ -58,6 +58,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
# read the OLE directory and see if this is a likely
|
||||
# to be a FlashPix file
|
||||
|
||||
assert self.fp is not None
|
||||
try:
|
||||
self.ole = olefile.OleFileIO(self.fp)
|
||||
except OSError as e:
|
||||
|
@ -229,6 +230,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
if y >= ysize:
|
||||
break # isn't really required
|
||||
|
||||
assert self.fp is not None
|
||||
self.stream = stream
|
||||
self._fp = self.fp
|
||||
self.fp = None
|
||||
|
|
|
@ -72,6 +72,7 @@ class FtexImageFile(ImageFile.ImageFile):
|
|||
format_description = "Texture File Format (IW2:EOC)"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(4)):
|
||||
msg = "not an FTEX file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -42,6 +42,7 @@ class GbrImageFile(ImageFile.ImageFile):
|
|||
format_description = "GIMP brush file"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
header_size = i32(self.fp.read(4))
|
||||
if header_size < 20:
|
||||
msg = "not a GIMP brush"
|
||||
|
@ -91,6 +92,8 @@ class GbrImageFile(ImageFile.ImageFile):
|
|||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if self._im is None:
|
||||
self.im = Image.core.new(self.mode, self.size)
|
||||
|
||||
assert self.fp is not None
|
||||
self.frombytes(self.fp.read(self._data_size))
|
||||
return Image.Image.load(self)
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import os
|
|||
import subprocess
|
||||
from enum import IntEnum
|
||||
from functools import cached_property
|
||||
from typing import IO, TYPE_CHECKING, Any, Literal, NamedTuple, Union
|
||||
from typing import IO, TYPE_CHECKING, Any, Literal, NamedTuple, Union, cast
|
||||
|
||||
from . import (
|
||||
Image,
|
||||
|
@ -45,6 +45,7 @@ from . import (
|
|||
from ._binary import i16le as i16
|
||||
from ._binary import o8
|
||||
from ._binary import o16le as o16
|
||||
from ._util import DeferredError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import _imaging
|
||||
|
@ -83,6 +84,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
global_palette = None
|
||||
|
||||
def data(self) -> bytes | None:
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(1)
|
||||
if s and s[0]:
|
||||
return self.fp.read(s[0])
|
||||
|
@ -96,6 +98,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# Screen
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(13)
|
||||
if not _accept(s):
|
||||
msg = "not a GIF file"
|
||||
|
@ -113,8 +116,8 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
# check if palette contains colour indices
|
||||
p = self.fp.read(3 << bits)
|
||||
if self._is_palette_needed(p):
|
||||
p = ImagePalette.raw("RGB", p)
|
||||
self.global_palette = self.palette = p
|
||||
palette = ImagePalette.raw("RGB", p)
|
||||
self.global_palette = self.palette = palette
|
||||
|
||||
self._fp = self.fp # FIXME: hack
|
||||
self.__rewind = self.fp.tell()
|
||||
|
@ -168,6 +171,8 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
raise EOFError(msg) from e
|
||||
|
||||
def _seek(self, frame: int, update_image: bool = True) -> None:
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
if frame == 0:
|
||||
# rewind
|
||||
self.__offset = 0
|
||||
|
@ -251,7 +256,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
info["comment"] += b"\n" + comment
|
||||
else:
|
||||
info["comment"] = comment
|
||||
s = None
|
||||
s = b""
|
||||
continue
|
||||
elif s[0] == 255 and frame == 0 and block is not None:
|
||||
#
|
||||
|
@ -294,7 +299,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
bits = self.fp.read(1)[0]
|
||||
self.__offset = self.fp.tell()
|
||||
break
|
||||
s = None
|
||||
s = b""
|
||||
|
||||
if interlace is None:
|
||||
msg = "image not found in GIF frame"
|
||||
|
@ -347,7 +352,10 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
if self._frame_palette:
|
||||
if color * 3 + 3 > len(self._frame_palette.palette):
|
||||
color = 0
|
||||
return tuple(self._frame_palette.palette[color * 3 : color * 3 + 3])
|
||||
return cast(
|
||||
tuple[int, int, int],
|
||||
tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]),
|
||||
)
|
||||
else:
|
||||
return (color, color, color)
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ class GribStubImageFile(ImageFile.StubImageFile):
|
|||
format_description = "GRIB"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
offset = self.fp.tell()
|
||||
|
||||
if not _accept(self.fp.read(8)):
|
||||
|
|
|
@ -40,6 +40,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile):
|
|||
format_description = "HDF5"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
offset = self.fp.tell()
|
||||
|
||||
if not _accept(self.fp.read(8)):
|
||||
|
|
|
@ -266,6 +266,7 @@ class IcnsImageFile(ImageFile.ImageFile):
|
|||
format_description = "Mac OS icns resource"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
self.icns = IcnsFile(self.fp)
|
||||
self._mode = "RGBA"
|
||||
self.info["sizes"] = self.icns.itersizes()
|
||||
|
|
|
@ -326,6 +326,7 @@ class IcoImageFile(ImageFile.ImageFile):
|
|||
format_description = "Windows Icon"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
self.ico = IcoFile(self.fp)
|
||||
self.info["sizes"] = self.ico.sizes()
|
||||
self.size = self.ico.entry[0].dim
|
||||
|
|
|
@ -31,6 +31,7 @@ import re
|
|||
from typing import IO, Any
|
||||
|
||||
from . import Image, ImageFile, ImagePalette
|
||||
from ._util import DeferredError
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Standard tags
|
||||
|
@ -124,6 +125,7 @@ class ImImageFile(ImageFile.ImageFile):
|
|||
# Quick rejection: if there's not an LF among the first
|
||||
# 100 bytes, this is (probably) not a text header.
|
||||
|
||||
assert self.fp is not None
|
||||
if b"\n" not in self.fp.read(100):
|
||||
msg = "not an IM file"
|
||||
raise SyntaxError(msg)
|
||||
|
@ -301,6 +303,8 @@ class ImImageFile(ImageFile.ImageFile):
|
|||
size = ((self.size[0] * bits + 7) // 8) * self.size[1]
|
||||
offs = self.__offset + frame * size
|
||||
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self.fp = self._fp
|
||||
|
||||
self.tile = [
|
||||
|
|
|
@ -604,22 +604,11 @@ class Image:
|
|||
return new
|
||||
|
||||
# Context manager support
|
||||
def __enter__(self):
|
||||
def __enter__(self) -> Image:
|
||||
return self
|
||||
|
||||
def _close_fp(self):
|
||||
if getattr(self, "_fp", False):
|
||||
if self._fp != self.fp:
|
||||
self._fp.close()
|
||||
self._fp = DeferredError(ValueError("Operation on closed image"))
|
||||
if self.fp:
|
||||
self.fp.close()
|
||||
|
||||
def __exit__(self, *args):
|
||||
if hasattr(self, "fp"):
|
||||
if getattr(self, "_exclusive_fp", False):
|
||||
self._close_fp()
|
||||
self.fp = None
|
||||
def __exit__(self, *args: object) -> None:
|
||||
pass
|
||||
|
||||
def close(self) -> None:
|
||||
"""
|
||||
|
@ -633,13 +622,6 @@ class Image:
|
|||
:py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for
|
||||
more information.
|
||||
"""
|
||||
if hasattr(self, "fp"):
|
||||
try:
|
||||
self._close_fp()
|
||||
self.fp = None
|
||||
except Exception as msg:
|
||||
logger.debug("Error closing: %s", msg)
|
||||
|
||||
if getattr(self, "map", None):
|
||||
self.map: mmap.mmap | None = None
|
||||
|
||||
|
@ -1543,10 +1525,14 @@ class Image:
|
|||
exif_info = bytes.fromhex(
|
||||
"".join(self.info["Raw profile type exif"].split("\n")[3:])
|
||||
)
|
||||
elif hasattr(self, "tag_v2"):
|
||||
self._exif.bigtiff = self.tag_v2._bigtiff
|
||||
self._exif.endian = self.tag_v2._endian
|
||||
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
|
||||
else:
|
||||
from . import TiffImagePlugin
|
||||
|
||||
if isinstance(self, TiffImagePlugin.TiffImageFile):
|
||||
self._exif.bigtiff = self.tag_v2._bigtiff
|
||||
self._exif.endian = self.tag_v2._endian
|
||||
assert self.fp is not None
|
||||
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
|
||||
if exif_info is not None:
|
||||
self._exif.load(exif_info)
|
||||
|
||||
|
@ -1566,52 +1552,6 @@ class Image:
|
|||
self._exif._loaded = False
|
||||
self.getexif()
|
||||
|
||||
def get_child_images(self) -> list[ImageFile.ImageFile]:
|
||||
child_images = []
|
||||
exif = self.getexif()
|
||||
ifds = []
|
||||
if ExifTags.Base.SubIFDs in exif:
|
||||
subifd_offsets = exif[ExifTags.Base.SubIFDs]
|
||||
if subifd_offsets:
|
||||
if not isinstance(subifd_offsets, tuple):
|
||||
subifd_offsets = (subifd_offsets,)
|
||||
for subifd_offset in subifd_offsets:
|
||||
ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset))
|
||||
ifd1 = exif.get_ifd(ExifTags.IFD.IFD1)
|
||||
if ifd1 and ifd1.get(513):
|
||||
assert exif._info is not None
|
||||
ifds.append((ifd1, exif._info.next))
|
||||
|
||||
offset = None
|
||||
for ifd, ifd_offset in ifds:
|
||||
current_offset = self.fp.tell()
|
||||
if offset is None:
|
||||
offset = current_offset
|
||||
|
||||
fp = self.fp
|
||||
if ifd is not None:
|
||||
thumbnail_offset = ifd.get(513)
|
||||
if thumbnail_offset is not None:
|
||||
thumbnail_offset += getattr(self, "_exif_offset", 0)
|
||||
self.fp.seek(thumbnail_offset)
|
||||
data = self.fp.read(ifd.get(514))
|
||||
fp = io.BytesIO(data)
|
||||
|
||||
with open(fp) as im:
|
||||
from . import TiffImagePlugin
|
||||
|
||||
if thumbnail_offset is None and isinstance(
|
||||
im, TiffImagePlugin.TiffImageFile
|
||||
):
|
||||
im._frame_pos = [ifd_offset]
|
||||
im._seek(0)
|
||||
im.load()
|
||||
child_images.append(im)
|
||||
|
||||
if offset is not None:
|
||||
self.fp.seek(offset)
|
||||
return child_images
|
||||
|
||||
def getim(self) -> CapsuleType:
|
||||
"""
|
||||
Returns a capsule that points to the internal image memory.
|
||||
|
@ -2522,7 +2462,10 @@ class Image:
|
|||
)
|
||||
|
||||
def save(
|
||||
self, fp: StrOrBytesPath | IO[bytes], format: str | None = None, **params: Any
|
||||
self,
|
||||
fp: StrOrBytesPath | IO[bytes] | io.TextIOWrapper,
|
||||
format: str | None = None,
|
||||
**params: Any,
|
||||
) -> None:
|
||||
"""
|
||||
Saves this image under the given filename. If no format is
|
||||
|
|
|
@ -31,18 +31,21 @@ from __future__ import annotations
|
|||
import abc
|
||||
import io
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
from typing import IO, TYPE_CHECKING, Any, NamedTuple, cast
|
||||
|
||||
from . import Image
|
||||
from . import ExifTags, Image
|
||||
from ._deprecate import deprecate
|
||||
from ._util import is_path
|
||||
from ._util import DeferredError, is_path
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ._typing import StrOrBytesPath
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MAXBLOCK = 65536
|
||||
|
||||
SAFEBLOCK = 1024 * 1024
|
||||
|
@ -127,6 +130,8 @@ class ImageFile(Image.Image):
|
|||
self.decoderconfig: tuple[Any, ...] = ()
|
||||
self.decodermaxblock = MAXBLOCK
|
||||
|
||||
self.fp: IO[bytes] | None
|
||||
self._fp: IO[bytes] | DeferredError
|
||||
if is_path(fp):
|
||||
# filename
|
||||
self.fp = open(fp, "rb")
|
||||
|
@ -163,6 +168,93 @@ class ImageFile(Image.Image):
|
|||
def _open(self) -> None:
|
||||
pass
|
||||
|
||||
# Context manager support
|
||||
def __enter__(self) -> ImageFile:
|
||||
return self
|
||||
|
||||
def _close_fp(self) -> None:
|
||||
if getattr(self, "_fp", False):
|
||||
if self._fp != self.fp and not isinstance(self._fp, DeferredError):
|
||||
self._fp.close()
|
||||
self._fp = DeferredError(ValueError("Operation on closed image"))
|
||||
if self.fp:
|
||||
self.fp.close()
|
||||
|
||||
def __exit__(self, *args: object) -> None:
|
||||
if getattr(self, "_exclusive_fp", False):
|
||||
self._close_fp()
|
||||
self.fp = None
|
||||
|
||||
def close(self) -> None:
|
||||
"""
|
||||
Closes the file pointer, if possible.
|
||||
|
||||
This operation will destroy the image core and release its memory.
|
||||
The image data will be unusable afterward.
|
||||
|
||||
This function is required to close images that have multiple frames or
|
||||
have not had their file read and closed by the
|
||||
:py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for
|
||||
more information.
|
||||
"""
|
||||
try:
|
||||
self._close_fp()
|
||||
self.fp = None
|
||||
except Exception as msg:
|
||||
logger.debug("Error closing: %s", msg)
|
||||
|
||||
super().close()
|
||||
|
||||
def get_child_images(self) -> list[ImageFile]:
|
||||
child_images = []
|
||||
exif = self.getexif()
|
||||
ifds = []
|
||||
if ExifTags.Base.SubIFDs in exif:
|
||||
subifd_offsets = exif[ExifTags.Base.SubIFDs]
|
||||
if subifd_offsets:
|
||||
if not isinstance(subifd_offsets, tuple):
|
||||
subifd_offsets = (subifd_offsets,)
|
||||
for subifd_offset in subifd_offsets:
|
||||
ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset))
|
||||
ifd1 = exif.get_ifd(ExifTags.IFD.IFD1)
|
||||
if ifd1 and ifd1.get(513):
|
||||
assert exif._info is not None
|
||||
ifds.append((ifd1, exif._info.next))
|
||||
|
||||
offset = None
|
||||
assert self.fp is not None
|
||||
for ifd, ifd_offset in ifds:
|
||||
current_offset = self.fp.tell()
|
||||
if offset is None:
|
||||
offset = current_offset
|
||||
|
||||
fp = self.fp
|
||||
if ifd is not None:
|
||||
thumbnail_offset = ifd.get(513)
|
||||
if thumbnail_offset is not None:
|
||||
thumbnail_offset += getattr(self, "_exif_offset", 0)
|
||||
self.fp.seek(thumbnail_offset)
|
||||
|
||||
length = ifd.get(514)
|
||||
assert isinstance(length, int)
|
||||
data = self.fp.read(length)
|
||||
fp = io.BytesIO(data)
|
||||
|
||||
with Image.open(fp) as im:
|
||||
from . import TiffImagePlugin
|
||||
|
||||
if thumbnail_offset is None and isinstance(
|
||||
im, TiffImagePlugin.TiffImageFile
|
||||
):
|
||||
im._frame_pos = [ifd_offset]
|
||||
im._seek(0)
|
||||
im.load()
|
||||
child_images.append(im)
|
||||
|
||||
if offset is not None:
|
||||
self.fp.seek(offset)
|
||||
return child_images
|
||||
|
||||
def get_format_mimetype(self) -> str | None:
|
||||
if self.custom_mimetype:
|
||||
return self.custom_mimetype
|
||||
|
@ -179,7 +271,7 @@ class ImageFile(Image.Image):
|
|||
|
||||
# raise exception if something's wrong. must be called
|
||||
# directly after open, and closes file when finished.
|
||||
if self._exclusive_fp:
|
||||
if self._exclusive_fp and self.fp:
|
||||
self.fp.close()
|
||||
self.fp = None
|
||||
|
||||
|
@ -199,6 +291,7 @@ class ImageFile(Image.Image):
|
|||
# As of pypy 2.1.0, memory mapping was failing here.
|
||||
use_mmap = use_mmap and not hasattr(sys, "pypy_version_info")
|
||||
|
||||
assert self.fp is not None
|
||||
readonly = 0
|
||||
|
||||
# look for read/seek overrides
|
||||
|
|
|
@ -77,6 +77,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
def field(self) -> tuple[tuple[int, int] | None, int]:
|
||||
#
|
||||
# get a IPTC field header
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(5)
|
||||
if not s.strip(b"\x00"):
|
||||
return None, 0
|
||||
|
@ -104,6 +105,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# load descriptive fields
|
||||
assert self.fp is not None
|
||||
while True:
|
||||
offset = self.fp.tell()
|
||||
tag, size = self.field()
|
||||
|
@ -157,6 +159,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
|
||||
offset, compression = self.tile[0][2:]
|
||||
|
||||
assert self.fp is not None
|
||||
self.fp.seek(offset)
|
||||
|
||||
# Copy image data to temporary file
|
||||
|
@ -165,6 +168,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
# To simplify access to the extracted file,
|
||||
# prepend a PPM header
|
||||
o.write(b"P5\n%d %d\n255\n" % self.size)
|
||||
assert self.fp is not None
|
||||
while True:
|
||||
type, size = self.field()
|
||||
if type != (8, 10):
|
||||
|
@ -188,7 +192,7 @@ Image.register_extension(IptcImageFile.format, ".iim")
|
|||
|
||||
|
||||
def getiptcinfo(
|
||||
im: ImageFile.ImageFile,
|
||||
im: Image.Image,
|
||||
) -> dict[tuple[int, int], bytes | list[bytes]] | None:
|
||||
"""
|
||||
Get IPTC information from TIFF, JPEG, or IPTC file.
|
||||
|
|
|
@ -248,6 +248,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
|||
format_description = "JPEG 2000 (ISO 15444)"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
sig = self.fp.read(4)
|
||||
if sig == b"\xff\x4f\xff\x51":
|
||||
self.codec = "j2k"
|
||||
|
@ -296,6 +297,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
|||
]
|
||||
|
||||
def _parse_comment(self) -> None:
|
||||
assert self.fp is not None
|
||||
hdr = self.fp.read(2)
|
||||
length = _binary.i16be(hdr)
|
||||
self.fp.seek(length - 2, os.SEEK_CUR)
|
||||
|
|
|
@ -60,6 +60,7 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
def Skip(self: JpegImageFile, marker: int) -> None:
|
||||
assert self.fp is not None
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
ImageFile._safe_read(self.fp, n)
|
||||
|
||||
|
@ -69,6 +70,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
|||
# Application marker. Store these in the APP dictionary.
|
||||
# Also look for well-known application markers.
|
||||
|
||||
assert self.fp is not None
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
s = ImageFile._safe_read(self.fp, n)
|
||||
|
||||
|
@ -170,6 +172,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
|||
def COM(self: JpegImageFile, marker: int) -> None:
|
||||
#
|
||||
# Comment marker. Store these in the APP dictionary.
|
||||
assert self.fp is not None
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
s = ImageFile._safe_read(self.fp, n)
|
||||
|
||||
|
@ -186,6 +189,7 @@ def SOF(self: JpegImageFile, marker: int) -> None:
|
|||
# mode. Note that this could be made a bit brighter, by
|
||||
# looking for JFIF and Adobe APP markers.
|
||||
|
||||
assert self.fp is not None
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
s = ImageFile._safe_read(self.fp, n)
|
||||
self._size = i16(s, 3), i16(s, 1)
|
||||
|
@ -234,6 +238,7 @@ def DQT(self: JpegImageFile, marker: int) -> None:
|
|||
# FIXME: The quantization tables can be used to estimate the
|
||||
# compression quality.
|
||||
|
||||
assert self.fp is not None
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
s = ImageFile._safe_read(self.fp, n)
|
||||
while len(s):
|
||||
|
@ -334,6 +339,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
|||
format_description = "JPEG (ISO 10918)"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(3)
|
||||
|
||||
if not _accept(s):
|
||||
|
@ -401,6 +407,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
|||
For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker
|
||||
so libjpeg can finish decoding
|
||||
"""
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(read_bytes)
|
||||
|
||||
if not s and ImageFile.LOAD_TRUNCATED_IMAGES and not hasattr(self, "_ended"):
|
||||
|
|
|
@ -67,6 +67,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
|||
self._n_frames = len(self.images)
|
||||
self.is_animated = self._n_frames > 1
|
||||
|
||||
assert self.fp is not None
|
||||
self.__fp = self.fp
|
||||
self.seek(0)
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ from . import (
|
|||
TiffImagePlugin,
|
||||
)
|
||||
from ._binary import o32le
|
||||
from ._util import DeferredError
|
||||
|
||||
|
||||
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
|
@ -98,6 +99,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
|||
_close_exclusive_fp_after_loading = False
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
self.fp.seek(0) # prep the fp in order to pass the JPEG test
|
||||
JpegImagePlugin.JpegImageFile._open(self)
|
||||
self._after_jpeg_open()
|
||||
|
@ -117,6 +119,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
|||
assert self.n_frames == len(self.__mpoffsets)
|
||||
del self.info["mpoffset"] # no longer needed
|
||||
self.is_animated = self.n_frames > 1
|
||||
assert self.fp is not None
|
||||
self._fp = self.fp # FIXME: hack
|
||||
self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame
|
||||
self.__frame = 0
|
||||
|
@ -125,11 +128,15 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
|||
self.readonly = 1
|
||||
|
||||
def load_seek(self, pos: int) -> None:
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self._fp.seek(pos)
|
||||
|
||||
def seek(self, frame: int) -> None:
|
||||
if not self._seek_check(frame):
|
||||
return
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self.fp = self._fp
|
||||
self.offset = self.__mpoffsets[frame]
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ from ._binary import i32be as i32
|
|||
from ._binary import o8
|
||||
from ._binary import o16be as o16
|
||||
from ._binary import o32be as o32
|
||||
from ._util import DeferredError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import _imaging
|
||||
|
@ -752,6 +753,7 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
format_description = "Portable network graphics"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(8)):
|
||||
msg = "not a PNG file"
|
||||
raise SyntaxError(msg)
|
||||
|
@ -869,6 +871,8 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _seek(self, frame: int, rewind: bool = False) -> None:
|
||||
assert self.png is not None
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
|
||||
self.dispose: _imaging.ImagingCore | None
|
||||
dispose_extent = None
|
||||
|
@ -981,6 +985,7 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
"""internal: read more image data"""
|
||||
|
||||
assert self.png is not None
|
||||
assert self.fp is not None
|
||||
while self.__idat == 0:
|
||||
# end of chunk, skip forward to next one
|
||||
|
||||
|
@ -1014,6 +1019,7 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
def load_end(self) -> None:
|
||||
"""internal: finished reading image data"""
|
||||
assert self.png is not None
|
||||
assert self.fp is not None
|
||||
if self.__idat != 0:
|
||||
self.fp.read(self.__idat)
|
||||
while True:
|
||||
|
|
|
@ -27,6 +27,7 @@ from ._binary import i16be as i16
|
|||
from ._binary import i32be as i32
|
||||
from ._binary import si16be as si16
|
||||
from ._binary import si32be as si32
|
||||
from ._util import DeferredError
|
||||
|
||||
MODES = {
|
||||
# (photoshop mode, bits) -> (pil mode, required channels)
|
||||
|
@ -60,6 +61,7 @@ class PsdImageFile(ImageFile.ImageFile):
|
|||
_close_exclusive_fp_after_loading = False
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
read = self.fp.read
|
||||
|
||||
#
|
||||
|
@ -148,6 +150,8 @@ class PsdImageFile(ImageFile.ImageFile):
|
|||
) -> list[tuple[str, str, tuple[int, int, int, int], list[ImageFile._Tile]]]:
|
||||
layers = []
|
||||
if self._layers_position is not None:
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self._fp.seek(self._layers_position)
|
||||
_layer_data = io.BytesIO(ImageFile._safe_read(self._fp, self._layers_size))
|
||||
layers = _layerinfo(_layer_data, self._layers_size)
|
||||
|
@ -174,6 +178,8 @@ class PsdImageFile(ImageFile.ImageFile):
|
|||
self._mode = mode
|
||||
self.tile = tile
|
||||
self.frame = layer
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self.fp = self._fp
|
||||
except IndexError as e:
|
||||
msg = "no such layer"
|
||||
|
|
|
@ -22,6 +22,7 @@ class QoiImageFile(ImageFile.ImageFile):
|
|||
format_description = "Quite OK Image"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(4)):
|
||||
msg = "not a QOI file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -40,6 +40,7 @@ import sys
|
|||
from typing import IO, TYPE_CHECKING, Any, cast
|
||||
|
||||
from . import Image, ImageFile
|
||||
from ._util import DeferredError
|
||||
|
||||
|
||||
def isInt(f: Any) -> int:
|
||||
|
@ -100,6 +101,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# check header
|
||||
assert self.fp is not None
|
||||
n = 27 * 4 # read 27 float values
|
||||
f = self.fp.read(n)
|
||||
|
||||
|
@ -181,6 +183,9 @@ class SpiderImageFile(ImageFile.ImageFile):
|
|||
if not self._seek_check(frame):
|
||||
return
|
||||
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
|
||||
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self.fp = self._fp
|
||||
self.fp.seek(self.stkoffset)
|
||||
self._open()
|
||||
|
@ -211,26 +216,27 @@ class SpiderImageFile(ImageFile.ImageFile):
|
|||
|
||||
|
||||
# given a list of filenames, return a list of images
|
||||
def loadImageSeries(filelist: list[str] | None = None) -> list[SpiderImageFile] | None:
|
||||
def loadImageSeries(filelist: list[str] | None = None) -> list[Image.Image] | None:
|
||||
"""create a list of :py:class:`~PIL.Image.Image` objects for use in a montage"""
|
||||
if filelist is None or len(filelist) < 1:
|
||||
return None
|
||||
|
||||
imglist = []
|
||||
byte_imgs = []
|
||||
for img in filelist:
|
||||
if not os.path.exists(img):
|
||||
print(f"unable to find {img}")
|
||||
continue
|
||||
try:
|
||||
with Image.open(img) as im:
|
||||
im = im.convert2byte()
|
||||
assert isinstance(im, SpiderImageFile)
|
||||
byte_im = im.convert2byte()
|
||||
except Exception:
|
||||
if not isSpiderImage(img):
|
||||
print(f"{img} is not a Spider image file")
|
||||
continue
|
||||
im.info["filename"] = img
|
||||
imglist.append(im)
|
||||
return imglist
|
||||
byte_im.info["filename"] = img
|
||||
byte_imgs.append(byte_im)
|
||||
return byte_imgs
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
@ -321,9 +327,9 @@ if __name__ == "__main__":
|
|||
outfile = sys.argv[2]
|
||||
|
||||
# perform some image operation
|
||||
im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
transposed_im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
print(
|
||||
f"saving a flipped version of {os.path.basename(filename)} "
|
||||
f"as {outfile} "
|
||||
)
|
||||
im.save(outfile, SpiderImageFile.format)
|
||||
transposed_im.save(outfile, SpiderImageFile.format)
|
||||
|
|
|
@ -58,7 +58,7 @@ from ._binary import i32be as i32
|
|||
from ._binary import o8
|
||||
from ._deprecate import deprecate
|
||||
from ._typing import StrOrBytesPath
|
||||
from ._util import is_path
|
||||
from ._util import DeferredError, is_path
|
||||
from .TiffTags import TYPES
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -1154,6 +1154,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
"""Open the first image in a TIFF file"""
|
||||
|
||||
# Header
|
||||
assert self.fp is not None
|
||||
ifh = self.fp.read(8)
|
||||
if ifh[2] == 43:
|
||||
ifh += self.fp.read(8)
|
||||
|
@ -1198,6 +1199,8 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
self.im = Image.core.new(self.mode, self.size)
|
||||
|
||||
def _seek(self, frame: int) -> None:
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self.fp = self._fp
|
||||
|
||||
# reset buffered io handle in case fp
|
||||
|
@ -1283,6 +1286,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
|
||||
# reset buffered io handle in case fp
|
||||
# was passed to libtiff, invalidating the buffer
|
||||
assert self.fp is not None
|
||||
self.fp.tell()
|
||||
|
||||
# load IFD data from fp before it is closed
|
||||
|
@ -1316,6 +1320,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
# To be nice on memory footprint, if there's a
|
||||
# file descriptor, use that instead of reading
|
||||
# into a string in python.
|
||||
assert self.fp is not None
|
||||
try:
|
||||
fp = hasattr(self.fp, "fileno") and self.fp.fileno()
|
||||
# flush the file descriptor, prevents error on pypy 2.4+
|
||||
|
|
|
@ -39,6 +39,7 @@ class WalImageFile(ImageFile.ImageFile):
|
|||
self._mode = "P"
|
||||
|
||||
# read header fields
|
||||
assert self.fp is not None
|
||||
header = self.fp.read(32 + 24 + 32 + 12)
|
||||
self._size = i32(header, 32), i32(header, 36)
|
||||
Image._decompression_bomb_check(self.size)
|
||||
|
@ -55,6 +56,7 @@ class WalImageFile(ImageFile.ImageFile):
|
|||
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if self._im is None:
|
||||
assert self.fp is not None
|
||||
self.im = Image.core.new(self.mode, self.size)
|
||||
self.frombytes(self.fp.read(self.size[0] * self.size[1]))
|
||||
self.putpalette(quake2palette)
|
||||
|
|
|
@ -47,6 +47,7 @@ class WebPImageFile(ImageFile.ImageFile):
|
|||
def _open(self) -> None:
|
||||
# Use the newer AnimDecoder API to parse the (possibly) animated file,
|
||||
# and access muxed chunks like ICC/EXIF/XMP.
|
||||
assert self.fp is not None
|
||||
self._decoder = _webp.WebPAnimDecoder(self.fp.read())
|
||||
|
||||
# Get info from decoder
|
||||
|
|
|
@ -49,6 +49,7 @@ if hasattr(Image.core, "drawwmf"):
|
|||
self.bbox = im.info["wmf_bbox"]
|
||||
|
||||
def load(self, im: ImageFile.StubImageFile) -> Image.Image:
|
||||
assert im.fp is not None
|
||||
im.fp.seek(0) # rewind
|
||||
return Image.frombytes(
|
||||
"RGB",
|
||||
|
@ -85,6 +86,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
|||
self._inch = None
|
||||
|
||||
# check placable header
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(80)
|
||||
|
||||
if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00":
|
||||
|
|
|
@ -58,15 +58,15 @@ class XVThumbImageFile(ImageFile.ImageFile):
|
|||
|
||||
# skip info comments
|
||||
while True:
|
||||
s = self.fp.readline()
|
||||
if not s:
|
||||
line = self.fp.readline()
|
||||
if not line:
|
||||
msg = "Unexpected EOF reading XV thumbnail file"
|
||||
raise SyntaxError(msg)
|
||||
if s[0] != 35: # ie. when not a comment: '#'
|
||||
if line[0] != 35: # ie. when not a comment: '#'
|
||||
break
|
||||
|
||||
# parse header line (already read)
|
||||
s = s.strip().split()
|
||||
s = line.strip().split()
|
||||
|
||||
self._mode = "P"
|
||||
self._size = int(s[0]), int(s[1])
|
||||
|
|
|
@ -37,17 +37,18 @@ class XpmImageFile(ImageFile.ImageFile):
|
|||
format_description = "X11 Pixel Map"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(9)):
|
||||
msg = "not an XPM file"
|
||||
raise SyntaxError(msg)
|
||||
|
||||
# skip forward to next string
|
||||
while True:
|
||||
s = self.fp.readline()
|
||||
if not s:
|
||||
line = self.fp.readline()
|
||||
if not line:
|
||||
msg = "broken XPM file"
|
||||
raise SyntaxError(msg)
|
||||
m = xpm_head.match(s)
|
||||
m = xpm_head.match(line)
|
||||
if m:
|
||||
break
|
||||
|
||||
|
@ -66,14 +67,14 @@ class XpmImageFile(ImageFile.ImageFile):
|
|||
palette = [b"\0\0\0"] * 256
|
||||
|
||||
for _ in range(pal):
|
||||
s = self.fp.readline()
|
||||
if s[-2:] == b"\r\n":
|
||||
s = s[:-2]
|
||||
elif s[-1:] in b"\r\n":
|
||||
s = s[:-1]
|
||||
line = self.fp.readline()
|
||||
if line[-2:] == b"\r\n":
|
||||
line = line[:-2]
|
||||
elif line[-1:] in b"\r\n":
|
||||
line = line[:-1]
|
||||
|
||||
c = s[1]
|
||||
s = s[2:-2].split()
|
||||
c = line[1]
|
||||
s = line[2:-2].split()
|
||||
|
||||
for i in range(0, len(s), 2):
|
||||
if s[i] == b"c":
|
||||
|
@ -83,9 +84,11 @@ class XpmImageFile(ImageFile.ImageFile):
|
|||
self.info["transparency"] = c
|
||||
elif rgb[:1] == b"#":
|
||||
# FIXME: handle colour names (see ImagePalette.py)
|
||||
rgb = int(rgb[1:], 16)
|
||||
rgb_int = int(rgb[1:], 16)
|
||||
palette[c] = (
|
||||
o8((rgb >> 16) & 255) + o8((rgb >> 8) & 255) + o8(rgb & 255)
|
||||
o8((rgb_int >> 16) & 255)
|
||||
+ o8((rgb_int >> 8) & 255)
|
||||
+ o8(rgb_int & 255)
|
||||
)
|
||||
else:
|
||||
# unknown colour
|
||||
|
@ -111,6 +114,7 @@ class XpmImageFile(ImageFile.ImageFile):
|
|||
|
||||
xsize, ysize = self.size
|
||||
|
||||
assert self.fp is not None
|
||||
s = [self.fp.readline()[1 : xsize + 1].ljust(xsize) for i in range(ysize)]
|
||||
|
||||
return b"".join(s)
|
||||
|
|
Loading…
Reference in New Issue
Block a user