Pillow

Форк
0
/
test_file_eps.py 
404 строки · 13.7 Кб
1
from __future__ import annotations
2

3
import io
4
from pathlib import Path
5

6
import pytest
7

8
from PIL import EpsImagePlugin, Image, UnidentifiedImageError, features
9

10
from .helper import (
11
    assert_image_similar,
12
    assert_image_similar_tofile,
13
    hopper,
14
    is_win32,
15
    mark_if_feature_version,
16
    skip_unless_feature,
17
)
18

19
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
20

21
# Our two EPS test files (they are identical except for their bounding boxes)
22
FILE1 = "Tests/images/zero_bb.eps"
23
FILE2 = "Tests/images/non_zero_bb.eps"
24

25
# Due to palletization, we'll need to convert these to RGB after load
26
FILE1_COMPARE = "Tests/images/zero_bb.png"
27
FILE1_COMPARE_SCALE2 = "Tests/images/zero_bb_scale2.png"
28

29
FILE2_COMPARE = "Tests/images/non_zero_bb.png"
30
FILE2_COMPARE_SCALE2 = "Tests/images/non_zero_bb_scale2.png"
31

32
# EPS test files with binary preview
33
FILE3 = "Tests/images/binary_preview_map.eps"
34

35
# Three unsigned 32bit little-endian values:
36
#   0xC6D3D0C5 magic number
37
#   byte position of start of postscript section (12)
38
#   byte length of postscript section (0)
39
# this byte length isn't valid, but we don't read it
40
simple_binary_header = b"\xc5\xd0\xd3\xc6\x0c\x00\x00\x00\x00\x00\x00\x00"
41

42
# taken from page 8 of the specification
43
# https://web.archive.org/web/20220120164601/https://www.adobe.com/content/dam/acom/en/devnet/actionscript/articles/5002.EPSF_Spec.pdf
44
simple_eps_file = (
45
    b"%!PS-Adobe-3.0 EPSF-3.0",
46
    b"%%BoundingBox: 5 5 105 105",
47
    b"10 setlinewidth",
48
    b"10 10 moveto",
49
    b"0 90 rlineto 90 0 rlineto 0 -90 rlineto closepath",
50
    b"stroke",
51
)
52
simple_eps_file_with_comments = (
53
    simple_eps_file[:1]
54
    + (
55
        b"%%Comment1: Some Value",
56
        b"%%SecondComment: Another Value",
57
    )
58
    + simple_eps_file[1:]
59
)
60
simple_eps_file_without_version = simple_eps_file[1:]
61
simple_eps_file_without_boundingbox = simple_eps_file[:1] + simple_eps_file[2:]
62
simple_eps_file_with_invalid_boundingbox = (
63
    simple_eps_file[:1] + (b"%%BoundingBox: a b c d",) + simple_eps_file[2:]
64
)
65
simple_eps_file_with_invalid_boundingbox_valid_imagedata = (
66
    simple_eps_file_with_invalid_boundingbox + (b"%ImageData: 100 100 8 3",)
67
)
68
simple_eps_file_with_long_ascii_comment = (
69
    simple_eps_file[:2] + (b"%%Comment: " + b"X" * 300,) + simple_eps_file[2:]
70
)
71
simple_eps_file_with_long_binary_data = (
72
    simple_eps_file[:2]
73
    + (
74
        b"%%BeginBinary: 300",
75
        b"\0" * 300,
76
        b"%%EndBinary",
77
    )
78
    + simple_eps_file[2:]
79
)
80

81

82
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
83
@pytest.mark.parametrize("filename, size", ((FILE1, (460, 352)), (FILE2, (360, 252))))
84
@pytest.mark.parametrize("scale", (1, 2))
85
def test_sanity(filename: str, size: tuple[int, int], scale: int) -> None:
86
    expected_size = tuple(s * scale for s in size)
87
    with Image.open(filename) as image:
88
        image.load(scale=scale)
89
        assert image.mode == "RGB"
90
        assert image.size == expected_size
91
        assert image.format == "EPS"
92

93

94
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
95
def test_load() -> None:
96
    with Image.open(FILE1) as im:
97
        assert im.load()[0, 0] == (255, 255, 255)
98

99
        # Test again now that it has already been loaded once
100
        assert im.load()[0, 0] == (255, 255, 255)
101

102

103
def test_binary() -> None:
104
    if HAS_GHOSTSCRIPT:
105
        assert EpsImagePlugin.gs_binary is not None
106
    else:
107
        assert EpsImagePlugin.gs_binary is False
108

109
    if not is_win32():
110
        assert EpsImagePlugin.gs_windows_binary is None
111
    elif not HAS_GHOSTSCRIPT:
112
        assert EpsImagePlugin.gs_windows_binary is False
113
    else:
114
        assert EpsImagePlugin.gs_windows_binary is not None
115

116

117
def test_invalid_file() -> None:
118
    invalid_file = "Tests/images/flower.jpg"
119
    with pytest.raises(SyntaxError):
120
        EpsImagePlugin.EpsImageFile(invalid_file)
121

122

123
def test_binary_header_only() -> None:
124
    data = io.BytesIO(simple_binary_header)
125
    with pytest.raises(SyntaxError, match='EPS header missing "%!PS-Adobe" comment'):
126
        EpsImagePlugin.EpsImageFile(data)
127

128

129
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
130
def test_missing_version_comment(prefix: bytes) -> None:
131
    data = io.BytesIO(prefix + b"\n".join(simple_eps_file_without_version))
132
    with pytest.raises(SyntaxError):
133
        EpsImagePlugin.EpsImageFile(data)
134

135

136
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
137
def test_missing_boundingbox_comment(prefix: bytes) -> None:
138
    data = io.BytesIO(prefix + b"\n".join(simple_eps_file_without_boundingbox))
139
    with pytest.raises(SyntaxError, match='EPS header missing "%%BoundingBox" comment'):
140
        EpsImagePlugin.EpsImageFile(data)
141

142

143
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
144
def test_invalid_boundingbox_comment(prefix: bytes) -> None:
145
    data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox))
146
    with pytest.raises(OSError, match="cannot determine EPS bounding box"):
147
        EpsImagePlugin.EpsImageFile(data)
148

149

150
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
151
def test_invalid_boundingbox_comment_valid_imagedata_comment(prefix: bytes) -> None:
152
    data = io.BytesIO(
153
        prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox_valid_imagedata)
154
    )
155
    with Image.open(data) as img:
156
        assert img.mode == "RGB"
157
        assert img.size == (100, 100)
158
        assert img.format == "EPS"
159

160

161
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
162
def test_ascii_comment_too_long(prefix: bytes) -> None:
163
    data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_ascii_comment))
164
    with pytest.raises(SyntaxError, match="not an EPS file"):
165
        EpsImagePlugin.EpsImageFile(data)
166

167

168
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
169
def test_long_binary_data(prefix: bytes) -> None:
170
    data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_binary_data))
171
    EpsImagePlugin.EpsImageFile(data)
172

173

174
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
175
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
176
def test_load_long_binary_data(prefix: bytes) -> None:
177
    data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_binary_data))
178
    with Image.open(data) as img:
179
        img.load()
180
        assert img.mode == "RGB"
181
        assert img.size == (100, 100)
182
        assert img.format == "EPS"
183

184

185
@mark_if_feature_version(
186
    pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
187
)
188
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
189
def test_cmyk() -> None:
190
    with Image.open("Tests/images/pil_sample_cmyk.eps") as cmyk_image:
191
        assert cmyk_image.mode == "CMYK"
192
        assert cmyk_image.size == (100, 100)
193
        assert cmyk_image.format == "EPS"
194

195
        cmyk_image.load()
196
        assert cmyk_image.mode == "RGB"
197

198
        if features.check("jpg"):
199
            assert_image_similar_tofile(
200
                cmyk_image, "Tests/images/pil_sample_rgb.jpg", 10
201
            )
202

203

204
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
205
def test_showpage() -> None:
206
    # See https://github.com/python-pillow/Pillow/issues/2615
207
    with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
208
        with Image.open("Tests/images/reqd_showpage.png") as target:
209
            # should not crash/hang
210
            plot_image.load()
211
            # fonts could be slightly different
212
            assert_image_similar(plot_image, target, 6)
213

214

215
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
216
def test_transparency() -> None:
217
    with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
218
        plot_image.load(transparency=True)
219
        assert plot_image.mode == "RGBA"
220

221
        with Image.open("Tests/images/reqd_showpage_transparency.png") as target:
222
            # fonts could be slightly different
223
            assert_image_similar(plot_image, target, 6)
224

225

226
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
227
def test_file_object(tmp_path: Path) -> None:
228
    # issue 479
229
    with Image.open(FILE1) as image1:
230
        with open(str(tmp_path / "temp.eps"), "wb") as fh:
231
            image1.save(fh, "EPS")
232

233

234
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
235
def test_bytesio_object() -> None:
236
    with open(FILE1, "rb") as f:
237
        img_bytes = io.BytesIO(f.read())
238

239
    with Image.open(img_bytes) as img:
240
        img.load()
241

242
        with Image.open(FILE1_COMPARE) as image1_scale1_compare:
243
            image1_scale1_compare = image1_scale1_compare.convert("RGB")
244
        image1_scale1_compare.load()
245
        assert_image_similar(img, image1_scale1_compare, 5)
246

247

248
def test_1_mode() -> None:
249
    with Image.open("Tests/images/1.eps") as im:
250
        assert im.mode == "1"
251

252

253
def test_image_mode_not_supported(tmp_path: Path) -> None:
254
    im = hopper("RGBA")
255
    tmpfile = str(tmp_path / "temp.eps")
256
    with pytest.raises(ValueError):
257
        im.save(tmpfile)
258

259

260
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
261
@skip_unless_feature("zlib")
262
def test_render_scale1() -> None:
263
    # We need png support for these render test
264

265
    # Zero bounding box
266
    with Image.open(FILE1) as image1_scale1:
267
        image1_scale1.load()
268
        with Image.open(FILE1_COMPARE) as image1_scale1_compare:
269
            image1_scale1_compare = image1_scale1_compare.convert("RGB")
270
        image1_scale1_compare.load()
271
        assert_image_similar(image1_scale1, image1_scale1_compare, 5)
272

273
    # Non-zero bounding box
274
    with Image.open(FILE2) as image2_scale1:
275
        image2_scale1.load()
276
        with Image.open(FILE2_COMPARE) as image2_scale1_compare:
277
            image2_scale1_compare = image2_scale1_compare.convert("RGB")
278
        image2_scale1_compare.load()
279
        assert_image_similar(image2_scale1, image2_scale1_compare, 10)
280

281

282
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
283
@skip_unless_feature("zlib")
284
def test_render_scale2() -> None:
285
    # We need png support for these render test
286

287
    # Zero bounding box
288
    with Image.open(FILE1) as image1_scale2:
289
        image1_scale2.load(scale=2)
290
        with Image.open(FILE1_COMPARE_SCALE2) as image1_scale2_compare:
291
            image1_scale2_compare = image1_scale2_compare.convert("RGB")
292
        image1_scale2_compare.load()
293
        assert_image_similar(image1_scale2, image1_scale2_compare, 5)
294

295
    # Non-zero bounding box
296
    with Image.open(FILE2) as image2_scale2:
297
        image2_scale2.load(scale=2)
298
        with Image.open(FILE2_COMPARE_SCALE2) as image2_scale2_compare:
299
            image2_scale2_compare = image2_scale2_compare.convert("RGB")
