Merge pull request #8039 from radarhere/psd_layers

This commit is contained in:
Hugo van Kemenade 2024-06-25 06:07:09 -06:00 committed by GitHub
commit d9f97b0bbf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 40 additions and 14 deletions

View File

@ -4,7 +4,7 @@ import warnings
import pytest import pytest
from PIL import Image, PsdImagePlugin, UnidentifiedImageError from PIL import Image, PsdImagePlugin
from .helper import assert_image_equal_tofile, assert_image_similar, hopper, is_pypy from .helper import assert_image_equal_tofile, assert_image_similar, hopper, is_pypy
@ -150,14 +150,6 @@ def test_combined_larger_than_size() -> None:
@pytest.mark.parametrize( @pytest.mark.parametrize(
"test_file,raises", "test_file,raises",
[ [
(
"Tests/images/timeout-1ee28a249896e05b83840ae8140622de8e648ba9.psd",
UnidentifiedImageError,
),
(
"Tests/images/timeout-598843abc37fc080ec36a2699ebbd44f795d3a6f.psd",
UnidentifiedImageError,
),
("Tests/images/timeout-c8efc3fded6426986ba867a399791bae544f59bc.psd", OSError), ("Tests/images/timeout-c8efc3fded6426986ba867a399791bae544f59bc.psd", OSError),
("Tests/images/timeout-dedc7a4ebd856d79b4359bbcc79e8ef231ce38f6.psd", OSError), ("Tests/images/timeout-dedc7a4ebd856d79b4359bbcc79e8ef231ce38f6.psd", OSError),
], ],
@ -167,3 +159,17 @@ def test_crashes(test_file: str, raises: type[Exception]) -> None:
with pytest.raises(raises): with pytest.raises(raises):
with Image.open(f): with Image.open(f):
pass pass
@pytest.mark.parametrize(
"test_file",
[
"Tests/images/timeout-1ee28a249896e05b83840ae8140622de8e648ba9.psd",
"Tests/images/timeout-598843abc37fc080ec36a2699ebbd44f795d3a6f.psd",
],
)
def test_layer_crashes(test_file: str) -> None:
with open(test_file, "rb") as f:
with Image.open(f) as im:
with pytest.raises(SyntaxError):
im.layers

View File

@ -18,6 +18,7 @@
from __future__ import annotations from __future__ import annotations
import io import io
from functools import cached_property
from . import Image, ImageFile, ImagePalette from . import Image, ImageFile, ImagePalette
from ._binary import i8 from ._binary import i8
@ -118,18 +119,17 @@ class PsdImageFile(ImageFile.ImageFile):
# #
# layer and mask information # layer and mask information
self.layers = [] self._layers_position = None
size = i32(read(4)) size = i32(read(4))
if size: if size:
end = self.fp.tell() + size end = self.fp.tell() + size
size = i32(read(4)) size = i32(read(4))
if size: if size:
_layer_data = io.BytesIO(ImageFile._safe_read(self.fp, size)) self._layers_position = self.fp.tell()
self.layers = _layerinfo(_layer_data, size) self._layers_size = size
self.fp.seek(end) self.fp.seek(end)
self.n_frames = len(self.layers) self._n_frames: int | None = None
self.is_animated = self.n_frames > 1
# #
# image descriptor # image descriptor
@ -141,6 +141,26 @@ class PsdImageFile(ImageFile.ImageFile):
self.frame = 1 self.frame = 1
self._min_frame = 1 self._min_frame = 1
@cached_property
def layers(self):
layers = []
if self._layers_position is not None:
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)
self._n_frames = len(layers)
return layers
@property
def n_frames(self) -> int:
if self._n_frames is None:
self._n_frames = len(self.layers)
return self._n_frames
@property
def is_animated(self) -> bool:
return len(self.layers) > 1
def seek(self, layer: int) -> None: def seek(self, layer: int) -> None:
if not self._seek_check(layer): if not self._seek_check(layer):
return return