diff --git a/Tests/images/hopper_long_name.im b/Tests/images/hopper_long_name.im new file mode 100644 index 00000000000..ff45b7c7539 Binary files /dev/null and b/Tests/images/hopper_long_name.im differ diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index b3a01b95a07..8bb58794c00 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -1,128 +1,139 @@ import io +import pytest from PIL import BmpImagePlugin, Image -from .helper import PillowTestCase, assert_image_equal, hopper +from .helper import assert_image_equal, hopper -class TestFileBmp(PillowTestCase): - def roundtrip(self, im): - outfile = self.tempfile("temp.bmp") +def test_sanity(tmp_path): + def roundtrip(im): + outfile = str(tmp_path / "temp.bmp") im.save(outfile, "BMP") with Image.open(outfile) as reloaded: reloaded.load() - self.assertEqual(im.mode, reloaded.mode) - self.assertEqual(im.size, reloaded.size) - self.assertEqual(reloaded.format, "BMP") - self.assertEqual(reloaded.get_format_mimetype(), "image/bmp") - - def test_sanity(self): - self.roundtrip(hopper()) - - self.roundtrip(hopper("1")) - self.roundtrip(hopper("L")) - self.roundtrip(hopper("P")) - self.roundtrip(hopper("RGB")) - - def test_invalid_file(self): - with open("Tests/images/flower.jpg", "rb") as fp: - self.assertRaises(SyntaxError, BmpImagePlugin.BmpImageFile, fp) - - def test_save_to_bytes(self): - output = io.BytesIO() - im = hopper() - im.save(output, "BMP") - - output.seek(0) - with Image.open(output) as reloaded: - self.assertEqual(im.mode, reloaded.mode) - self.assertEqual(im.size, reloaded.size) - self.assertEqual(reloaded.format, "BMP") - - def test_save_too_large(self): - outfile = self.tempfile("temp.bmp") - with Image.new("RGB", (1, 1)) as im: - im._size = (37838, 37838) - with self.assertRaises(ValueError): - im.save(outfile) - - def test_dpi(self): - dpi = (72, 72) - - output = io.BytesIO() - with hopper() as im: - im.save(output, "BMP", dpi=dpi) - - output.seek(0) - with Image.open(output) as reloaded: - self.assertEqual(reloaded.info["dpi"], dpi) - - def test_save_bmp_with_dpi(self): - # Test for #1301 - # Arrange - outfile = self.tempfile("temp.jpg") - with Image.open("Tests/images/hopper.bmp") as im: - - # Act - im.save(outfile, "JPEG", dpi=im.info["dpi"]) - - # Assert - with Image.open(outfile) as reloaded: - reloaded.load() - self.assertEqual(im.info["dpi"], reloaded.info["dpi"]) - self.assertEqual(im.size, reloaded.size) - self.assertEqual(reloaded.format, "JPEG") - - def test_load_dpi_rounding(self): - # Round up - with Image.open("Tests/images/hopper.bmp") as im: - self.assertEqual(im.info["dpi"], (96, 96)) - - # Round down - with Image.open("Tests/images/hopper_roundDown.bmp") as im: - self.assertEqual(im.info["dpi"], (72, 72)) - - def test_save_dpi_rounding(self): - outfile = self.tempfile("temp.bmp") - with Image.open("Tests/images/hopper.bmp") as im: - im.save(outfile, dpi=(72.2, 72.2)) - with Image.open(outfile) as reloaded: - self.assertEqual(reloaded.info["dpi"], (72, 72)) - - im.save(outfile, dpi=(72.8, 72.8)) - with Image.open(outfile) as reloaded: - self.assertEqual(reloaded.info["dpi"], (73, 73)) + assert im.mode == reloaded.mode + assert im.size == reloaded.size + assert reloaded.format == "BMP" + assert reloaded.get_format_mimetype() == "image/bmp" + + roundtrip(hopper()) + + roundtrip(hopper("1")) + roundtrip(hopper("L")) + roundtrip(hopper("P")) + roundtrip(hopper("RGB")) + - def test_load_dib(self): - # test for #1293, Imagegrab returning Unsupported Bitfields Format - with Image.open("Tests/images/clipboard.dib") as im: - self.assertEqual(im.format, "DIB") - self.assertEqual(im.get_format_mimetype(), "image/bmp") +def test_invalid_file(): + with open("Tests/images/flower.jpg", "rb") as fp: + with pytest.raises(SyntaxError): + BmpImagePlugin.BmpImageFile(fp) - with Image.open("Tests/images/clipboard_target.png") as target: - assert_image_equal(im, target) - def test_save_dib(self): - outfile = self.tempfile("temp.dib") +def test_save_to_bytes(): + output = io.BytesIO() + im = hopper() + im.save(output, "BMP") - with Image.open("Tests/images/clipboard.dib") as im: + output.seek(0) + with Image.open(output) as reloaded: + assert im.mode == reloaded.mode + assert im.size == reloaded.size + assert reloaded.format == "BMP" + + +def test_save_too_large(tmp_path): + outfile = str(tmp_path / "temp.bmp") + with Image.new("RGB", (1, 1)) as im: + im._size = (37838, 37838) + with pytest.raises(ValueError): im.save(outfile) - with Image.open(outfile) as reloaded: - self.assertEqual(reloaded.format, "DIB") - self.assertEqual(reloaded.get_format_mimetype(), "image/bmp") - assert_image_equal(im, reloaded) - def test_rgba_bitfields(self): - # This test image has been manually hexedited - # to change the bitfield compression in the header from XBGR to RGBA - with Image.open("Tests/images/rgb32bf-rgba.bmp") as im: +def test_dpi(): + dpi = (72, 72) + + output = io.BytesIO() + with hopper() as im: + im.save(output, "BMP", dpi=dpi) + + output.seek(0) + with Image.open(output) as reloaded: + assert reloaded.info["dpi"] == dpi + + +def test_save_bmp_with_dpi(tmp_path): + # Test for #1301 + # Arrange + outfile = str(tmp_path / "temp.jpg") + with Image.open("Tests/images/hopper.bmp") as im: + + # Act + im.save(outfile, "JPEG", dpi=im.info["dpi"]) + + # Assert + with Image.open(outfile) as reloaded: + reloaded.load() + assert im.info["dpi"] == reloaded.info["dpi"] + assert im.size == reloaded.size + assert reloaded.format == "JPEG" + + +def test_load_dpi_rounding(): + # Round up + with Image.open("Tests/images/hopper.bmp") as im: + assert im.info["dpi"] == (96, 96) - # So before the comparing the image, swap the channels - b, g, r = im.split()[1:] - im = Image.merge("RGB", (r, g, b)) + # Round down + with Image.open("Tests/images/hopper_roundDown.bmp") as im: + assert im.info["dpi"] == (72, 72) - with Image.open("Tests/images/bmp/q/rgb32bf-xbgr.bmp") as target: + +def test_save_dpi_rounding(tmp_path): + outfile = str(tmp_path / "temp.bmp") + with Image.open("Tests/images/hopper.bmp") as im: + im.save(outfile, dpi=(72.2, 72.2)) + with Image.open(outfile) as reloaded: + assert reloaded.info["dpi"] == (72, 72) + + im.save(outfile, dpi=(72.8, 72.8)) + with Image.open(outfile) as reloaded: + assert reloaded.info["dpi"] == (73, 73) + + +def test_load_dib(): + # test for #1293, Imagegrab returning Unsupported Bitfields Format + with Image.open("Tests/images/clipboard.dib") as im: + assert im.format == "DIB" + assert im.get_format_mimetype() == "image/bmp" + + with Image.open("Tests/images/clipboard_target.png") as target: assert_image_equal(im, target) + + +def test_save_dib(tmp_path): + outfile = str(tmp_path / "temp.dib") + + with Image.open("Tests/images/clipboard.dib") as im: + im.save(outfile) + + with Image.open(outfile) as reloaded: + assert reloaded.format == "DIB" + assert reloaded.get_format_mimetype() == "image/bmp" + assert_image_equal(im, reloaded) + + +def test_rgba_bitfields(): + # This test image has been manually hexedited + # to change the bitfield compression in the header from XBGR to RGBA + with Image.open("Tests/images/rgb32bf-rgba.bmp") as im: + + # So before the comparing the image, swap the channels + b, g, r = im.split()[1:] + im = Image.merge("RGB", (r, g, b)) + + with Image.open("Tests/images/bmp/q/rgb32bf-xbgr.bmp") as target: + assert_image_equal(im, target) diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index ec046020ed3..30a9fd52a07 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -1,89 +1,110 @@ -import unittest +import filecmp import pytest from PIL import Image, ImImagePlugin -from .helper import PillowTestCase, assert_image_equal, hopper, is_pypy +from .helper import assert_image_equal, hopper, is_pypy # sample im TEST_IM = "Tests/images/hopper.im" -class TestFileIm(PillowTestCase): - def test_sanity(self): +def test_sanity(): + with Image.open(TEST_IM) as im: + im.load() + assert im.mode == "RGB" + assert im.size == (128, 128) + assert im.format == "IM" + + +def test_name_limit(tmp_path): + out = str(tmp_path / ("name_limit_test" * 7 + ".im")) + with Image.open(TEST_IM) as im: + im.save(out) + assert filecmp.cmp(out, "Tests/images/hopper_long_name.im") + + +@pytest.mark.skipif(is_pypy(), reason="Requires CPython") +def test_unclosed_file(): + def open(): + im = Image.open(TEST_IM) + im.load() + + pytest.warns(ResourceWarning, open) + + +def test_closed_file(): + def open(): + im = Image.open(TEST_IM) + im.load() + im.close() + + pytest.warns(None, open) + + +def test_context_manager(): + def open(): with Image.open(TEST_IM) as im: im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "IM") - - @unittest.skipIf(is_pypy(), "Requires CPython") - def test_unclosed_file(self): - def open(): - im = Image.open(TEST_IM) - im.load() - pytest.warns(ResourceWarning, open) + pytest.warns(None, open) - def test_closed_file(self): - def open(): - im = Image.open(TEST_IM) - im.load() - im.close() - pytest.warns(None, open) +def test_tell(): + # Arrange + with Image.open(TEST_IM) as im: - def test_context_manager(self): - def open(): - with Image.open(TEST_IM) as im: - im.load() + # Act + frame = im.tell() - pytest.warns(None, open) + # Assert + assert frame == 0 - def test_tell(self): - # Arrange - with Image.open(TEST_IM) as im: - # Act - frame = im.tell() +def test_n_frames(): + with Image.open(TEST_IM) as im: + assert im.n_frames == 1 + assert not im.is_animated - # Assert - self.assertEqual(frame, 0) - def test_n_frames(self): - with Image.open(TEST_IM) as im: - self.assertEqual(im.n_frames, 1) - self.assertFalse(im.is_animated) +def test_eoferror(): + with Image.open(TEST_IM) as im: + n_frames = im.n_frames - def test_eoferror(self): - with Image.open(TEST_IM) as im: - n_frames = im.n_frames + # Test seeking past the last frame + with pytest.raises(EOFError): + im.seek(n_frames) + assert im.tell() < n_frames + + # Test that seeking to the last frame does not raise an error + im.seek(n_frames - 1) + + +def test_roundtrip(tmp_path): + def roundtrip(mode): + out = str(tmp_path / "temp.im") + im = hopper(mode) + im.save(out) + with Image.open(out) as reread: + assert_image_equal(reread, im) - # Test seeking past the last frame - self.assertRaises(EOFError, im.seek, n_frames) - self.assertLess(im.tell(), n_frames) + for mode in ["RGB", "P", "PA"]: + roundtrip(mode) - # Test that seeking to the last frame does not raise an error - im.seek(n_frames - 1) - def test_roundtrip(self): - for mode in ["RGB", "P", "PA"]: - out = self.tempfile("temp.im") - im = hopper(mode) - im.save(out) - with Image.open(out) as reread: +def test_save_unsupported_mode(tmp_path): + out = str(tmp_path / "temp.im") + im = hopper("HSV") + with pytest.raises(ValueError): + im.save(out) - assert_image_equal(reread, im) - def test_save_unsupported_mode(self): - out = self.tempfile("temp.im") - im = hopper("HSV") - self.assertRaises(ValueError, im.save, out) +def test_invalid_file(): + invalid_file = "Tests/images/flower.jpg" - def test_invalid_file(self): - invalid_file = "Tests/images/flower.jpg" + with pytest.raises(SyntaxError): + ImImagePlugin.ImImageFile(invalid_file) - self.assertRaises(SyntaxError, ImImagePlugin.ImImageFile, invalid_file) - def test_number(self): - self.assertEqual(1.2, ImImagePlugin.number("1.2")) +def test_number(): + assert ImImagePlugin.number("1.2") == 1.2 diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 12c9237f082..8b03f35da8c 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -26,6 +26,7 @@ # +import os import re from . import Image, ImageFile, ImagePalette @@ -347,7 +348,14 @@ def _save(im, fp, filename): fp.write(("Image type: %s image\r\n" % image_type).encode("ascii")) if filename: - fp.write(("Name: %s\r\n" % filename).encode("ascii")) + # Each line must be 100 characters or less, + # or: SyntaxError("not an IM file") + # 8 characters are used for "Name: " and "\r\n" + # Keep just the filename, ditch the potentially overlong path + name, ext = os.path.splitext(os.path.basename(filename)) + name = "".join([name[: 92 - len(ext)], ext]) + + fp.write(("Name: %s\r\n" % name).encode("ascii")) fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode("ascii")) fp.write(("File size (no of images): %d\r\n" % frames).encode("ascii")) if im.mode in ["P", "PA"]: