Merge pull request #8281 from Yay295/eps_test
BIN
Tests/images/eps/1.bmp
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Tests/images/eps/1_boundingbox_after_imagedata.eps
Normal file
BIN
Tests/images/eps/1_second_imagedata.eps
Normal file
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
@ -8,6 +8,7 @@ import pytest
|
||||||
from PIL import EpsImagePlugin, Image, UnidentifiedImageError, features
|
from PIL import EpsImagePlugin, Image, UnidentifiedImageError, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
assert_image_equal_tofile,
|
||||||
assert_image_similar,
|
assert_image_similar,
|
||||||
assert_image_similar_tofile,
|
assert_image_similar_tofile,
|
||||||
hopper,
|
hopper,
|
||||||
|
@ -19,18 +20,18 @@ from .helper import (
|
||||||
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
|
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
|
||||||
|
|
||||||
# Our two EPS test files (they are identical except for their bounding boxes)
|
# Our two EPS test files (they are identical except for their bounding boxes)
|
||||||
FILE1 = "Tests/images/zero_bb.eps"
|
FILE1 = "Tests/images/eps/zero_bb.eps"
|
||||||
FILE2 = "Tests/images/non_zero_bb.eps"
|
FILE2 = "Tests/images/eps/non_zero_bb.eps"
|
||||||
|
|
||||||
# Due to palletization, we'll need to convert these to RGB after load
|
# Due to palletization, we'll need to convert these to RGB after load
|
||||||
FILE1_COMPARE = "Tests/images/zero_bb.png"
|
FILE1_COMPARE = "Tests/images/eps/zero_bb.png"
|
||||||
FILE1_COMPARE_SCALE2 = "Tests/images/zero_bb_scale2.png"
|
FILE1_COMPARE_SCALE2 = "Tests/images/eps/zero_bb_scale2.png"
|
||||||
|
|
||||||
FILE2_COMPARE = "Tests/images/non_zero_bb.png"
|
FILE2_COMPARE = "Tests/images/eps/non_zero_bb.png"
|
||||||
FILE2_COMPARE_SCALE2 = "Tests/images/non_zero_bb_scale2.png"
|
FILE2_COMPARE_SCALE2 = "Tests/images/eps/non_zero_bb_scale2.png"
|
||||||
|
|
||||||
# EPS test files with binary preview
|
# EPS test files with binary preview
|
||||||
FILE3 = "Tests/images/binary_preview_map.eps"
|
FILE3 = "Tests/images/eps/binary_preview_map.eps"
|
||||||
|
|
||||||
# Three unsigned 32bit little-endian values:
|
# Three unsigned 32bit little-endian values:
|
||||||
# 0xC6D3D0C5 magic number
|
# 0xC6D3D0C5 magic number
|
||||||
|
@ -126,6 +127,15 @@ def test_binary_header_only() -> None:
|
||||||
EpsImagePlugin.EpsImageFile(data)
|
EpsImagePlugin.EpsImageFile(data)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
|
||||||
|
def test_simple_eps_file(prefix: bytes) -> None:
|
||||||
|
data = io.BytesIO(prefix + b"\n".join(simple_eps_file))
|
||||||
|
with Image.open(data) as img:
|
||||||
|
assert img.mode == "RGB"
|
||||||
|
assert img.size == (100, 100)
|
||||||
|
assert img.format == "EPS"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
|
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
|
||||||
def test_missing_version_comment(prefix: bytes) -> None:
|
def test_missing_version_comment(prefix: bytes) -> None:
|
||||||
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_without_version))
|
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_without_version))
|
||||||
|
@ -141,23 +151,21 @@ def test_missing_boundingbox_comment(prefix: bytes) -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
|
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
|
||||||
def test_invalid_boundingbox_comment(prefix: bytes) -> None:
|
@pytest.mark.parametrize(
|
||||||
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox))
|
"file_lines",
|
||||||
|
(
|
||||||
|
simple_eps_file_with_invalid_boundingbox,
|
||||||
|
simple_eps_file_with_invalid_boundingbox_valid_imagedata,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_invalid_boundingbox_comment(
|
||||||
|
prefix: bytes, file_lines: tuple[bytes, ...]
|
||||||
|
) -> None:
|
||||||
|
data = io.BytesIO(prefix + b"\n".join(file_lines))
|
||||||
with pytest.raises(OSError, match="cannot determine EPS bounding box"):
|
with pytest.raises(OSError, match="cannot determine EPS bounding box"):
|
||||||
EpsImagePlugin.EpsImageFile(data)
|
EpsImagePlugin.EpsImageFile(data)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
|
|
||||||
def test_invalid_boundingbox_comment_valid_imagedata_comment(prefix: bytes) -> None:
|
|
||||||
data = io.BytesIO(
|
|
||||||
prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox_valid_imagedata)
|
|
||||||
)
|
|
||||||
with Image.open(data) as img:
|
|
||||||
assert img.mode == "RGB"
|
|
||||||
assert img.size == (100, 100)
|
|
||||||
assert img.format == "EPS"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
|
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
|
||||||
def test_ascii_comment_too_long(prefix: bytes) -> None:
|
def test_ascii_comment_too_long(prefix: bytes) -> None:
|
||||||
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_ascii_comment))
|
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_ascii_comment))
|
||||||
|
@ -177,7 +185,7 @@ def test_load_long_binary_data(prefix: bytes) -> None:
|
||||||
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_binary_data))
|
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_binary_data))
|
||||||
with Image.open(data) as img:
|
with Image.open(data) as img:
|
||||||
img.load()
|
img.load()
|
||||||
assert img.mode == "RGB"
|
assert img.mode == "1"
|
||||||
assert img.size == (100, 100)
|
assert img.size == (100, 100)
|
||||||
assert img.format == "EPS"
|
assert img.format == "EPS"
|
||||||
|
|
||||||
|
@ -187,7 +195,7 @@ def test_load_long_binary_data(prefix: bytes) -> None:
|
||||||
)
|
)
|
||||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||||
def test_cmyk() -> None:
|
def test_cmyk() -> None:
|
||||||
with Image.open("Tests/images/pil_sample_cmyk.eps") as cmyk_image:
|
with Image.open("Tests/images/eps/pil_sample_cmyk.eps") as cmyk_image:
|
||||||
assert cmyk_image.mode == "CMYK"
|
assert cmyk_image.mode == "CMYK"
|
||||||
assert cmyk_image.size == (100, 100)
|
assert cmyk_image.size == (100, 100)
|
||||||
assert cmyk_image.format == "EPS"
|
assert cmyk_image.format == "EPS"
|
||||||
|
@ -204,8 +212,8 @@ def test_cmyk() -> None:
|
||||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||||
def test_showpage() -> None:
|
def test_showpage() -> None:
|
||||||
# See https://github.com/python-pillow/Pillow/issues/2615
|
# See https://github.com/python-pillow/Pillow/issues/2615
|
||||||
with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
|
with Image.open("Tests/images/eps/reqd_showpage.eps") as plot_image:
|
||||||
with Image.open("Tests/images/reqd_showpage.png") as target:
|
with Image.open("Tests/images/eps/reqd_showpage.png") as target:
|
||||||
# should not crash/hang
|
# should not crash/hang
|
||||||
plot_image.load()
|
plot_image.load()
|
||||||
# fonts could be slightly different
|
# fonts could be slightly different
|
||||||
|
@ -214,11 +222,11 @@ def test_showpage() -> None:
|
||||||
|
|
||||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||||
def test_transparency() -> None:
|
def test_transparency() -> None:
|
||||||
with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
|
with Image.open("Tests/images/eps/reqd_showpage.eps") as plot_image:
|
||||||
plot_image.load(transparency=True)
|
plot_image.load(transparency=True)
|
||||||
assert plot_image.mode == "RGBA"
|
assert plot_image.mode == "RGBA"
|
||||||
|
|
||||||
with Image.open("Tests/images/reqd_showpage_transparency.png") as target:
|
with Image.open("Tests/images/eps/reqd_showpage_transparency.png") as target:
|
||||||
# fonts could be slightly different
|
# fonts could be slightly different
|
||||||
assert_image_similar(plot_image, target, 6)
|
assert_image_similar(plot_image, target, 6)
|
||||||
|
|
||||||
|
@ -245,9 +253,19 @@ def test_bytesio_object() -> None:
|
||||||
assert_image_similar(img, image1_scale1_compare, 5)
|
assert_image_similar(img, image1_scale1_compare, 5)
|
||||||
|
|
||||||
|
|
||||||
def test_1_mode() -> None:
|
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||||
with Image.open("Tests/images/1.eps") as im:
|
@pytest.mark.parametrize(
|
||||||
assert im.mode == "1"
|
# These images have an "ImageData" descriptor.
|
||||||
|
"filename",
|
||||||
|
(
|
||||||
|
"Tests/images/eps/1.eps",
|
||||||
|
"Tests/images/eps/1_boundingbox_after_imagedata.eps",
|
||||||
|
"Tests/images/eps/1_second_imagedata.eps",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_1(filename: str) -> None:
|
||||||
|
with Image.open(filename) as im:
|
||||||
|
assert_image_equal_tofile(im, "Tests/images/eps/1.bmp")
|
||||||
|
|
||||||
|
|
||||||
def test_image_mode_not_supported(tmp_path: Path) -> None:
|
def test_image_mode_not_supported(tmp_path: Path) -> None:
|
||||||
|
@ -302,7 +320,9 @@ def test_render_scale2() -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||||
@pytest.mark.parametrize("filename", (FILE1, FILE2, "Tests/images/illu10_preview.eps"))
|
@pytest.mark.parametrize(
|
||||||
|
"filename", (FILE1, FILE2, "Tests/images/eps/illu10_preview.eps")
|
||||||
|
)
|
||||||
def test_resize(filename: str) -> None:
|
def test_resize(filename: str) -> None:
|
||||||
with Image.open(filename) as im:
|
with Image.open(filename) as im:
|
||||||
new_size = (100, 100)
|
new_size = (100, 100)
|
||||||
|
@ -344,10 +364,10 @@ def test_readline(prefix: bytes, line_ending: bytes) -> None:
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"filename",
|
"filename",
|
||||||
(
|
(
|
||||||
"Tests/images/illu10_no_preview.eps",
|
"Tests/images/eps/illu10_no_preview.eps",
|
||||||
"Tests/images/illu10_preview.eps",
|
"Tests/images/eps/illu10_preview.eps",
|
||||||
"Tests/images/illuCS6_no_preview.eps",
|
"Tests/images/eps/illuCS6_no_preview.eps",
|
||||||
"Tests/images/illuCS6_preview.eps",
|
"Tests/images/eps/illuCS6_preview.eps",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_open_eps(filename: str) -> None:
|
def test_open_eps(filename: str) -> None:
|
||||||
|
@ -359,7 +379,7 @@ def test_open_eps(filename: str) -> None:
|
||||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||||
def test_emptyline() -> None:
|
def test_emptyline() -> None:
|
||||||
# Test file includes an empty line in the header data
|
# Test file includes an empty line in the header data
|
||||||
emptyline_file = "Tests/images/zero_bb_emptyline.eps"
|
emptyline_file = "Tests/images/eps/zero_bb_emptyline.eps"
|
||||||
|
|
||||||
with Image.open(emptyline_file) as image:
|
with Image.open(emptyline_file) as image:
|
||||||
image.load()
|
image.load()
|
||||||
|
@ -371,7 +391,7 @@ def test_emptyline() -> None:
|
||||||
@pytest.mark.timeout(timeout=5)
|
@pytest.mark.timeout(timeout=5)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"test_file",
|
"test_file",
|
||||||
["Tests/images/timeout-d675703545fee17acab56e5fec644c19979175de.eps"],
|
["Tests/images/eps/timeout-d675703545fee17acab56e5fec644c19979175de.eps"],
|
||||||
)
|
)
|
||||||
def test_timeout(test_file: str) -> None:
|
def test_timeout(test_file: str) -> None:
|
||||||
with open(test_file, "rb") as f:
|
with open(test_file, "rb") as f:
|
||||||
|
@ -384,7 +404,7 @@ def test_bounding_box_in_trailer() -> None:
|
||||||
# Check bounding boxes are parsed in the same way
|
# Check bounding boxes are parsed in the same way
|
||||||
# when specified in the header and the trailer
|
# when specified in the header and the trailer
|
||||||
with (
|
with (
|
||||||
Image.open("Tests/images/zero_bb_trailer.eps") as trailer_image,
|
Image.open("Tests/images/eps/zero_bb_trailer.eps") as trailer_image,
|
||||||
Image.open(FILE1) as header_image,
|
Image.open(FILE1) as header_image,
|
||||||
):
|
):
|
||||||
assert trailer_image.size == header_image.size
|
assert trailer_image.size == header_image.size
|
||||||
|
@ -392,12 +412,12 @@ def test_bounding_box_in_trailer() -> None:
|
||||||
|
|
||||||
def test_eof_before_bounding_box() -> None:
|
def test_eof_before_bounding_box() -> None:
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
with Image.open("Tests/images/zero_bb_eof_before_boundingbox.eps"):
|
with Image.open("Tests/images/eps/zero_bb_eof_before_boundingbox.eps"):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_data_after_eof() -> None:
|
def test_invalid_data_after_eof() -> None:
|
||||||
with open("Tests/images/illuCS6_preview.eps", "rb") as f:
|
with open("Tests/images/eps/illuCS6_preview.eps", "rb") as f:
|
||||||
img_bytes = io.BytesIO(f.read() + b"\r\n%" + (b" " * 255))
|
img_bytes = io.BytesIO(f.read() + b"\r\n%" + (b" " * 255))
|
||||||
|
|
||||||
with Image.open(img_bytes) as img:
|
with Image.open(img_bytes) as img:
|
||||||
|
|
|
@ -56,10 +56,10 @@ def helper_pickle_string(protocol: int, test_file: str, mode: str | None) -> Non
|
||||||
),
|
),
|
||||||
("Tests/images/hopper.tif", None),
|
("Tests/images/hopper.tif", None),
|
||||||
("Tests/images/test-card.png", None),
|
("Tests/images/test-card.png", None),
|
||||||
("Tests/images/zero_bb.png", None),
|
("Tests/images/eps/zero_bb.png", None),
|
||||||
("Tests/images/zero_bb_scale2.png", None),
|
("Tests/images/eps/zero_bb_scale2.png", None),
|
||||||
("Tests/images/non_zero_bb.png", None),
|
("Tests/images/eps/non_zero_bb.png", None),
|
||||||
("Tests/images/non_zero_bb_scale2.png", None),
|
("Tests/images/eps/non_zero_bb_scale2.png", None),
|
||||||
("Tests/images/p_trns_single.png", None),
|
("Tests/images/p_trns_single.png", None),
|
||||||
("Tests/images/pil123p.png", None),
|
("Tests/images/pil123p.png", None),
|
||||||
("Tests/images/itxt_chunks.png", None),
|
("Tests/images/itxt_chunks.png", None),
|
||||||
|
|
|
@ -121,7 +121,13 @@ def Ghostscript(
|
||||||
lengthfile -= len(s)
|
lengthfile -= len(s)
|
||||||
f.write(s)
|
f.write(s)
|
||||||
|
|
||||||
device = "pngalpha" if transparency else "ppmraw"
|
if transparency:
|
||||||
|
# "RGBA"
|
||||||
|
device = "pngalpha"
|
||||||
|
else:
|
||||||
|
# "pnmraw" automatically chooses between
|
||||||
|
# PBM ("1"), PGM ("L"), and PPM ("RGB").
|
||||||
|
device = "pnmraw"
|
||||||
|
|
||||||
# Build Ghostscript command
|
# Build Ghostscript command
|
||||||
command = [
|
command = [
|
||||||
|
@ -151,8 +157,9 @@ def Ghostscript(
|
||||||
startupinfo = subprocess.STARTUPINFO()
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
subprocess.check_call(command, startupinfo=startupinfo)
|
subprocess.check_call(command, startupinfo=startupinfo)
|
||||||
out_im = Image.open(outfile)
|
with Image.open(outfile) as out_im:
|
||||||
out_im.load()
|
out_im.load()
|
||||||
|
return out_im.im.copy()
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
os.unlink(outfile)
|
os.unlink(outfile)
|
||||||
|
@ -161,10 +168,6 @@ def Ghostscript(
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
im = out_im.im.copy()
|
|
||||||
out_im.close()
|
|
||||||
return im
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5)
|
return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5)
|
||||||
|
@ -191,6 +194,11 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
self._mode = "RGB"
|
self._mode = "RGB"
|
||||||
|
|
||||||
|
# When reading header comments, the first comment is used.
|
||||||
|
# When reading trailer comments, the last comment is used.
|
||||||
|
bounding_box: list[int] | None = None
|
||||||
|
imagedata_size: tuple[int, int] | None = None
|
||||||
|
|
||||||
byte_arr = bytearray(255)
|
byte_arr = bytearray(255)
|
||||||
bytes_mv = memoryview(byte_arr)
|
bytes_mv = memoryview(byte_arr)
|
||||||
bytes_read = 0
|
bytes_read = 0
|
||||||
|
@ -211,8 +219,8 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
msg = 'EPS header missing "%%BoundingBox" comment'
|
msg = 'EPS header missing "%%BoundingBox" comment'
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
def _read_comment(s: str) -> bool:
|
def read_comment(s: str) -> bool:
|
||||||
nonlocal reading_trailer_comments
|
nonlocal bounding_box, reading_trailer_comments
|
||||||
try:
|
try:
|
||||||
m = split.match(s)
|
m = split.match(s)
|
||||||
except re.error as e:
|
except re.error as e:
|
||||||
|
@ -227,18 +235,12 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
if k == "BoundingBox":
|
if k == "BoundingBox":
|
||||||
if v == "(atend)":
|
if v == "(atend)":
|
||||||
reading_trailer_comments = True
|
reading_trailer_comments = True
|
||||||
elif not self.tile or (trailer_reached and reading_trailer_comments):
|
elif not bounding_box or (trailer_reached and reading_trailer_comments):
|
||||||
try:
|
try:
|
||||||
# Note: The DSC spec says that BoundingBox
|
# Note: The DSC spec says that BoundingBox
|
||||||
# fields should be integers, but some drivers
|
# fields should be integers, but some drivers
|
||||||
# put floating point values there anyway.
|
# put floating point values there anyway.
|
||||||
box = [int(float(i)) for i in v.split()]
|
bounding_box = [int(float(i)) for i in v.split()]
|
||||||
self._size = box[2] - box[0], box[3] - box[1]
|
|
||||||
self.tile = [
|
|
||||||
ImageFile._Tile(
|
|
||||||
"eps", (0, 0) + self.size, offset, (length, box)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return True
|
return True
|
||||||
|
@ -289,7 +291,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
s = str(bytes_mv[:bytes_read], "latin-1")
|
s = str(bytes_mv[:bytes_read], "latin-1")
|
||||||
if not _read_comment(s):
|
if not read_comment(s):
|
||||||
m = field.match(s)
|
m = field.match(s)
|
||||||
if m:
|
if m:
|
||||||
k = m.group(1)
|
k = m.group(1)
|
||||||
|
@ -308,6 +310,12 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
# Check for an "ImageData" descriptor
|
# Check for an "ImageData" descriptor
|
||||||
# https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577413_pgfId-1035096
|
# https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577413_pgfId-1035096
|
||||||
|
|
||||||
|
# If we've already read an "ImageData" descriptor,
|
||||||
|
# don't read another one.
|
||||||
|
if imagedata_size:
|
||||||
|
bytes_read = 0
|
||||||
|
continue
|
||||||
|
|
||||||
# Values:
|
# Values:
|
||||||
# columns
|
# columns
|
||||||
# rows
|
# rows
|
||||||
|
@ -333,22 +341,35 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
self._size = columns, rows
|
# Parse the columns and rows after checking the bit depth and mode
|
||||||
return
|
# in case the bit depth and/or mode are invalid.
|
||||||
|
imagedata_size = columns, rows
|
||||||
elif bytes_mv[:5] == b"%%EOF":
|
elif bytes_mv[:5] == b"%%EOF":
|
||||||
break
|
break
|
||||||
elif trailer_reached and reading_trailer_comments:
|
elif trailer_reached and reading_trailer_comments:
|
||||||
# Load EPS trailer
|
# Load EPS trailer
|
||||||
s = str(bytes_mv[:bytes_read], "latin-1")
|
s = str(bytes_mv[:bytes_read], "latin-1")
|
||||||
_read_comment(s)
|
read_comment(s)
|
||||||
elif bytes_mv[:9] == b"%%Trailer":
|
elif bytes_mv[:9] == b"%%Trailer":
|
||||||
trailer_reached = True
|
trailer_reached = True
|
||||||
bytes_read = 0
|
bytes_read = 0
|
||||||
|
|
||||||
if not self.tile:
|
# A "BoundingBox" is always required,
|
||||||
|
# even if an "ImageData" descriptor size exists.
|
||||||
|
if not bounding_box:
|
||||||
msg = "cannot determine EPS bounding box"
|
msg = "cannot determine EPS bounding box"
|
||||||
raise OSError(msg)
|
raise OSError(msg)
|
||||||
|
|
||||||
|
# An "ImageData" size takes precedence over the "BoundingBox".
|
||||||
|
self._size = imagedata_size or (
|
||||||
|
bounding_box[2] - bounding_box[0],
|
||||||
|
bounding_box[3] - bounding_box[1],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tile = [
|
||||||
|
ImageFile._Tile("eps", (0, 0) + self.size, offset, (length, bounding_box))
|
||||||
|
]
|
||||||
|
|
||||||
def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]:
|
def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]:
|
||||||
s = fp.read(4)
|
s = fp.read(4)
|
||||||
|
|
||||||
|
|