mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
Merge pull request #8039 from radarhere/psd_layers
This commit is contained in:
commit
d9f97b0bbf
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user