300
        image2_scale2_compare.load()
301
        assert_image_similar(image2_scale2, image2_scale2_compare, 10)
302

303

304
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
305
@pytest.mark.parametrize("filename", (FILE1, FILE2, "Tests/images/illu10_preview.eps"))
306
def test_resize(filename: str) -> None:
307
    with Image.open(filename) as im:
308
        new_size = (100, 100)
309
        im = im.resize(new_size)
310
        assert im.size == new_size
311

312

313
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
314
@pytest.mark.parametrize("filename", (FILE1, FILE2))
315
def test_thumbnail(filename: str) -> None:
316
    # Issue #619
317
    with Image.open(filename) as im:
318
        new_size = (100, 100)
319
        im.thumbnail(new_size)
320
        assert max(im.size) == max(new_size)
321

322

323
def test_read_binary_preview() -> None:
324
    # Issue 302
325
    # open image with binary preview
326
    with Image.open(FILE3):
327
        pass
328

329

330
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
331
@pytest.mark.parametrize(
332
    "line_ending",
333
    (b"\r\n", b"\n", b"\n\r", b"\r"),
334
)
335
def test_readline(prefix: bytes, line_ending: bytes) -> None:
336
    simple_file = prefix + line_ending.join(simple_eps_file_with_comments)
337
    data = io.BytesIO(simple_file)
338
    test_file = EpsImagePlugin.EpsImageFile(data)
339
    assert test_file.info["Comment1"] == "Some Value"
340
    assert test_file.info["SecondComment"] == "Another Value"
341
    assert test_file.size == (100, 100)
342

343

344
@pytest.mark.parametrize(
345
    "filename",
346
    (
347
        "Tests/images/illu10_no_preview.eps",
348
        "Tests/images/illu10_preview.eps",
349
        "Tests/images/illuCS6_no_preview.eps",
350
        "Tests/images/illuCS6_preview.eps",
351
    ),
352
)
353
def test_open_eps(filename: str) -> None:
354
    # https://github.com/python-pillow/Pillow/issues/1104
355
    with Image.open(filename) as img:
356
        assert img.mode == "RGB"
357

358

359
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
360
def test_emptyline() -> None:
361
    # Test file includes an empty line in the header data
362
    emptyline_file = "Tests/images/zero_bb_emptyline.eps"
363

364
    with Image.open(emptyline_file) as image:
365
        image.load()
366
    assert image.mode == "RGB"
367
    assert image.size == (460, 352)
368
    assert image.format == "EPS"
369

370

371
@pytest.mark.timeout(timeout=5)
372
@pytest.mark.parametrize(
373
    "test_file",
374
    ["Tests/images/timeout-d675703545fee17acab56e5fec644c19979175de.eps"],
375
)
376
def test_timeout(test_file: str) -> None:
377
    with open(test_file, "rb") as f:
378
        with pytest.raises(UnidentifiedImageError):
379
            with Image.open(f):
380
                pass
381

382

383
def test_bounding_box_in_trailer() -> None:
384
    # Check bounding boxes are parsed in the same way
385
    # when specified in the header and the trailer
386
    with (
387
        Image.open("Tests/images/zero_bb_trailer.eps") as trailer_image,
388
        Image.open(FILE1) as header_image,
389
    ):
390
        assert trailer_image.size == header_image.size
391

392

393
def test_eof_before_bounding_box() -> None:
394
    with pytest.raises(OSError):
395
        with Image.open("Tests/images/zero_bb_eof_before_boundingbox.eps"):
396
            pass
397

398

399
def test_invalid_data_after_eof() -> None:
400
    with open("Tests/images/illuCS6_preview.eps", "rb") as f:
401
        img_bytes = io.BytesIO(f.read() + b"\r\n%" + (b" " * 255))
402

403
    with Image.open(img_bytes) as img:
404
        assert img.mode == "RGB"
405

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.