From 6fe4375f28015f0c1a7c3cb6fbc9ea3c4f405679 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 3 Aug 2024 11:13:38 -0500 Subject: [PATCH 01/14] move eps test images to their own folder Co-authored-by: Andrew Murray --- Tests/images/{ => eps}/1.eps | Bin Tests/images/{ => eps}/binary_preview_map.eps | Bin Tests/images/{ => eps}/create_eps.gnuplot | 0 Tests/images/{ => eps}/illu10_no_preview.eps | Bin Tests/images/{ => eps}/illu10_preview.eps | Bin Tests/images/{ => eps}/illuCS6_no_preview.eps | Bin Tests/images/{ => eps}/illuCS6_preview.eps | Bin Tests/images/{ => eps}/non_zero_bb.eps | Bin Tests/images/{ => eps}/non_zero_bb.png | Bin Tests/images/{ => eps}/non_zero_bb_scale2.png | Bin Tests/images/{ => eps}/pil_sample_cmyk.eps | Bin Tests/images/{ => eps}/reqd_showpage.eps | Bin Tests/images/{ => eps}/reqd_showpage.png | Bin .../{ => eps}/reqd_showpage_transparency.png | Bin ...75703545fee17acab56e5fec644c19979175de.eps | Bin Tests/images/{ => eps}/zero_bb.eps | Bin Tests/images/{ => eps}/zero_bb.png | Bin Tests/images/{ => eps}/zero_bb_emptyline.eps | Bin .../zero_bb_eof_before_boundingbox.eps | Bin Tests/images/{ => eps}/zero_bb_scale2.png | Bin Tests/images/{ => eps}/zero_bb_trailer.eps | Bin Tests/test_file_eps.py | 48 +++++++++--------- Tests/test_pickle.py | 8 +-- 23 files changed, 29 insertions(+), 27 deletions(-) rename Tests/images/{ => eps}/1.eps (100%) rename Tests/images/{ => eps}/binary_preview_map.eps (100%) rename Tests/images/{ => eps}/create_eps.gnuplot (100%) rename Tests/images/{ => eps}/illu10_no_preview.eps (100%) rename Tests/images/{ => eps}/illu10_preview.eps (100%) rename Tests/images/{ => eps}/illuCS6_no_preview.eps (100%) rename Tests/images/{ => eps}/illuCS6_preview.eps (100%) rename Tests/images/{ => eps}/non_zero_bb.eps (100%) rename Tests/images/{ => eps}/non_zero_bb.png (100%) rename Tests/images/{ => eps}/non_zero_bb_scale2.png (100%) rename Tests/images/{ => eps}/pil_sample_cmyk.eps (100%) rename Tests/images/{ => eps}/reqd_showpage.eps (100%) rename Tests/images/{ => eps}/reqd_showpage.png (100%) rename Tests/images/{ => eps}/reqd_showpage_transparency.png (100%) rename Tests/images/{ => eps}/timeout-d675703545fee17acab56e5fec644c19979175de.eps (100%) rename Tests/images/{ => eps}/zero_bb.eps (100%) rename Tests/images/{ => eps}/zero_bb.png (100%) rename Tests/images/{ => eps}/zero_bb_emptyline.eps (100%) rename Tests/images/{ => eps}/zero_bb_eof_before_boundingbox.eps (100%) rename Tests/images/{ => eps}/zero_bb_scale2.png (100%) rename Tests/images/{ => eps}/zero_bb_trailer.eps (100%) diff --git a/Tests/images/1.eps b/Tests/images/eps/1.eps similarity index 100% rename from Tests/images/1.eps rename to Tests/images/eps/1.eps diff --git a/Tests/images/binary_preview_map.eps b/Tests/images/eps/binary_preview_map.eps similarity index 100% rename from Tests/images/binary_preview_map.eps rename to Tests/images/eps/binary_preview_map.eps diff --git a/Tests/images/create_eps.gnuplot b/Tests/images/eps/create_eps.gnuplot similarity index 100% rename from Tests/images/create_eps.gnuplot rename to Tests/images/eps/create_eps.gnuplot diff --git a/Tests/images/illu10_no_preview.eps b/Tests/images/eps/illu10_no_preview.eps similarity index 100% rename from Tests/images/illu10_no_preview.eps rename to Tests/images/eps/illu10_no_preview.eps diff --git a/Tests/images/illu10_preview.eps b/Tests/images/eps/illu10_preview.eps similarity index 100% rename from Tests/images/illu10_preview.eps rename to Tests/images/eps/illu10_preview.eps diff --git a/Tests/images/illuCS6_no_preview.eps b/Tests/images/eps/illuCS6_no_preview.eps similarity index 100% rename from Tests/images/illuCS6_no_preview.eps rename to Tests/images/eps/illuCS6_no_preview.eps diff --git a/Tests/images/illuCS6_preview.eps b/Tests/images/eps/illuCS6_preview.eps similarity index 100% rename from Tests/images/illuCS6_preview.eps rename to Tests/images/eps/illuCS6_preview.eps diff --git a/Tests/images/non_zero_bb.eps b/Tests/images/eps/non_zero_bb.eps similarity index 100% rename from Tests/images/non_zero_bb.eps rename to Tests/images/eps/non_zero_bb.eps diff --git a/Tests/images/non_zero_bb.png b/Tests/images/eps/non_zero_bb.png similarity index 100% rename from Tests/images/non_zero_bb.png rename to Tests/images/eps/non_zero_bb.png diff --git a/Tests/images/non_zero_bb_scale2.png b/Tests/images/eps/non_zero_bb_scale2.png similarity index 100% rename from Tests/images/non_zero_bb_scale2.png rename to Tests/images/eps/non_zero_bb_scale2.png diff --git a/Tests/images/pil_sample_cmyk.eps b/Tests/images/eps/pil_sample_cmyk.eps similarity index 100% rename from Tests/images/pil_sample_cmyk.eps rename to Tests/images/eps/pil_sample_cmyk.eps diff --git a/Tests/images/reqd_showpage.eps b/Tests/images/eps/reqd_showpage.eps similarity index 100% rename from Tests/images/reqd_showpage.eps rename to Tests/images/eps/reqd_showpage.eps diff --git a/Tests/images/reqd_showpage.png b/Tests/images/eps/reqd_showpage.png similarity index 100% rename from Tests/images/reqd_showpage.png rename to Tests/images/eps/reqd_showpage.png diff --git a/Tests/images/reqd_showpage_transparency.png b/Tests/images/eps/reqd_showpage_transparency.png similarity index 100% rename from Tests/images/reqd_showpage_transparency.png rename to Tests/images/eps/reqd_showpage_transparency.png diff --git a/Tests/images/timeout-d675703545fee17acab56e5fec644c19979175de.eps b/Tests/images/eps/timeout-d675703545fee17acab56e5fec644c19979175de.eps similarity index 100% rename from Tests/images/timeout-d675703545fee17acab56e5fec644c19979175de.eps rename to Tests/images/eps/timeout-d675703545fee17acab56e5fec644c19979175de.eps diff --git a/Tests/images/zero_bb.eps b/Tests/images/eps/zero_bb.eps similarity index 100% rename from Tests/images/zero_bb.eps rename to Tests/images/eps/zero_bb.eps diff --git a/Tests/images/zero_bb.png b/Tests/images/eps/zero_bb.png similarity index 100% rename from Tests/images/zero_bb.png rename to Tests/images/eps/zero_bb.png diff --git a/Tests/images/zero_bb_emptyline.eps b/Tests/images/eps/zero_bb_emptyline.eps similarity index 100% rename from Tests/images/zero_bb_emptyline.eps rename to Tests/images/eps/zero_bb_emptyline.eps diff --git a/Tests/images/zero_bb_eof_before_boundingbox.eps b/Tests/images/eps/zero_bb_eof_before_boundingbox.eps similarity index 100% rename from Tests/images/zero_bb_eof_before_boundingbox.eps rename to Tests/images/eps/zero_bb_eof_before_boundingbox.eps diff --git a/Tests/images/zero_bb_scale2.png b/Tests/images/eps/zero_bb_scale2.png similarity index 100% rename from Tests/images/zero_bb_scale2.png rename to Tests/images/eps/zero_bb_scale2.png diff --git a/Tests/images/zero_bb_trailer.eps b/Tests/images/eps/zero_bb_trailer.eps similarity index 100% rename from Tests/images/zero_bb_trailer.eps rename to Tests/images/eps/zero_bb_trailer.eps diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index d54deb515..89471fb5a 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -19,18 +19,18 @@ from .helper import ( HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript() # Our two EPS test files (they are identical except for their bounding boxes) -FILE1 = "Tests/images/zero_bb.eps" -FILE2 = "Tests/images/non_zero_bb.eps" +FILE1 = "Tests/images/eps/zero_bb.eps" +FILE2 = "Tests/images/eps/non_zero_bb.eps" # Due to palletization, we'll need to convert these to RGB after load -FILE1_COMPARE = "Tests/images/zero_bb.png" -FILE1_COMPARE_SCALE2 = "Tests/images/zero_bb_scale2.png" +FILE1_COMPARE = "Tests/images/eps/zero_bb.png" +FILE1_COMPARE_SCALE2 = "Tests/images/eps/zero_bb_scale2.png" -FILE2_COMPARE = "Tests/images/non_zero_bb.png" -FILE2_COMPARE_SCALE2 = "Tests/images/non_zero_bb_scale2.png" +FILE2_COMPARE = "Tests/images/eps/non_zero_bb.png" +FILE2_COMPARE_SCALE2 = "Tests/images/eps/non_zero_bb_scale2.png" # 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: # 0xC6D3D0C5 magic number @@ -187,7 +187,7 @@ def test_load_long_binary_data(prefix: bytes) -> None: ) @pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available") 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.size == (100, 100) assert cmyk_image.format == "EPS" @@ -204,8 +204,8 @@ def test_cmyk() -> None: @pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available") def test_showpage() -> None: # 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/reqd_showpage.png") as target: + with Image.open("Tests/images/eps/reqd_showpage.eps") as plot_image: + with Image.open("Tests/images/eps/reqd_showpage.png") as target: # should not crash/hang plot_image.load() # fonts could be slightly different @@ -214,11 +214,11 @@ 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: + with Image.open("Tests/images/eps/reqd_showpage.eps") as plot_image: plot_image.load(transparency=True) 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 assert_image_similar(plot_image, target, 6) @@ -246,7 +246,7 @@ def test_bytesio_object() -> None: def test_1_mode() -> None: - with Image.open("Tests/images/1.eps") as im: + with Image.open("Tests/images/eps/1.eps") as im: assert im.mode == "1" @@ -302,7 +302,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")) +@pytest.mark.parametrize( + "filename", (FILE1, FILE2, "Tests/images/eps/illu10_preview.eps") +) def test_resize(filename: str) -> None: with Image.open(filename) as im: new_size = (100, 100) @@ -344,10 +346,10 @@ def test_readline(prefix: bytes, line_ending: bytes) -> None: @pytest.mark.parametrize( "filename", ( - "Tests/images/illu10_no_preview.eps", - "Tests/images/illu10_preview.eps", - "Tests/images/illuCS6_no_preview.eps", - "Tests/images/illuCS6_preview.eps", + "Tests/images/eps/illu10_no_preview.eps", + "Tests/images/eps/illu10_preview.eps", + "Tests/images/eps/illuCS6_no_preview.eps", + "Tests/images/eps/illuCS6_preview.eps", ), ) def test_open_eps(filename: str) -> None: @@ -359,7 +361,7 @@ def test_open_eps(filename: str) -> None: @pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available") def test_emptyline() -> None: # 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: image.load() @@ -371,7 +373,7 @@ def test_emptyline() -> None: @pytest.mark.timeout(timeout=5) @pytest.mark.parametrize( "test_file", - ["Tests/images/timeout-d675703545fee17acab56e5fec644c19979175de.eps"], + ["Tests/images/eps/timeout-d675703545fee17acab56e5fec644c19979175de.eps"], ) def test_timeout(test_file: str) -> None: with open(test_file, "rb") as f: @@ -384,7 +386,7 @@ def test_bounding_box_in_trailer() -> None: # Check bounding boxes are parsed in the same way # when specified in the header and the trailer 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, ): assert trailer_image.size == header_image.size @@ -392,12 +394,12 @@ def test_bounding_box_in_trailer() -> None: def test_eof_before_bounding_box() -> None: 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 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)) with Image.open(img_bytes) as img: diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index be143e9c6..d250ba369 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -56,10 +56,10 @@ def helper_pickle_string(protocol: int, test_file: str, mode: str | None) -> Non ), ("Tests/images/hopper.tif", None), ("Tests/images/test-card.png", None), - ("Tests/images/zero_bb.png", None), - ("Tests/images/zero_bb_scale2.png", None), - ("Tests/images/non_zero_bb.png", None), - ("Tests/images/non_zero_bb_scale2.png", None), + ("Tests/images/eps/zero_bb.png", None), + ("Tests/images/eps/zero_bb_scale2.png", None), + ("Tests/images/eps/non_zero_bb.png", None), + ("Tests/images/eps/non_zero_bb_scale2.png", None), ("Tests/images/p_trns_single.png", None), ("Tests/images/pil123p.png", None), ("Tests/images/itxt_chunks.png", None), From 283b41afa033c64ce5baf9643356599240ff6367 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 3 Aug 2024 11:47:34 -0500 Subject: [PATCH 02/14] test 1.eps size and data --- Tests/images/eps/1.bmp | Bin 0 -> 1202 bytes Tests/test_file_eps.py | 6 ++++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Tests/images/eps/1.bmp diff --git a/Tests/images/eps/1.bmp b/Tests/images/eps/1.bmp new file mode 100644 index 0000000000000000000000000000000000000000..194c85784c51ac3e30ea62381102eeca429fe63e GIT binary patch literal 1202 zcmZ|PZERCj7zgnGIlJA?O3~Y{V_O;Vu5@!VzEn35gb%0fWo&GkZt!8mC3a&%L`cAw z1RcgpH?TFE>7*Jn&33my7z%Mgqr{h_4ieBQ8H?zQi7CbpMh&1W;e#yoIc59k={>jS z_dL(ZxjpBm;m6Nt$hSD%_fYSl9-+?Yqgrq=uf}|_1G%M3g>&J}?a!;hVv|b)`u@Lh zz9g5+AzgDm72)2kpOgz`P;_7dVXhsT9<6leYq;=)k=*&2tGlEmND;rgC2p1%Ze9D* zRH-TmYa(_)9A05`1VXA5z^^6B(}_LI(p%m zVoCNl6^sjC`zujrc9qK8kPfYR;$)yQtB4Fu=`8rwbK`hwmn1tehLNB%J+=N#wH+w7 zMTy?A{~9Z<>etENz$n8bT6*l(I>RP2Lqy82W$O9#Ss3IM7CX&fJ$i{HSBHr3OIWe% zK41G;bD6RWiDZq-SND41zGKQCjk&uj%zrM*i9~}kEWNAh#d|LItgL-sQB={bbM5ua zTW^S{vd+ZkBJxFlKtv@OhHhMCqq!a2UOLJCHul72(i!K6KPQHs%PIr9Bf>{-ub1y;2om(3L|qHsOC@$nWg&jA+QaVd?4R8m z%^Tb4AfE31VECZQOK5V9zIs>oPxRVkhPr_r=6!$s_)~~alEHY&m(Wk$FhhO8yftC| z((=TW-%c`18C-taQuJ?8Vrax73A#K7!w&^MKKGy?1Of`JKlZN2vRbA$`Tu<0-`3Evk+i91Sz` zEkRiIMCf)wlbvC$63naCqw{gKkF9Z9IA+;oX&_W~YFRbqYKe$LIDGFF_{q9*Y@i!; z&fzXX-jYGzkKu%bNaq6(JORy1rFlB&kKw7{Dvj(=^Mlmm=$Fz$g>n|bBNpv4H9bEe z8L|Z4F-2Ij3<_rG5d}w;VX3nK+73sxIiP{!w3R@Y1-%1(l~Jmb None: assert_image_similar(img, image1_scale1_compare, 5) -def test_1_mode() -> None: +@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available") +def test_1() -> None: with Image.open("Tests/images/eps/1.eps") as im: - assert im.mode == "1" + assert_image_equal_tofile(im, "Tests/images/eps/1.bmp") def test_image_mode_not_supported(tmp_path: Path) -> None: From 3ccecd91cea6625612ef7e05d485058a7783bf07 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 3 Aug 2024 18:04:05 -0500 Subject: [PATCH 03/14] convert eps using pnmraw instead of ppmraw This lets Ghostscript choose the best device to use (pbmraw, pgmraw, ppmraw) based on the image data. --- Tests/test_file_eps.py | 2 +- src/PIL/EpsImagePlugin.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index f0f235f81..bca516a78 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -178,7 +178,7 @@ def test_load_long_binary_data(prefix: bytes) -> None: data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_binary_data)) with Image.open(data) as img: img.load() - assert img.mode == "RGB" + assert img.mode == "1" assert img.size == (100, 100) assert img.format == "EPS" diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index dd6ae4a77..f6c1ea2a5 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -121,7 +121,13 @@ def Ghostscript( lengthfile -= len(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 command = [ From 00bbd4a5b99447aad9def172a05ab768fe428dd6 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sun, 4 Aug 2024 00:48:12 -0500 Subject: [PATCH 04/14] use "with Image" instead of closing manually --- src/PIL/EpsImagePlugin.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index f6c1ea2a5..cbf48de18 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -157,8 +157,9 @@ def Ghostscript( startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW subprocess.check_call(command, startupinfo=startupinfo) - out_im = Image.open(outfile) - out_im.load() + with Image.open(outfile) as out_im: + out_im.load() + return out_im.im.copy() finally: try: os.unlink(outfile) @@ -167,10 +168,6 @@ def Ghostscript( except OSError: pass - im = out_im.im.copy() - out_im.close() - return im - def _accept(prefix: bytes) -> bool: return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5) From 6b168a3e2bafe1c957de8e9705ec9a17dd62fd84 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Tue, 6 Aug 2024 21:02:46 -0500 Subject: [PATCH 05/14] add EPS test for image with ImageData and BoundingBox (atend) --- Tests/images/eps/1_atend.eps | Bin 0 -> 45867 bytes Tests/test_file_eps.py | 40 +++++++++++++-------- src/PIL/EpsImagePlugin.py | 65 ++++++++++++++++++++++------------- 3 files changed, 66 insertions(+), 39 deletions(-) create mode 100644 Tests/images/eps/1_atend.eps diff --git a/Tests/images/eps/1_atend.eps b/Tests/images/eps/1_atend.eps new file mode 100644 index 0000000000000000000000000000000000000000..08f8c4681a90908a2ca3d7695d19f150a5787816 GIT binary patch literal 45867 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 None: 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)) def test_missing_version_comment(prefix: bytes) -> None: data = io.BytesIO(prefix + b"\n".join(simple_eps_file_without_version)) @@ -142,23 +151,19 @@ def test_missing_boundingbox_comment(prefix: bytes) -> None: @pytest.mark.parametrize("prefix", (b"", simple_binary_header)) -def test_invalid_boundingbox_comment(prefix: bytes) -> None: - data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox)) +@pytest.mark.parametrize( + "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: list[bytes]) -> None: + data = io.BytesIO(prefix + b"\n".join(file_lines)) with pytest.raises(OSError, match="cannot determine EPS bounding box"): 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)) def test_ascii_comment_too_long(prefix: bytes) -> None: data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_ascii_comment)) @@ -247,8 +252,13 @@ def test_bytesio_object() -> None: @pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available") -def test_1() -> None: - with Image.open("Tests/images/eps/1.eps") as im: +@pytest.mark.parametrize( + # These images have an "ImageData" descriptor. + "filename", + ("Tests/images/eps/1.eps", "Tests/images/eps/1_atend.eps"), +) +def test_1(filename: str) -> None: + with Image.open(filename) as im: assert_image_equal_tofile(im, "Tests/images/eps/1.bmp") diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index cbf48de18..ce8e54908 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -194,6 +194,11 @@ class EpsImageFile(ImageFile.ImageFile): 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) bytes_mv = memoryview(byte_arr) bytes_read = 0 @@ -214,8 +219,8 @@ class EpsImageFile(ImageFile.ImageFile): msg = 'EPS header missing "%%BoundingBox" comment' raise SyntaxError(msg) - def _read_comment(s: str) -> bool: - nonlocal reading_trailer_comments + def read_comment(s: str) -> bool: + nonlocal bounding_box, reading_trailer_comments try: m = split.match(s) except re.error as e: @@ -230,18 +235,12 @@ class EpsImageFile(ImageFile.ImageFile): if k == "BoundingBox": if v == "(atend)": 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: # Note: The DSC spec says that BoundingBox # fields should be integers, but some drivers # put floating point values there anyway. - 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) - ) - ] + bounding_box = [int(float(i)) for i in v.split()] except Exception: pass return True @@ -292,7 +291,7 @@ class EpsImageFile(ImageFile.ImageFile): continue s = str(bytes_mv[:bytes_read], "latin-1") - if not _read_comment(s): + if not read_comment(s): m = field.match(s) if m: k = m.group(1) @@ -326,32 +325,50 @@ class EpsImageFile(ImageFile.ImageFile): int(value) for value in image_data_values[:4] ) - if bit_depth == 1: - self._mode = "1" - elif bit_depth == 8: - try: - self._mode = self.mode_map[mode_id] - except ValueError: - break - else: - break + if not imagedata_size: + imagedata_size = columns, rows - self._size = columns, rows - return + if bit_depth == 1: + self._mode = "1" + elif bit_depth == 8: + try: + self._mode = self.mode_map[mode_id] + except ValueError: + pass elif bytes_mv[:5] == b"%%EOF": break elif trailer_reached and reading_trailer_comments: # Load EPS trailer s = str(bytes_mv[:bytes_read], "latin-1") - _read_comment(s) + read_comment(s) elif bytes_mv[:9] == b"%%Trailer": trailer_reached = True 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" raise OSError(msg) + # An "ImageData" size takes precedence over the "BoundingBox". + if imagedata_size: + self._size = imagedata_size + else: + self._size = ( + bounding_box[2] - bounding_box[0], + bounding_box[3] - bounding_box[1], + ) + + self.tile = [ + ImageFile._Tile( + codec_name="eps", + extents=(0, 0) + self._size, + offset=offset, + args=(length, bounding_box), + ) + ] + def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]: s = fp.read(4) From 56e4ad0dea6bcfcfa5ce8e0af07a512fd7f5a392 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Mon, 19 Aug 2024 19:24:30 -0500 Subject: [PATCH 06/14] don't name positional arguments --- src/PIL/EpsImagePlugin.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index ce8e54908..44391fcb1 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -361,12 +361,7 @@ class EpsImageFile(ImageFile.ImageFile): ) self.tile = [ - ImageFile._Tile( - codec_name="eps", - extents=(0, 0) + self._size, - offset=offset, - args=(length, bounding_box), - ) + ImageFile._Tile("eps", (0, 0) + self._size, offset, (length, bounding_box)) ] def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]: From f3fe22d2f2345ed0e3eed9dc89a0cd1df728af9e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 3 Sep 2024 23:22:35 +1000 Subject: [PATCH 07/14] Break if the bit depth or mode id are unknown --- src/PIL/EpsImagePlugin.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 44391fcb1..c7b1f2164 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -320,21 +320,26 @@ class EpsImageFile(ImageFile.ImageFile): # binary/ascii (1: binary, 2: ascii) # data start identifier (the image data follows after a single line # consisting only of this quoted value) + if imagedata_size: + bytes_read = 0 + continue + image_data_values = byte_arr[11:bytes_read].split(None, 7) columns, rows, bit_depth, mode_id = ( int(value) for value in image_data_values[:4] ) - if not imagedata_size: - imagedata_size = columns, rows + if bit_depth == 1: + self._mode = "1" + elif bit_depth == 8: + try: + self._mode = self.mode_map[mode_id] + except ValueError: + break + else: + break - if bit_depth == 1: - self._mode = "1" - elif bit_depth == 8: - try: - self._mode = self.mode_map[mode_id] - except ValueError: - pass + imagedata_size = columns, rows elif bytes_mv[:5] == b"%%EOF": break elif trailer_reached and reading_trailer_comments: From 75286a4e408f363ee8befc36d0ba082fa25fbd32 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Tue, 3 Sep 2024 09:03:37 -0500 Subject: [PATCH 08/14] add some comments --- src/PIL/EpsImagePlugin.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index c7b1f2164..da0bf3153 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -310,6 +310,12 @@ class EpsImageFile(ImageFile.ImageFile): # Check for an "ImageData" descriptor # 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: # columns # rows @@ -320,10 +326,6 @@ class EpsImageFile(ImageFile.ImageFile): # binary/ascii (1: binary, 2: ascii) # data start identifier (the image data follows after a single line # consisting only of this quoted value) - if imagedata_size: - bytes_read = 0 - continue - image_data_values = byte_arr[11:bytes_read].split(None, 7) columns, rows, bit_depth, mode_id = ( int(value) for value in image_data_values[:4] @@ -339,6 +341,8 @@ class EpsImageFile(ImageFile.ImageFile): else: break + # Read the columns and rows after checking the bit depth and mode + # in case the bit depth and/or mode are invalid. imagedata_size = columns, rows elif bytes_mv[:5] == b"%%EOF": break From 782f0e8a5a8e61073ea2686e722e0575bbe09b6b Mon Sep 17 00:00:00 2001 From: Yay295 Date: Tue, 10 Sep 2024 08:31:01 -0500 Subject: [PATCH 09/14] change "Read" to "Parse" in comment Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/EpsImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index da0bf3153..1c1b6e0b5 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -341,7 +341,7 @@ class EpsImageFile(ImageFile.ImageFile): else: break - # Read the columns and rows after checking the bit depth and mode + # Parse the columns and rows after checking the bit depth and mode # in case the bit depth and/or mode are invalid. imagedata_size = columns, rows elif bytes_mv[:5] == b"%%EOF": From c0d04e8b34f759797e45bccac74f49bddfaa7a41 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Tue, 1 Oct 2024 09:33:33 -0500 Subject: [PATCH 10/14] use .size instead of ._size Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/EpsImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 1c1b6e0b5..27582f2a8 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -370,7 +370,7 @@ class EpsImageFile(ImageFile.ImageFile): ) self.tile = [ - ImageFile._Tile("eps", (0, 0) + self._size, offset, (length, bounding_box)) + ImageFile._Tile("eps", (0, 0) + self.size, offset, (length, bounding_box)) ] def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]: From f9c69deaae2e029601dddba48268fadf630a18eb Mon Sep 17 00:00:00 2001 From: Yay295 Date: Tue, 1 Oct 2024 09:35:22 -0500 Subject: [PATCH 11/14] simplify setting self._size Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/EpsImagePlugin.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 27582f2a8..fb1e301c0 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -361,13 +361,10 @@ class EpsImageFile(ImageFile.ImageFile): raise OSError(msg) # An "ImageData" size takes precedence over the "BoundingBox". - if imagedata_size: - self._size = imagedata_size - else: - self._size = ( - bounding_box[2] - bounding_box[0], - bounding_box[3] - bounding_box[1], - ) + 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)) From 8e6d518ea89035077fb4a855f61f3b5bab26a9c4 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 5 Oct 2024 08:05:00 -0500 Subject: [PATCH 12/14] change parameter type from list to tuple Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_file_eps.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 94ab5d327..7ae0900fd 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -158,7 +158,9 @@ def test_missing_boundingbox_comment(prefix: bytes) -> None: simple_eps_file_with_invalid_boundingbox_valid_imagedata, ), ) -def test_invalid_boundingbox_comment(prefix: bytes, file_lines: list[bytes]) -> None: +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"): EpsImagePlugin.EpsImageFile(data) From d4fedc852c2a78b9dc93b67f93d6211f7337ca43 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 5 Oct 2024 15:21:42 +1000 Subject: [PATCH 13/14] Rename test image --- ..._atend.eps => 1_boundingbox_after_imagedata.eps} | Bin Tests/test_file_eps.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Tests/images/eps/{1_atend.eps => 1_boundingbox_after_imagedata.eps} (100%) diff --git a/Tests/images/eps/1_atend.eps b/Tests/images/eps/1_boundingbox_after_imagedata.eps similarity index 100% rename from Tests/images/eps/1_atend.eps rename to Tests/images/eps/1_boundingbox_after_imagedata.eps diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 7ae0900fd..0a69ee6e5 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -257,7 +257,7 @@ def test_bytesio_object() -> None: @pytest.mark.parametrize( # These images have an "ImageData" descriptor. "filename", - ("Tests/images/eps/1.eps", "Tests/images/eps/1_atend.eps"), + ("Tests/images/eps/1.eps", "Tests/images/eps/1_boundingbox_after_imagedata.eps"), ) def test_1(filename: str) -> None: with Image.open(filename) as im: From 1b57b32caf99f7fb5b9c6b83e439832f5a55dc83 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 6 Oct 2024 08:02:17 +1100 Subject: [PATCH 14/14] Test ignoring second ImageData --- Tests/images/eps/1_second_imagedata.eps | Bin 0 -> 45834 bytes Tests/test_file_eps.py | 6 +++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Tests/images/eps/1_second_imagedata.eps diff --git a/Tests/images/eps/1_second_imagedata.eps b/Tests/images/eps/1_second_imagedata.eps new file mode 100644 index 0000000000000000000000000000000000000000..e6309a3b4ede316d83a2bb84388f1d29140b433f GIT binary patch literal 45834 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 None: @pytest.mark.parametrize( # These images have an "ImageData" descriptor. "filename", - ("Tests/images/eps/1.eps", "Tests/images/eps/1_boundingbox_after_imagedata.eps"), + ( + "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: