Skip to content

Commit 0967575

Browse files
committed
add EPS test for image with ImageData and BoundingBox (atend)
1 parent 8251dbe commit 0967575

File tree

3 files changed

+61
-34
lines changed

3 files changed

+61
-34
lines changed

Tests/images/eps/1_atend.eps

44.8 KB
Binary file not shown.

Tests/test_file_eps.py

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,15 @@ def test_binary_header_only() -> None:
129129
EpsImagePlugin.EpsImageFile(data)
130130

131131

132+
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
133+
def test_simple_eps_file(prefix: bytes) -> None:
134+
data = io.BytesIO(prefix + b"\n".join(simple_eps_file))
135+
with Image.open(data) as img:
136+
assert img.mode == "RGB"
137+
assert img.size == (100, 100)
138+
assert img.format == "EPS"
139+
140+
132141
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
133142
def test_missing_version_comment(prefix: bytes) -> None:
134143
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_without_version))
@@ -144,23 +153,19 @@ def test_missing_boundingbox_comment(prefix: bytes) -> None:
144153

145154

146155
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
147-
def test_invalid_boundingbox_comment(prefix: bytes) -> None:
148-
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox))
156+
@pytest.mark.parametrize(
157+
"file_lines",
158+
(
159+
simple_eps_file_with_invalid_boundingbox,
160+
simple_eps_file_with_invalid_boundingbox_valid_imagedata,
161+
),
162+
)
163+
def test_invalid_boundingbox_comment(prefix: bytes, file_lines: list[bytes]) -> None:
164+
data = io.BytesIO(prefix + b"\n".join(file_lines))
149165
with pytest.raises(OSError, match="cannot determine EPS bounding box"):
150166
EpsImagePlugin.EpsImageFile(data)
151167

152168

153-
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
154-
def test_invalid_boundingbox_comment_valid_imagedata_comment(prefix: bytes) -> None:
155-
data = io.BytesIO(
156-
prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox_valid_imagedata)
157-
)
158-
with Image.open(data) as img:
159-
assert img.mode == "RGB"
160-
assert img.size == (100, 100)
161-
assert img.format == "EPS"
162-
163-
164169
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
165170
def test_ascii_comment_too_long(prefix: bytes) -> None:
166171
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_ascii_comment))
@@ -249,8 +254,13 @@ def test_bytesio_object() -> None:
249254

250255

251256
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
252-
def test_1() -> None:
253-
with Image.open("Tests/images/eps/1.eps") as im:
257+
@pytest.mark.parametrize(
258+
# These images have an "ImageData" descriptor.
259+
"filename",
260+
("Tests/images/eps/1.eps", "Tests/images/eps/1_atend.eps"),
261+
)
262+
def test_1(filename: str) -> None:
263+
with Image.open(filename) as im:
254264
assert_image_equal_tofile(im, "Tests/images/eps/1.bmp")
255265

256266

src/PIL/EpsImagePlugin.py

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,11 @@ def _open(self) -> None:
193193
self.fp.seek(offset)
194194

195195
self._mode = "RGB"
196-
self._size = None
196+
197+
# The last occurrence of one of these values
198+
# takes precedence over prior occurrences.
199+
bounding_box = None
200+
imagedata_size = None
197201

198202
byte_arr = bytearray(255)
199203
bytes_mv = memoryview(byte_arr)
@@ -215,8 +219,8 @@ def check_required_header_comments() -> None:
215219
msg = 'EPS header missing "%%BoundingBox" comment'
216220
raise SyntaxError(msg)
217221

218-
def _read_comment(s: str) -> bool:
219-
nonlocal reading_trailer_comments
222+
def read_comment(s: str) -> bool:
223+
nonlocal bounding_box, reading_trailer_comments
220224
try:
221225
m = split.match(s)
222226
except re.error as e:
@@ -231,18 +235,12 @@ def _read_comment(s: str) -> bool:
231235
if k == "BoundingBox":
232236
if v == "(atend)":
233237
reading_trailer_comments = True
234-
elif not self._size or (trailer_reached and reading_trailer_comments):
238+
else:
235239
try:
236240
# Note: The DSC spec says that BoundingBox
237241
# fields should be integers, but some drivers
238242
# put floating point values there anyway.
239-
box = [int(float(i)) for i in v.split()]
240-
self._size = box[2] - box[0], box[3] - box[1]
241-
self.tile = [
242-
ImageFile._Tile(
243-
"eps", (0, 0) + self.size, offset, (length, box)
244-
)
245-
]
243+
bounding_box = [int(float(i)) for i in v.split()]
246244
except Exception:
247245
pass
248246
return True
@@ -293,7 +291,7 @@ def _read_comment(s: str) -> bool:
293291
continue
294292

295293
s = str(bytes_mv[:bytes_read], "latin-1")
296-
if not _read_comment(s):
294+
if not read_comment(s):
297295
m = field.match(s)
298296
if m:
299297
k = m.group(1)
@@ -327,32 +325,51 @@ def _read_comment(s: str) -> bool:
327325
int(value) for value in image_data_values[:4]
328326
)
329327

328+
imagedata_size = (columns, rows)
329+
330330
if bit_depth == 1:
331331
self._mode = "1"
332332
elif bit_depth == 8:
333333
try:
334334
self._mode = self.mode_map[mode_id]
335335
except ValueError:
336-
break
336+
pass
337337
else:
338-
break
339-
340-
self._size = columns, rows
341-
return
338+
pass
342339
elif bytes_mv[:5] == b"%%EOF":
343340
break
344341
elif trailer_reached and reading_trailer_comments:
345342
# Load EPS trailer
346343
s = str(bytes_mv[:bytes_read], "latin-1")
347-
_read_comment(s)
344+
read_comment(s)
348345
elif bytes_mv[:9] == b"%%Trailer":
349346
trailer_reached = True
350347
bytes_read = 0
351348

352-
if not self._size:
349+
# A "BoundingBox" is always required,
350+
# even if an "ImageData" descriptor size exists.
351+
if not bounding_box:
353352
msg = "cannot determine EPS bounding box"
354353
raise OSError(msg)
355354

355+
# An "ImageData" size takes precedence over the "BoundingBox".
356+
if imagedata_size:
357+
self._size = imagedata_size
358+
else:
359+
self._size = (
360+
bounding_box[2] - bounding_box[0],
361+
bounding_box[3] - bounding_box[1],
362+
)
363+
364+
self.tile = [
365+
ImageFile._Tile(
366+
codec_name="eps",
367+
extents=(0, 0) + self._size,
368+
offset=offset,
369+
args=(length, bounding_box),
370+
)
371+
]
372+
356373
def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]:
357374
s = fp.read(4)
358375

0 commit comments

Comments
 (0)