mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-24 17:06:16 +03:00
Merge remote-tracking branch 'upstream/master' into rm-2.7
This commit is contained in:
commit
cc63f66575
2
.github/workflows/macos-install.sh
vendored
2
.github/workflows/macos-install.sh
vendored
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
brew install libtiff libjpeg webp little-cms2
|
brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype
|
||||||
|
|
||||||
PYTHONOPTIMIZE=0 pip install cffi
|
PYTHONOPTIMIZE=0 pip install cffi
|
||||||
pip install coverage
|
pip install coverage
|
||||||
|
|
2
.github/workflows/test-docker.yml
vendored
2
.github/workflows/test-docker.yml
vendored
|
@ -19,8 +19,8 @@ jobs:
|
||||||
centos-7-amd64,
|
centos-7-amd64,
|
||||||
amazon-1-amd64,
|
amazon-1-amd64,
|
||||||
amazon-2-amd64,
|
amazon-2-amd64,
|
||||||
fedora-29-amd64,
|
|
||||||
fedora-30-amd64,
|
fedora-30-amd64,
|
||||||
|
fedora-31-amd64,
|
||||||
]
|
]
|
||||||
dockerTag: [master]
|
dockerTag: [master]
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,8 @@ matrix:
|
||||||
- env: DOCKER="centos-7-amd64" DOCKER_TAG="master"
|
- env: DOCKER="centos-7-amd64" DOCKER_TAG="master"
|
||||||
- env: DOCKER="amazon-1-amd64" DOCKER_TAG="master"
|
- env: DOCKER="amazon-1-amd64" DOCKER_TAG="master"
|
||||||
- env: DOCKER="amazon-2-amd64" DOCKER_TAG="master"
|
- env: DOCKER="amazon-2-amd64" DOCKER_TAG="master"
|
||||||
- env: DOCKER="fedora-29-amd64" DOCKER_TAG="master"
|
|
||||||
- env: DOCKER="fedora-30-amd64" DOCKER_TAG="master"
|
- env: DOCKER="fedora-30-amd64" DOCKER_TAG="master"
|
||||||
|
- env: DOCKER="fedora-31-amd64" DOCKER_TAG="master"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
|
|
|
@ -5,6 +5,9 @@ Changelog (Pillow)
|
||||||
7.0.0 (unreleased)
|
7.0.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Removed CI testing of Fedora 29 #4165
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
- Added pypy3 to tox envlist #4137
|
- Added pypy3 to tox envlist #4137
|
||||||
[jdufresne]
|
[jdufresne]
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ class TestBmpReference(PillowTestCase):
|
||||||
|
|
||||||
def open(f):
|
def open(f):
|
||||||
try:
|
try:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
except Exception: # as msg:
|
except Exception: # as msg:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -46,8 +46,8 @@ class TestBmpReference(PillowTestCase):
|
||||||
]
|
]
|
||||||
for f in self.get_files("q"):
|
for f in self.get_files("q"):
|
||||||
try:
|
try:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
if os.path.basename(f) not in supported:
|
if os.path.basename(f) not in supported:
|
||||||
print("Please add %s to the partially supported bmp specs." % f)
|
print("Please add %s to the partially supported bmp specs." % f)
|
||||||
except Exception: # as msg:
|
except Exception: # as msg:
|
||||||
|
@ -87,17 +87,17 @@ class TestBmpReference(PillowTestCase):
|
||||||
|
|
||||||
for f in self.get_files("g"):
|
for f in self.get_files("g"):
|
||||||
try:
|
try:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
compare = Image.open(get_compare(f))
|
with Image.open(get_compare(f)) as compare:
|
||||||
compare.load()
|
compare.load()
|
||||||
if im.mode == "P":
|
if im.mode == "P":
|
||||||
# assert image similar doesn't really work
|
# assert image similar doesn't really work
|
||||||
# with paletized image, since the palette might
|
# with paletized image, since the palette might
|
||||||
# be differently ordered for an equivalent image.
|
# be differently ordered for an equivalent image.
|
||||||
im = im.convert("RGBA")
|
im = im.convert("RGBA")
|
||||||
compare = im.convert("RGBA")
|
compare = im.convert("RGBA")
|
||||||
self.assert_image_similar(im, compare, 5)
|
self.assert_image_similar(im, compare, 5)
|
||||||
|
|
||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
# there are three here that are unsupported:
|
# there are three here that are unsupported:
|
||||||
|
|
|
@ -14,7 +14,8 @@ class TestDecompressionBomb(PillowTestCase):
|
||||||
def test_no_warning_small_file(self):
|
def test_no_warning_small_file(self):
|
||||||
# Implicit assert: no warning.
|
# Implicit assert: no warning.
|
||||||
# A warning would cause a failure.
|
# A warning would cause a failure.
|
||||||
Image.open(TEST_FILE)
|
with Image.open(TEST_FILE):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_no_warning_no_limit(self):
|
def test_no_warning_no_limit(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -25,21 +26,28 @@ class TestDecompressionBomb(PillowTestCase):
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
# Implicit assert: no warning.
|
# Implicit assert: no warning.
|
||||||
# A warning would cause a failure.
|
# A warning would cause a failure.
|
||||||
Image.open(TEST_FILE)
|
with Image.open(TEST_FILE):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_warning(self):
|
def test_warning(self):
|
||||||
# Set limit to trigger warning on the test file
|
# Set limit to trigger warning on the test file
|
||||||
Image.MAX_IMAGE_PIXELS = 128 * 128 - 1
|
Image.MAX_IMAGE_PIXELS = 128 * 128 - 1
|
||||||
self.assertEqual(Image.MAX_IMAGE_PIXELS, 128 * 128 - 1)
|
self.assertEqual(Image.MAX_IMAGE_PIXELS, 128 * 128 - 1)
|
||||||
|
|
||||||
self.assert_warning(Image.DecompressionBombWarning, Image.open, TEST_FILE)
|
def open():
|
||||||
|
with Image.open(TEST_FILE):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assert_warning(Image.DecompressionBombWarning, open)
|
||||||
|
|
||||||
def test_exception(self):
|
def test_exception(self):
|
||||||
# Set limit to trigger exception on the test file
|
# Set limit to trigger exception on the test file
|
||||||
Image.MAX_IMAGE_PIXELS = 64 * 128 - 1
|
Image.MAX_IMAGE_PIXELS = 64 * 128 - 1
|
||||||
self.assertEqual(Image.MAX_IMAGE_PIXELS, 64 * 128 - 1)
|
self.assertEqual(Image.MAX_IMAGE_PIXELS, 64 * 128 - 1)
|
||||||
|
|
||||||
self.assertRaises(Image.DecompressionBombError, lambda: Image.open(TEST_FILE))
|
with self.assertRaises(Image.DecompressionBombError):
|
||||||
|
with Image.open(TEST_FILE):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_exception_ico(self):
|
def test_exception_ico(self):
|
||||||
with self.assertRaises(Image.DecompressionBombError):
|
with self.assertRaises(Image.DecompressionBombError):
|
||||||
|
@ -53,6 +61,7 @@ class TestDecompressionBomb(PillowTestCase):
|
||||||
class TestDecompressionCrop(PillowTestCase):
|
class TestDecompressionCrop(PillowTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.src = hopper()
|
self.src = hopper()
|
||||||
|
self.addCleanup(self.src.close)
|
||||||
Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width * 4 - 1
|
Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width * 4 - 1
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
|
|
@ -5,14 +5,14 @@ from .helper import PillowTestCase
|
||||||
|
|
||||||
class TestFileBlp(PillowTestCase):
|
class TestFileBlp(PillowTestCase):
|
||||||
def test_load_blp2_raw(self):
|
def test_load_blp2_raw(self):
|
||||||
im = Image.open("Tests/images/blp/blp2_raw.blp")
|
with Image.open("Tests/images/blp/blp2_raw.blp") as im:
|
||||||
target = Image.open("Tests/images/blp/blp2_raw.png")
|
with Image.open("Tests/images/blp/blp2_raw.png") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_load_blp2_dxt1(self):
|
def test_load_blp2_dxt1(self):
|
||||||
im = Image.open("Tests/images/blp/blp2_dxt1.blp")
|
with Image.open("Tests/images/blp/blp2_dxt1.blp") as im:
|
||||||
target = Image.open("Tests/images/blp/blp2_dxt1.png")
|
with Image.open("Tests/images/blp/blp2_dxt1.png") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_load_blp2_dxt1a(self):
|
def test_load_blp2_dxt1a(self):
|
||||||
im = Image.open("Tests/images/blp/blp2_dxt1a.blp")
|
im = Image.open("Tests/images/blp/blp2_dxt1a.blp")
|
||||||
|
|
|
@ -46,13 +46,12 @@ class TestFileBmp(PillowTestCase):
|
||||||
dpi = (72, 72)
|
dpi = (72, 72)
|
||||||
|
|
||||||
output = io.BytesIO()
|
output = io.BytesIO()
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
im.save(output, "BMP", dpi=dpi)
|
im.save(output, "BMP", dpi=dpi)
|
||||||
|
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
reloaded = Image.open(output)
|
with Image.open(output) as reloaded:
|
||||||
|
self.assertEqual(reloaded.info["dpi"], dpi)
|
||||||
self.assertEqual(reloaded.info["dpi"], dpi)
|
|
||||||
|
|
||||||
def test_save_bmp_with_dpi(self):
|
def test_save_bmp_with_dpi(self):
|
||||||
# Test for #1301
|
# Test for #1301
|
||||||
|
@ -72,24 +71,24 @@ class TestFileBmp(PillowTestCase):
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open("Tests/images/hopper.bmp")
|
with Image.open("Tests/images/hopper.bmp") as im:
|
||||||
self.assertEqual(im.info["dpi"], (96, 96))
|
self.assertEqual(im.info["dpi"], (96, 96))
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/hopper_roundDown.bmp")
|
with Image.open("Tests/images/hopper_roundDown.bmp") as im:
|
||||||
self.assertEqual(im.info["dpi"], (72, 72))
|
self.assertEqual(im.info["dpi"], (72, 72))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
outfile = self.tempfile("temp.bmp")
|
outfile = self.tempfile("temp.bmp")
|
||||||
im = Image.open("Tests/images/hopper.bmp")
|
im = Image.open("Tests/images/hopper.bmp")
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.2, 72.2))
|
im.save(outfile, dpi=(72.2, 72.2))
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.8, 72.8))
|
im.save(outfile, dpi=(72.8, 72.8))
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
||||||
|
|
||||||
def test_load_dib(self):
|
def test_load_dib(self):
|
||||||
# test for #1293, Imagegrab returning Unsupported Bitfields Format
|
# test for #1293, Imagegrab returning Unsupported Bitfields Format
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/gfs.t06z.rassda.tm00.bufr_d"
|
||||||
class TestFileBufrStub(PillowTestCase):
|
class TestFileBufrStub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "BUFR")
|
self.assertEqual(im.format, "BUFR")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,10 +28,10 @@ class TestFileBufrStub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -11,8 +11,8 @@ class TestFileContainer(PillowTestCase):
|
||||||
dir(ContainerIO)
|
dir(ContainerIO)
|
||||||
|
|
||||||
def test_isatty(self):
|
def test_isatty(self):
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
container = ContainerIO.ContainerIO(im, 0, 0)
|
container = ContainerIO.ContainerIO(im, 0, 0)
|
||||||
|
|
||||||
self.assertFalse(container.isatty())
|
self.assertFalse(container.isatty())
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import DcxImagePlugin, Image
|
from PIL import DcxImagePlugin, Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
# Created with ImageMagick: convert hopper.ppm hopper.dcx
|
# Created with ImageMagick: convert hopper.ppm hopper.dcx
|
||||||
TEST_FILE = "Tests/images/hopper.dcx"
|
TEST_FILE = "Tests/images/hopper.dcx"
|
||||||
|
@ -11,19 +13,35 @@ class TestFileDcx(PillowTestCase):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertIsInstance(im, DcxImagePlugin.DcxImageFile)
|
self.assertIsInstance(im, DcxImagePlugin.DcxImageFile)
|
||||||
orig = hopper()
|
orig = hopper()
|
||||||
self.assert_image_equal(im, orig)
|
self.assert_image_equal(im, orig)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(TEST_FILE)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(TEST_FILE) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
|
@ -32,34 +50,34 @@ class TestFileDcx(PillowTestCase):
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
frame = im.tell()
|
frame = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(frame, 0)
|
self.assertEqual(frame, 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_seek_too_far(self):
|
def test_seek_too_far(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
frame = 999 # too big on purpose
|
frame = 999 # too big on purpose
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(EOFError, im.seek, frame)
|
self.assertRaises(EOFError, im.seek, frame)
|
||||||
|
|
|
@ -25,30 +25,30 @@ class TestFileEps(PillowTestCase):
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
# Regular scale
|
# Regular scale
|
||||||
image1 = Image.open(file1)
|
with Image.open(file1) as image1:
|
||||||
image1.load()
|
image1.load()
|
||||||
self.assertEqual(image1.mode, "RGB")
|
self.assertEqual(image1.mode, "RGB")
|
||||||
self.assertEqual(image1.size, (460, 352))
|
self.assertEqual(image1.size, (460, 352))
|
||||||
self.assertEqual(image1.format, "EPS")
|
self.assertEqual(image1.format, "EPS")
|
||||||
|
|
||||||
image2 = Image.open(file2)
|
with Image.open(file2) as image2:
|
||||||
image2.load()
|
image2.load()
|
||||||
self.assertEqual(image2.mode, "RGB")
|
self.assertEqual(image2.mode, "RGB")
|
||||||
self.assertEqual(image2.size, (360, 252))
|
self.assertEqual(image2.size, (360, 252))
|
||||||
self.assertEqual(image2.format, "EPS")
|
self.assertEqual(image2.format, "EPS")
|
||||||
|
|
||||||
# Double scale
|
# Double scale
|
||||||
image1_scale2 = Image.open(file1)
|
with Image.open(file1) as image1_scale2:
|
||||||
image1_scale2.load(scale=2)
|
image1_scale2.load(scale=2)
|
||||||
self.assertEqual(image1_scale2.mode, "RGB")
|
self.assertEqual(image1_scale2.mode, "RGB")
|
||||||
self.assertEqual(image1_scale2.size, (920, 704))
|
self.assertEqual(image1_scale2.size, (920, 704))
|
||||||
self.assertEqual(image1_scale2.format, "EPS")
|
self.assertEqual(image1_scale2.format, "EPS")
|
||||||
|
|
||||||
image2_scale2 = Image.open(file2)
|
with Image.open(file2) as image2_scale2:
|
||||||
image2_scale2.load(scale=2)
|
image2_scale2.load(scale=2)
|
||||||
self.assertEqual(image2_scale2.mode, "RGB")
|
self.assertEqual(image2_scale2.mode, "RGB")
|
||||||
self.assertEqual(image2_scale2.size, (720, 504))
|
self.assertEqual(image2_scale2.size, (720, 504))
|
||||||
self.assertEqual(image2_scale2.format, "EPS")
|
self.assertEqual(image2_scale2.format, "EPS")
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -57,43 +57,42 @@ class TestFileEps(PillowTestCase):
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_cmyk(self):
|
def test_cmyk(self):
|
||||||
cmyk_image = Image.open("Tests/images/pil_sample_cmyk.eps")
|
with Image.open("Tests/images/pil_sample_cmyk.eps") as cmyk_image:
|
||||||
|
|
||||||
self.assertEqual(cmyk_image.mode, "CMYK")
|
self.assertEqual(cmyk_image.mode, "CMYK")
|
||||||
self.assertEqual(cmyk_image.size, (100, 100))
|
self.assertEqual(cmyk_image.size, (100, 100))
|
||||||
self.assertEqual(cmyk_image.format, "EPS")
|
self.assertEqual(cmyk_image.format, "EPS")
|
||||||
|
|
||||||
cmyk_image.load()
|
cmyk_image.load()
|
||||||
self.assertEqual(cmyk_image.mode, "RGB")
|
self.assertEqual(cmyk_image.mode, "RGB")
|
||||||
|
|
||||||
if "jpeg_decoder" in dir(Image.core):
|
if "jpeg_decoder" in dir(Image.core):
|
||||||
target = Image.open("Tests/images/pil_sample_rgb.jpg")
|
target = Image.open("Tests/images/pil_sample_rgb.jpg")
|
||||||
self.assert_image_similar(cmyk_image, target, 10)
|
self.assert_image_similar(cmyk_image, target, 10)
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_showpage(self):
|
def test_showpage(self):
|
||||||
# See https://github.com/python-pillow/Pillow/issues/2615
|
# See https://github.com/python-pillow/Pillow/issues/2615
|
||||||
plot_image = Image.open("Tests/images/reqd_showpage.eps")
|
with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
|
||||||
target = Image.open("Tests/images/reqd_showpage.png")
|
with Image.open("Tests/images/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
|
self.assert_image_similar(plot_image, target, 6)
|
||||||
self.assert_image_similar(plot_image, target, 6)
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_file_object(self):
|
def test_file_object(self):
|
||||||
# issue 479
|
# issue 479
|
||||||
image1 = Image.open(file1)
|
with Image.open(file1) as image1:
|
||||||
with open(self.tempfile("temp_file.eps"), "wb") as fh:
|
with open(self.tempfile("temp_file.eps"), "wb") as fh:
|
||||||
image1.save(fh, "EPS")
|
image1.save(fh, "EPS")
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_iobase_object(self):
|
def test_iobase_object(self):
|
||||||
# issue 479
|
# issue 479
|
||||||
image1 = Image.open(file1)
|
with Image.open(file1) as image1:
|
||||||
with open(self.tempfile("temp_iobase.eps"), "wb") as fh:
|
with open(self.tempfile("temp_iobase.eps"), "wb") as fh:
|
||||||
image1.save(fh, "EPS")
|
image1.save(fh, "EPS")
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_bytesio_object(self):
|
def test_bytesio_object(self):
|
||||||
|
@ -120,18 +119,18 @@ class TestFileEps(PillowTestCase):
|
||||||
self.skipTest("zip/deflate support not available")
|
self.skipTest("zip/deflate support not available")
|
||||||
|
|
||||||
# Zero bounding box
|
# Zero bounding box
|
||||||
image1_scale1 = Image.open(file1)
|
with Image.open(file1) as image1_scale1:
|
||||||
image1_scale1.load()
|
image1_scale1.load()
|
||||||
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
|
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
|
||||||
image1_scale1_compare.load()
|
image1_scale1_compare.load()
|
||||||
self.assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
self.assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
||||||
|
|
||||||
# Non-Zero bounding box
|
# Non-Zero bounding box
|
||||||
image2_scale1 = Image.open(file2)
|
with Image.open(file2) as image2_scale1:
|
||||||
image2_scale1.load()
|
image2_scale1.load()
|
||||||
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
|
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
|
||||||
image2_scale1_compare.load()
|
image2_scale1_compare.load()
|
||||||
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_render_scale2(self):
|
def test_render_scale2(self):
|
||||||
|
@ -141,57 +140,44 @@ class TestFileEps(PillowTestCase):
|
||||||
self.skipTest("zip/deflate support not available")
|
self.skipTest("zip/deflate support not available")
|
||||||
|
|
||||||
# Zero bounding box
|
# Zero bounding box
|
||||||
image1_scale2 = Image.open(file1)
|
with Image.open(file1) as image1_scale2:
|
||||||
image1_scale2.load(scale=2)
|
image1_scale2.load(scale=2)
|
||||||
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
|
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
|
||||||
image1_scale2_compare.load()
|
image1_scale2_compare.load()
|
||||||
self.assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
self.assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
||||||
|
|
||||||
# Non-Zero bounding box
|
# Non-Zero bounding box
|
||||||
image2_scale2 = Image.open(file2)
|
with Image.open(file2) as image2_scale2:
|
||||||
image2_scale2.load(scale=2)
|
image2_scale2.load(scale=2)
|
||||||
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
|
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
|
||||||
image2_scale2_compare.load()
|
image2_scale2_compare.load()
|
||||||
self.assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
self.assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_resize(self):
|
def test_resize(self):
|
||||||
# Arrange
|
files = [file1, file2, "Tests/images/illu10_preview.eps"]
|
||||||
image1 = Image.open(file1)
|
for fn in files:
|
||||||
image2 = Image.open(file2)
|
with Image.open(fn) as im:
|
||||||
image3 = Image.open("Tests/images/illu10_preview.eps")
|
new_size = (100, 100)
|
||||||
new_size = (100, 100)
|
im = im.resize(new_size)
|
||||||
|
self.assertEqual(im.size, new_size)
|
||||||
# Act
|
|
||||||
image1 = image1.resize(new_size)
|
|
||||||
image2 = image2.resize(new_size)
|
|
||||||
image3 = image3.resize(new_size)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertEqual(image1.size, new_size)
|
|
||||||
self.assertEqual(image2.size, new_size)
|
|
||||||
self.assertEqual(image3.size, new_size)
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_thumbnail(self):
|
def test_thumbnail(self):
|
||||||
# Issue #619
|
# Issue #619
|
||||||
# Arrange
|
# Arrange
|
||||||
image1 = Image.open(file1)
|
files = [file1, file2]
|
||||||
image2 = Image.open(file2)
|
for fn in files:
|
||||||
new_size = (100, 100)
|
with Image.open(file1) as im:
|
||||||
|
new_size = (100, 100)
|
||||||
# Act
|
im.thumbnail(new_size)
|
||||||
image1.thumbnail(new_size)
|
self.assertEqual(max(im.size), max(new_size))
|
||||||
image2.thumbnail(new_size)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertEqual(max(image1.size), max(new_size))
|
|
||||||
self.assertEqual(max(image2.size), max(new_size))
|
|
||||||
|
|
||||||
def test_read_binary_preview(self):
|
def test_read_binary_preview(self):
|
||||||
# Issue 302
|
# Issue 302
|
||||||
# open image with binary preview
|
# open image with binary preview
|
||||||
Image.open(file3)
|
with Image.open(file3):
|
||||||
|
pass
|
||||||
|
|
||||||
def _test_readline(self, t, ending):
|
def _test_readline(self, t, ending):
|
||||||
ending = "Failure with line ending: %s" % (
|
ending = "Failure with line ending: %s" % (
|
||||||
|
@ -239,16 +225,16 @@ class TestFileEps(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
for filename in FILES:
|
for filename in FILES:
|
||||||
img = Image.open(filename)
|
with Image.open(filename) as img:
|
||||||
self.assertEqual(img.mode, "RGB")
|
self.assertEqual(img.mode, "RGB")
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_emptyline(self):
|
def test_emptyline(self):
|
||||||
# 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/zero_bb_emptyline.eps"
|
||||||
|
|
||||||
image = Image.open(emptyline_file)
|
with Image.open(emptyline_file) as image:
|
||||||
image.load()
|
image.load()
|
||||||
self.assertEqual(image.mode, "RGB")
|
self.assertEqual(image.mode, "RGB")
|
||||||
self.assertEqual(image.size, (460, 352))
|
self.assertEqual(image.size, (460, 352))
|
||||||
self.assertEqual(image.format, "EPS")
|
self.assertEqual(image.format, "EPS")
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/hopper.fits"
|
||||||
class TestFileFitsStub(PillowTestCase):
|
class TestFileFitsStub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "FITS")
|
self.assertEqual(im.format, "FITS")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,19 +28,19 @@ class TestFileFitsStub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
dummy_fp = None
|
dummy_fp = None
|
||||||
dummy_filename = "dummy.filename"
|
dummy_filename = "dummy.filename"
|
||||||
|
|
||||||
# Act / Assert: stub cannot save without an implemented handler
|
# Act / Assert: stub cannot save without an implemented handler
|
||||||
self.assertRaises(IOError, im.save, dummy_filename)
|
self.assertRaises(IOError, im.save, dummy_filename)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename
|
IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import FliImagePlugin, Image
|
from PIL import FliImagePlugin, Image
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase, is_pypy
|
||||||
|
|
||||||
# created as an export of a palette image from Gimp2.6
|
# created as an export of a palette image from Gimp2.6
|
||||||
# save as...-> hopper.fli, default options.
|
# save as...-> hopper.fli, default options.
|
||||||
|
@ -12,36 +14,52 @@ animated_test_file = "Tests/images/a.fli"
|
||||||
|
|
||||||
class TestFileFli(PillowTestCase):
|
class TestFileFli(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(static_test_file)
|
with Image.open(static_test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "FLI")
|
self.assertEqual(im.format, "FLI")
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (320, 200))
|
self.assertEqual(im.size, (320, 200))
|
||||||
self.assertEqual(im.format, "FLI")
|
self.assertEqual(im.format, "FLI")
|
||||||
self.assertEqual(im.info["duration"], 71)
|
self.assertEqual(im.info["duration"], 71)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(static_test_file)
|
im = Image.open(static_test_file)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(static_test_file)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(static_test_file) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(static_test_file)
|
with Image.open(static_test_file) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
frame = im.tell()
|
frame = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(frame, 0)
|
self.assertEqual(frame, 0)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -49,50 +67,50 @@ class TestFileFli(PillowTestCase):
|
||||||
self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file)
|
self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(static_test_file)
|
with Image.open(static_test_file) as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
self.assertEqual(im.n_frames, 384)
|
self.assertEqual(im.n_frames, 384)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_seek_tell(self):
|
def test_seek_tell(self):
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
|
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 0)
|
self.assertEqual(layer_number, 0)
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 0)
|
self.assertEqual(layer_number, 0)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 2)
|
self.assertEqual(layer_number, 2)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
im.seek(50)
|
im.seek(50)
|
||||||
|
|
||||||
expected = Image.open("Tests/images/a_fli.png")
|
with Image.open("Tests/images/a_fli.png") as expected:
|
||||||
self.assert_image_equal(im, expected)
|
self.assert_image_equal(im, expected)
|
||||||
|
|
|
@ -10,8 +10,6 @@ class TestFileGbr(PillowTestCase):
|
||||||
self.assertRaises(SyntaxError, GbrImagePlugin.GbrImageFile, invalid_file)
|
self.assertRaises(SyntaxError, GbrImagePlugin.GbrImageFile, invalid_file)
|
||||||
|
|
||||||
def test_gbr_file(self):
|
def test_gbr_file(self):
|
||||||
im = Image.open("Tests/images/gbr.gbr")
|
with Image.open("Tests/images/gbr.gbr") as im:
|
||||||
|
with Image.open("Tests/images/gbr.png") as target:
|
||||||
target = Image.open("Tests/images/gbr.png")
|
self.assert_image_equal(target, im)
|
||||||
|
|
||||||
self.assert_image_equal(target, im)
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ TEST_GD_FILE = "Tests/images/hopper.gd"
|
||||||
|
|
||||||
class TestFileGd(PillowTestCase):
|
class TestFileGd(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = GdImageFile.open(TEST_GD_FILE)
|
with GdImageFile.open(TEST_GD_FILE) as im:
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "GD")
|
self.assertEqual(im.format, "GD")
|
||||||
|
|
||||||
def test_bad_mode(self):
|
def test_bad_mode(self):
|
||||||
self.assertRaises(ValueError, GdImageFile.open, TEST_GD_FILE, "bad mode")
|
self.assertRaises(ValueError, GdImageFile.open, TEST_GD_FILE, "bad mode")
|
||||||
|
|
|
@ -2,7 +2,7 @@ from io import BytesIO
|
||||||
|
|
||||||
from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette
|
from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, netpbm_available, unittest
|
from .helper import PillowTestCase, hopper, is_pypy, netpbm_available, unittest
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import _webp
|
from PIL import _webp
|
||||||
|
@ -26,18 +26,34 @@ class TestFileGif(PillowTestCase):
|
||||||
self.skipTest("gif support not available") # can this happen?
|
self.skipTest("gif support not available") # can this happen?
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "GIF")
|
self.assertEqual(im.format, "GIF")
|
||||||
self.assertEqual(im.info["version"], b"GIF89a")
|
self.assertEqual(im.info["version"], b"GIF89a")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_GIF)
|
im = Image.open(TEST_GIF)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(TEST_GIF)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(TEST_GIF) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
|
@ -110,67 +126,67 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = hopper()
|
im = hopper()
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assert_image_similar(reread.convert("RGB"), im, 50)
|
self.assert_image_similar(reread.convert("RGB"), im, 50)
|
||||||
|
|
||||||
def test_roundtrip2(self):
|
def test_roundtrip2(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/403
|
# see https://github.com/python-pillow/Pillow/issues/403
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
im2 = im.copy()
|
im2 = im.copy()
|
||||||
im2.save(out)
|
im2.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assert_image_similar(reread.convert("RGB"), hopper(), 50)
|
self.assert_image_similar(reread.convert("RGB"), hopper(), 50)
|
||||||
|
|
||||||
def test_roundtrip_save_all(self):
|
def test_roundtrip_save_all(self):
|
||||||
# Single frame image
|
# Single frame image
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = hopper()
|
im = hopper()
|
||||||
im.save(out, save_all=True)
|
im.save(out, save_all=True)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assert_image_similar(reread.convert("RGB"), im, 50)
|
self.assert_image_similar(reread.convert("RGB"), im, 50)
|
||||||
|
|
||||||
# Multiframe image
|
# Multiframe image
|
||||||
im = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, save_all=True)
|
im.save(out, save_all=True)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assertEqual(reread.n_frames, 5)
|
self.assertEqual(reread.n_frames, 5)
|
||||||
|
|
||||||
def test_headers_saving_for_animated_gifs(self):
|
def test_headers_saving_for_animated_gifs(self):
|
||||||
important_headers = ["background", "version", "duration", "loop"]
|
important_headers = ["background", "version", "duration", "loop"]
|
||||||
# Multiframe image
|
# Multiframe image
|
||||||
im = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||||
|
|
||||||
info = im.info.copy()
|
info = im.info.copy()
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, save_all=True)
|
im.save(out, save_all=True)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
for header in important_headers:
|
for header in important_headers:
|
||||||
self.assertEqual(info[header], reread.info[header])
|
self.assertEqual(info[header], reread.info[header])
|
||||||
|
|
||||||
def test_palette_handling(self):
|
def test_palette_handling(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/513
|
# see https://github.com/python-pillow/Pillow/issues/513
|
||||||
|
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
im = im.convert("RGB")
|
im = im.convert("RGB")
|
||||||
|
|
||||||
im = im.resize((100, 100), Image.LANCZOS)
|
im = im.resize((100, 100), Image.LANCZOS)
|
||||||
im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256)
|
im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256)
|
||||||
|
|
||||||
f = self.tempfile("temp.gif")
|
f = self.tempfile("temp.gif")
|
||||||
im2.save(f, optimize=True)
|
im2.save(f, optimize=True)
|
||||||
|
|
||||||
reloaded = Image.open(f)
|
with Image.open(f) as reloaded:
|
||||||
|
|
||||||
self.assert_image_similar(im, reloaded.convert("RGB"), 10)
|
self.assert_image_similar(im, reloaded.convert("RGB"), 10)
|
||||||
|
|
||||||
def test_palette_434(self):
|
def test_palette_434(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/434
|
# see https://github.com/python-pillow/Pillow/issues/434
|
||||||
|
@ -183,108 +199,115 @@ class TestFileGif(PillowTestCase):
|
||||||
return reloaded
|
return reloaded
|
||||||
|
|
||||||
orig = "Tests/images/test.colors.gif"
|
orig = "Tests/images/test.colors.gif"
|
||||||
im = Image.open(orig)
|
with Image.open(orig) as im:
|
||||||
|
|
||||||
self.assert_image_similar(im, roundtrip(im), 1)
|
with roundtrip(im) as reloaded:
|
||||||
self.assert_image_similar(im, roundtrip(im, optimize=True), 1)
|
self.assert_image_similar(im, reloaded, 1)
|
||||||
|
with roundtrip(im, optimize=True) as reloaded:
|
||||||
|
self.assert_image_similar(im, reloaded, 1)
|
||||||
|
|
||||||
im = im.convert("RGB")
|
im = im.convert("RGB")
|
||||||
# check automatic P conversion
|
# check automatic P conversion
|
||||||
reloaded = roundtrip(im).convert("RGB")
|
with roundtrip(im) as reloaded:
|
||||||
self.assert_image_equal(im, reloaded)
|
reloaded = reloaded.convert("RGB")
|
||||||
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
||||||
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
||||||
def test_save_netpbm_bmp_mode(self):
|
def test_save_netpbm_bmp_mode(self):
|
||||||
img = Image.open(TEST_GIF).convert("RGB")
|
with Image.open(TEST_GIF) as img:
|
||||||
|
img = img.convert("RGB")
|
||||||
|
|
||||||
tempfile = self.tempfile("temp.gif")
|
tempfile = self.tempfile("temp.gif")
|
||||||
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
||||||
self.assert_image_similar(img, Image.open(tempfile).convert("RGB"), 0)
|
with Image.open(tempfile) as reloaded:
|
||||||
|
self.assert_image_similar(img, reloaded.convert("RGB"), 0)
|
||||||
|
|
||||||
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
||||||
def test_save_netpbm_l_mode(self):
|
def test_save_netpbm_l_mode(self):
|
||||||
img = Image.open(TEST_GIF).convert("L")
|
with Image.open(TEST_GIF) as img:
|
||||||
|
img = img.convert("L")
|
||||||
|
|
||||||
tempfile = self.tempfile("temp.gif")
|
tempfile = self.tempfile("temp.gif")
|
||||||
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
||||||
self.assert_image_similar(img, Image.open(tempfile).convert("L"), 0)
|
with Image.open(tempfile) as reloaded:
|
||||||
|
self.assert_image_similar(img, reloaded.convert("L"), 0)
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
img = Image.open("Tests/images/dispose_none.gif")
|
with Image.open("Tests/images/dispose_none.gif") as img:
|
||||||
framecount = 0
|
framecount = 0
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
framecount += 1
|
framecount += 1
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
self.assertEqual(framecount, 5)
|
self.assertEqual(framecount, 5)
|
||||||
|
|
||||||
def test_seek_info(self):
|
def test_seek_info(self):
|
||||||
im = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as im:
|
||||||
info = im.info.copy()
|
info = im.info.copy()
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
|
|
||||||
self.assertEqual(im.info, info)
|
self.assertEqual(im.info, info)
|
||||||
|
|
||||||
def test_seek_rewind(self):
|
def test_seek_rewind(self):
|
||||||
im = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as im:
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
|
|
||||||
expected = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as expected:
|
||||||
expected.seek(1)
|
expected.seek(1)
|
||||||
self.assert_image_equal(im, expected)
|
self.assert_image_equal(im, expected)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
for path, n_frames in [[TEST_GIF, 1], ["Tests/images/iss634.gif", 42]]:
|
for path, n_frames in [[TEST_GIF, 1], ["Tests/images/iss634.gif", 42]]:
|
||||||
# Test is_animated before n_frames
|
# Test is_animated before n_frames
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.is_animated, n_frames != 1)
|
self.assertEqual(im.is_animated, n_frames != 1)
|
||||||
|
|
||||||
# Test is_animated after n_frames
|
# Test is_animated after n_frames
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.n_frames, n_frames)
|
self.assertEqual(im.n_frames, n_frames)
|
||||||
self.assertEqual(im.is_animated, n_frames != 1)
|
self.assertEqual(im.is_animated, n_frames != 1)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_dispose_none(self):
|
def test_dispose_none(self):
|
||||||
img = Image.open("Tests/images/dispose_none.gif")
|
with Image.open("Tests/images/dispose_none.gif") as img:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, 1)
|
self.assertEqual(img.disposal_method, 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_dispose_background(self):
|
def test_dispose_background(self):
|
||||||
img = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as img:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, 2)
|
self.assertEqual(img.disposal_method, 2)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_dispose_previous(self):
|
def test_dispose_previous(self):
|
||||||
img = Image.open("Tests/images/dispose_prev.gif")
|
with Image.open("Tests/images/dispose_prev.gif") as img:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, 3)
|
self.assertEqual(img.disposal_method, 3)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_save_dispose(self):
|
def test_save_dispose(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -297,10 +320,10 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], disposal=method
|
out, save_all=True, append_images=im_list[1:], disposal=method
|
||||||
)
|
)
|
||||||
img = Image.open(out)
|
with Image.open(out) as img:
|
||||||
for _ in range(2):
|
for _ in range(2):
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, method)
|
self.assertEqual(img.disposal_method, method)
|
||||||
|
|
||||||
# check per frame disposal
|
# check per frame disposal
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
|
@ -310,11 +333,11 @@ class TestFileGif(PillowTestCase):
|
||||||
disposal=tuple(range(len(im_list))),
|
disposal=tuple(range(len(im_list))),
|
||||||
)
|
)
|
||||||
|
|
||||||
img = Image.open(out)
|
with Image.open(out) as img:
|
||||||
|
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, i + 1)
|
self.assertEqual(img.disposal_method, i + 1)
|
||||||
|
|
||||||
def test_dispose2_palette(self):
|
def test_dispose2_palette(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -334,17 +357,16 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
im_list[0].save(out, save_all=True, append_images=im_list[1:], disposal=2)
|
im_list[0].save(out, save_all=True, append_images=im_list[1:], disposal=2)
|
||||||
|
|
||||||
img = Image.open(out)
|
with Image.open(out) as img:
|
||||||
|
for i, circle in enumerate(circles):
|
||||||
|
img.seek(i)
|
||||||
|
rgb_img = img.convert("RGB")
|
||||||
|
|
||||||
for i, circle in enumerate(circles):
|
# Check top left pixel matches background
|
||||||
img.seek(i)
|
self.assertEqual(rgb_img.getpixel((0, 0)), (255, 0, 0))
|
||||||
rgb_img = img.convert("RGB")
|
|
||||||
|
|
||||||
# Check top left pixel matches background
|
# Center remains red every frame
|
||||||
self.assertEqual(rgb_img.getpixel((0, 0)), (255, 0, 0))
|
self.assertEqual(rgb_img.getpixel((50, 50)), circle)
|
||||||
|
|
||||||
# Center remains red every frame
|
|
||||||
self.assertEqual(rgb_img.getpixel((50, 50)), circle)
|
|
||||||
|
|
||||||
def test_dispose2_diff(self):
|
def test_dispose2_diff(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -373,20 +395,19 @@ class TestFileGif(PillowTestCase):
|
||||||
out, save_all=True, append_images=im_list[1:], disposal=2, transparency=0
|
out, save_all=True, append_images=im_list[1:], disposal=2, transparency=0
|
||||||
)
|
)
|
||||||
|
|
||||||
img = Image.open(out)
|
with Image.open(out) as img:
|
||||||
|
for i, colours in enumerate(circles):
|
||||||
|
img.seek(i)
|
||||||
|
rgb_img = img.convert("RGBA")
|
||||||
|
|
||||||
for i, colours in enumerate(circles):
|
# Check left circle is correct colour
|
||||||
img.seek(i)
|
self.assertEqual(rgb_img.getpixel((20, 50)), colours[0])
|
||||||
rgb_img = img.convert("RGBA")
|
|
||||||
|
|
||||||
# Check left circle is correct colour
|
# Check right circle is correct colour
|
||||||
self.assertEqual(rgb_img.getpixel((20, 50)), colours[0])
|
self.assertEqual(rgb_img.getpixel((80, 50)), colours[1])
|
||||||
|
|
||||||
# Check right circle is correct colour
|
# Check BG is correct colour
|
||||||
self.assertEqual(rgb_img.getpixel((80, 50)), colours[1])
|
self.assertEqual(rgb_img.getpixel((1, 1)), (255, 255, 255, 0))
|
||||||
|
|
||||||
# Check BG is correct colour
|
|
||||||
self.assertEqual(rgb_img.getpixel((1, 1)), (255, 255, 255, 0))
|
|
||||||
|
|
||||||
def test_dispose2_background(self):
|
def test_dispose2_background(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -409,17 +430,17 @@ class TestFileGif(PillowTestCase):
|
||||||
out, save_all=True, append_images=im_list[1:], disposal=[0, 2], background=1
|
out, save_all=True, append_images=im_list[1:], disposal=[0, 2], background=1
|
||||||
)
|
)
|
||||||
|
|
||||||
im = Image.open(out)
|
with Image.open(out) as im:
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.getpixel((0, 0)), 0)
|
self.assertEqual(im.getpixel((0, 0)), 0)
|
||||||
|
|
||||||
def test_iss634(self):
|
def test_iss634(self):
|
||||||
img = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as img:
|
||||||
# seek to the second frame
|
# seek to the second frame
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
# all transparent pixels should be replaced with the color from the
|
# all transparent pixels should be replaced with the color from the
|
||||||
# first frame
|
# first frame
|
||||||
self.assertEqual(img.histogram()[img.info["transparency"]], 0)
|
self.assertEqual(img.histogram()[img.info["transparency"]], 0)
|
||||||
|
|
||||||
def test_duration(self):
|
def test_duration(self):
|
||||||
duration = 1000
|
duration = 1000
|
||||||
|
@ -431,8 +452,8 @@ class TestFileGif(PillowTestCase):
|
||||||
im.info["duration"] = 100
|
im.info["duration"] = 100
|
||||||
im.save(out, duration=duration)
|
im.save(out, duration=duration)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.info["duration"], duration)
|
self.assertEqual(reread.info["duration"], duration)
|
||||||
|
|
||||||
def test_multiple_duration(self):
|
def test_multiple_duration(self):
|
||||||
duration_list = [1000, 2000, 3000]
|
duration_list = [1000, 2000, 3000]
|
||||||
|
@ -448,27 +469,27 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
||||||
)
|
)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
for duration in duration_list:
|
for duration in duration_list:
|
||||||
self.assertEqual(reread.info["duration"], duration)
|
self.assertEqual(reread.info["duration"], duration)
|
||||||
try:
|
try:
|
||||||
reread.seek(reread.tell() + 1)
|
reread.seek(reread.tell() + 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# duration as tuple
|
# duration as tuple
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=tuple(duration_list)
|
out, save_all=True, append_images=im_list[1:], duration=tuple(duration_list)
|
||||||
)
|
)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
for duration in duration_list:
|
for duration in duration_list:
|
||||||
self.assertEqual(reread.info["duration"], duration)
|
self.assertEqual(reread.info["duration"], duration)
|
||||||
try:
|
try:
|
||||||
reread.seek(reread.tell() + 1)
|
reread.seek(reread.tell() + 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_identical_frames(self):
|
def test_identical_frames(self):
|
||||||
duration_list = [1000, 1500, 2000, 4000]
|
duration_list = [1000, 1500, 2000, 4000]
|
||||||
|
@ -485,13 +506,13 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
||||||
)
|
)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
# Assert that the first three frames were combined
|
# Assert that the first three frames were combined
|
||||||
self.assertEqual(reread.n_frames, 2)
|
self.assertEqual(reread.n_frames, 2)
|
||||||
|
|
||||||
# Assert that the new duration is the total of the identical frames
|
# Assert that the new duration is the total of the identical frames
|
||||||
self.assertEqual(reread.info["duration"], 4500)
|
self.assertEqual(reread.info["duration"], 4500)
|
||||||
|
|
||||||
def test_identical_frames_to_single_frame(self):
|
def test_identical_frames_to_single_frame(self):
|
||||||
for duration in ([1000, 1500, 2000, 4000], (1000, 1500, 2000, 4000), 8500):
|
for duration in ([1000, 1500, 2000, 4000], (1000, 1500, 2000, 4000), 8500):
|
||||||
|
@ -505,13 +526,12 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=duration
|
out, save_all=True, append_images=im_list[1:], duration=duration
|
||||||
)
|
)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
# Assert that all frames were combined
|
||||||
|
self.assertEqual(reread.n_frames, 1)
|
||||||
|
|
||||||
# Assert that all frames were combined
|
# Assert that the new duration is the total of the identical frames
|
||||||
self.assertEqual(reread.n_frames, 1)
|
self.assertEqual(reread.info["duration"], 8500)
|
||||||
|
|
||||||
# Assert that the new duration is the total of the identical frames
|
|
||||||
self.assertEqual(reread.info["duration"], 8500)
|
|
||||||
|
|
||||||
def test_number_of_loops(self):
|
def test_number_of_loops(self):
|
||||||
number_of_loops = 2
|
number_of_loops = 2
|
||||||
|
@ -519,18 +539,18 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
im.save(out, loop=number_of_loops)
|
im.save(out, loop=number_of_loops)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assertEqual(reread.info["loop"], number_of_loops)
|
self.assertEqual(reread.info["loop"], number_of_loops)
|
||||||
|
|
||||||
def test_background(self):
|
def test_background(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
im.info["background"] = 1
|
im.info["background"] = 1
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assertEqual(reread.info["background"], im.info["background"])
|
self.assertEqual(reread.info["background"], im.info["background"])
|
||||||
|
|
||||||
if HAVE_WEBP and _webp.HAVE_WEBPANIM:
|
if HAVE_WEBP and _webp.HAVE_WEBPANIM:
|
||||||
im = Image.open("Tests/images/hopper.webp")
|
im = Image.open("Tests/images/hopper.webp")
|
||||||
|
@ -538,16 +558,18 @@ class TestFileGif(PillowTestCase):
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
||||||
def test_comment(self):
|
def test_comment(self):
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
self.assertEqual(im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0")
|
self.assertEqual(
|
||||||
|
im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0"
|
||||||
|
)
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
im.info["comment"] = b"Test comment text"
|
im.info["comment"] = b"Test comment text"
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assertEqual(reread.info["comment"], im.info["comment"])
|
self.assertEqual(reread.info["comment"], im.info["comment"])
|
||||||
|
|
||||||
def test_comment_over_255(self):
|
def test_comment_over_255(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -557,22 +579,22 @@ class TestFileGif(PillowTestCase):
|
||||||
comment += comment
|
comment += comment
|
||||||
im.info["comment"] = comment
|
im.info["comment"] = comment
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assertEqual(reread.info["comment"], comment)
|
self.assertEqual(reread.info["comment"], comment)
|
||||||
|
|
||||||
def test_zero_comment_subblocks(self):
|
def test_zero_comment_subblocks(self):
|
||||||
im = Image.open("Tests/images/hopper_zero_comment_subblocks.gif")
|
with Image.open("Tests/images/hopper_zero_comment_subblocks.gif") as im:
|
||||||
expected = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as expected:
|
||||||
self.assert_image_equal(im, expected)
|
self.assert_image_equal(im, expected)
|
||||||
|
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
|
||||||
def assertVersionAfterSave(im, version):
|
def assertVersionAfterSave(im, version):
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.info["version"], version)
|
self.assertEqual(reread.info["version"], version)
|
||||||
|
|
||||||
# Test that GIF87a is used by default
|
# Test that GIF87a is used by default
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
|
@ -588,12 +610,12 @@ class TestFileGif(PillowTestCase):
|
||||||
assertVersionAfterSave(im, b"GIF89a")
|
assertVersionAfterSave(im, b"GIF89a")
|
||||||
|
|
||||||
# Test that a GIF87a image is also saved in that format
|
# Test that a GIF87a image is also saved in that format
|
||||||
im = Image.open("Tests/images/test.colors.gif")
|
with Image.open("Tests/images/test.colors.gif") as im:
|
||||||
assertVersionAfterSave(im, b"GIF87a")
|
assertVersionAfterSave(im, b"GIF87a")
|
||||||
|
|
||||||
# Test that a GIF89a image is also saved in that format
|
# Test that a GIF89a image is also saved in that format
|
||||||
im.info["version"] = b"GIF89a"
|
im.info["version"] = b"GIF89a"
|
||||||
assertVersionAfterSave(im, b"GIF87a")
|
assertVersionAfterSave(im, b"GIF87a")
|
||||||
|
|
||||||
def test_append_images(self):
|
def test_append_images(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -603,8 +625,8 @@ class TestFileGif(PillowTestCase):
|
||||||
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
|
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
|
||||||
im.copy().save(out, save_all=True, append_images=ims)
|
im.copy().save(out, save_all=True, append_images=ims)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 3)
|
self.assertEqual(reread.n_frames, 3)
|
||||||
|
|
||||||
# Tests appending using a generator
|
# Tests appending using a generator
|
||||||
def imGenerator(ims):
|
def imGenerator(ims):
|
||||||
|
@ -612,16 +634,16 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
im.save(out, save_all=True, append_images=imGenerator(ims))
|
im.save(out, save_all=True, append_images=imGenerator(ims))
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 3)
|
self.assertEqual(reread.n_frames, 3)
|
||||||
|
|
||||||
# Tests appending single and multiple frame images
|
# Tests appending single and multiple frame images
|
||||||
im = Image.open("Tests/images/dispose_none.gif")
|
with Image.open("Tests/images/dispose_none.gif") as im:
|
||||||
ims = [Image.open("Tests/images/dispose_prev.gif")]
|
with Image.open("Tests/images/dispose_prev.gif") as im2:
|
||||||
im.save(out, save_all=True, append_images=ims)
|
im.save(out, save_all=True, append_images=[im2])
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 10)
|
self.assertEqual(reread.n_frames, 10)
|
||||||
|
|
||||||
def test_transparent_optimize(self):
|
def test_transparent_optimize(self):
|
||||||
# from issue #2195, if the transparent color is incorrectly
|
# from issue #2195, if the transparent color is incorrectly
|
||||||
|
@ -639,9 +661,9 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, transparency=253)
|
im.save(out, transparency=253)
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
|
|
||||||
self.assertEqual(reloaded.info["transparency"], 253)
|
self.assertEqual(reloaded.info["transparency"], 253)
|
||||||
|
|
||||||
def test_rgb_transparency(self):
|
def test_rgb_transparency(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -651,8 +673,8 @@ class TestFileGif(PillowTestCase):
|
||||||
im.info["transparency"] = (255, 0, 0)
|
im.info["transparency"] = (255, 0, 0)
|
||||||
self.assert_warning(UserWarning, im.save, out)
|
self.assert_warning(UserWarning, im.save, out)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertNotIn("transparency", reloaded.info)
|
self.assertNotIn("transparency", reloaded.info)
|
||||||
|
|
||||||
# Multiple frames
|
# Multiple frames
|
||||||
im = Image.new("RGB", (1, 1))
|
im = Image.new("RGB", (1, 1))
|
||||||
|
@ -660,8 +682,8 @@ class TestFileGif(PillowTestCase):
|
||||||
ims = [Image.new("RGB", (1, 1))]
|
ims = [Image.new("RGB", (1, 1))]
|
||||||
self.assert_warning(UserWarning, im.save, out, save_all=True, append_images=ims)
|
self.assert_warning(UserWarning, im.save, out, save_all=True, append_images=ims)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertNotIn("transparency", reloaded.info)
|
self.assertNotIn("transparency", reloaded.info)
|
||||||
|
|
||||||
def test_bbox(self):
|
def test_bbox(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -670,8 +692,8 @@ class TestFileGif(PillowTestCase):
|
||||||
ims = [Image.new("RGB", (100, 100), "#000")]
|
ims = [Image.new("RGB", (100, 100), "#000")]
|
||||||
im.save(out, save_all=True, append_images=ims)
|
im.save(out, save_all=True, append_images=ims)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 2)
|
self.assertEqual(reread.n_frames, 2)
|
||||||
|
|
||||||
def test_palette_save_L(self):
|
def test_palette_save_L(self):
|
||||||
# generate an L mode image with a separate palette
|
# generate an L mode image with a separate palette
|
||||||
|
@ -683,9 +705,9 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im_l.save(out, palette=palette)
|
im_l.save(out, palette=palette)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
|
|
||||||
self.assert_image_equal(reloaded.convert("RGB"), im.convert("RGB"))
|
self.assert_image_equal(reloaded.convert("RGB"), im.convert("RGB"))
|
||||||
|
|
||||||
def test_palette_save_P(self):
|
def test_palette_save_P(self):
|
||||||
# pass in a different palette, then construct what the image
|
# pass in a different palette, then construct what the image
|
||||||
|
@ -698,9 +720,9 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, palette=palette)
|
im.save(out, palette=palette)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
im.putpalette(palette)
|
im.putpalette(palette)
|
||||||
self.assert_image_equal(reloaded, im)
|
self.assert_image_equal(reloaded, im)
|
||||||
|
|
||||||
def test_palette_save_ImagePalette(self):
|
def test_palette_save_ImagePalette(self):
|
||||||
# pass in a different palette, as an ImagePalette.ImagePalette
|
# pass in a different palette, as an ImagePalette.ImagePalette
|
||||||
|
@ -712,9 +734,9 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, palette=palette)
|
im.save(out, palette=palette)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
im.putpalette(palette)
|
im.putpalette(palette)
|
||||||
self.assert_image_equal(reloaded, im)
|
self.assert_image_equal(reloaded, im)
|
||||||
|
|
||||||
def test_save_I(self):
|
def test_save_I(self):
|
||||||
# Test saving something that would trigger the auto-convert to 'L'
|
# Test saving something that would trigger the auto-convert to 'L'
|
||||||
|
@ -724,8 +746,8 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assert_image_equal(reloaded.convert("L"), im.convert("L"))
|
self.assert_image_equal(reloaded.convert("L"), im.convert("L"))
|
||||||
|
|
||||||
def test_getdata(self):
|
def test_getdata(self):
|
||||||
# test getheader/getdata against legacy values
|
# test getheader/getdata against legacy values
|
||||||
|
@ -756,14 +778,13 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
def test_lzw_bits(self):
|
def test_lzw_bits(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/2811
|
# see https://github.com/python-pillow/Pillow/issues/2811
|
||||||
im = Image.open("Tests/images/issue_2811.gif")
|
with Image.open("Tests/images/issue_2811.gif") as im:
|
||||||
|
self.assertEqual(im.tile[0][3][0], 11) # LZW bits
|
||||||
self.assertEqual(im.tile[0][3][0], 11) # LZW bits
|
# codec error prepatch
|
||||||
# codec error prepatch
|
im.load()
|
||||||
im.load()
|
|
||||||
|
|
||||||
def test_extents(self):
|
def test_extents(self):
|
||||||
im = Image.open("Tests/images/test_extents.gif")
|
with Image.open("Tests/images/test_extents.gif") as im:
|
||||||
self.assertEqual(im.size, (100, 100))
|
self.assertEqual(im.size, (100, 100))
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.size, (150, 150))
|
self.assertEqual(im.size, (150, 150))
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/WAlaska.wind.7days.grb"
|
||||||
class TestFileGribStub(PillowTestCase):
|
class TestFileGribStub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "GRIB")
|
self.assertEqual(im.format, "GRIB")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,10 +28,10 @@ class TestFileGribStub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/hdf5.h5"
|
||||||
class TestFileHdf5Stub(PillowTestCase):
|
class TestFileHdf5Stub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "HDF5")
|
self.assertEqual(im.format, "HDF5")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,19 +28,19 @@ class TestFileHdf5Stub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
dummy_fp = None
|
dummy_fp = None
|
||||||
dummy_filename = "dummy.filename"
|
dummy_filename = "dummy.filename"
|
||||||
|
|
||||||
# Act / Assert: stub cannot save without an implemented handler
|
# Act / Assert: stub cannot save without an implemented handler
|
||||||
self.assertRaises(IOError, im.save, dummy_filename)
|
self.assertRaises(IOError, im.save, dummy_filename)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
IOError, Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename
|
IOError, Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,14 +15,14 @@ class TestFileIcns(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
# Loading this icon by default should result in the largest size
|
# Loading this icon by default should result in the largest size
|
||||||
# (512x512@2x) being loaded
|
# (512x512@2x) being loaded
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert that there is no unclosed file warning
|
# Assert that there is no unclosed file warning
|
||||||
self.assert_warning(None, im.load)
|
self.assert_warning(None, im.load)
|
||||||
|
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (1024, 1024))
|
self.assertEqual(im.size, (1024, 1024))
|
||||||
self.assertEqual(im.format, "ICNS")
|
self.assertEqual(im.format, "ICNS")
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
|
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
|
@ -56,31 +56,31 @@ class TestFileIcns(PillowTestCase):
|
||||||
def test_sizes(self):
|
def test_sizes(self):
|
||||||
# Check that we can load all of the sizes, and that the final pixel
|
# Check that we can load all of the sizes, and that the final pixel
|
||||||
# dimensions are as expected
|
# dimensions are as expected
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
for w, h, r in im.info["sizes"]:
|
for w, h, r in im.info["sizes"]:
|
||||||
wr = w * r
|
wr = w * r
|
||||||
hr = h * r
|
hr = h * r
|
||||||
im.size = (w, h, r)
|
im.size = (w, h, r)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (wr, hr))
|
self.assertEqual(im.size, (wr, hr))
|
||||||
|
|
||||||
# Check that we cannot load an incorrect size
|
# Check that we cannot load an incorrect size
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
im.size = (1, 1)
|
im.size = (1, 1)
|
||||||
|
|
||||||
def test_older_icon(self):
|
def test_older_icon(self):
|
||||||
# This icon was made with Icon Composer rather than iconutil; it still
|
# This icon was made with Icon Composer rather than iconutil; it still
|
||||||
# uses PNG rather than JP2, however (since it was made on 10.9).
|
# uses PNG rather than JP2, however (since it was made on 10.9).
|
||||||
im = Image.open("Tests/images/pillow2.icns")
|
with Image.open("Tests/images/pillow2.icns") as im:
|
||||||
for w, h, r in im.info["sizes"]:
|
for w, h, r in im.info["sizes"]:
|
||||||
wr = w * r
|
wr = w * r
|
||||||
hr = h * r
|
hr = h * r
|
||||||
im2 = Image.open("Tests/images/pillow2.icns")
|
with Image.open("Tests/images/pillow2.icns") as im2:
|
||||||
im2.size = (w, h, r)
|
im2.size = (w, h, r)
|
||||||
im2.load()
|
im2.load()
|
||||||
self.assertEqual(im2.mode, "RGBA")
|
self.assertEqual(im2.mode, "RGBA")
|
||||||
self.assertEqual(im2.size, (wr, hr))
|
self.assertEqual(im2.size, (wr, hr))
|
||||||
|
|
||||||
def test_jp2_icon(self):
|
def test_jp2_icon(self):
|
||||||
# This icon was made by using Uli Kusterer's oldiconutil to replace
|
# This icon was made by using Uli Kusterer's oldiconutil to replace
|
||||||
|
@ -93,15 +93,15 @@ class TestFileIcns(PillowTestCase):
|
||||||
if not enable_jpeg2k:
|
if not enable_jpeg2k:
|
||||||
return
|
return
|
||||||
|
|
||||||
im = Image.open("Tests/images/pillow3.icns")
|
with Image.open("Tests/images/pillow3.icns") as im:
|
||||||
for w, h, r in im.info["sizes"]:
|
for w, h, r in im.info["sizes"]:
|
||||||
wr = w * r
|
wr = w * r
|
||||||
hr = h * r
|
hr = h * r
|
||||||
im2 = Image.open("Tests/images/pillow3.icns")
|
with Image.open("Tests/images/pillow3.icns") as im2:
|
||||||
im2.size = (w, h, r)
|
im2.size = (w, h, r)
|
||||||
im2.load()
|
im2.load()
|
||||||
self.assertEqual(im2.mode, "RGBA")
|
self.assertEqual(im2.mode, "RGBA")
|
||||||
self.assertEqual(im2.size, (wr, hr))
|
self.assertEqual(im2.size, (wr, hr))
|
||||||
|
|
||||||
def test_getimage(self):
|
def test_getimage(self):
|
||||||
with open(TEST_FILE, "rb") as fp:
|
with open(TEST_FILE, "rb") as fp:
|
||||||
|
|
|
@ -9,8 +9,8 @@ TEST_ICO_FILE = "Tests/images/hopper.ico"
|
||||||
|
|
||||||
class TestFileIco(PillowTestCase):
|
class TestFileIco(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_ICO_FILE)
|
with Image.open(TEST_ICO_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (16, 16))
|
self.assertEqual(im.size, (16, 16))
|
||||||
self.assertEqual(im.format, "ICO")
|
self.assertEqual(im.format, "ICO")
|
||||||
|
@ -46,22 +46,22 @@ class TestFileIco(PillowTestCase):
|
||||||
self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS))
|
self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS))
|
||||||
|
|
||||||
def test_incorrect_size(self):
|
def test_incorrect_size(self):
|
||||||
im = Image.open(TEST_ICO_FILE)
|
with Image.open(TEST_ICO_FILE) as im:
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
im.size = (1, 1)
|
im.size = (1, 1)
|
||||||
|
|
||||||
def test_save_256x256(self):
|
def test_save_256x256(self):
|
||||||
"""Issue #2264 https://github.com/python-pillow/Pillow/issues/2264"""
|
"""Issue #2264 https://github.com/python-pillow/Pillow/issues/2264"""
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/hopper_256x256.ico")
|
with Image.open("Tests/images/hopper_256x256.ico") as im:
|
||||||
outfile = self.tempfile("temp_saved_hopper_256x256.ico")
|
outfile = self.tempfile("temp_saved_hopper_256x256.ico")
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
im_saved = Image.open(outfile)
|
with Image.open(outfile) as im_saved:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im_saved.size, (256, 256))
|
self.assertEqual(im_saved.size, (256, 256))
|
||||||
|
|
||||||
def test_only_save_relevant_sizes(self):
|
def test_only_save_relevant_sizes(self):
|
||||||
"""Issue #2266 https://github.com/python-pillow/Pillow/issues/2266
|
"""Issue #2266 https://github.com/python-pillow/Pillow/issues/2266
|
||||||
|
@ -69,35 +69,35 @@ class TestFileIco(PillowTestCase):
|
||||||
and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes
|
and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes
|
||||||
"""
|
"""
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/python.ico") # 16x16, 32x32, 48x48
|
with Image.open("Tests/images/python.ico") as im: # 16x16, 32x32, 48x48
|
||||||
outfile = self.tempfile("temp_saved_python.ico")
|
outfile = self.tempfile("temp_saved_python.ico")
|
||||||
|
# Act
|
||||||
|
im.save(outfile)
|
||||||
|
|
||||||
# Act
|
with Image.open(outfile) as im_saved:
|
||||||
im.save(outfile)
|
# Assert
|
||||||
im_saved = Image.open(outfile)
|
self.assertEqual(
|
||||||
|
im_saved.info["sizes"], {(16, 16), (24, 24), (32, 32), (48, 48)}
|
||||||
# Assert
|
)
|
||||||
self.assertEqual(
|
|
||||||
im_saved.info["sizes"], {(16, 16), (24, 24), (32, 32), (48, 48)}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_unexpected_size(self):
|
def test_unexpected_size(self):
|
||||||
# This image has been manually hexedited to state that it is 16x32
|
# This image has been manually hexedited to state that it is 16x32
|
||||||
# while the image within is still 16x16
|
# while the image within is still 16x16
|
||||||
im = self.assert_warning(
|
def open():
|
||||||
UserWarning, Image.open, "Tests/images/hopper_unexpected.ico"
|
with Image.open("Tests/images/hopper_unexpected.ico") as im:
|
||||||
)
|
self.assertEqual(im.size, (16, 16))
|
||||||
self.assertEqual(im.size, (16, 16))
|
|
||||||
|
self.assert_warning(UserWarning, open)
|
||||||
|
|
||||||
def test_draw_reloaded(self):
|
def test_draw_reloaded(self):
|
||||||
im = Image.open(TEST_ICO_FILE)
|
with Image.open(TEST_ICO_FILE) as im:
|
||||||
outfile = self.tempfile("temp_saved_hopper_draw.ico")
|
outfile = self.tempfile("temp_saved_hopper_draw.ico")
|
||||||
|
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
draw.line((0, 0) + im.size, "#f00")
|
draw.line((0, 0) + im.size, "#f00")
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
im = Image.open(outfile)
|
with Image.open(outfile) as im:
|
||||||
im.save("Tests/images/hopper_draw.ico")
|
im.save("Tests/images/hopper_draw.ico")
|
||||||
reloaded = Image.open("Tests/images/hopper_draw.ico")
|
with Image.open("Tests/images/hopper_draw.ico") as reloaded:
|
||||||
self.assert_image_equal(im, reloaded)
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, ImImagePlugin
|
from PIL import Image, ImImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
# sample im
|
# sample im
|
||||||
TEST_IM = "Tests/images/hopper.im"
|
TEST_IM = "Tests/images/hopper.im"
|
||||||
|
@ -8,53 +10,69 @@ TEST_IM = "Tests/images/hopper.im"
|
||||||
|
|
||||||
class TestFileIm(PillowTestCase):
|
class TestFileIm(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "IM")
|
self.assertEqual(im.format, "IM")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_IM)
|
im = Image.open(TEST_IM)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(TEST_IM)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(TEST_IM) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
frame = im.tell()
|
frame = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(frame, 0)
|
self.assertEqual(frame, 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_roundtrip(self):
|
def test_roundtrip(self):
|
||||||
for mode in ["RGB", "P", "PA"]:
|
for mode in ["RGB", "P", "PA"]:
|
||||||
out = self.tempfile("temp.im")
|
out = self.tempfile("temp.im")
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assert_image_equal(reread, im)
|
self.assert_image_equal(reread, im)
|
||||||
|
|
||||||
def test_save_unsupported_mode(self):
|
def test_save_unsupported_mode(self):
|
||||||
out = self.tempfile("temp.im")
|
out = self.tempfile("temp.im")
|
||||||
|
|
|
@ -11,20 +11,20 @@ TEST_FILE = "Tests/images/iptc.jpg"
|
||||||
class TestFileIptc(PillowTestCase):
|
class TestFileIptc(PillowTestCase):
|
||||||
def test_getiptcinfo_jpg_none(self):
|
def test_getiptcinfo_jpg_none(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsNone(iptc)
|
self.assertIsNone(iptc)
|
||||||
|
|
||||||
def test_getiptcinfo_jpg_found(self):
|
def test_getiptcinfo_jpg_found(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsInstance(iptc, dict)
|
self.assertIsInstance(iptc, dict)
|
||||||
|
@ -33,10 +33,10 @@ class TestFileIptc(PillowTestCase):
|
||||||
|
|
||||||
def test_getiptcinfo_tiff_none(self):
|
def test_getiptcinfo_tiff_none(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/hopper.tif")
|
with Image.open("Tests/images/hopper.tif") as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsNone(iptc)
|
self.assertIsNone(iptc)
|
||||||
|
|
|
@ -53,14 +53,14 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
def test_app(self):
|
def test_app(self):
|
||||||
# Test APP/COM reader (@PIL135)
|
# Test APP/COM reader (@PIL135)
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.applist[0], ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00")
|
im.applist[0], ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00")
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.applist[1], ("COM", b"File written by Adobe Photoshop\xa8 4.0\x00")
|
im.applist[1], ("COM", b"File written by Adobe Photoshop\xa8 4.0\x00")
|
||||||
)
|
)
|
||||||
self.assertEqual(len(im.applist), 2)
|
self.assertEqual(len(im.applist), 2)
|
||||||
|
|
||||||
def test_cmyk(self):
|
def test_cmyk(self):
|
||||||
# Test CMYK handling. Thanks to Tim and Charlie for test data,
|
# Test CMYK handling. Thanks to Tim and Charlie for test data,
|
||||||
|
@ -99,20 +99,20 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
def test_icc(self):
|
def test_icc(self):
|
||||||
# Test ICC support
|
# Test ICC support
|
||||||
im1 = Image.open("Tests/images/rgb.jpg")
|
with Image.open("Tests/images/rgb.jpg") as im1:
|
||||||
icc_profile = im1.info["icc_profile"]
|
icc_profile = im1.info["icc_profile"]
|
||||||
self.assertEqual(len(icc_profile), 3144)
|
self.assertEqual(len(icc_profile), 3144)
|
||||||
# Roundtrip via physical file.
|
# Roundtrip via physical file.
|
||||||
f = self.tempfile("temp.jpg")
|
f = self.tempfile("temp.jpg")
|
||||||
im1.save(f, icc_profile=icc_profile)
|
im1.save(f, icc_profile=icc_profile)
|
||||||
im2 = Image.open(f)
|
with Image.open(f) as im2:
|
||||||
self.assertEqual(im2.info.get("icc_profile"), icc_profile)
|
self.assertEqual(im2.info.get("icc_profile"), icc_profile)
|
||||||
# Roundtrip via memory buffer.
|
# Roundtrip via memory buffer.
|
||||||
im1 = self.roundtrip(hopper())
|
im1 = self.roundtrip(hopper())
|
||||||
im2 = self.roundtrip(hopper(), icc_profile=icc_profile)
|
im2 = self.roundtrip(hopper(), icc_profile=icc_profile)
|
||||||
self.assert_image_equal(im1, im2)
|
self.assert_image_equal(im1, im2)
|
||||||
self.assertFalse(im1.info.get("icc_profile"))
|
self.assertFalse(im1.info.get("icc_profile"))
|
||||||
self.assertTrue(im2.info.get("icc_profile"))
|
self.assertTrue(im2.info.get("icc_profile"))
|
||||||
|
|
||||||
def test_icc_big(self):
|
def test_icc_big(self):
|
||||||
# Make sure that the "extra" support handles large blocks
|
# Make sure that the "extra" support handles large blocks
|
||||||
|
@ -205,24 +205,24 @@ class TestFileJpeg(PillowTestCase):
|
||||||
im.save(f, "JPEG", quality=90, exif=b"1" * 65532)
|
im.save(f, "JPEG", quality=90, exif=b"1" * 65532)
|
||||||
|
|
||||||
def test_exif_typeerror(self):
|
def test_exif_typeerror(self):
|
||||||
im = Image.open("Tests/images/exif_typeerror.jpg")
|
with Image.open("Tests/images/exif_typeerror.jpg") as im:
|
||||||
# Should not raise a TypeError
|
# Should not raise a TypeError
|
||||||
im._getexif()
|
im._getexif()
|
||||||
|
|
||||||
def test_exif_gps(self):
|
def test_exif_gps(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/exif_gps.jpg")
|
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||||
gps_index = 34853
|
gps_index = 34853
|
||||||
expected_exif_gps = {
|
expected_exif_gps = {
|
||||||
0: b"\x00\x00\x00\x01",
|
0: b"\x00\x00\x00\x01",
|
||||||
2: (4294967295, 1),
|
2: (4294967295, 1),
|
||||||
5: b"\x01",
|
5: b"\x01",
|
||||||
30: 65535,
|
30: 65535,
|
||||||
29: "1999:99:99 99:99:99",
|
29: "1999:99:99 99:99:99",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
exif = im._getexif()
|
exif = im._getexif()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(exif[gps_index], expected_exif_gps)
|
self.assertEqual(exif[gps_index], expected_exif_gps)
|
||||||
|
@ -256,17 +256,17 @@ class TestFileJpeg(PillowTestCase):
|
||||||
33434: (4294967295, 1),
|
33434: (4294967295, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
im = Image.open("Tests/images/exif_gps.jpg")
|
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||||
exif = im._getexif()
|
exif = im._getexif()
|
||||||
|
|
||||||
for tag, value in expected_exif.items():
|
for tag, value in expected_exif.items():
|
||||||
self.assertEqual(value, exif[tag])
|
self.assertEqual(value, exif[tag])
|
||||||
|
|
||||||
def test_exif_gps_typeerror(self):
|
def test_exif_gps_typeerror(self):
|
||||||
im = Image.open("Tests/images/exif_gps_typeerror.jpg")
|
with Image.open("Tests/images/exif_gps_typeerror.jpg") as im:
|
||||||
|
|
||||||
# Should not raise a TypeError
|
# Should not raise a TypeError
|
||||||
im._getexif()
|
im._getexif()
|
||||||
|
|
||||||
def test_progressive_compat(self):
|
def test_progressive_compat(self):
|
||||||
im1 = self.roundtrip(hopper())
|
im1 = self.roundtrip(hopper())
|
||||||
|
@ -329,13 +329,13 @@ class TestFileJpeg(PillowTestCase):
|
||||||
self.assertRaises(TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
|
self.assertRaises(TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
|
||||||
|
|
||||||
def test_exif(self):
|
def test_exif(self):
|
||||||
im = Image.open("Tests/images/pil_sample_rgb.jpg")
|
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||||
info = im._getexif()
|
info = im._getexif()
|
||||||
self.assertEqual(info[305], "Adobe Photoshop CS Macintosh")
|
self.assertEqual(info[305], "Adobe Photoshop CS Macintosh")
|
||||||
|
|
||||||
def test_mp(self):
|
def test_mp(self):
|
||||||
im = Image.open("Tests/images/pil_sample_rgb.jpg")
|
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||||
self.assertIsNone(im._getmp())
|
self.assertIsNone(im._getmp())
|
||||||
|
|
||||||
def test_quality_keep(self):
|
def test_quality_keep(self):
|
||||||
# RGB
|
# RGB
|
||||||
|
@ -354,11 +354,13 @@ class TestFileJpeg(PillowTestCase):
|
||||||
def test_junk_jpeg_header(self):
|
def test_junk_jpeg_header(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/630
|
# https://github.com/python-pillow/Pillow/issues/630
|
||||||
filename = "Tests/images/junk_jpeg_header.jpg"
|
filename = "Tests/images/junk_jpeg_header.jpg"
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_ff00_jpeg_header(self):
|
def test_ff00_jpeg_header(self):
|
||||||
filename = "Tests/images/jpeg_ff00_header.jpg"
|
filename = "Tests/images/jpeg_ff00_header.jpg"
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_truncated_jpeg_should_read_all_the_data(self):
|
def test_truncated_jpeg_should_read_all_the_data(self):
|
||||||
filename = "Tests/images/truncated_jpeg.jpg"
|
filename = "Tests/images/truncated_jpeg.jpg"
|
||||||
|
@ -370,14 +372,13 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
def test_truncated_jpeg_throws_IOError(self):
|
def test_truncated_jpeg_throws_IOError(self):
|
||||||
filename = "Tests/images/truncated_jpeg.jpg"
|
filename = "Tests/images/truncated_jpeg.jpg"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
im.load()
|
||||||
|
|
||||||
with self.assertRaises(IOError):
|
# Test that the error is raised if loaded a second time
|
||||||
im.load()
|
with self.assertRaises(IOError):
|
||||||
|
im.load()
|
||||||
# Test that the error is raised if loaded a second time
|
|
||||||
with self.assertRaises(IOError):
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
def _n_qtables_helper(self, n, test_file):
|
def _n_qtables_helper(self, n, test_file):
|
||||||
im = Image.open(test_file)
|
im = Image.open(test_file)
|
||||||
|
@ -483,9 +484,9 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
||||||
def test_load_djpeg(self):
|
def test_load_djpeg(self):
|
||||||
img = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as img:
|
||||||
img.load_djpeg()
|
img.load_djpeg()
|
||||||
self.assert_image_similar(img, Image.open(TEST_FILE), 0)
|
self.assert_image_similar(img, Image.open(TEST_FILE), 0)
|
||||||
|
|
||||||
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
|
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
|
||||||
def test_save_cjpeg(self):
|
def test_save_cjpeg(self):
|
||||||
|
@ -525,10 +526,10 @@ class TestFileJpeg(PillowTestCase):
|
||||||
# Act
|
# Act
|
||||||
# Shouldn't raise error
|
# Shouldn't raise error
|
||||||
fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
|
fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
|
||||||
im = self.assert_warning(UserWarning, Image.open, fn)
|
with self.assert_warning(UserWarning, Image.open, fn) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "JPEG")
|
self.assertEqual(im.format, "JPEG")
|
||||||
|
|
||||||
def test_save_correct_modes(self):
|
def test_save_correct_modes(self):
|
||||||
out = BytesIO()
|
out = BytesIO()
|
||||||
|
@ -558,106 +559,106 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open("Tests/images/iptc_roundUp.jpg")
|
with Image.open("Tests/images/iptc_roundUp.jpg") as im:
|
||||||
self.assertEqual(im.info["dpi"], (44, 44))
|
self.assertEqual(im.info["dpi"], (44, 44))
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/iptc_roundDown.jpg")
|
with Image.open("Tests/images/iptc_roundDown.jpg") as im:
|
||||||
self.assertEqual(im.info["dpi"], (2, 2))
|
self.assertEqual(im.info["dpi"], (2, 2))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
outfile = self.tempfile("temp.jpg")
|
outfile = self.tempfile("temp.jpg")
|
||||||
im = Image.open("Tests/images/hopper.jpg")
|
im = Image.open("Tests/images/hopper.jpg")
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.2, 72.2))
|
im.save(outfile, dpi=(72.2, 72.2))
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.8, 72.8))
|
im.save(outfile, dpi=(72.8, 72.8))
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
||||||
|
|
||||||
def test_dpi_tuple_from_exif(self):
|
def test_dpi_tuple_from_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This Photoshop CC 2017 image has DPI in EXIF not metadata
|
# This Photoshop CC 2017 image has DPI in EXIF not metadata
|
||||||
# EXIF XResolution is (2000000, 10000)
|
# EXIF XResolution is (2000000, 10000)
|
||||||
im = Image.open("Tests/images/photoshop-200dpi.jpg")
|
with Image.open("Tests/images/photoshop-200dpi.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertEqual(im.info.get("dpi"), (200, 200))
|
self.assertEqual(im.info.get("dpi"), (200, 200))
|
||||||
|
|
||||||
def test_dpi_int_from_exif(self):
|
def test_dpi_int_from_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This image has DPI in EXIF not metadata
|
# This image has DPI in EXIF not metadata
|
||||||
# EXIF XResolution is 72
|
# EXIF XResolution is 72
|
||||||
im = Image.open("Tests/images/exif-72dpi-int.jpg")
|
with Image.open("Tests/images/exif-72dpi-int.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_dpi_from_dpcm_exif(self):
|
def test_dpi_from_dpcm_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This is photoshop-200dpi.jpg with EXIF resolution unit set to cm:
|
# This is photoshop-200dpi.jpg with EXIF resolution unit set to cm:
|
||||||
# exiftool -exif:ResolutionUnit=cm photoshop-200dpi.jpg
|
# exiftool -exif:ResolutionUnit=cm photoshop-200dpi.jpg
|
||||||
im = Image.open("Tests/images/exif-200dpcm.jpg")
|
with Image.open("Tests/images/exif-200dpcm.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertEqual(im.info.get("dpi"), (508, 508))
|
self.assertEqual(im.info.get("dpi"), (508, 508))
|
||||||
|
|
||||||
def test_dpi_exif_zero_division(self):
|
def test_dpi_exif_zero_division(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This is photoshop-200dpi.jpg with EXIF resolution set to 0/0:
|
# This is photoshop-200dpi.jpg with EXIF resolution set to 0/0:
|
||||||
# exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg
|
# exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg
|
||||||
im = Image.open("Tests/images/exif-dpi-zerodivision.jpg")
|
with Image.open("Tests/images/exif-dpi-zerodivision.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
# This should return the default, and not raise a ZeroDivisionError
|
# This should return the default, and not raise a ZeroDivisionError
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_no_dpi_in_exif(self):
|
def test_no_dpi_in_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This is photoshop-200dpi.jpg with resolution removed from EXIF:
|
# This is photoshop-200dpi.jpg with resolution removed from EXIF:
|
||||||
# exiftool "-*resolution*"= photoshop-200dpi.jpg
|
# exiftool "-*resolution*"= photoshop-200dpi.jpg
|
||||||
im = Image.open("Tests/images/no-dpi-in-exif.jpg")
|
with Image.open("Tests/images/no-dpi-in-exif.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
# "When the image resolution is unknown, 72 [dpi] is designated."
|
# "When the image resolution is unknown, 72 [dpi] is designated."
|
||||||
# http://www.exiv2.org/tags.html
|
# http://www.exiv2.org/tags.html
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_invalid_exif(self):
|
def test_invalid_exif(self):
|
||||||
# This is no-dpi-in-exif with the tiff header of the exif block
|
# This is no-dpi-in-exif with the tiff header of the exif block
|
||||||
# hexedited from MM * to FF FF FF FF
|
# hexedited from MM * to FF FF FF FF
|
||||||
im = Image.open("Tests/images/invalid-exif.jpg")
|
with Image.open("Tests/images/invalid-exif.jpg") as im:
|
||||||
|
|
||||||
# This should return the default, and not a SyntaxError or
|
# This should return the default, and not a SyntaxError or
|
||||||
# OSError for unidentified image.
|
# OSError for unidentified image.
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_ifd_offset_exif(self):
|
def test_ifd_offset_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This image has been manually hexedited to have an IFD offset of 10,
|
# This image has been manually hexedited to have an IFD offset of 10,
|
||||||
# in contrast to normal 8
|
# in contrast to normal 8
|
||||||
im = Image.open("Tests/images/exif-ifd-offset.jpg")
|
with Image.open("Tests/images/exif-ifd-offset.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertEqual(im._getexif()[306], "2017:03:13 23:03:09")
|
self.assertEqual(im._getexif()[306], "2017:03:13 23:03:09")
|
||||||
|
|
||||||
def test_photoshop(self):
|
def test_photoshop(self):
|
||||||
im = Image.open("Tests/images/photoshop-200dpi.jpg")
|
with Image.open("Tests/images/photoshop-200dpi.jpg") as im:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.info["photoshop"][0x03ED],
|
im.info["photoshop"][0x03ED],
|
||||||
{
|
{
|
||||||
"XResolution": 200.0,
|
"XResolution": 200.0,
|
||||||
"DisplayedUnitsX": 1,
|
"DisplayedUnitsX": 1,
|
||||||
"YResolution": 200.0,
|
"YResolution": 200.0,
|
||||||
"DisplayedUnitsY": 1,
|
"DisplayedUnitsY": 1,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# This image does not contain a Photoshop header string
|
# This image does not contain a Photoshop header string
|
||||||
im = Image.open("Tests/images/app13.jpg")
|
with Image.open("Tests/images/app13.jpg") as im:
|
||||||
self.assertNotIn("photoshop", im.info)
|
self.assertNotIn("photoshop", im.info)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(is_win32(), "Windows only")
|
@unittest.skipUnless(is_win32(), "Windows only")
|
||||||
|
|
|
@ -42,9 +42,9 @@ class TestFileJpeg2k(PillowTestCase):
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/jp2")
|
self.assertEqual(im.get_format_mimetype(), "image/jp2")
|
||||||
|
|
||||||
def test_jpf(self):
|
def test_jpf(self):
|
||||||
im = Image.open("Tests/images/balloon.jpf")
|
with Image.open("Tests/images/balloon.jpf") as im:
|
||||||
self.assertEqual(im.format, "JPEG2000")
|
self.assertEqual(im.format, "JPEG2000")
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/jpx")
|
self.assertEqual(im.get_format_mimetype(), "image/jpx")
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
|
@ -141,15 +141,14 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
def test_write_metadata(self):
|
def test_write_metadata(self):
|
||||||
""" Test metadata writing through libtiff """
|
""" Test metadata writing through libtiff """
|
||||||
for legacy_api in [False, True]:
|
for legacy_api in [False, True]:
|
||||||
img = Image.open("Tests/images/hopper_g4.tif")
|
|
||||||
f = self.tempfile("temp.tiff")
|
f = self.tempfile("temp.tiff")
|
||||||
|
with Image.open("Tests/images/hopper_g4.tif") as img:
|
||||||
|
img.save(f, tiffinfo=img.tag)
|
||||||
|
|
||||||
img.save(f, tiffinfo=img.tag)
|
if legacy_api:
|
||||||
|
original = img.tag.named()
|
||||||
if legacy_api:
|
else:
|
||||||
original = img.tag.named()
|
original = img.tag_v2.named()
|
||||||
else:
|
|
||||||
original = img.tag_v2.named()
|
|
||||||
|
|
||||||
# PhotometricInterpretation is set from SAVE_INFO,
|
# PhotometricInterpretation is set from SAVE_INFO,
|
||||||
# not the original image.
|
# not the original image.
|
||||||
|
@ -160,11 +159,11 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
"PhotometricInterpretation",
|
"PhotometricInterpretation",
|
||||||
]
|
]
|
||||||
|
|
||||||
loaded = Image.open(f)
|
with Image.open(f) as loaded:
|
||||||
if legacy_api:
|
if legacy_api:
|
||||||
reloaded = loaded.tag.named()
|
reloaded = loaded.tag.named()
|
||||||
else:
|
else:
|
||||||
reloaded = loaded.tag_v2.named()
|
reloaded = loaded.tag_v2.named()
|
||||||
|
|
||||||
for tag, value in itertools.chain(reloaded.items(), original.items()):
|
for tag, value in itertools.chain(reloaded.items(), original.items()):
|
||||||
if tag not in ignored:
|
if tag not in ignored:
|
||||||
|
@ -298,21 +297,21 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
im.save(out, tiffinfo=tiffinfo)
|
im.save(out, tiffinfo=tiffinfo)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
for tag, value in tiffinfo.items():
|
for tag, value in tiffinfo.items():
|
||||||
reloaded_value = reloaded.tag_v2[tag]
|
reloaded_value = reloaded.tag_v2[tag]
|
||||||
if (
|
if (
|
||||||
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
|
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
|
||||||
and libtiff
|
and libtiff
|
||||||
):
|
):
|
||||||
# libtiff does not support real RATIONALS
|
# libtiff does not support real RATIONALS
|
||||||
self.assertAlmostEqual(float(reloaded_value), float(value))
|
self.assertAlmostEqual(float(reloaded_value), float(value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if libtiff and isinstance(value, bytes):
|
if libtiff and isinstance(value, bytes):
|
||||||
value = value.decode()
|
value = value.decode()
|
||||||
|
|
||||||
self.assertEqual(reloaded_value, value)
|
self.assertEqual(reloaded_value, value)
|
||||||
|
|
||||||
# Test with types
|
# Test with types
|
||||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
|
@ -339,8 +338,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = True
|
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||||
im.save(out, dpi=(72, 72))
|
im.save(out, dpi=(72, 72))
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (72.0, 72.0))
|
self.assertEqual(reloaded.info["dpi"], (72.0, 72.0))
|
||||||
|
|
||||||
def test_g3_compression(self):
|
def test_g3_compression(self):
|
||||||
i = Image.open("Tests/images/hopper_g4_500.tif")
|
i = Image.open("Tests/images/hopper_g4_500.tif")
|
||||||
|
@ -400,9 +399,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
orig.tag[269] = "temp.tif"
|
orig.tag[269] = "temp.tif"
|
||||||
orig.save(out)
|
orig.save(out)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual("temp.tif", reread.tag_v2[269])
|
self.assertEqual("temp.tif", reread.tag_v2[269])
|
||||||
self.assertEqual("temp.tif", reread.tag[269][0])
|
self.assertEqual("temp.tif", reread.tag[269][0])
|
||||||
|
|
||||||
def test_12bit_rawmode(self):
|
def test_12bit_rawmode(self):
|
||||||
""" Are we generating the same interpretation
|
""" Are we generating the same interpretation
|
||||||
|
@ -509,36 +508,36 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
def test_multipage(self):
|
def test_multipage(self):
|
||||||
# issue #862
|
# issue #862
|
||||||
TiffImagePlugin.READ_LIBTIFF = True
|
TiffImagePlugin.READ_LIBTIFF = True
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
|
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
||||||
self.assertTrue(im.tag.next)
|
self.assertTrue(im.tag.next)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
||||||
self.assertTrue(im.tag.next)
|
self.assertTrue(im.tag.next)
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
self.assertFalse(im.tag.next)
|
self.assertFalse(im.tag.next)
|
||||||
self.assertEqual(im.size, (20, 20))
|
self.assertEqual(im.size, (20, 20))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
||||||
|
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
|
|
||||||
def test_multipage_nframes(self):
|
def test_multipage_nframes(self):
|
||||||
# issue #862
|
# issue #862
|
||||||
TiffImagePlugin.READ_LIBTIFF = True
|
TiffImagePlugin.READ_LIBTIFF = True
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
frames = im.n_frames
|
frames = im.n_frames
|
||||||
self.assertEqual(frames, 3)
|
self.assertEqual(frames, 3)
|
||||||
for _ in range(frames):
|
for _ in range(frames):
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
# Should not raise ValueError: I/O operation on closed file
|
# Should not raise ValueError: I/O operation on closed file
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
|
|
||||||
|
@ -644,9 +643,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
# /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
|
# /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
|
||||||
# -dNOPAUSE /tmp/test.pdf -c quit
|
# -dNOPAUSE /tmp/test.pdf -c quit
|
||||||
infile = "Tests/images/total-pages-zero.tif"
|
infile = "Tests/images/total-pages-zero.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
# Should not divide by zero
|
# Should not divide by zero
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
def test_fd_duplication(self):
|
def test_fd_duplication(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/1651
|
# https://github.com/python-pillow/Pillow/issues/1651
|
||||||
|
@ -674,21 +673,21 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
self.assertEqual(icc, icc_libtiff)
|
self.assertEqual(icc, icc_libtiff)
|
||||||
|
|
||||||
def test_multipage_compression(self):
|
def test_multipage_compression(self):
|
||||||
im = Image.open("Tests/images/compression.tif")
|
with Image.open("Tests/images/compression.tif") as im:
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im._compression, "tiff_ccitt")
|
self.assertEqual(im._compression, "tiff_ccitt")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im._compression, "packbits")
|
self.assertEqual(im._compression, "packbits")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im._compression, "tiff_ccitt")
|
self.assertEqual(im._compression, "tiff_ccitt")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
def test_save_tiff_with_jpegtables(self):
|
def test_save_tiff_with_jpegtables(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -824,8 +823,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
def test_no_rows_per_strip(self):
|
def test_no_rows_per_strip(self):
|
||||||
# This image does not have a RowsPerStrip TIFF tag
|
# This image does not have a RowsPerStrip TIFF tag
|
||||||
infile = "Tests/images/no_rows_per_strip.tif"
|
infile = "Tests/images/no_rows_per_strip.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (950, 975))
|
self.assertEqual(im.size, (950, 975))
|
||||||
|
|
||||||
def test_orientation(self):
|
def test_orientation(self):
|
||||||
|
|
|
@ -16,42 +16,42 @@ TEST_FILE = "Tests/images/hopper.mic"
|
||||||
@unittest.skipUnless(features.check("libtiff"), "libtiff not installed")
|
@unittest.skipUnless(features.check("libtiff"), "libtiff not installed")
|
||||||
class TestFileMic(PillowTestCase):
|
class TestFileMic(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "MIC")
|
self.assertEqual(im.format, "MIC")
|
||||||
|
|
||||||
# Adjust for the gamma of 2.2 encoded into the file
|
# Adjust for the gamma of 2.2 encoded into the file
|
||||||
lut = ImagePalette.make_gamma_lut(1 / 2.2)
|
lut = ImagePalette.make_gamma_lut(1 / 2.2)
|
||||||
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
|
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
|
||||||
|
|
||||||
im2 = hopper("RGBA")
|
im2 = hopper("RGBA")
|
||||||
self.assert_image_similar(im, im2, 10)
|
self.assert_image_similar(im, im2, 10)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
|
|
||||||
def test_is_animated(self):
|
def test_is_animated(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
self.assertRaises(EOFError, im.seek, 99)
|
self.assertRaises(EOFError, im.seek, 99)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Test an invalid OLE file
|
# Test an invalid OLE file
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
import unittest
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase, is_pypy
|
||||||
|
|
||||||
test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"]
|
test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"]
|
||||||
|
|
||||||
|
@ -25,78 +26,97 @@ class TestFileMpo(PillowTestCase):
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (640, 480))
|
self.assertEqual(im.size, (640, 480))
|
||||||
self.assertEqual(im.format, "MPO")
|
self.assertEqual(im.format, "MPO")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(test_files[0])
|
im = Image.open(test_files[0])
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(test_files[0])
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(test_files[0]) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_app(self):
|
def test_app(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
# Test APP/COM reader (@PIL135)
|
# Test APP/COM reader (@PIL135)
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.applist[0][0], "APP1")
|
self.assertEqual(im.applist[0][0], "APP1")
|
||||||
self.assertEqual(im.applist[1][0], "APP2")
|
self.assertEqual(im.applist[1][0], "APP2")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.applist[1][1][:16], b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00"
|
im.applist[1][1][:16],
|
||||||
)
|
b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00",
|
||||||
self.assertEqual(len(im.applist), 2)
|
)
|
||||||
|
self.assertEqual(len(im.applist), 2)
|
||||||
|
|
||||||
def test_exif(self):
|
def test_exif(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
info = im._getexif()
|
info = im._getexif()
|
||||||
self.assertEqual(info[272], "Nintendo 3DS")
|
self.assertEqual(info[272], "Nintendo 3DS")
|
||||||
self.assertEqual(info[296], 2)
|
self.assertEqual(info[296], 2)
|
||||||
self.assertEqual(info[34665], 188)
|
self.assertEqual(info[34665], 188)
|
||||||
|
|
||||||
def test_frame_size(self):
|
def test_frame_size(self):
|
||||||
# This image has been hexedited to contain a different size
|
# This image has been hexedited to contain a different size
|
||||||
# in the EXIF data of the second frame
|
# in the EXIF data of the second frame
|
||||||
im = Image.open("Tests/images/sugarshack_frame_size.mpo")
|
with Image.open("Tests/images/sugarshack_frame_size.mpo") as im:
|
||||||
self.assertEqual(im.size, (640, 480))
|
self.assertEqual(im.size, (640, 480))
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.size, (680, 480))
|
self.assertEqual(im.size, (680, 480))
|
||||||
|
|
||||||
def test_parallax(self):
|
def test_parallax(self):
|
||||||
# Nintendo
|
# Nintendo
|
||||||
im = Image.open("Tests/images/sugarshack.mpo")
|
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
self.assertEqual(exif.get_ifd(0x927C)[0x1101]["Parallax"], -44.798187255859375)
|
self.assertEqual(
|
||||||
|
exif.get_ifd(0x927C)[0x1101]["Parallax"], -44.798187255859375
|
||||||
|
)
|
||||||
|
|
||||||
# Fujifilm
|
# Fujifilm
|
||||||
im = Image.open("Tests/images/fujifilm.mpo")
|
with Image.open("Tests/images/fujifilm.mpo") as im:
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
self.assertEqual(exif.get_ifd(0x927C)[0xB211], -3.125)
|
self.assertEqual(exif.get_ifd(0x927C)[0xB211], -3.125)
|
||||||
|
|
||||||
def test_mp(self):
|
def test_mp(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
self.assertEqual(mpinfo[45056], b"0100")
|
self.assertEqual(mpinfo[45056], b"0100")
|
||||||
self.assertEqual(mpinfo[45057], 2)
|
self.assertEqual(mpinfo[45057], 2)
|
||||||
|
|
||||||
def test_mp_offset(self):
|
def test_mp_offset(self):
|
||||||
# This image has been manually hexedited to have an IFD offset of 10
|
# This image has been manually hexedited to have an IFD offset of 10
|
||||||
# in APP2 data, in contrast to normal 8
|
# in APP2 data, in contrast to normal 8
|
||||||
im = Image.open("Tests/images/sugarshack_ifd_offset.mpo")
|
with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im:
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
self.assertEqual(mpinfo[45056], b"0100")
|
self.assertEqual(mpinfo[45056], b"0100")
|
||||||
self.assertEqual(mpinfo[45057], 2)
|
self.assertEqual(mpinfo[45057], 2)
|
||||||
|
|
||||||
def test_mp_attribute(self):
|
def test_mp_attribute(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
frameNumber = 0
|
frameNumber = 0
|
||||||
for mpentry in mpinfo[45058]:
|
for mpentry in mpinfo[45058]:
|
||||||
mpattr = mpentry["Attribute"]
|
mpattr = mpentry["Attribute"]
|
||||||
|
@ -113,62 +133,62 @@ class TestFileMpo(PillowTestCase):
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
# prior to first image raises an error, both blatant and borderline
|
# prior to first image raises an error, both blatant and borderline
|
||||||
self.assertRaises(EOFError, im.seek, -1)
|
self.assertRaises(EOFError, im.seek, -1)
|
||||||
self.assertRaises(EOFError, im.seek, -523)
|
self.assertRaises(EOFError, im.seek, -523)
|
||||||
# after the final image raises an error,
|
# after the final image raises an error,
|
||||||
# both blatant and borderline
|
# both blatant and borderline
|
||||||
self.assertRaises(EOFError, im.seek, 2)
|
self.assertRaises(EOFError, im.seek, 2)
|
||||||
self.assertRaises(EOFError, im.seek, 523)
|
self.assertRaises(EOFError, im.seek, 523)
|
||||||
# bad calls shouldn't change the frame
|
# bad calls shouldn't change the frame
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
# this one will work
|
# this one will work
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.tell(), 1)
|
self.assertEqual(im.tell(), 1)
|
||||||
# and this one, too
|
# and this one, too
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open("Tests/images/sugarshack.mpo")
|
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||||
self.assertEqual(im.n_frames, 2)
|
self.assertEqual(im.n_frames, 2)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open("Tests/images/sugarshack.mpo")
|
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_image_grab(self):
|
def test_image_grab(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
im0 = im.tobytes()
|
im0 = im.tobytes()
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.tell(), 1)
|
self.assertEqual(im.tell(), 1)
|
||||||
im1 = im.tobytes()
|
im1 = im.tobytes()
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
im02 = im.tobytes()
|
im02 = im.tobytes()
|
||||||
self.assertEqual(im0, im02)
|
self.assertEqual(im0, im02)
|
||||||
self.assertNotEqual(im0, im1)
|
self.assertNotEqual(im0, im1)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Note that only individual frames can be saved at present
|
# Note that only individual frames can be saved at present
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
jpg0 = self.frame_roundtrip(im)
|
jpg0 = self.frame_roundtrip(im)
|
||||||
self.assert_image_similar(im, jpg0, 30)
|
self.assert_image_similar(im, jpg0, 30)
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.tell(), 1)
|
self.assertEqual(im.tell(), 1)
|
||||||
jpg1 = self.frame_roundtrip(im)
|
jpg1 = self.frame_roundtrip(im)
|
||||||
self.assert_image_similar(im, jpg1, 30)
|
self.assert_image_similar(im, jpg1, 30)
|
||||||
|
|
|
@ -82,26 +82,26 @@ class TestFilePdf(PillowTestCase):
|
||||||
self.helper_save_as_pdf("RGB", save_all=True)
|
self.helper_save_as_pdf("RGB", save_all=True)
|
||||||
|
|
||||||
# Multiframe image
|
# Multiframe image
|
||||||
im = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||||
|
|
||||||
outfile = self.tempfile("temp.pdf")
|
outfile = self.tempfile("temp.pdf")
|
||||||
im.save(outfile, save_all=True)
|
im.save(outfile, save_all=True)
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
|
||||||
# Append images
|
# Append images
|
||||||
ims = [hopper()]
|
ims = [hopper()]
|
||||||
im.copy().save(outfile, save_all=True, append_images=ims)
|
im.copy().save(outfile, save_all=True, append_images=ims)
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
|
||||||
# Test appending using a generator
|
# Test appending using a generator
|
||||||
def imGenerator(ims):
|
def imGenerator(ims):
|
||||||
yield from ims
|
yield from ims
|
||||||
|
|
||||||
im.save(outfile, save_all=True, append_images=imGenerator(ims))
|
im.save(outfile, save_all=True, append_images=imGenerator(ims))
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
@ -115,10 +115,10 @@ class TestFilePdf(PillowTestCase):
|
||||||
|
|
||||||
def test_multiframe_normal_save(self):
|
def test_multiframe_normal_save(self):
|
||||||
# Test saving a multiframe image without save_all
|
# Test saving a multiframe image without save_all
|
||||||
im = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||||
|
|
||||||
outfile = self.tempfile("temp.pdf")
|
outfile = self.tempfile("temp.pdf")
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
|
|
@ -80,12 +80,12 @@ class TestFilePng(PillowTestCase):
|
||||||
|
|
||||||
hopper("RGB").save(test_file)
|
hopper("RGB").save(test_file)
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "PNG")
|
self.assertEqual(im.format, "PNG")
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/png")
|
self.assertEqual(im.get_format_mimetype(), "image/png")
|
||||||
|
|
||||||
for mode in ["1", "L", "P", "RGB", "I", "I;16"]:
|
for mode in ["1", "L", "P", "RGB", "I", "I;16"]:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
|
@ -392,12 +392,12 @@ class TestFilePng(PillowTestCase):
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open(TEST_PNG_FILE)
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
self.assertEqual(im.info["dpi"], (96, 96))
|
self.assertEqual(im.info["dpi"], (96, 96))
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/icc_profile_none.png")
|
with Image.open("Tests/images/icc_profile_none.png") as im:
|
||||||
self.assertEqual(im.info["dpi"], (72, 72))
|
self.assertEqual(im.info["dpi"], (72, 72))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
im = Image.open(TEST_PNG_FILE)
|
im = Image.open(TEST_PNG_FILE)
|
||||||
|
@ -505,19 +505,19 @@ class TestFilePng(PillowTestCase):
|
||||||
def test_trns_null(self):
|
def test_trns_null(self):
|
||||||
# Check reading images with null tRNS value, issue #1239
|
# Check reading images with null tRNS value, issue #1239
|
||||||
test_file = "Tests/images/tRNS_null_1x1.png"
|
test_file = "Tests/images/tRNS_null_1x1.png"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
self.assertEqual(im.info["transparency"], 0)
|
self.assertEqual(im.info["transparency"], 0)
|
||||||
|
|
||||||
def test_save_icc_profile(self):
|
def test_save_icc_profile(self):
|
||||||
im = Image.open("Tests/images/icc_profile_none.png")
|
with Image.open("Tests/images/icc_profile_none.png") as im:
|
||||||
self.assertIsNone(im.info["icc_profile"])
|
self.assertIsNone(im.info["icc_profile"])
|
||||||
|
|
||||||
with_icc = Image.open("Tests/images/icc_profile.png")
|
with Image.open("Tests/images/icc_profile.png") as with_icc:
|
||||||
expected_icc = with_icc.info["icc_profile"]
|
expected_icc = with_icc.info["icc_profile"]
|
||||||
|
|
||||||
im = roundtrip(im, icc_profile=expected_icc)
|
im = roundtrip(im, icc_profile=expected_icc)
|
||||||
self.assertEqual(im.info["icc_profile"], expected_icc)
|
self.assertEqual(im.info["icc_profile"], expected_icc)
|
||||||
|
|
||||||
def test_discard_icc_profile(self):
|
def test_discard_icc_profile(self):
|
||||||
im = Image.open("Tests/images/icc_profile.png")
|
im = Image.open("Tests/images/icc_profile.png")
|
||||||
|
@ -610,8 +610,8 @@ class TestFilePng(PillowTestCase):
|
||||||
test_file = self.tempfile("temp.png")
|
test_file = self.tempfile("temp.png")
|
||||||
im.save(test_file)
|
im.save(test_file)
|
||||||
|
|
||||||
reloaded = Image.open(test_file)
|
with Image.open(test_file) as reloaded:
|
||||||
exif = reloaded._getexif()
|
exif = reloaded._getexif()
|
||||||
self.assertEqual(exif[274], 1)
|
self.assertEqual(exif[274], 1)
|
||||||
|
|
||||||
def test_exif_from_jpg(self):
|
def test_exif_from_jpg(self):
|
||||||
|
@ -620,8 +620,8 @@ class TestFilePng(PillowTestCase):
|
||||||
test_file = self.tempfile("temp.png")
|
test_file = self.tempfile("temp.png")
|
||||||
im.save(test_file)
|
im.save(test_file)
|
||||||
|
|
||||||
reloaded = Image.open(test_file)
|
with Image.open(test_file) as reloaded:
|
||||||
exif = reloaded._getexif()
|
exif = reloaded._getexif()
|
||||||
self.assertEqual(exif[305], "Adobe Photoshop CS Macintosh")
|
self.assertEqual(exif[305], "Adobe Photoshop CS Macintosh")
|
||||||
|
|
||||||
def test_exif_argument(self):
|
def test_exif_argument(self):
|
||||||
|
@ -630,8 +630,8 @@ class TestFilePng(PillowTestCase):
|
||||||
test_file = self.tempfile("temp.png")
|
test_file = self.tempfile("temp.png")
|
||||||
im.save(test_file, exif=b"exifstring")
|
im.save(test_file, exif=b"exifstring")
|
||||||
|
|
||||||
reloaded = Image.open(test_file)
|
with Image.open(test_file) as reloaded:
|
||||||
self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring")
|
self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring")
|
||||||
|
|
||||||
@unittest.skipUnless(
|
@unittest.skipUnless(
|
||||||
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP support not installed with animation"
|
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP support not installed with animation"
|
||||||
|
|
|
@ -66,10 +66,10 @@ class TestFilePpm(PillowTestCase):
|
||||||
|
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
f.write("P4\n128 128\n255")
|
f.write("P4\n128 128\n255")
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/x-portable-bitmap")
|
self.assertEqual(im.get_format_mimetype(), "image/x-portable-bitmap")
|
||||||
|
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
f.write("PyCMYK\n128 128\n255")
|
f.write("PyCMYK\n128 128\n255")
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/x-portable-anymap")
|
self.assertEqual(im.get_format_mimetype(), "image/x-portable-anymap")
|
||||||
|
|
|
@ -1,26 +1,45 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, PsdImagePlugin
|
from PIL import Image, PsdImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
test_file = "Tests/images/hopper.psd"
|
test_file = "Tests/images/hopper.psd"
|
||||||
|
|
||||||
|
|
||||||
class TestImagePsd(PillowTestCase):
|
class TestImagePsd(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "PSD")
|
self.assertEqual(im.format, "PSD")
|
||||||
|
|
||||||
im2 = hopper()
|
im2 = hopper()
|
||||||
self.assert_image_similar(im, im2, 4.8)
|
self.assert_image_similar(im, im2, 4.8)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(test_file)
|
im = Image.open(test_file)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(test_file)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(test_file)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
|
@ -29,64 +48,63 @@ class TestImagePsd(PillowTestCase):
|
||||||
self.assertRaises(SyntaxError, PsdImagePlugin.PsdImageFile, invalid_file)
|
self.assertRaises(SyntaxError, PsdImagePlugin.PsdImageFile, invalid_file)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open("Tests/images/hopper_merged.psd")
|
with Image.open("Tests/images/hopper_merged.psd") as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.n_frames, 2)
|
self.assertEqual(im.n_frames, 2)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
# PSD seek index starts at 1 rather than 0
|
# PSD seek index starts at 1 rather than 0
|
||||||
n_frames = im.n_frames + 1
|
n_frames = im.n_frames + 1
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_seek_tell(self):
|
def test_seek_tell(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
self.assertRaises(EOFError, im.seek, 0)
|
self.assertRaises(EOFError, im.seek, 0)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 2)
|
self.assertEqual(layer_number, 2)
|
||||||
|
|
||||||
def test_seek_eoferror(self):
|
def test_seek_eoferror(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
self.assertRaises(EOFError, im.seek, -1)
|
self.assertRaises(EOFError, im.seek, -1)
|
||||||
|
|
||||||
def test_open_after_exclusive_load(self):
|
def test_open_after_exclusive_load(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
im.seek(im.tell() + 1)
|
im.seek(im.tell() + 1)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
def test_icc_profile(self):
|
def test_icc_profile(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertIn("icc_profile", im.info)
|
self.assertIn("icc_profile", im.info)
|
||||||
|
|
||||||
icc_profile = im.info["icc_profile"]
|
icc_profile = im.info["icc_profile"]
|
||||||
self.assertEqual(len(icc_profile), 3144)
|
self.assertEqual(len(icc_profile), 3144)
|
||||||
|
|
||||||
def test_no_icc_profile(self):
|
def test_no_icc_profile(self):
|
||||||
im = Image.open("Tests/images/hopper_merged.psd")
|
with Image.open("Tests/images/hopper_merged.psd") as im:
|
||||||
|
self.assertNotIn("icc_profile", im.info)
|
||||||
self.assertNotIn("icc_profile", im.info)
|
|
||||||
|
|
||||||
def test_combined_larger_than_size(self):
|
def test_combined_larger_than_size(self):
|
||||||
# The 'combined' sizes of the individual parts is larger than the
|
# The 'combined' sizes of the individual parts is larger than the
|
||||||
|
|
|
@ -1,26 +1,43 @@
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import unittest
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image, ImageSequence, SpiderImagePlugin
|
from PIL import Image, ImageSequence, SpiderImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hopper.spider"
|
TEST_FILE = "Tests/images/hopper.spider"
|
||||||
|
|
||||||
|
|
||||||
class TestImageSpider(PillowTestCase):
|
class TestImageSpider(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "SPIDER")
|
self.assertEqual(im.format, "SPIDER")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(TEST_FILE)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(TEST_FILE) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
|
@ -32,10 +49,10 @@ class TestImageSpider(PillowTestCase):
|
||||||
im.save(temp, "SPIDER")
|
im.save(temp, "SPIDER")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
im2 = Image.open(temp)
|
with Image.open(temp) as im2:
|
||||||
self.assertEqual(im2.mode, "F")
|
self.assertEqual(im2.mode, "F")
|
||||||
self.assertEqual(im2.size, (128, 128))
|
self.assertEqual(im2.size, (128, 128))
|
||||||
self.assertEqual(im2.format, "SPIDER")
|
self.assertEqual(im2.format, "SPIDER")
|
||||||
|
|
||||||
def test_tempfile(self):
|
def test_tempfile(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -57,18 +74,18 @@ class TestImageSpider(PillowTestCase):
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
index = im.tell()
|
index = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(index, 0)
|
self.assertEqual(index, 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_loadImageSeries(self):
|
def test_loadImageSeries(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -109,15 +126,14 @@ class TestImageSpider(PillowTestCase):
|
||||||
self.assertRaises(IOError, Image.open, invalid_file)
|
self.assertRaises(IOError, Image.open, invalid_file)
|
||||||
|
|
||||||
def test_nonstack_file(self):
|
def test_nonstack_file(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
self.assertRaises(EOFError, im.seek, 0)
|
||||||
self.assertRaises(EOFError, im.seek, 0)
|
|
||||||
|
|
||||||
def test_nonstack_dos(self):
|
def test_nonstack_dos(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
for i, frame in enumerate(ImageSequence.Iterator(im)):
|
for i, frame in enumerate(ImageSequence.Iterator(im)):
|
||||||
if i > 1:
|
if i > 1:
|
||||||
self.fail("Non-stack DOS file test failed")
|
self.fail("Non-stack DOS file test failed")
|
||||||
|
|
||||||
# for issue #4093
|
# for issue #4093
|
||||||
def test_odd_size(self):
|
def test_odd_size(self):
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, TarIO
|
from PIL import Image, TarIO
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase, is_pypy
|
||||||
|
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
|
|
||||||
|
@ -19,17 +21,30 @@ class TestFileTar(PillowTestCase):
|
||||||
["jpeg_decoder", "hopper.jpg", "JPEG"],
|
["jpeg_decoder", "hopper.jpg", "JPEG"],
|
||||||
]:
|
]:
|
||||||
if codec in codecs:
|
if codec in codecs:
|
||||||
tar = TarIO.TarIO(TEST_TAR_FILE, test_path)
|
with TarIO.TarIO(TEST_TAR_FILE, test_path) as tar:
|
||||||
im = Image.open(tar)
|
im = Image.open(tar)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, format)
|
self.assertEqual(im.format, format)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
|
def test_unclosed_file(self):
|
||||||
|
def open():
|
||||||
|
TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
def test_close(self):
|
def test_close(self):
|
||||||
tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
|
def open():
|
||||||
tar.close()
|
tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
|
||||||
|
tar.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_contextmanager(self):
|
def test_contextmanager(self):
|
||||||
with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"):
|
def open():
|
||||||
pass
|
with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
|
@ -76,20 +76,20 @@ class TestFileTga(PillowTestCase):
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (100, 100))
|
self.assertEqual(im.size, (100, 100))
|
||||||
|
|
||||||
def test_id_field_rle(self):
|
def test_id_field_rle(self):
|
||||||
# tga file with id field
|
# tga file with id field
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (199, 199))
|
self.assertEqual(im.size, (199, 199))
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
|
@ -99,14 +99,14 @@ class TestFileTga(PillowTestCase):
|
||||||
|
|
||||||
# Save
|
# Save
|
||||||
im.save(out)
|
im.save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (100, 100))
|
self.assertEqual(test_im.size, (100, 100))
|
||||||
self.assertEqual(test_im.info["id_section"], im.info["id_section"])
|
self.assertEqual(test_im.info["id_section"], im.info["id_section"])
|
||||||
|
|
||||||
# RGBA save
|
# RGBA save
|
||||||
im.convert("RGBA").save(out)
|
im.convert("RGBA").save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (100, 100))
|
self.assertEqual(test_im.size, (100, 100))
|
||||||
|
|
||||||
def test_save_id_section(self):
|
def test_save_id_section(self):
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
|
@ -116,27 +116,27 @@ class TestFileTga(PillowTestCase):
|
||||||
|
|
||||||
# Check there is no id section
|
# Check there is no id section
|
||||||
im.save(out)
|
im.save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertNotIn("id_section", test_im.info)
|
self.assertNotIn("id_section", test_im.info)
|
||||||
|
|
||||||
# Save with custom id section
|
# Save with custom id section
|
||||||
im.save(out, id_section=b"Test content")
|
im.save(out, id_section=b"Test content")
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["id_section"], b"Test content")
|
self.assertEqual(test_im.info["id_section"], b"Test content")
|
||||||
|
|
||||||
# Save with custom id section greater than 255 characters
|
# Save with custom id section greater than 255 characters
|
||||||
id_section = b"Test content" * 25
|
id_section = b"Test content" * 25
|
||||||
self.assert_warning(UserWarning, lambda: im.save(out, id_section=id_section))
|
self.assert_warning(UserWarning, lambda: im.save(out, id_section=id_section))
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["id_section"], id_section[:255])
|
self.assertEqual(test_im.info["id_section"], id_section[:255])
|
||||||
|
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
# Save with no id section
|
# Save with no id section
|
||||||
im.save(out, id_section="")
|
im.save(out, id_section="")
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertNotIn("id_section", test_im.info)
|
self.assertNotIn("id_section", test_im.info)
|
||||||
|
|
||||||
def test_save_orientation(self):
|
def test_save_orientation(self):
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
|
@ -146,8 +146,8 @@ class TestFileTga(PillowTestCase):
|
||||||
out = self.tempfile("temp.tga")
|
out = self.tempfile("temp.tga")
|
||||||
|
|
||||||
im.save(out, orientation=1)
|
im.save(out, orientation=1)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["orientation"], 1)
|
self.assertEqual(test_im.info["orientation"], 1)
|
||||||
|
|
||||||
def test_save_rle(self):
|
def test_save_rle(self):
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
|
@ -158,19 +158,19 @@ class TestFileTga(PillowTestCase):
|
||||||
|
|
||||||
# Save
|
# Save
|
||||||
im.save(out)
|
im.save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (199, 199))
|
self.assertEqual(test_im.size, (199, 199))
|
||||||
self.assertEqual(test_im.info["compression"], "tga_rle")
|
self.assertEqual(test_im.info["compression"], "tga_rle")
|
||||||
|
|
||||||
# Save without compression
|
# Save without compression
|
||||||
im.save(out, compression=None)
|
im.save(out, compression=None)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertNotIn("compression", test_im.info)
|
self.assertNotIn("compression", test_im.info)
|
||||||
|
|
||||||
# RGBA save
|
# RGBA save
|
||||||
im.convert("RGBA").save(out)
|
im.convert("RGBA").save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (199, 199))
|
self.assertEqual(test_im.size, (199, 199))
|
||||||
|
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
im = Image.open(test_file)
|
im = Image.open(test_file)
|
||||||
|
@ -178,8 +178,8 @@ class TestFileTga(PillowTestCase):
|
||||||
|
|
||||||
# Save with compression
|
# Save with compression
|
||||||
im.save(out, compression="tga_rle")
|
im.save(out, compression="tga_rle")
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["compression"], "tga_rle")
|
self.assertEqual(test_im.info["compression"], "tga_rle")
|
||||||
|
|
||||||
def test_save_l_transparency(self):
|
def test_save_l_transparency(self):
|
||||||
# There are 559 transparent pixels in la.tga.
|
# There are 559 transparent pixels in la.tga.
|
||||||
|
|
|
@ -5,7 +5,7 @@ from io import BytesIO
|
||||||
from PIL import Image, TiffImagePlugin
|
from PIL import Image, TiffImagePlugin
|
||||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, is_win32, unittest
|
from .helper import PillowTestCase, hopper, is_pypy, is_win32, unittest
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -17,32 +17,53 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
hopper("RGB").save(filename)
|
hopper("RGB").save(filename)
|
||||||
|
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "TIFF")
|
self.assertEqual(im.format, "TIFF")
|
||||||
|
|
||||||
hopper("1").save(filename)
|
hopper("1").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("L").save(filename)
|
hopper("L").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("P").save(filename)
|
hopper("P").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("RGB").save(filename)
|
hopper("RGB").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("I").save(filename)
|
hopper("I").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
im = Image.open("Tests/images/multipage.tiff")
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open("Tests/images/multipage.tiff")
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_mac_tiff(self):
|
def test_mac_tiff(self):
|
||||||
|
@ -74,55 +95,55 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
def test_xyres_tiff(self):
|
def test_xyres_tiff(self):
|
||||||
filename = "Tests/images/pil168.tif"
|
filename = "Tests/images/pil168.tif"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# legacy api
|
# legacy api
|
||||||
self.assertIsInstance(im.tag[X_RESOLUTION][0], tuple)
|
self.assertIsInstance(im.tag[X_RESOLUTION][0], tuple)
|
||||||
self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple)
|
self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple)
|
||||||
|
|
||||||
# v2 api
|
# v2 api
|
||||||
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
|
|
||||||
self.assertEqual(im.info["dpi"], (72.0, 72.0))
|
self.assertEqual(im.info["dpi"], (72.0, 72.0))
|
||||||
|
|
||||||
def test_xyres_fallback_tiff(self):
|
def test_xyres_fallback_tiff(self):
|
||||||
filename = "Tests/images/compression.tif"
|
filename = "Tests/images/compression.tif"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# v2 api
|
# v2 api
|
||||||
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT])
|
self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT])
|
||||||
|
|
||||||
# Legacy.
|
# Legacy.
|
||||||
self.assertEqual(im.info["resolution"], (100.0, 100.0))
|
self.assertEqual(im.info["resolution"], (100.0, 100.0))
|
||||||
# Fallback "inch".
|
# Fallback "inch".
|
||||||
self.assertEqual(im.info["dpi"], (100.0, 100.0))
|
self.assertEqual(im.info["dpi"], (100.0, 100.0))
|
||||||
|
|
||||||
def test_int_resolution(self):
|
def test_int_resolution(self):
|
||||||
filename = "Tests/images/pil168.tif"
|
filename = "Tests/images/pil168.tif"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Try to read a file where X,Y_RESOLUTION are ints
|
# Try to read a file where X,Y_RESOLUTION are ints
|
||||||
im.tag_v2[X_RESOLUTION] = 71
|
im.tag_v2[X_RESOLUTION] = 71
|
||||||
im.tag_v2[Y_RESOLUTION] = 71
|
im.tag_v2[Y_RESOLUTION] = 71
|
||||||
im._setup()
|
im._setup()
|
||||||
self.assertEqual(im.info["dpi"], (71.0, 71.0))
|
self.assertEqual(im.info["dpi"], (71.0, 71.0))
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
for resolutionUnit, dpi in ((None, (72, 73)), (2, (72, 73)), (3, (183, 185))):
|
for resolutionUnit, dpi in ((None, (72, 73)), (2, (72, 73)), (3, (183, 185))):
|
||||||
im = Image.open(
|
with Image.open(
|
||||||
"Tests/images/hopper_roundDown_" + str(resolutionUnit) + ".tif"
|
"Tests/images/hopper_roundDown_" + str(resolutionUnit) + ".tif"
|
||||||
)
|
) as im:
|
||||||
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
||||||
self.assertEqual(im.info["dpi"], (dpi[0], dpi[0]))
|
self.assertEqual(im.info["dpi"], (dpi[0], dpi[0]))
|
||||||
|
|
||||||
im = Image.open(
|
with Image.open(
|
||||||
"Tests/images/hopper_roundUp_" + str(resolutionUnit) + ".tif"
|
"Tests/images/hopper_roundUp_" + str(resolutionUnit) + ".tif"
|
||||||
)
|
) as im:
|
||||||
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
||||||
self.assertEqual(im.info["dpi"], (dpi[1], dpi[1]))
|
self.assertEqual(im.info["dpi"], (dpi[1], dpi[1]))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
outfile = self.tempfile("temp.tif")
|
outfile = self.tempfile("temp.tif")
|
||||||
|
@ -154,9 +175,9 @@ class TestFileTiff(PillowTestCase):
|
||||||
TiffImagePlugin.PREFIXES.pop()
|
TiffImagePlugin.PREFIXES.pop()
|
||||||
|
|
||||||
def test_bad_exif(self):
|
def test_bad_exif(self):
|
||||||
i = Image.open("Tests/images/hopper_bad_exif.jpg")
|
with Image.open("Tests/images/hopper_bad_exif.jpg") as i:
|
||||||
# Should not raise struct.error.
|
# Should not raise struct.error.
|
||||||
self.assert_warning(UserWarning, i._getexif)
|
self.assert_warning(UserWarning, i._getexif)
|
||||||
|
|
||||||
def test_save_rgba(self):
|
def test_save_rgba(self):
|
||||||
im = hopper("RGBA")
|
im = hopper("RGBA")
|
||||||
|
@ -229,44 +250,44 @@ class TestFileTiff(PillowTestCase):
|
||||||
["Tests/images/multipage-lastframe.tif", 1],
|
["Tests/images/multipage-lastframe.tif", 1],
|
||||||
["Tests/images/multipage.tiff", 3],
|
["Tests/images/multipage.tiff", 3],
|
||||||
]:
|
]:
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.n_frames, n_frames)
|
self.assertEqual(im.n_frames, n_frames)
|
||||||
self.assertEqual(im.is_animated, n_frames != 1)
|
self.assertEqual(im.is_animated, n_frames != 1)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open("Tests/images/multipage-lastframe.tif")
|
with Image.open("Tests/images/multipage-lastframe.tif") as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_multipage(self):
|
def test_multipage(self):
|
||||||
# issue #862
|
# issue #862
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
# file is a multipage tiff: 10x10 green, 10x10 red, 20x20 blue
|
# file is a multipage tiff: 10x10 green, 10x10 red, 20x20 blue
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (20, 20))
|
self.assertEqual(im.size, (20, 20))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
||||||
|
|
||||||
def test_multipage_last_frame(self):
|
def test_multipage_last_frame(self):
|
||||||
im = Image.open("Tests/images/multipage-lastframe.tif")
|
im = Image.open("Tests/images/multipage-lastframe.tif")
|
||||||
|
@ -276,62 +297,62 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
def test___str__(self):
|
def test___str__(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
ret = str(im.ifd)
|
ret = str(im.ifd)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsInstance(ret, str)
|
self.assertIsInstance(ret, str)
|
||||||
|
|
||||||
def test_dict(self):
|
def test_dict(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# v2 interface
|
# v2 interface
|
||||||
v2_tags = {
|
v2_tags = {
|
||||||
256: 55,
|
256: 55,
|
||||||
257: 43,
|
257: 43,
|
||||||
258: (8, 8, 8, 8),
|
258: (8, 8, 8, 8),
|
||||||
259: 1,
|
259: 1,
|
||||||
262: 2,
|
262: 2,
|
||||||
296: 2,
|
296: 2,
|
||||||
273: (8,),
|
273: (8,),
|
||||||
338: (1,),
|
338: (1,),
|
||||||
277: 4,
|
277: 4,
|
||||||
279: (9460,),
|
279: (9460,),
|
||||||
282: 72.0,
|
282: 72.0,
|
||||||
283: 72.0,
|
283: 72.0,
|
||||||
284: 1,
|
284: 1,
|
||||||
}
|
}
|
||||||
self.assertEqual(dict(im.tag_v2), v2_tags)
|
self.assertEqual(dict(im.tag_v2), v2_tags)
|
||||||
|
|
||||||
# legacy interface
|
# legacy interface
|
||||||
legacy_tags = {
|
legacy_tags = {
|
||||||
256: (55,),
|
256: (55,),
|
||||||
257: (43,),
|
257: (43,),
|
||||||
258: (8, 8, 8, 8),
|
258: (8, 8, 8, 8),
|
||||||
259: (1,),
|
259: (1,),
|
||||||
262: (2,),
|
262: (2,),
|
||||||
296: (2,),
|
296: (2,),
|
||||||
273: (8,),
|
273: (8,),
|
||||||
338: (1,),
|
338: (1,),
|
||||||
277: (4,),
|
277: (4,),
|
||||||
279: (9460,),
|
279: (9460,),
|
||||||
282: ((720000, 10000),),
|
282: ((720000, 10000),),
|
||||||
283: ((720000, 10000),),
|
283: ((720000, 10000),),
|
||||||
284: (1,),
|
284: (1,),
|
||||||
}
|
}
|
||||||
self.assertEqual(dict(im.tag), legacy_tags)
|
self.assertEqual(dict(im.tag), legacy_tags)
|
||||||
|
|
||||||
def test__delitem__(self):
|
def test__delitem__(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
len_before = len(dict(im.ifd))
|
len_before = len(dict(im.ifd))
|
||||||
del im.ifd[256]
|
del im.ifd[256]
|
||||||
len_after = len(dict(im.ifd))
|
len_after = len(dict(im.ifd))
|
||||||
self.assertEqual(len_before, len_after + 1)
|
self.assertEqual(len_before, len_after + 1)
|
||||||
|
|
||||||
def test_load_byte(self):
|
def test_load_byte(self):
|
||||||
for legacy_api in [False, True]:
|
for legacy_api in [False, True]:
|
||||||
|
@ -360,16 +381,16 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_seek_eof(self):
|
def test_seek_eof(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
self.assertRaises(EOFError, im.seek, -1)
|
self.assertRaises(EOFError, im.seek, -1)
|
||||||
self.assertRaises(EOFError, im.seek, 1)
|
self.assertRaises(EOFError, im.seek, 1)
|
||||||
|
|
||||||
def test__limit_rational_int(self):
|
def test__limit_rational_int(self):
|
||||||
from PIL.TiffImagePlugin import _limit_rational
|
from PIL.TiffImagePlugin import _limit_rational
|
||||||
|
@ -430,15 +451,15 @@ class TestFileTiff(PillowTestCase):
|
||||||
kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36}
|
kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36}
|
||||||
filename = self.tempfile("temp.tif")
|
filename = self.tempfile("temp.tif")
|
||||||
hopper("RGB").save(filename, **kwargs)
|
hopper("RGB").save(filename, **kwargs)
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# legacy interface
|
# legacy interface
|
||||||
self.assertEqual(im.tag[X_RESOLUTION][0][0], 72)
|
self.assertEqual(im.tag[X_RESOLUTION][0][0], 72)
|
||||||
self.assertEqual(im.tag[Y_RESOLUTION][0][0], 36)
|
self.assertEqual(im.tag[Y_RESOLUTION][0][0], 36)
|
||||||
|
|
||||||
# v2 interface
|
# v2 interface
|
||||||
self.assertEqual(im.tag_v2[X_RESOLUTION], 72)
|
self.assertEqual(im.tag_v2[X_RESOLUTION], 72)
|
||||||
self.assertEqual(im.tag_v2[Y_RESOLUTION], 36)
|
self.assertEqual(im.tag_v2[Y_RESOLUTION], 36)
|
||||||
|
|
||||||
def test_roundtrip_tiff_uint16(self):
|
def test_roundtrip_tiff_uint16(self):
|
||||||
# Test an image of all '0' values
|
# Test an image of all '0' values
|
||||||
|
@ -471,9 +492,8 @@ class TestFileTiff(PillowTestCase):
|
||||||
def test_strip_planar_raw_with_overviews(self):
|
def test_strip_planar_raw_with_overviews(self):
|
||||||
# gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16
|
# gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16
|
||||||
infile = "Tests/images/tiff_strip_planar_raw_with_overviews.tif"
|
infile = "Tests/images/tiff_strip_planar_raw_with_overviews.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_tiled_planar_raw(self):
|
def test_tiled_planar_raw(self):
|
||||||
# gdal_translate -of GTiff -co TILED=YES -co BLOCKXSIZE=32 \
|
# gdal_translate -of GTiff -co TILED=YES -co BLOCKXSIZE=32 \
|
||||||
|
@ -535,9 +555,8 @@ class TestFileTiff(PillowTestCase):
|
||||||
# Try save-load round trip to make sure both handle icc_profile.
|
# Try save-load round trip to make sure both handle icc_profile.
|
||||||
tmpfile = self.tempfile("temp.tif")
|
tmpfile = self.tempfile("temp.tif")
|
||||||
im.save(tmpfile, "TIFF", compression="raw")
|
im.save(tmpfile, "TIFF", compression="raw")
|
||||||
reloaded = Image.open(tmpfile)
|
with Image.open(tmpfile) as reloaded:
|
||||||
|
self.assertEqual(b"Dummy value", reloaded.info["icc_profile"])
|
||||||
self.assertEqual(b"Dummy value", reloaded.info["icc_profile"])
|
|
||||||
|
|
||||||
def test_close_on_load_exclusive(self):
|
def test_close_on_load_exclusive(self):
|
||||||
# similar to test_fd_leak, but runs on unixlike os
|
# similar to test_fd_leak, but runs on unixlike os
|
||||||
|
|
|
@ -52,77 +52,81 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
|
|
||||||
img.save(f, tiffinfo=info)
|
img.save(f, tiffinfo=info)
|
||||||
|
|
||||||
loaded = Image.open(f)
|
with Image.open(f) as loaded:
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),))
|
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),))
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),))
|
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),))
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
|
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
|
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata,))
|
self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata,))
|
||||||
self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata)
|
self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata)
|
||||||
|
|
||||||
loaded_float = loaded.tag[tag_ids["RollAngle"]][0]
|
loaded_float = loaded.tag[tag_ids["RollAngle"]][0]
|
||||||
self.assertAlmostEqual(loaded_float, floatdata, places=5)
|
self.assertAlmostEqual(loaded_float, floatdata, places=5)
|
||||||
loaded_double = loaded.tag[tag_ids["YawAngle"]][0]
|
loaded_double = loaded.tag[tag_ids["YawAngle"]][0]
|
||||||
self.assertAlmostEqual(loaded_double, doubledata)
|
self.assertAlmostEqual(loaded_double, doubledata)
|
||||||
|
|
||||||
# check with 2 element ImageJMetaDataByteCounts, issue #2006
|
# check with 2 element ImageJMetaDataByteCounts, issue #2006
|
||||||
|
|
||||||
info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8)
|
info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8)
|
||||||
img.save(f, tiffinfo=info)
|
img.save(f, tiffinfo=info)
|
||||||
loaded = Image.open(f)
|
with Image.open(f) as loaded:
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
|
self.assertEqual(
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
|
loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8)
|
||||||
|
)
|
||||||
|
|
||||||
def test_read_metadata(self):
|
def test_read_metadata(self):
|
||||||
img = Image.open("Tests/images/hopper_g4.tif")
|
with Image.open("Tests/images/hopper_g4.tif") as img:
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{
|
{
|
||||||
"YResolution": IFDRational(4294967295, 113653537),
|
"YResolution": IFDRational(4294967295, 113653537),
|
||||||
"PlanarConfiguration": 1,
|
"PlanarConfiguration": 1,
|
||||||
"BitsPerSample": (1,),
|
"BitsPerSample": (1,),
|
||||||
"ImageLength": 128,
|
"ImageLength": 128,
|
||||||
"Compression": 4,
|
"Compression": 4,
|
||||||
"FillOrder": 1,
|
"FillOrder": 1,
|
||||||
"RowsPerStrip": 128,
|
"RowsPerStrip": 128,
|
||||||
"ResolutionUnit": 3,
|
"ResolutionUnit": 3,
|
||||||
"PhotometricInterpretation": 0,
|
"PhotometricInterpretation": 0,
|
||||||
"PageNumber": (0, 1),
|
"PageNumber": (0, 1),
|
||||||
"XResolution": IFDRational(4294967295, 113653537),
|
"XResolution": IFDRational(4294967295, 113653537),
|
||||||
"ImageWidth": 128,
|
"ImageWidth": 128,
|
||||||
"Orientation": 1,
|
"Orientation": 1,
|
||||||
"StripByteCounts": (1968,),
|
"StripByteCounts": (1968,),
|
||||||
"SamplesPerPixel": 1,
|
"SamplesPerPixel": 1,
|
||||||
"StripOffsets": (8,),
|
"StripOffsets": (8,),
|
||||||
},
|
},
|
||||||
img.tag_v2.named(),
|
img.tag_v2.named(),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{
|
{
|
||||||
"YResolution": ((4294967295, 113653537),),
|
"YResolution": ((4294967295, 113653537),),
|
||||||
"PlanarConfiguration": (1,),
|
"PlanarConfiguration": (1,),
|
||||||
"BitsPerSample": (1,),
|
"BitsPerSample": (1,),
|
||||||
"ImageLength": (128,),
|
"ImageLength": (128,),
|
||||||
"Compression": (4,),
|
"Compression": (4,),
|
||||||
"FillOrder": (1,),
|
"FillOrder": (1,),
|
||||||
"RowsPerStrip": (128,),
|
"RowsPerStrip": (128,),
|
||||||
"ResolutionUnit": (3,),
|
"ResolutionUnit": (3,),
|
||||||
"PhotometricInterpretation": (0,),
|
"PhotometricInterpretation": (0,),
|
||||||
"PageNumber": (0, 1),
|
"PageNumber": (0, 1),
|
||||||
"XResolution": ((4294967295, 113653537),),
|
"XResolution": ((4294967295, 113653537),),
|
||||||
"ImageWidth": (128,),
|
"ImageWidth": (128,),
|
||||||
"Orientation": (1,),
|
"Orientation": (1,),
|
||||||
"StripByteCounts": (1968,),
|
"StripByteCounts": (1968,),
|
||||||
"SamplesPerPixel": (1,),
|
"SamplesPerPixel": (1,),
|
||||||
"StripOffsets": (8,),
|
"StripOffsets": (8,),
|
||||||
},
|
},
|
||||||
img.tag.named(),
|
img.tag.named(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_write_metadata(self):
|
def test_write_metadata(self):
|
||||||
""" Test metadata writing through the python code """
|
""" Test metadata writing through the python code """
|
||||||
|
@ -131,10 +135,10 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
f = self.tempfile("temp.tiff")
|
f = self.tempfile("temp.tiff")
|
||||||
img.save(f, tiffinfo=img.tag)
|
img.save(f, tiffinfo=img.tag)
|
||||||
|
|
||||||
loaded = Image.open(f)
|
with Image.open(f) as loaded:
|
||||||
|
|
||||||
original = img.tag_v2.named()
|
original = img.tag_v2.named()
|
||||||
reloaded = loaded.tag_v2.named()
|
reloaded = loaded.tag_v2.named()
|
||||||
|
|
||||||
for k, v in original.items():
|
for k, v in original.items():
|
||||||
if isinstance(v, IFDRational):
|
if isinstance(v, IFDRational):
|
||||||
|
@ -187,18 +191,18 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
out = self.tempfile("temp.tiff")
|
out = self.tempfile("temp.tiff")
|
||||||
|
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertNotIsInstance(im.info["icc_profile"], tuple)
|
self.assertNotIsInstance(im.info["icc_profile"], tuple)
|
||||||
self.assertEqual(im.info["icc_profile"], reloaded.info["icc_profile"])
|
self.assertEqual(im.info["icc_profile"], reloaded.info["icc_profile"])
|
||||||
|
|
||||||
def test_iccprofile_binary(self):
|
def test_iccprofile_binary(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/1526
|
# https://github.com/python-pillow/Pillow/issues/1526
|
||||||
# We should be able to load this,
|
# We should be able to load this,
|
||||||
# but probably won't be able to save it.
|
# but probably won't be able to save it.
|
||||||
|
|
||||||
im = Image.open("Tests/images/hopper.iccprofile_binary.tif")
|
with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im:
|
||||||
self.assertEqual(im.tag_v2.tagtype[34675], 1)
|
self.assertEqual(im.tag_v2.tagtype[34675], 1)
|
||||||
self.assertTrue(im.info["icc_profile"])
|
self.assertTrue(im.info["icc_profile"])
|
||||||
|
|
||||||
def test_iccprofile_save_png(self):
|
def test_iccprofile_save_png(self):
|
||||||
im = Image.open("Tests/images/hopper.iccprofile.tif")
|
im = Image.open("Tests/images/hopper.iccprofile.tif")
|
||||||
|
@ -218,9 +222,9 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
out = self.tempfile("temp.tiff")
|
out = self.tempfile("temp.tiff")
|
||||||
im.save(out, tiffinfo=info, compression="raw")
|
im.save(out, tiffinfo=info, compression="raw")
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertEqual(0, reloaded.tag_v2[41988].numerator)
|
self.assertEqual(0, reloaded.tag_v2[41988].numerator)
|
||||||
self.assertEqual(0, reloaded.tag_v2[41988].denominator)
|
self.assertEqual(0, reloaded.tag_v2[41988].denominator)
|
||||||
|
|
||||||
def test_empty_values(self):
|
def test_empty_values(self):
|
||||||
data = io.BytesIO(
|
data = io.BytesIO(
|
||||||
|
@ -243,9 +247,9 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
self.assertIsInstance(im.tag_v2[34377][0], bytes)
|
self.assertIsInstance(im.tag_v2[34377][0], bytes)
|
||||||
out = self.tempfile("temp.tiff")
|
out = self.tempfile("temp.tiff")
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertEqual(len(reloaded.tag_v2[34377]), 1)
|
self.assertEqual(len(reloaded.tag_v2[34377]), 1)
|
||||||
self.assertIsInstance(reloaded.tag_v2[34377][0], bytes)
|
self.assertIsInstance(reloaded.tag_v2[34377][0], bytes)
|
||||||
|
|
||||||
def test_too_many_entries(self):
|
def test_too_many_entries(self):
|
||||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
|
|
|
@ -158,19 +158,19 @@ class TestFileWebp(PillowTestCase):
|
||||||
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP save all not available"
|
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP save all not available"
|
||||||
)
|
)
|
||||||
def test_background_from_gif(self):
|
def test_background_from_gif(self):
|
||||||
im = Image.open("Tests/images/chi.gif")
|
with Image.open("Tests/images/chi.gif") as im:
|
||||||
original_value = im.convert("RGB").getpixel((1, 1))
|
original_value = im.convert("RGB").getpixel((1, 1))
|
||||||
|
|
||||||
# Save as WEBP
|
# Save as WEBP
|
||||||
out_webp = self.tempfile("temp.webp")
|
out_webp = self.tempfile("temp.webp")
|
||||||
im.save(out_webp, save_all=True)
|
im.save(out_webp, save_all=True)
|
||||||
|
|
||||||
# Save as GIF
|
# Save as GIF
|
||||||
out_gif = self.tempfile("temp.gif")
|
out_gif = self.tempfile("temp.gif")
|
||||||
Image.open(out_webp).save(out_gif)
|
Image.open(out_webp).save(out_gif)
|
||||||
|
|
||||||
reread = Image.open(out_gif)
|
with Image.open(out_gif) as reread:
|
||||||
reread_value = reread.convert("RGB").getpixel((1, 1))
|
reread_value = reread.convert("RGB").getpixel((1, 1))
|
||||||
difference = sum(
|
difference = sum(
|
||||||
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
|
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
|
||||||
)
|
)
|
||||||
|
|
|
@ -103,7 +103,8 @@ class TestFileWebpAlpha(PillowTestCase):
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
file_path = "Tests/images/transparent.gif"
|
file_path = "Tests/images/transparent.gif"
|
||||||
Image.open(file_path).save(temp_file)
|
with Image.open(file_path) as im:
|
||||||
|
im.save(temp_file)
|
||||||
image = Image.open(temp_file)
|
image = Image.open(temp_file)
|
||||||
|
|
||||||
self.assertEqual(image.mode, "RGBA")
|
self.assertEqual(image.mode, "RGBA")
|
||||||
|
@ -112,6 +113,7 @@ class TestFileWebpAlpha(PillowTestCase):
|
||||||
|
|
||||||
image.load()
|
image.load()
|
||||||
image.getdata()
|
image.getdata()
|
||||||
target = Image.open(file_path).convert("RGBA")
|
with Image.open(file_path) as im:
|
||||||
|
target = im.convert("RGBA")
|
||||||
|
|
||||||
self.assert_image_similar(image, target, 25.0)
|
self.assert_image_similar(image, target, 25.0)
|
||||||
|
|
|
@ -28,13 +28,13 @@ class TestFileWebpAnimation(PillowTestCase):
|
||||||
attributes correctly.
|
attributes correctly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
im = Image.open("Tests/images/hopper.webp")
|
with Image.open("Tests/images/hopper.webp") as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open("Tests/images/iss634.webp")
|
with Image.open("Tests/images/iss634.webp") as im:
|
||||||
self.assertEqual(im.n_frames, 42)
|
self.assertEqual(im.n_frames, 42)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_write_animation_L(self):
|
def test_write_animation_L(self):
|
||||||
"""
|
"""
|
||||||
|
@ -43,23 +43,23 @@ class TestFileWebpAnimation(PillowTestCase):
|
||||||
visually similar.
|
visually similar.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
orig = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as orig:
|
||||||
self.assertGreater(orig.n_frames, 1)
|
self.assertGreater(orig.n_frames, 1)
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
orig.save(temp_file, save_all=True)
|
orig.save(temp_file, save_all=True)
|
||||||
im = Image.open(temp_file)
|
im = Image.open(temp_file)
|
||||||
self.assertEqual(im.n_frames, orig.n_frames)
|
self.assertEqual(im.n_frames, orig.n_frames)
|
||||||
|
|
||||||
# Compare first and last frames to the original animated GIF
|
# Compare first and last frames to the original animated GIF
|
||||||
orig.load()
|
orig.load()
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
||||||
orig.seek(orig.n_frames - 1)
|
orig.seek(orig.n_frames - 1)
|
||||||
im.seek(im.n_frames - 1)
|
im.seek(im.n_frames - 1)
|
||||||
orig.load()
|
orig.load()
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
||||||
|
|
||||||
def test_write_animation_RGB(self):
|
def test_write_animation_RGB(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -24,21 +24,21 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
def test_read_exif_metadata(self):
|
def test_read_exif_metadata(self):
|
||||||
|
|
||||||
file_path = "Tests/images/flower.webp"
|
file_path = "Tests/images/flower.webp"
|
||||||
image = Image.open(file_path)
|
with Image.open(file_path) as image:
|
||||||
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
exif_data = image.info.get("exif", None)
|
exif_data = image.info.get("exif", None)
|
||||||
self.assertTrue(exif_data)
|
self.assertTrue(exif_data)
|
||||||
|
|
||||||
exif = image._getexif()
|
exif = image._getexif()
|
||||||
|
|
||||||
# camera make
|
# camera make
|
||||||
self.assertEqual(exif[271], "Canon")
|
self.assertEqual(exif[271], "Canon")
|
||||||
|
|
||||||
jpeg_image = Image.open("Tests/images/flower.jpg")
|
with Image.open("Tests/images/flower.jpg") as jpeg_image:
|
||||||
expected_exif = jpeg_image.info["exif"]
|
expected_exif = jpeg_image.info["exif"]
|
||||||
|
|
||||||
self.assertEqual(exif_data, expected_exif)
|
self.assertEqual(exif_data, expected_exif)
|
||||||
|
|
||||||
def test_write_exif_metadata(self):
|
def test_write_exif_metadata(self):
|
||||||
file_path = "Tests/images/flower.jpg"
|
file_path = "Tests/images/flower.jpg"
|
||||||
|
@ -60,17 +60,17 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
def test_read_icc_profile(self):
|
def test_read_icc_profile(self):
|
||||||
|
|
||||||
file_path = "Tests/images/flower2.webp"
|
file_path = "Tests/images/flower2.webp"
|
||||||
image = Image.open(file_path)
|
with Image.open(file_path) as image:
|
||||||
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
self.assertTrue(image.info.get("icc_profile", None))
|
self.assertTrue(image.info.get("icc_profile", None))
|
||||||
|
|
||||||
icc = image.info["icc_profile"]
|
icc = image.info["icc_profile"]
|
||||||
|
|
||||||
jpeg_image = Image.open("Tests/images/flower2.jpg")
|
with Image.open("Tests/images/flower2.jpg") as jpeg_image:
|
||||||
expected_icc = jpeg_image.info["icc_profile"]
|
expected_icc = jpeg_image.info["icc_profile"]
|
||||||
|
|
||||||
self.assertEqual(icc, expected_icc)
|
self.assertEqual(icc, expected_icc)
|
||||||
|
|
||||||
def test_write_icc_metadata(self):
|
def test_write_icc_metadata(self):
|
||||||
file_path = "Tests/images/flower2.jpg"
|
file_path = "Tests/images/flower2.jpg"
|
||||||
|
@ -126,10 +126,10 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
xmp=xmp_data,
|
xmp=xmp_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
image = Image.open(temp_file)
|
with Image.open(temp_file) as image:
|
||||||
self.assertIn("icc_profile", image.info)
|
self.assertIn("icc_profile", image.info)
|
||||||
self.assertIn("exif", image.info)
|
self.assertIn("exif", image.info)
|
||||||
self.assertIn("xmp", image.info)
|
self.assertIn("xmp", image.info)
|
||||||
self.assertEqual(iccp_data, image.info.get("icc_profile", None))
|
self.assertEqual(iccp_data, image.info.get("icc_profile", None))
|
||||||
self.assertEqual(exif_data, image.info.get("exif", None))
|
self.assertEqual(exif_data, image.info.get("exif", None))
|
||||||
self.assertEqual(xmp_data, image.info.get("xmp", None))
|
self.assertEqual(xmp_data, image.info.get("xmp", None))
|
||||||
|
|
|
@ -7,24 +7,24 @@ class TestFileWmf(PillowTestCase):
|
||||||
def test_load_raw(self):
|
def test_load_raw(self):
|
||||||
|
|
||||||
# Test basic EMF open and rendering
|
# Test basic EMF open and rendering
|
||||||
im = Image.open("Tests/images/drawing.emf")
|
with Image.open("Tests/images/drawing.emf") as im:
|
||||||
if hasattr(Image.core, "drawwmf"):
|
if hasattr(Image.core, "drawwmf"):
|
||||||
# Currently, support for WMF/EMF is Windows-only
|
# Currently, support for WMF/EMF is Windows-only
|
||||||
im.load()
|
im.load()
|
||||||
# Compare to reference rendering
|
# Compare to reference rendering
|
||||||
imref = Image.open("Tests/images/drawing_emf_ref.png")
|
imref = Image.open("Tests/images/drawing_emf_ref.png")
|
||||||
imref.load()
|
imref.load()
|
||||||
self.assert_image_similar(im, imref, 0)
|
self.assert_image_similar(im, imref, 0)
|
||||||
|
|
||||||
# Test basic WMF open and rendering
|
# Test basic WMF open and rendering
|
||||||
im = Image.open("Tests/images/drawing.wmf")
|
with Image.open("Tests/images/drawing.wmf") as im:
|
||||||
if hasattr(Image.core, "drawwmf"):
|
if hasattr(Image.core, "drawwmf"):
|
||||||
# Currently, support for WMF/EMF is Windows-only
|
# Currently, support for WMF/EMF is Windows-only
|
||||||
im.load()
|
im.load()
|
||||||
# Compare to reference rendering
|
# Compare to reference rendering
|
||||||
imref = Image.open("Tests/images/drawing_wmf_ref.png")
|
imref = Image.open("Tests/images/drawing_wmf_ref.png")
|
||||||
imref.load()
|
imref.load()
|
||||||
self.assert_image_similar(im, imref, 2.0)
|
self.assert_image_similar(im, imref, 2.0)
|
||||||
|
|
||||||
def test_register_handler(self):
|
def test_register_handler(self):
|
||||||
class TestHandler:
|
class TestHandler:
|
||||||
|
@ -46,12 +46,12 @@ class TestFileWmf(PillowTestCase):
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open("Tests/images/drawing.emf")
|
with Image.open("Tests/images/drawing.emf") as im:
|
||||||
self.assertEqual(im.info["dpi"], 1424)
|
self.assertEqual(im.info["dpi"], 1424)
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/drawing_roundDown.emf")
|
with Image.open("Tests/images/drawing_roundDown.emf") as im:
|
||||||
self.assertEqual(im.info["dpi"], 1426)
|
self.assertEqual(im.info["dpi"], 1426)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
|
@ -42,11 +42,11 @@ class TestFileXbm(PillowTestCase):
|
||||||
filename = "Tests/images/hopper.xbm"
|
filename = "Tests/images/hopper.xbm"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.mode, "1")
|
self.assertEqual(im.mode, "1")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
|
|
||||||
def test_open_filename_with_underscore(self):
|
def test_open_filename_with_underscore(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -54,8 +54,8 @@ class TestFileXbm(PillowTestCase):
|
||||||
filename = "Tests/images/hopper_underscore.xbm"
|
filename = "Tests/images/hopper_underscore.xbm"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.mode, "1")
|
self.assertEqual(im.mode, "1")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
|
|
|
@ -23,11 +23,11 @@ class TestFileXpm(PillowTestCase):
|
||||||
|
|
||||||
def test_load_read(self):
|
def test_load_read(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
dummy_bytes = 1
|
dummy_bytes = 1
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
data = im.load_read(dummy_bytes)
|
data = im.load_read(dummy_bytes)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(len(data), 16384)
|
self.assertEqual(len(data), 16384)
|
||||||
|
|
|
@ -90,9 +90,9 @@ class TestImage(PillowTestCase):
|
||||||
def test_pathlib(self):
|
def test_pathlib(self):
|
||||||
from PIL.Image import Path
|
from PIL.Image import Path
|
||||||
|
|
||||||
im = Image.open(Path("Tests/images/multipage-mmap.tiff"))
|
with Image.open(Path("Tests/images/multipage-mmap.tiff")) as im:
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
|
|
||||||
im = Image.open(Path("Tests/images/hopper.jpg"))
|
im = Image.open(Path("Tests/images/hopper.jpg"))
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
|
@ -339,7 +339,8 @@ class TestImage(PillowTestCase):
|
||||||
def test_registered_extensions(self):
|
def test_registered_extensions(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# Open an image to trigger plugin registration
|
# Open an image to trigger plugin registration
|
||||||
Image.open("Tests/images/rgb.jpg")
|
with Image.open("Tests/images/rgb.jpg"):
|
||||||
|
pass
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
extensions = Image.registered_extensions()
|
extensions = Image.registered_extensions()
|
||||||
|
@ -441,10 +442,10 @@ class TestImage(PillowTestCase):
|
||||||
|
|
||||||
def test_offset_not_implemented(self):
|
def test_offset_not_implemented(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(NotImplementedError, im.offset, None)
|
self.assertRaises(NotImplementedError, im.offset, None)
|
||||||
|
|
||||||
def test_fromstring(self):
|
def test_fromstring(self):
|
||||||
self.assertRaises(NotImplementedError, Image.fromstring)
|
self.assertRaises(NotImplementedError, Image.fromstring)
|
||||||
|
@ -515,8 +516,8 @@ class TestImage(PillowTestCase):
|
||||||
|
|
||||||
def test_remap_palette(self):
|
def test_remap_palette(self):
|
||||||
# Test illegal image mode
|
# Test illegal image mode
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
self.assertRaises(ValueError, im.remap_palette, None)
|
self.assertRaises(ValueError, im.remap_palette, None)
|
||||||
|
|
||||||
def test__new(self):
|
def test__new(self):
|
||||||
from PIL import ImagePalette
|
from PIL import ImagePalette
|
||||||
|
@ -580,12 +581,12 @@ class TestImage(PillowTestCase):
|
||||||
|
|
||||||
def test_overrun(self):
|
def test_overrun(self):
|
||||||
for file in ["fli_overrun.bin", "sgi_overrun.bin", "pcx_overrun.bin"]:
|
for file in ["fli_overrun.bin", "sgi_overrun.bin", "pcx_overrun.bin"]:
|
||||||
im = Image.open(os.path.join("Tests/images", file))
|
with Image.open(os.path.join("Tests/images", file)) as im:
|
||||||
try:
|
try:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertFail()
|
self.assertFail()
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
self.assertEqual(str(e), "buffer overrun when reading image file")
|
self.assertEqual(str(e), "buffer overrun when reading image file")
|
||||||
|
|
||||||
|
|
||||||
class MockEncoder:
|
class MockEncoder:
|
||||||
|
|
|
@ -144,11 +144,11 @@ class TestImageConvert(PillowTestCase):
|
||||||
|
|
||||||
def test_gif_with_rgba_palette_to_p(self):
|
def test_gif_with_rgba_palette_to_p(self):
|
||||||
# See https://github.com/python-pillow/Pillow/issues/2433
|
# See https://github.com/python-pillow/Pillow/issues/2433
|
||||||
im = Image.open("Tests/images/hopper.gif")
|
with Image.open("Tests/images/hopper.gif") as im:
|
||||||
im.info["transparency"] = 255
|
im.info["transparency"] = 255
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.palette.mode, "RGBA")
|
self.assertEqual(im.palette.mode, "RGBA")
|
||||||
im_p = im.convert("P")
|
im_p = im.convert("P")
|
||||||
|
|
||||||
# Should not raise ValueError: unrecognized raw mode
|
# Should not raise ValueError: unrecognized raw mode
|
||||||
im_p.load()
|
im_p.load()
|
||||||
|
|
|
@ -5,12 +5,15 @@ from .test_imageqt import PillowQtTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestFromQImage(PillowQtTestCase, PillowTestCase):
|
class TestFromQImage(PillowQtTestCase, PillowTestCase):
|
||||||
|
def setUp(self):
|
||||||
files_to_test = [
|
super(TestFromQImage, self).setUp()
|
||||||
hopper(),
|
self.files_to_test = [
|
||||||
Image.open("Tests/images/transparent.png"),
|
hopper(),
|
||||||
Image.open("Tests/images/7x13.png"),
|
Image.open("Tests/images/transparent.png"),
|
||||||
]
|
Image.open("Tests/images/7x13.png"),
|
||||||
|
]
|
||||||
|
for im in self.files_to_test:
|
||||||
|
self.addCleanup(im.close)
|
||||||
|
|
||||||
def roundtrip(self, expected):
|
def roundtrip(self, expected):
|
||||||
# PIL -> Qt
|
# PIL -> Qt
|
||||||
|
|
|
@ -6,8 +6,8 @@ from .helper import PillowTestCase, hopper
|
||||||
class TestImageMode(PillowTestCase):
|
class TestImageMode(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
|
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
im.mode
|
im.mode
|
||||||
|
|
||||||
from PIL import ImageMode
|
from PIL import ImageMode
|
||||||
|
|
||||||
|
|
|
@ -148,5 +148,5 @@ class TestImageResize(PillowTestCase):
|
||||||
resize(mode, (188, 214))
|
resize(mode, (188, 214))
|
||||||
|
|
||||||
# Test unknown resampling filter
|
# Test unknown resampling filter
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
self.assertRaises(ValueError, im.resize, (10, 10), "unknown")
|
self.assertRaises(ValueError, im.resize, (10, 10), "unknown")
|
||||||
|
|
|
@ -39,11 +39,11 @@ class TestImageThumbnail(PillowTestCase):
|
||||||
|
|
||||||
def test_no_resize(self):
|
def test_no_resize(self):
|
||||||
# Check that draft() can resize the image to the destination size
|
# Check that draft() can resize the image to the destination size
|
||||||
im = Image.open("Tests/images/hopper.jpg")
|
with Image.open("Tests/images/hopper.jpg") as im:
|
||||||
im.draft(None, (64, 64))
|
im.draft(None, (64, 64))
|
||||||
self.assertEqual(im.size, (64, 64))
|
self.assertEqual(im.size, (64, 64))
|
||||||
|
|
||||||
# Test thumbnail(), where only draft() is necessary to resize the image
|
# Test thumbnail(), where only draft() is necessary to resize the image
|
||||||
im = Image.open("Tests/images/hopper.jpg")
|
with Image.open("Tests/images/hopper.jpg") as im:
|
||||||
im.thumbnail((64, 64))
|
im.thumbnail((64, 64))
|
||||||
self.assert_image(im, im.mode, (64, 64))
|
self.assert_image(im, im.mode, (64, 64))
|
||||||
|
|
|
@ -156,21 +156,21 @@ class TestImageTransform(PillowTestCase):
|
||||||
self.test_mesh()
|
self.test_mesh()
|
||||||
|
|
||||||
def test_missing_method_data(self):
|
def test_missing_method_data(self):
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
self.assertRaises(ValueError, im.transform, (100, 100), None)
|
self.assertRaises(ValueError, im.transform, (100, 100), None)
|
||||||
|
|
||||||
def test_unknown_resampling_filter(self):
|
def test_unknown_resampling_filter(self):
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
(w, h) = im.size
|
(w, h) = im.size
|
||||||
for resample in (Image.BOX, "unknown"):
|
for resample in (Image.BOX, "unknown"):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
ValueError,
|
ValueError,
|
||||||
im.transform,
|
im.transform,
|
||||||
(100, 100),
|
(100, 100),
|
||||||
Image.EXTENT,
|
Image.EXTENT,
|
||||||
(0, 0, w, h),
|
(0, 0, w, h),
|
||||||
resample,
|
resample,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestImageTransformAffine(PillowTestCase):
|
class TestImageTransformAffine(PillowTestCase):
|
||||||
|
|
|
@ -58,10 +58,10 @@ class TestImageCms(PillowTestCase):
|
||||||
i = ImageCms.applyTransform(hopper(), t)
|
i = ImageCms.applyTransform(hopper(), t)
|
||||||
self.assert_image(i, "RGB", (128, 128))
|
self.assert_image(i, "RGB", (128, 128))
|
||||||
|
|
||||||
i = hopper()
|
with hopper() as i:
|
||||||
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
|
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
|
||||||
ImageCms.applyTransform(hopper(), t, inPlace=True)
|
ImageCms.applyTransform(hopper(), t, inPlace=True)
|
||||||
self.assert_image(i, "RGB", (128, 128))
|
self.assert_image(i, "RGB", (128, 128))
|
||||||
|
|
||||||
p = ImageCms.createProfile("sRGB")
|
p = ImageCms.createProfile("sRGB")
|
||||||
o = ImageCms.getOpenProfile(SRGB)
|
o = ImageCms.getOpenProfile(SRGB)
|
||||||
|
@ -151,8 +151,8 @@ class TestImageCms(PillowTestCase):
|
||||||
def test_extensions(self):
|
def test_extensions(self):
|
||||||
# extensions
|
# extensions
|
||||||
|
|
||||||
i = Image.open("Tests/images/rgb.jpg")
|
with Image.open("Tests/images/rgb.jpg") as i:
|
||||||
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
|
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
ImageCms.getProfileName(p).strip(),
|
ImageCms.getProfileName(p).strip(),
|
||||||
"IEC 61966-2.1 Default RGB colour space - sRGB",
|
"IEC 61966-2.1 Default RGB colour space - sRGB",
|
||||||
|
@ -166,9 +166,10 @@ class TestImageCms(PillowTestCase):
|
||||||
self.assertRaises(ValueError, t.apply_in_place, hopper("RGBA"))
|
self.assertRaises(ValueError, t.apply_in_place, hopper("RGBA"))
|
||||||
|
|
||||||
# the procedural pyCMS API uses PyCMSError for all sorts of errors
|
# the procedural pyCMS API uses PyCMSError for all sorts of errors
|
||||||
self.assertRaises(
|
with hopper() as im:
|
||||||
ImageCms.PyCMSError, ImageCms.profileToProfile, hopper(), "foo", "bar"
|
self.assertRaises(
|
||||||
)
|
ImageCms.PyCMSError, ImageCms.profileToProfile, im, "foo", "bar"
|
||||||
|
)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
ImageCms.PyCMSError, ImageCms.buildTransform, "foo", "bar", "RGB", "RGB"
|
ImageCms.PyCMSError, ImageCms.buildTransform, "foo", "bar", "RGB", "RGB"
|
||||||
)
|
)
|
||||||
|
@ -266,8 +267,8 @@ class TestImageCms(PillowTestCase):
|
||||||
self.assert_image_similar(hopper(), out, 2)
|
self.assert_image_similar(hopper(), out, 2)
|
||||||
|
|
||||||
def test_profile_tobytes(self):
|
def test_profile_tobytes(self):
|
||||||
i = Image.open("Tests/images/rgb.jpg")
|
with Image.open("Tests/images/rgb.jpg") as i:
|
||||||
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
|
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
|
||||||
|
|
||||||
p2 = ImageCms.getOpenProfile(BytesIO(p.tobytes()))
|
p2 = ImageCms.getOpenProfile(BytesIO(p.tobytes()))
|
||||||
|
|
||||||
|
|
|
@ -45,10 +45,10 @@ class TestImageDraw(PillowTestCase):
|
||||||
draw.rectangle(list(range(4)))
|
draw.rectangle(list(range(4)))
|
||||||
|
|
||||||
def test_valueerror(self):
|
def test_valueerror(self):
|
||||||
im = Image.open("Tests/images/chi.gif")
|
with Image.open("Tests/images/chi.gif") as im:
|
||||||
|
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
draw.line((0, 0), fill=(0, 0, 0))
|
draw.line((0, 0), fill=(0, 0, 0))
|
||||||
|
|
||||||
def test_mode_mismatch(self):
|
def test_mode_mismatch(self):
|
||||||
im = hopper("RGB").copy()
|
im = hopper("RGB").copy()
|
||||||
|
|
|
@ -115,13 +115,13 @@ class TestImageFile(PillowTestCase):
|
||||||
if "zip_encoder" not in codecs:
|
if "zip_encoder" not in codecs:
|
||||||
self.skipTest("PNG (zlib) encoder not available")
|
self.skipTest("PNG (zlib) encoder not available")
|
||||||
|
|
||||||
im = Image.open("Tests/images/truncated_image.png")
|
with Image.open("Tests/images/truncated_image.png") as im:
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
# Test that the error is raised if loaded a second time
|
# Test that the error is raised if loaded a second time
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
def test_truncated_without_errors(self):
|
def test_truncated_without_errors(self):
|
||||||
if "zip_encoder" not in codecs:
|
if "zip_encoder" not in codecs:
|
||||||
|
@ -258,12 +258,12 @@ class TestPyDecoder(PillowTestCase):
|
||||||
exif[40963] = 455
|
exif[40963] = 455
|
||||||
exif[11] = "Pillow test"
|
exif[11] = "Pillow test"
|
||||||
im.save(out, exif=exif)
|
im.save(out, exif=exif)
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
reloaded_exif = reloaded.getexif()
|
reloaded_exif = reloaded.getexif()
|
||||||
self.assertEqual(reloaded_exif[258], 8)
|
self.assertEqual(reloaded_exif[258], 8)
|
||||||
self.assertNotIn(40960, exif)
|
self.assertNotIn(40960, exif)
|
||||||
self.assertEqual(reloaded_exif[40963], 455)
|
self.assertEqual(reloaded_exif[40963], 455)
|
||||||
self.assertEqual(exif[11], "Pillow test")
|
self.assertEqual(exif[11], "Pillow test")
|
||||||
|
|
||||||
im = Image.open("Tests/images/no-dpi-in-exif.jpg") # Big endian
|
im = Image.open("Tests/images/no-dpi-in-exif.jpg") # Big endian
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
|
@ -278,12 +278,12 @@ class TestPyDecoder(PillowTestCase):
|
||||||
exif[40963] = 455
|
exif[40963] = 455
|
||||||
exif[305] = "Pillow test"
|
exif[305] = "Pillow test"
|
||||||
im.save(out, exif=exif)
|
im.save(out, exif=exif)
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
reloaded_exif = reloaded.getexif()
|
reloaded_exif = reloaded.getexif()
|
||||||
self.assertEqual(reloaded_exif[258], 8)
|
self.assertEqual(reloaded_exif[258], 8)
|
||||||
self.assertNotIn(40960, exif)
|
self.assertNotIn(40960, exif)
|
||||||
self.assertEqual(reloaded_exif[40963], 455)
|
self.assertEqual(reloaded_exif[40963], 455)
|
||||||
self.assertEqual(exif[305], "Pillow test")
|
self.assertEqual(exif[305], "Pillow test")
|
||||||
|
|
||||||
@unittest.skipIf(
|
@unittest.skipIf(
|
||||||
not HAVE_WEBP or not _webp.HAVE_WEBPANIM,
|
not HAVE_WEBP or not _webp.HAVE_WEBPANIM,
|
||||||
|
@ -300,11 +300,11 @@ class TestPyDecoder(PillowTestCase):
|
||||||
exif[305] = "Pillow test"
|
exif[305] = "Pillow test"
|
||||||
|
|
||||||
def check_exif():
|
def check_exif():
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
reloaded_exif = reloaded.getexif()
|
reloaded_exif = reloaded.getexif()
|
||||||
self.assertEqual(reloaded_exif[258], 8)
|
self.assertEqual(reloaded_exif[258], 8)
|
||||||
self.assertEqual(reloaded_exif[40963], 455)
|
self.assertEqual(reloaded_exif[40963], 455)
|
||||||
self.assertEqual(exif[305], "Pillow test")
|
self.assertEqual(exif[305], "Pillow test")
|
||||||
|
|
||||||
im.save(out, exif=exif)
|
im.save(out, exif=exif)
|
||||||
check_exif()
|
check_exif()
|
||||||
|
@ -323,23 +323,13 @@ class TestPyDecoder(PillowTestCase):
|
||||||
exif[305] = "Pillow test"
|
exif[305] = "Pillow test"
|
||||||
im.save(out, exif=exif)
|
im.save(out, exif=exif)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
reloaded_exif = reloaded.getexif()
|
reloaded_exif = reloaded.getexif()
|
||||||
self.assertEqual(reloaded_exif, {258: 8, 40963: 455, 305: "Pillow test"})
|
self.assertEqual(reloaded_exif, {258: 8, 40963: 455, 305: "Pillow test"})
|
||||||
|
|
||||||
def test_exif_interop(self):
|
def test_exif_interop(self):
|
||||||
im = Image.open("Tests/images/flower.jpg")
|
with Image.open("Tests/images/flower.jpg") as im:
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
exif.get_ifd(0xA005), {1: "R98", 2: b"0100", 4097: 2272, 4098: 1704}
|
exif.get_ifd(0xA005), {1: "R98", 2: b"0100", 4097: 2272, 4098: 1704}
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_exif_shared(self):
|
|
||||||
im = Image.open("Tests/images/exif.png")
|
|
||||||
exif = im.getexif()
|
|
||||||
self.assertIs(im.getexif(), exif)
|
|
||||||
|
|
||||||
def test_exif_str(self):
|
|
||||||
im = Image.open("Tests/images/exif.png")
|
|
||||||
exif = im.getexif()
|
|
||||||
self.assertEqual(str(exif), "{274: 1}")
|
|
||||||
|
|
|
@ -2,57 +2,61 @@ from PIL import Image, ImageFilter
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
im = Image.open("Tests/images/hopper.ppm")
|
|
||||||
snakes = Image.open("Tests/images/color_snakes.png")
|
|
||||||
|
|
||||||
|
|
||||||
class TestImageOpsUsm(PillowTestCase):
|
class TestImageOpsUsm(PillowTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestImageOpsUsm, self).setUp()
|
||||||
|
self.im = Image.open("Tests/images/hopper.ppm")
|
||||||
|
self.addCleanup(self.im.close)
|
||||||
|
self.snakes = Image.open("Tests/images/color_snakes.png")
|
||||||
|
self.addCleanup(self.snakes.close)
|
||||||
|
|
||||||
def test_filter_api(self):
|
def test_filter_api(self):
|
||||||
|
|
||||||
test_filter = ImageFilter.GaussianBlur(2.0)
|
test_filter = ImageFilter.GaussianBlur(2.0)
|
||||||
i = im.filter(test_filter)
|
i = self.im.filter(test_filter)
|
||||||
self.assertEqual(i.mode, "RGB")
|
self.assertEqual(i.mode, "RGB")
|
||||||
self.assertEqual(i.size, (128, 128))
|
self.assertEqual(i.size, (128, 128))
|
||||||
|
|
||||||
test_filter = ImageFilter.UnsharpMask(2.0, 125, 8)
|
test_filter = ImageFilter.UnsharpMask(2.0, 125, 8)
|
||||||
i = im.filter(test_filter)
|
i = self.im.filter(test_filter)
|
||||||
self.assertEqual(i.mode, "RGB")
|
self.assertEqual(i.mode, "RGB")
|
||||||
self.assertEqual(i.size, (128, 128))
|
self.assertEqual(i.size, (128, 128))
|
||||||
|
|
||||||
def test_usm_formats(self):
|
def test_usm_formats(self):
|
||||||
|
|
||||||
usm = ImageFilter.UnsharpMask
|
usm = ImageFilter.UnsharpMask
|
||||||
self.assertRaises(ValueError, im.convert("1").filter, usm)
|
self.assertRaises(ValueError, self.im.convert("1").filter, usm)
|
||||||
im.convert("L").filter(usm)
|
self.im.convert("L").filter(usm)
|
||||||
self.assertRaises(ValueError, im.convert("I").filter, usm)
|
self.assertRaises(ValueError, self.im.convert("I").filter, usm)
|
||||||
self.assertRaises(ValueError, im.convert("F").filter, usm)
|
self.assertRaises(ValueError, self.im.convert("F").filter, usm)
|
||||||
im.convert("RGB").filter(usm)
|
self.im.convert("RGB").filter(usm)
|
||||||
im.convert("RGBA").filter(usm)
|
self.im.convert("RGBA").filter(usm)
|
||||||
im.convert("CMYK").filter(usm)
|
self.im.convert("CMYK").filter(usm)
|
||||||
self.assertRaises(ValueError, im.convert("YCbCr").filter, usm)
|
self.assertRaises(ValueError, self.im.convert("YCbCr").filter, usm)
|
||||||
|
|
||||||
def test_blur_formats(self):
|
def test_blur_formats(self):
|
||||||
|
|
||||||
blur = ImageFilter.GaussianBlur
|
blur = ImageFilter.GaussianBlur
|
||||||
self.assertRaises(ValueError, im.convert("1").filter, blur)
|
self.assertRaises(ValueError, self.im.convert("1").filter, blur)
|
||||||
blur(im.convert("L"))
|
blur(self.im.convert("L"))
|
||||||
self.assertRaises(ValueError, im.convert("I").filter, blur)
|
self.assertRaises(ValueError, self.im.convert("I").filter, blur)
|
||||||
self.assertRaises(ValueError, im.convert("F").filter, blur)
|
self.assertRaises(ValueError, self.im.convert("F").filter, blur)
|
||||||
im.convert("RGB").filter(blur)
|
self.im.convert("RGB").filter(blur)
|
||||||
im.convert("RGBA").filter(blur)
|
self.im.convert("RGBA").filter(blur)
|
||||||
im.convert("CMYK").filter(blur)
|
self.im.convert("CMYK").filter(blur)
|
||||||
self.assertRaises(ValueError, im.convert("YCbCr").filter, blur)
|
self.assertRaises(ValueError, self.im.convert("YCbCr").filter, blur)
|
||||||
|
|
||||||
def test_usm_accuracy(self):
|
def test_usm_accuracy(self):
|
||||||
|
|
||||||
src = snakes.convert("RGB")
|
src = self.snakes.convert("RGB")
|
||||||
i = src.filter(ImageFilter.UnsharpMask(5, 1024, 0))
|
i = src.filter(ImageFilter.UnsharpMask(5, 1024, 0))
|
||||||
# Image should not be changed because it have only 0 and 255 levels.
|
# Image should not be changed because it have only 0 and 255 levels.
|
||||||
self.assertEqual(i.tobytes(), src.tobytes())
|
self.assertEqual(i.tobytes(), src.tobytes())
|
||||||
|
|
||||||
def test_blur_accuracy(self):
|
def test_blur_accuracy(self):
|
||||||
|
|
||||||
i = snakes.filter(ImageFilter.GaussianBlur(0.4))
|
i = self.snakes.filter(ImageFilter.GaussianBlur(0.4))
|
||||||
# These pixels surrounded with pixels with 255 intensity.
|
# These pixels surrounded with pixels with 255 intensity.
|
||||||
# They must be very close to 255.
|
# They must be very close to 255.
|
||||||
for x, y, c in [
|
for x, y, c in [
|
||||||
|
|
|
@ -24,25 +24,25 @@ class TestImageSequence(PillowTestCase):
|
||||||
self.assertRaises(AttributeError, ImageSequence.Iterator, 0)
|
self.assertRaises(AttributeError, ImageSequence.Iterator, 0)
|
||||||
|
|
||||||
def test_iterator(self):
|
def test_iterator(self):
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
i = ImageSequence.Iterator(im)
|
i = ImageSequence.Iterator(im)
|
||||||
for index in range(0, im.n_frames):
|
for index in range(0, im.n_frames):
|
||||||
self.assertEqual(i[index], next(i))
|
self.assertEqual(i[index], next(i))
|
||||||
self.assertRaises(IndexError, lambda: i[index + 1])
|
self.assertRaises(IndexError, lambda: i[index + 1])
|
||||||
self.assertRaises(StopIteration, next, i)
|
self.assertRaises(StopIteration, next, i)
|
||||||
|
|
||||||
def test_iterator_min_frame(self):
|
def test_iterator_min_frame(self):
|
||||||
im = Image.open("Tests/images/hopper.psd")
|
with Image.open("Tests/images/hopper.psd") as im:
|
||||||
i = ImageSequence.Iterator(im)
|
i = ImageSequence.Iterator(im)
|
||||||
for index in range(1, im.n_frames):
|
for index in range(1, im.n_frames):
|
||||||
self.assertEqual(i[index], next(i))
|
self.assertEqual(i[index], next(i))
|
||||||
|
|
||||||
def _test_multipage_tiff(self):
|
def _test_multipage_tiff(self):
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
for index, frame in enumerate(ImageSequence.Iterator(im)):
|
for index, frame in enumerate(ImageSequence.Iterator(im)):
|
||||||
frame.load()
|
frame.load()
|
||||||
self.assertEqual(index, im.tell())
|
self.assertEqual(index, im.tell())
|
||||||
frame.convert("RGB")
|
frame.convert("RGB")
|
||||||
|
|
||||||
def test_tiff(self):
|
def test_tiff(self):
|
||||||
self._test_multipage_tiff()
|
self._test_multipage_tiff()
|
||||||
|
@ -58,41 +58,41 @@ class TestImageSequence(PillowTestCase):
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
|
|
||||||
def test_consecutive(self):
|
def test_consecutive(self):
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
firstFrame = None
|
firstFrame = None
|
||||||
for frame in ImageSequence.Iterator(im):
|
for frame in ImageSequence.Iterator(im):
|
||||||
if firstFrame is None:
|
if firstFrame is None:
|
||||||
firstFrame = frame.copy()
|
firstFrame = frame.copy()
|
||||||
for frame in ImageSequence.Iterator(im):
|
for frame in ImageSequence.Iterator(im):
|
||||||
self.assert_image_equal(frame, firstFrame)
|
self.assert_image_equal(frame, firstFrame)
|
||||||
break
|
break
|
||||||
|
|
||||||
def test_palette_mmap(self):
|
def test_palette_mmap(self):
|
||||||
# Using mmap in ImageFile can require to reload the palette.
|
# Using mmap in ImageFile can require to reload the palette.
|
||||||
im = Image.open("Tests/images/multipage-mmap.tiff")
|
with Image.open("Tests/images/multipage-mmap.tiff") as im:
|
||||||
color1 = im.getpalette()[0:3]
|
color1 = im.getpalette()[0:3]
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
color2 = im.getpalette()[0:3]
|
color2 = im.getpalette()[0:3]
|
||||||
self.assertEqual(color1, color2)
|
self.assertEqual(color1, color2)
|
||||||
|
|
||||||
def test_all_frames(self):
|
def test_all_frames(self):
|
||||||
# Test a single image
|
# Test a single image
|
||||||
im = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as im:
|
||||||
ims = ImageSequence.all_frames(im)
|
ims = ImageSequence.all_frames(im)
|
||||||
|
|
||||||
self.assertEqual(len(ims), 42)
|
self.assertEqual(len(ims), 42)
|
||||||
for i, im_frame in enumerate(ims):
|
for i, im_frame in enumerate(ims):
|
||||||
self.assertFalse(im_frame is im)
|
self.assertFalse(im_frame is im)
|
||||||
|
|
||||||
im.seek(i)
|
im.seek(i)
|
||||||
self.assert_image_equal(im, im_frame)
|
self.assert_image_equal(im, im_frame)
|
||||||
|
|
||||||
# Test a series of images
|
# Test a series of images
|
||||||
ims = ImageSequence.all_frames([im, hopper(), im])
|
ims = ImageSequence.all_frames([im, hopper(), im])
|
||||||
self.assertEqual(len(ims), 85)
|
self.assertEqual(len(ims), 85)
|
||||||
|
|
||||||
# Test an operation
|
# Test an operation
|
||||||
ims = ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90))
|
ims = ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90))
|
||||||
for i, im_frame in enumerate(ims):
|
for i, im_frame in enumerate(ims):
|
||||||
im.seek(i)
|
im.seek(i)
|
||||||
self.assert_image_equal(im.rotate(90), im_frame)
|
self.assert_image_equal(im.rotate(90), im_frame)
|
||||||
|
|
|
@ -27,8 +27,8 @@ class TestImageShow(PillowTestCase):
|
||||||
ImageShow.register(viewer, -1)
|
ImageShow.register(viewer, -1)
|
||||||
|
|
||||||
for mode in ("1", "I;16", "LA", "RGB", "RGBA"):
|
for mode in ("1", "I;16", "LA", "RGB", "RGBA"):
|
||||||
im = hopper(mode)
|
with hopper() as im:
|
||||||
self.assertTrue(ImageShow.show(im))
|
self.assertTrue(ImageShow.show(im))
|
||||||
self.assertTrue(viewer.methodCalled)
|
self.assertTrue(viewer.methodCalled)
|
||||||
|
|
||||||
# Restore original state
|
# Restore original state
|
||||||
|
|
|
@ -24,13 +24,15 @@ path = "Tests/images/hopper.jpg"
|
||||||
|
|
||||||
class TestLocale(PillowTestCase):
|
class TestLocale(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
Image.open(path)
|
with Image.open(path):
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
locale.setlocale(locale.LC_ALL, "polish")
|
locale.setlocale(locale.LC_ALL, "polish")
|
||||||
except locale.Error:
|
except locale.Error:
|
||||||
unittest.skip("Polish locale not available")
|
unittest.skip("Polish locale not available")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Image.open(path)
|
with Image.open(path):
|
||||||
|
pass
|
||||||
finally:
|
finally:
|
||||||
locale.setlocale(locale.LC_ALL, (None, None))
|
locale.setlocale(locale.LC_ALL, (None, None))
|
||||||
|
|
|
@ -23,9 +23,9 @@ class TestMap(PillowTestCase):
|
||||||
Image.MAX_IMAGE_PIXELS = None
|
Image.MAX_IMAGE_PIXELS = None
|
||||||
|
|
||||||
# This image hits the offset test.
|
# This image hits the offset test.
|
||||||
im = Image.open("Tests/images/l2rgb_read.bmp")
|
with Image.open("Tests/images/l2rgb_read.bmp") as im:
|
||||||
with self.assertRaises((ValueError, MemoryError, IOError)):
|
with self.assertRaises((ValueError, MemoryError, IOError)):
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
Image.MAX_IMAGE_PIXELS = max_pixels
|
Image.MAX_IMAGE_PIXELS = max_pixels
|
||||||
|
|
||||||
|
|
|
@ -47,10 +47,10 @@ class TestModeI16(PillowTestCase):
|
||||||
filename = self.tempfile("temp.im")
|
filename = self.tempfile("temp.im")
|
||||||
imIn.save(filename)
|
imIn.save(filename)
|
||||||
|
|
||||||
imOut = Image.open(filename)
|
with Image.open(filename) as imOut:
|
||||||
|
|
||||||
self.verify(imIn)
|
self.verify(imIn)
|
||||||
self.verify(imOut)
|
self.verify(imOut)
|
||||||
|
|
||||||
imOut = imIn.crop((0, 0, w, h))
|
imOut = imIn.crop((0, 0, w, h))
|
||||||
self.verify(imOut)
|
self.verify(imOut)
|
||||||
|
|
|
@ -24,7 +24,8 @@ class TestShellInjection(PillowTestCase):
|
||||||
dest_file = self.tempfile(filename)
|
dest_file = self.tempfile(filename)
|
||||||
save_func(src_img, 0, dest_file)
|
save_func(src_img, 0, dest_file)
|
||||||
# If file can't be opened, shell injection probably occurred
|
# If file can't be opened, shell injection probably occurred
|
||||||
Image.open(dest_file).load()
|
with Image.open(dest_file) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
||||||
def test_load_djpeg_filename(self):
|
def test_load_djpeg_filename(self):
|
||||||
|
@ -32,8 +33,8 @@ class TestShellInjection(PillowTestCase):
|
||||||
src_file = self.tempfile(filename)
|
src_file = self.tempfile(filename)
|
||||||
shutil.copy(TEST_JPG, src_file)
|
shutil.copy(TEST_JPG, src_file)
|
||||||
|
|
||||||
im = Image.open(src_file)
|
with Image.open(src_file) as im:
|
||||||
im.load_djpeg()
|
im.load_djpeg()
|
||||||
|
|
||||||
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
|
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
|
||||||
def test_save_cjpeg_filename(self):
|
def test_save_cjpeg_filename(self):
|
||||||
|
@ -42,10 +43,12 @@ class TestShellInjection(PillowTestCase):
|
||||||
|
|
||||||
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
||||||
def test_save_netpbm_filename_bmp_mode(self):
|
def test_save_netpbm_filename_bmp_mode(self):
|
||||||
im = Image.open(TEST_GIF).convert("RGB")
|
with Image.open(TEST_GIF) as im:
|
||||||
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
|
im = im.convert("RGB")
|
||||||
|
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
|
||||||
|
|
||||||
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
||||||
def test_save_netpbm_filename_l_mode(self):
|
def test_save_netpbm_filename_l_mode(self):
|
||||||
im = Image.open(TEST_GIF).convert("L")
|
with Image.open(TEST_GIF) as im:
|
||||||
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
|
im = im.convert("L")
|
||||||
|
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
|
||||||
|
|
|
@ -54,5 +54,7 @@ class Test_IFDRational(PillowTestCase):
|
||||||
res = IFDRational(301, 1)
|
res = IFDRational(301, 1)
|
||||||
im.save(out, dpi=(res, res), compression="raw")
|
im.save(out, dpi=(res, res), compression="raw")
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertEqual(float(IFDRational(301, 1)), float(reloaded.tag_v2[282]))
|
self.assertEqual(
|
||||||
|
float(IFDRational(301, 1)), float(reloaded.tag_v2[282])
|
||||||
|
)
|
||||||
|
|
|
@ -63,10 +63,10 @@ jobs:
|
||||||
|
|
||||||
- template: .azure-pipelines/jobs/test-docker.yml
|
- template: .azure-pipelines/jobs/test-docker.yml
|
||||||
parameters:
|
parameters:
|
||||||
docker: 'fedora-29-amd64'
|
docker: 'fedora-30-amd64'
|
||||||
name: 'fedora_29_amd64'
|
name: 'fedora_30_amd64'
|
||||||
|
|
||||||
- template: .azure-pipelines/jobs/test-docker.yml
|
- template: .azure-pipelines/jobs/test-docker.yml
|
||||||
parameters:
|
parameters:
|
||||||
docker: 'fedora-30-amd64'
|
docker: 'fedora-31-amd64'
|
||||||
name: 'fedora_30_amd64'
|
name: 'fedora_31_amd64'
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# install extra test images
|
# install extra test images
|
||||||
|
|
||||||
rm -rf test_images
|
|
||||||
|
|
||||||
# Use SVN to just fetch a single Git subdirectory
|
# Use SVN to just fetch a single Git subdirectory
|
||||||
svn_checkout()
|
svn_export()
|
||||||
{
|
{
|
||||||
if [ ! -z $1 ]; then
|
if [ ! -z $1 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "Retrying svn checkout..."
|
echo "Retrying svn export..."
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
svn checkout https://github.com/python-pillow/pillow-depends/trunk/test_images
|
svn export --force https://github.com/python-pillow/pillow-depends/trunk/test_images ../Tests/images
|
||||||
}
|
}
|
||||||
svn_checkout || svn_checkout retry || svn_checkout retry || svn_checkout retry
|
svn_export || svn_export retry || svn_export retry || svn_export retry
|
||||||
|
|
||||||
cp -r test_images/* ../Tests/images
|
|
||||||
|
|
|
@ -6,12 +6,13 @@ Goals
|
||||||
|
|
||||||
The fork author's goal is to foster and support active development of PIL through:
|
The fork author's goal is to foster and support active development of PIL through:
|
||||||
|
|
||||||
- Continuous integration testing via `Travis CI`_ and `AppVeyor`_
|
- Continuous integration testing via `Travis CI`_, `AppVeyor`_ and `GitHub Actions`_
|
||||||
- Publicized development activity on `GitHub`_
|
- Publicized development activity on `GitHub`_
|
||||||
- Regular releases to the `Python Package Index`_
|
- Regular releases to the `Python Package Index`_
|
||||||
|
|
||||||
.. _Travis CI: https://travis-ci.org/python-pillow/Pillow
|
.. _Travis CI: https://travis-ci.org/python-pillow/Pillow
|
||||||
.. _AppVeyor: https://ci.appveyor.com/project/Python-pillow/pillow
|
.. _AppVeyor: https://ci.appveyor.com/project/Python-pillow/pillow
|
||||||
|
.. _GitHub Actions: https://github.com/python-pillow/Pillow/actions
|
||||||
.. _GitHub: https://github.com/python-pillow/Pillow
|
.. _GitHub: https://github.com/python-pillow/Pillow
|
||||||
.. _Python Package Index: https://pypi.org/project/Pillow/
|
.. _Python Package Index: https://pypi.org/project/Pillow/
|
||||||
|
|
||||||
|
|
|
@ -96,9 +96,9 @@ Create JPEG thumbnails
|
||||||
outfile = os.path.splitext(infile)[0] + ".thumbnail"
|
outfile = os.path.splitext(infile)[0] + ".thumbnail"
|
||||||
if infile != outfile:
|
if infile != outfile:
|
||||||
try:
|
try:
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
im.thumbnail(size)
|
im.thumbnail(size)
|
||||||
im.save(outfile, "JPEG")
|
im.save(outfile, "JPEG")
|
||||||
except IOError:
|
except IOError:
|
||||||
print("cannot create thumbnail for", infile)
|
print("cannot create thumbnail for", infile)
|
||||||
|
|
||||||
|
@ -263,7 +263,8 @@ Converting between modes
|
||||||
::
|
::
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
im = Image.open("hopper.ppm").convert("L")
|
with Image.open("hopper.ppm") as im:
|
||||||
|
im = im.convert("L")
|
||||||
|
|
||||||
The library supports transformations between each supported mode and the “L”
|
The library supports transformations between each supported mode and the “L”
|
||||||
and “RGB” modes. To convert between other modes, you may have to use an
|
and “RGB” modes. To convert between other modes, you may have to use an
|
||||||
|
@ -379,15 +380,15 @@ Reading sequences
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
im = Image.open("animation.gif")
|
with Image.open("animation.gif") as im:
|
||||||
im.seek(1) # skip to the second frame
|
im.seek(1) # skip to the second frame
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while 1:
|
while 1:
|
||||||
im.seek(im.tell()+1)
|
im.seek(im.tell()+1)
|
||||||
# do something to im
|
# do something to im
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass # end of sequence
|
pass # end of sequence
|
||||||
|
|
||||||
As seen in this example, you’ll get an :py:exc:`EOFError` exception when the
|
As seen in this example, you’ll get an :py:exc:`EOFError` exception when the
|
||||||
sequence ends.
|
sequence ends.
|
||||||
|
@ -418,32 +419,34 @@ Drawing Postscript
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL import PSDraw
|
from PIL import PSDraw
|
||||||
|
|
||||||
im = Image.open("hopper.ppm")
|
with Image.open("hopper.ppm") as im:
|
||||||
title = "hopper"
|
title = "hopper"
|
||||||
box = (1*72, 2*72, 7*72, 10*72) # in points
|
box = (1*72, 2*72, 7*72, 10*72) # in points
|
||||||
|
|
||||||
ps = PSDraw.PSDraw() # default is sys.stdout
|
ps = PSDraw.PSDraw() # default is sys.stdout
|
||||||
ps.begin_document(title)
|
ps.begin_document(title)
|
||||||
|
|
||||||
# draw the image (75 dpi)
|
# draw the image (75 dpi)
|
||||||
ps.image(box, im, 75)
|
ps.image(box, im, 75)
|
||||||
ps.rectangle(box)
|
ps.rectangle(box)
|
||||||
|
|
||||||
# draw title
|
# draw title
|
||||||
ps.setfont("HelveticaNarrow-Bold", 36)
|
ps.setfont("HelveticaNarrow-Bold", 36)
|
||||||
ps.text((3*72, 4*72), title)
|
ps.text((3*72, 4*72), title)
|
||||||
|
|
||||||
ps.end_document()
|
ps.end_document()
|
||||||
|
|
||||||
More on reading images
|
More on reading images
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
As described earlier, the :py:func:`~PIL.Image.open` function of the
|
As described earlier, the :py:func:`~PIL.Image.open` function of the
|
||||||
:py:mod:`~PIL.Image` module is used to open an image file. In most cases, you
|
:py:mod:`~PIL.Image` module is used to open an image file. In most cases, you
|
||||||
simply pass it the filename as an argument::
|
simply pass it the filename as an argument. ``Image.open()`` can be used a
|
||||||
|
context manager::
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
im = Image.open("hopper.ppm")
|
with Image.open("hopper.ppm") as im:
|
||||||
|
...
|
||||||
|
|
||||||
If everything goes well, the result is an :py:class:`PIL.Image.Image` object.
|
If everything goes well, the result is an :py:class:`PIL.Image.Image` object.
|
||||||
Otherwise, an :exc:`IOError` exception is raised.
|
Otherwise, an :exc:`IOError` exception is raised.
|
||||||
|
@ -509,11 +512,12 @@ This is only available for JPEG and MPO files.
|
||||||
::
|
::
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
im = Image.open(file)
|
|
||||||
print("original =", im.mode, im.size)
|
|
||||||
|
|
||||||
im.draft("L", (100, 100))
|
with Image.open(file) as im:
|
||||||
print("draft =", im.mode, im.size)
|
print("original =", im.mode, im.size)
|
||||||
|
|
||||||
|
im.draft("L", (100, 100))
|
||||||
|
print("draft =", im.mode, im.size)
|
||||||
|
|
||||||
This prints something like::
|
This prints something like::
|
||||||
|
|
||||||
|
|
|
@ -400,10 +400,10 @@ These platforms are built and tested for every change.
|
||||||
+----------------------------------+-------------------------------+-----------------------+
|
+----------------------------------+-------------------------------+-----------------------+
|
||||||
| Debian 10 Buster | 2.7, 3.7 |x86 |
|
| Debian 10 Buster | 2.7, 3.7 |x86 |
|
||||||
+----------------------------------+-------------------------------+-----------------------+
|
+----------------------------------+-------------------------------+-----------------------+
|
||||||
| Fedora 29 | 2.7, 3.7 |x86-64 |
|
|
||||||
+----------------------------------+-------------------------------+-----------------------+
|
|
||||||
| Fedora 30 | 2.7, 3.7 |x86-64 |
|
| Fedora 30 | 2.7, 3.7 |x86-64 |
|
||||||
+----------------------------------+-------------------------------+-----------------------+
|
+----------------------------------+-------------------------------+-----------------------+
|
||||||
|
| Fedora 31 | 3.7 |x86-64 |
|
||||||
|
+----------------------------------+-------------------------------+-----------------------+
|
||||||
| macOS 10.13 High Sierra* | 2.7, 3.5, 3.6, 3.7, 3.8 |x86-64 |
|
| macOS 10.13 High Sierra* | 2.7, 3.5, 3.6, 3.7, 3.8 |x86-64 |
|
||||||
+----------------------------------+-------------------------------+-----------------------+
|
+----------------------------------+-------------------------------+-----------------------+
|
||||||
| Ubuntu Linux 16.04 LTS | 2.7, 3.5, 3.6, 3.7, 3.8, |x86-64 |
|
| Ubuntu Linux 16.04 LTS | 2.7, 3.5, 3.6, 3.7, 3.8, |x86-64 |
|
||||||
|
|
|
@ -8,27 +8,25 @@ object, or a file-like object. Pillow uses the filename or ``Path`` to open a
|
||||||
file, so for the rest of this article, they will all be treated as a file-like
|
file, so for the rest of this article, they will all be treated as a file-like
|
||||||
object.
|
object.
|
||||||
|
|
||||||
The first four of these items are equivalent, the last is dangerous
|
The following are all equivalent::
|
||||||
and may fail::
|
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import io
|
import io
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
im = Image.open('test.jpg')
|
with Image.open('test.jpg') as im:
|
||||||
|
...
|
||||||
|
|
||||||
im2 = Image.open(pathlib.Path('test.jpg'))
|
with Image.open(pathlib.Path('test.jpg')) as im2:
|
||||||
|
...
|
||||||
|
|
||||||
f = open('test.jpg', 'rb')
|
with open('test.jpg', 'rb') as f:
|
||||||
im3 = Image.open(f)
|
im3 = Image.open(f)
|
||||||
|
...
|
||||||
|
|
||||||
with open('test.jpg', 'rb') as f:
|
with open('test.jpg', 'rb') as f:
|
||||||
im4 = Image.open(io.BytesIO(f.read()))
|
im4 = Image.open(io.BytesIO(f.read()))
|
||||||
|
...
|
||||||
# Dangerous FAIL:
|
|
||||||
with open('test.jpg', 'rb') as f:
|
|
||||||
im5 = Image.open(f)
|
|
||||||
im5.load() # FAILS, closed file
|
|
||||||
|
|
||||||
If a filename or a path-like object is passed to Pillow, then the resulting
|
If a filename or a path-like object is passed to Pillow, then the resulting
|
||||||
file object opened by Pillow may also be closed by Pillow after the
|
file object opened by Pillow may also be closed by Pillow after the
|
||||||
|
@ -38,13 +36,6 @@ have multiple frames.
|
||||||
Pillow cannot in general close and reopen a file, so any access to
|
Pillow cannot in general close and reopen a file, so any access to
|
||||||
that file needs to be prior to the close.
|
that file needs to be prior to the close.
|
||||||
|
|
||||||
Issues
|
|
||||||
------
|
|
||||||
|
|
||||||
* Using the file context manager to provide a file-like object to
|
|
||||||
Pillow is dangerous unless the context of the image is limited to
|
|
||||||
the context of the file.
|
|
||||||
|
|
||||||
Image Lifecycle
|
Image Lifecycle
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
@ -70,9 +61,9 @@ Image Lifecycle
|
||||||
... # image operations here.
|
... # image operations here.
|
||||||
|
|
||||||
|
|
||||||
The lifecycle of a single-frame image is relatively simple. The file
|
The lifecycle of a single-frame image is relatively simple. The file must
|
||||||
must remain open until the ``load()`` or ``close()`` function is
|
remain open until the ``load()`` or ``close()`` function is called or the
|
||||||
called.
|
context manager exits.
|
||||||
|
|
||||||
Multi-frame images are more complicated. The ``load()`` method is not
|
Multi-frame images are more complicated. The ``load()`` method is not
|
||||||
a terminal method, so it should not close the underlying file. In general,
|
a terminal method, so it should not close the underlying file. In general,
|
||||||
|
@ -87,14 +78,16 @@ Complications
|
||||||
libtiff (if working on an actual file). Since libtiff closes the file
|
libtiff (if working on an actual file). Since libtiff closes the file
|
||||||
descriptor internally, it is duplicated prior to passing it into libtiff.
|
descriptor internally, it is duplicated prior to passing it into libtiff.
|
||||||
|
|
||||||
* I don't think that there's any way to make this safe without
|
* After a file has been closed, operations that require file access will fail::
|
||||||
changing the lazy loading::
|
|
||||||
|
|
||||||
# Dangerous FAIL:
|
|
||||||
with open('test.jpg', 'rb') as f:
|
with open('test.jpg', 'rb') as f:
|
||||||
im5 = Image.open(f)
|
im5 = Image.open(f)
|
||||||
im5.load() # FAILS, closed file
|
im5.load() # FAILS, closed file
|
||||||
|
|
||||||
|
with Image.open('test.jpg') as im6:
|
||||||
|
pass
|
||||||
|
im6.load() # FAILS, closed file
|
||||||
|
|
||||||
|
|
||||||
Proposed File Handling
|
Proposed File Handling
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -104,5 +97,6 @@ Proposed File Handling
|
||||||
|
|
||||||
* ``Image.Image.seek()`` should never close the image file.
|
* ``Image.Image.seek()`` should never close the image file.
|
||||||
|
|
||||||
* Users of the library should call ``Image.Image.close()`` on any
|
* Users of the library should use a context manager or call
|
||||||
multi-frame image to ensure that the underlying file is closed.
|
``Image.Image.close()`` on any image opened with a filename or ``Path``
|
||||||
|
object to ensure that the underlying file is closed.
|
||||||
|
|
|
@ -41,8 +41,8 @@ def testimage():
|
||||||
|
|
||||||
Or open existing files:
|
Or open existing files:
|
||||||
|
|
||||||
>>> im = Image.open("Tests/images/hopper.gif")
|
>>> with Image.open("Tests/images/hopper.gif") as im:
|
||||||
>>> _info(im)
|
... _info(im)
|
||||||
('GIF', 'P', (128, 128))
|
('GIF', 'P', (128, 128))
|
||||||
>>> _info(Image.open("Tests/images/hopper.ppm"))
|
>>> _info(Image.open("Tests/images/hopper.ppm"))
|
||||||
('PPM', 'RGB', (128, 128))
|
('PPM', 'RGB', (128, 128))
|
||||||
|
|
|
@ -119,7 +119,7 @@ def decode_dxt3(data):
|
||||||
bits = struct.unpack_from("<8B", block)
|
bits = struct.unpack_from("<8B", block)
|
||||||
color0, color1 = struct.unpack_from("<HH", block, 8)
|
color0, color1 = struct.unpack_from("<HH", block, 8)
|
||||||
|
|
||||||
code, = struct.unpack_from("<I", block, 12)
|
(code,) = struct.unpack_from("<I", block, 12)
|
||||||
|
|
||||||
r0, g0, b0 = unpack_565(color0)
|
r0, g0, b0 = unpack_565(color0)
|
||||||
r1, g1, b1 = unpack_565(color1)
|
r1, g1, b1 = unpack_565(color1)
|
||||||
|
@ -177,7 +177,7 @@ def decode_dxt5(data):
|
||||||
|
|
||||||
color0, color1 = struct.unpack_from("<HH", block, 8)
|
color0, color1 = struct.unpack_from("<HH", block, 8)
|
||||||
|
|
||||||
code, = struct.unpack_from("<I", block, 12)
|
(code,) = struct.unpack_from("<I", block, 12)
|
||||||
|
|
||||||
r0, g0, b0 = unpack_565(color0)
|
r0, g0, b0 = unpack_565(color0)
|
||||||
r1, g1, b1 = unpack_565(color1)
|
r1, g1, b1 = unpack_565(color1)
|
||||||
|
@ -255,19 +255,19 @@ class BlpImageFile(ImageFile.ImageFile):
|
||||||
self.tile = [(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))]
|
self.tile = [(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))]
|
||||||
|
|
||||||
def _read_blp_header(self):
|
def _read_blp_header(self):
|
||||||
self._blp_compression, = struct.unpack("<i", self.fp.read(4))
|
(self._blp_compression,) = struct.unpack("<i", self.fp.read(4))
|
||||||
|
|
||||||
self._blp_encoding, = struct.unpack("<b", self.fp.read(1))
|
(self._blp_encoding,) = struct.unpack("<b", self.fp.read(1))
|
||||||
self._blp_alpha_depth, = struct.unpack("<b", self.fp.read(1))
|
(self._blp_alpha_depth,) = struct.unpack("<b", self.fp.read(1))
|
||||||
self._blp_alpha_encoding, = struct.unpack("<b", self.fp.read(1))
|
(self._blp_alpha_encoding,) = struct.unpack("<b", self.fp.read(1))
|
||||||
self._blp_mips, = struct.unpack("<b", self.fp.read(1))
|
(self._blp_mips,) = struct.unpack("<b", self.fp.read(1))
|
||||||
|
|
||||||
self._size = struct.unpack("<II", self.fp.read(8))
|
self._size = struct.unpack("<II", self.fp.read(8))
|
||||||
|
|
||||||
if self.magic == b"BLP1":
|
if self.magic == b"BLP1":
|
||||||
# Only present for BLP1
|
# Only present for BLP1
|
||||||
self._blp_encoding, = struct.unpack("<i", self.fp.read(4))
|
(self._blp_encoding,) = struct.unpack("<i", self.fp.read(4))
|
||||||
self._blp_subtype, = struct.unpack("<i", self.fp.read(4))
|
(self._blp_subtype,) = struct.unpack("<i", self.fp.read(4))
|
||||||
|
|
||||||
self._blp_offsets = struct.unpack("<16I", self.fp.read(16 * 4))
|
self._blp_offsets = struct.unpack("<16I", self.fp.read(16 * 4))
|
||||||
self._blp_lengths = struct.unpack("<16I", self.fp.read(16 * 4))
|
self._blp_lengths = struct.unpack("<16I", self.fp.read(16 * 4))
|
||||||
|
@ -297,19 +297,19 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _read_blp_header(self):
|
def _read_blp_header(self):
|
||||||
self._blp_compression, = struct.unpack("<i", self.fd.read(4))
|
(self._blp_compression,) = struct.unpack("<i", self.fd.read(4))
|
||||||
|
|
||||||
self._blp_encoding, = struct.unpack("<b", self.fd.read(1))
|
(self._blp_encoding,) = struct.unpack("<b", self.fd.read(1))
|
||||||
self._blp_alpha_depth, = struct.unpack("<b", self.fd.read(1))
|
(self._blp_alpha_depth,) = struct.unpack("<b", self.fd.read(1))
|
||||||
self._blp_alpha_encoding, = struct.unpack("<b", self.fd.read(1))
|
(self._blp_alpha_encoding,) = struct.unpack("<b", self.fd.read(1))
|
||||||
self._blp_mips, = struct.unpack("<b", self.fd.read(1))
|
(self._blp_mips,) = struct.unpack("<b", self.fd.read(1))
|
||||||
|
|
||||||
self.size = struct.unpack("<II", self.fd.read(8))
|
self.size = struct.unpack("<II", self.fd.read(8))
|
||||||
|
|
||||||
if self.magic == b"BLP1":
|
if self.magic == b"BLP1":
|
||||||
# Only present for BLP1
|
# Only present for BLP1
|
||||||
self._blp_encoding, = struct.unpack("<i", self.fd.read(4))
|
(self._blp_encoding,) = struct.unpack("<i", self.fd.read(4))
|
||||||
self._blp_subtype, = struct.unpack("<i", self.fd.read(4))
|
(self._blp_subtype,) = struct.unpack("<i", self.fd.read(4))
|
||||||
|
|
||||||
self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4))
|
self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4))
|
||||||
self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4))
|
self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4))
|
||||||
|
@ -327,7 +327,7 @@ class BLP1Decoder(_BLPBaseDecoder):
|
||||||
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
|
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
offset, = struct.unpack("<B", _data.read(1))
|
(offset,) = struct.unpack("<B", _data.read(1))
|
||||||
except struct.error:
|
except struct.error:
|
||||||
break
|
break
|
||||||
b, g, r, a = palette[offset]
|
b, g, r, a = palette[offset]
|
||||||
|
@ -346,7 +346,7 @@ class BLP1Decoder(_BLPBaseDecoder):
|
||||||
def _decode_jpeg_stream(self):
|
def _decode_jpeg_stream(self):
|
||||||
from PIL.JpegImagePlugin import JpegImageFile
|
from PIL.JpegImagePlugin import JpegImageFile
|
||||||
|
|
||||||
jpeg_header_size, = struct.unpack("<I", self.fd.read(4))
|
(jpeg_header_size,) = struct.unpack("<I", self.fd.read(4))
|
||||||
jpeg_header = self.fd.read(jpeg_header_size)
|
jpeg_header = self.fd.read(jpeg_header_size)
|
||||||
self.fd.read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
|
self.fd.read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
|
||||||
data = self.fd.read(self._blp_lengths[0])
|
data = self.fd.read(self._blp_lengths[0])
|
||||||
|
@ -372,7 +372,7 @@ class BLP2Decoder(_BLPBaseDecoder):
|
||||||
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
|
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
offset, = struct.unpack("<B", _data.read(1))
|
(offset,) = struct.unpack("<B", _data.read(1))
|
||||||
except struct.error:
|
except struct.error:
|
||||||
break
|
break
|
||||||
b, g, r, a = palette[offset]
|
b, g, r, a = palette[offset]
|
||||||
|
|
|
@ -122,7 +122,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
# pixel format
|
# pixel format
|
||||||
pfsize, pfflags = struct.unpack("<2I", header.read(8))
|
pfsize, pfflags = struct.unpack("<2I", header.read(8))
|
||||||
fourcc = header.read(4)
|
fourcc = header.read(4)
|
||||||
bitcount, = struct.unpack("<I", header.read(4))
|
(bitcount,) = struct.unpack("<I", header.read(4))
|
||||||
masks = struct.unpack("<4I", header.read(16))
|
masks = struct.unpack("<4I", header.read(16))
|
||||||
if pfflags & 0x40:
|
if pfflags & 0x40:
|
||||||
# DDPF_RGB - Texture contains uncompressed RGB data
|
# DDPF_RGB - Texture contains uncompressed RGB data
|
||||||
|
|
|
@ -79,7 +79,7 @@ class FtexImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
format, where = struct.unpack("<2i", self.fp.read(8))
|
format, where = struct.unpack("<2i", self.fp.read(8))
|
||||||
self.fp.seek(where)
|
self.fp.seek(where)
|
||||||
mipmap_size, = struct.unpack("<i", self.fp.read(4))
|
(mipmap_size,) = struct.unpack("<i", self.fp.read(4))
|
||||||
|
|
||||||
data = self.fp.read(mipmap_size)
|
data = self.fp.read(mipmap_size)
|
||||||
|
|
||||||
|
|
|
@ -597,9 +597,6 @@ class Image:
|
||||||
# object is gone.
|
# object is gone.
|
||||||
self.im = deferred_error(ValueError("Operation on closed image"))
|
self.im = deferred_error(ValueError("Operation on closed image"))
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.__exit__()
|
|
||||||
|
|
||||||
def _copy(self):
|
def _copy(self):
|
||||||
self.load()
|
self.load()
|
||||||
self.im = self.im.copy()
|
self.im = self.im.copy()
|
||||||
|
@ -3190,7 +3187,7 @@ class Exif(MutableMapping):
|
||||||
continue
|
continue
|
||||||
size = count * unit_size
|
size = count * unit_size
|
||||||
if size > 4:
|
if size > 4:
|
||||||
offset, = struct.unpack("<L", data)
|
(offset,) = struct.unpack("<L", data)
|
||||||
data = ifd_data[offset - 12 : offset + size - 12]
|
data = ifd_data[offset - 12 : offset + size - 12]
|
||||||
else:
|
else:
|
||||||
data = data[:size]
|
data = data[:size]
|
||||||
|
@ -3220,7 +3217,7 @@ class Exif(MutableMapping):
|
||||||
)
|
)
|
||||||
if ifd_tag == 0x1101:
|
if ifd_tag == 0x1101:
|
||||||
# CameraInfo
|
# CameraInfo
|
||||||
offset, = struct.unpack(">L", data)
|
(offset,) = struct.unpack(">L", data)
|
||||||
self.fp.seek(offset)
|
self.fp.seek(offset)
|
||||||
|
|
||||||
camerainfo = {"ModelID": self.fp.read(4)}
|
camerainfo = {"ModelID": self.fp.read(4)}
|
||||||
|
|
|
@ -103,21 +103,24 @@ class ImageFile(Image.Image):
|
||||||
self._exclusive_fp = None
|
self._exclusive_fp = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._open()
|
try:
|
||||||
except (
|
self._open()
|
||||||
IndexError, # end of data
|
except (
|
||||||
TypeError, # end of data (ord)
|
IndexError, # end of data
|
||||||
KeyError, # unsupported mode
|
TypeError, # end of data (ord)
|
||||||
EOFError, # got header but not the first frame
|
KeyError, # unsupported mode
|
||||||
struct.error,
|
EOFError, # got header but not the first frame
|
||||||
) as v:
|
struct.error,
|
||||||
|
) as v:
|
||||||
|
raise SyntaxError(v)
|
||||||
|
|
||||||
|
if not self.mode or self.size[0] <= 0:
|
||||||
|
raise SyntaxError("not identified by this driver")
|
||||||
|
except BaseException:
|
||||||
# close the file only if we have opened it this constructor
|
# close the file only if we have opened it this constructor
|
||||||
if self._exclusive_fp:
|
if self._exclusive_fp:
|
||||||
self.fp.close()
|
self.fp.close()
|
||||||
raise SyntaxError(v)
|
raise
|
||||||
|
|
||||||
if not self.mode or self.size[0] <= 0:
|
|
||||||
raise SyntaxError("not identified by this driver")
|
|
||||||
|
|
||||||
def draft(self, mode, size):
|
def draft(self, mode, size):
|
||||||
"""Set draft mode"""
|
"""Set draft mode"""
|
||||||
|
|
|
@ -216,7 +216,8 @@ def loadImageSeries(filelist=None):
|
||||||
print("unable to find %s" % img)
|
print("unable to find %s" % img)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
im = Image.open(img).convert2byte()
|
with Image.open(img) as im:
|
||||||
|
im = im.convert2byte()
|
||||||
except Exception:
|
except Exception:
|
||||||
if not isSpiderImage(img):
|
if not isSpiderImage(img):
|
||||||
print(img + " is not a Spider image file")
|
print(img + " is not a Spider image file")
|
||||||
|
|
|
@ -63,8 +63,5 @@ class TarIO(ContainerIO.ContainerIO):
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.fh.close()
|
self.fh.close()
|
||||||
|
|
|
@ -470,7 +470,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
else:
|
else:
|
||||||
raise SyntaxError("not a TIFF IFD")
|
raise SyntaxError("not a TIFF IFD")
|
||||||
self.reset()
|
self.reset()
|
||||||
self.next, = self._unpack("L", ifh[4:])
|
(self.next,) = self._unpack("L", ifh[4:])
|
||||||
self._legacy_api = False
|
self._legacy_api = False
|
||||||
|
|
||||||
prefix = property(lambda self: self._prefix)
|
prefix = property(lambda self: self._prefix)
|
||||||
|
@ -570,7 +570,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
]: # rationals
|
]: # rationals
|
||||||
values = (values,)
|
values = (values,)
|
||||||
try:
|
try:
|
||||||
dest[tag], = values
|
(dest[tag],) = values
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# We've got a builtin tag with 1 expected entry
|
# We've got a builtin tag with 1 expected entry
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
|
@ -738,7 +738,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
size = count * unit_size
|
size = count * unit_size
|
||||||
if size > 4:
|
if size > 4:
|
||||||
here = fp.tell()
|
here = fp.tell()
|
||||||
offset, = self._unpack("L", data)
|
(offset,) = self._unpack("L", data)
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print(
|
print(
|
||||||
"Tag Location: {} - Data Location: {}".format(here, offset),
|
"Tag Location: {} - Data Location: {}".format(here, offset),
|
||||||
|
@ -770,7 +770,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
else:
|
else:
|
||||||
print("- value:", self[tag])
|
print("- value:", self[tag])
|
||||||
|
|
||||||
self.next, = self._unpack("L", self._ensure_read(fp, 4))
|
(self.next,) = self._unpack("L", self._ensure_read(fp, 4))
|
||||||
except OSError as msg:
|
except OSError as msg:
|
||||||
warnings.warn(str(msg))
|
warnings.warn(str(msg))
|
||||||
return
|
return
|
||||||
|
@ -1766,11 +1766,11 @@ class AppendingTiffWriter:
|
||||||
return self.f.write(data)
|
return self.f.write(data)
|
||||||
|
|
||||||
def readShort(self):
|
def readShort(self):
|
||||||
value, = struct.unpack(self.shortFmt, self.f.read(2))
|
(value,) = struct.unpack(self.shortFmt, self.f.read(2))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def readLong(self):
|
def readLong(self):
|
||||||
value, = struct.unpack(self.longFmt, self.f.read(4))
|
(value,) = struct.unpack(self.longFmt, self.f.read(4))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def rewriteLastShortToLong(self, value):
|
def rewriteLastShortToLong(self, value):
|
||||||
|
|
|
@ -45,9 +45,7 @@ def extract(src, dest):
|
||||||
|
|
||||||
def extract_libs():
|
def extract_libs():
|
||||||
for name, lib in libs.items():
|
for name, lib in libs.items():
|
||||||
filename = lib["filename"]
|
filename = fetch(lib["url"])
|
||||||
if not os.path.exists(filename):
|
|
||||||
filename = fetch(lib["url"])
|
|
||||||
if name == "openjpeg":
|
if name == "openjpeg":
|
||||||
for compiler in all_compilers():
|
for compiler in all_compilers():
|
||||||
if not os.path.exists(
|
if not os.path.exists(
|
||||||
|
|
|
@ -26,96 +26,95 @@ libs = {
|
||||||
# },
|
# },
|
||||||
"zlib": {
|
"zlib": {
|
||||||
"url": "http://zlib.net/zlib1211.zip",
|
"url": "http://zlib.net/zlib1211.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "zlib1211.zip",
|
"filename": "zlib1211.zip",
|
||||||
"dir": "zlib-1.2.11",
|
"dir": "zlib-1.2.11",
|
||||||
},
|
},
|
||||||
"jpeg": {
|
"jpeg": {
|
||||||
"url": "http://www.ijg.org/files/jpegsr9c.zip",
|
"url": "http://www.ijg.org/files/jpegsr9c.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "jpegsr9c.zip",
|
"filename": "jpegsr9c.zip",
|
||||||
"dir": "jpeg-9c",
|
"dir": "jpeg-9c",
|
||||||
},
|
},
|
||||||
"tiff": {
|
"tiff": {
|
||||||
"url": "ftp://download.osgeo.org/libtiff/tiff-4.0.10.tar.gz",
|
"url": "ftp://download.osgeo.org/libtiff/tiff-4.0.10.tar.gz",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "tiff-4.0.10.tar.gz",
|
"filename": "tiff-4.0.10.tar.gz",
|
||||||
"dir": "tiff-4.0.10",
|
"dir": "tiff-4.0.10",
|
||||||
},
|
},
|
||||||
"freetype": {
|
"freetype": {
|
||||||
"url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz", # noqa: E501
|
"url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz", # noqa: E501
|
||||||
"filename": PILLOW_DEPENDS_DIR + "freetype-2.10.1.tar.gz",
|
"filename": "freetype-2.10.1.tar.gz",
|
||||||
"dir": "freetype-2.10.1",
|
"dir": "freetype-2.10.1",
|
||||||
},
|
},
|
||||||
"lcms-2.7": {
|
"lcms-2.7": {
|
||||||
"url": SF_MIRROR + "/project/lcms/lcms/2.7/lcms2-2.7.zip",
|
"url": SF_MIRROR + "/project/lcms/lcms/2.7/lcms2-2.7.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "lcms2-2.7.zip",
|
"filename": "lcms2-2.7.zip",
|
||||||
"dir": "lcms2-2.7",
|
"dir": "lcms2-2.7",
|
||||||
},
|
},
|
||||||
"lcms-2.8": {
|
"lcms-2.8": {
|
||||||
"url": SF_MIRROR + "/project/lcms/lcms/2.8/lcms2-2.8.zip",
|
"url": SF_MIRROR + "/project/lcms/lcms/2.8/lcms2-2.8.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "lcms2-2.8.zip",
|
"filename": "lcms2-2.8.zip",
|
||||||
"dir": "lcms2-2.8",
|
"dir": "lcms2-2.8",
|
||||||
},
|
},
|
||||||
"ghostscript": {
|
"ghostscript": {
|
||||||
"url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs927/ghostscript-9.27.tar.gz", # noqa: E501
|
"url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs927/ghostscript-9.27.tar.gz", # noqa: E501
|
||||||
"filename": PILLOW_DEPENDS_DIR + "ghostscript-9.27.tar.gz",
|
"filename": "ghostscript-9.27.tar.gz",
|
||||||
"dir": "ghostscript-9.27",
|
"dir": "ghostscript-9.27",
|
||||||
},
|
},
|
||||||
"tcl-8.5": {
|
"tcl-8.5": {
|
||||||
"url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tcl8519-src.zip",
|
"url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tcl8519-src.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "tcl8519-src.zip",
|
"filename": "tcl8519-src.zip",
|
||||||
"dir": "",
|
"dir": "",
|
||||||
},
|
},
|
||||||
"tk-8.5": {
|
"tk-8.5": {
|
||||||
"url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tk8519-src.zip",
|
"url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tk8519-src.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "tk8519-src.zip",
|
"filename": "tk8519-src.zip",
|
||||||
"dir": "",
|
"dir": "",
|
||||||
"version": "8.5.19",
|
"version": "8.5.19",
|
||||||
},
|
},
|
||||||
"tcl-8.6": {
|
"tcl-8.6": {
|
||||||
"url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tcl869-src.zip",
|
"url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tcl869-src.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "tcl869-src.zip",
|
"filename": "tcl869-src.zip",
|
||||||
"dir": "",
|
"dir": "",
|
||||||
},
|
},
|
||||||
"tk-8.6": {
|
"tk-8.6": {
|
||||||
"url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tk869-src.zip",
|
"url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tk869-src.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "tk869-src.zip",
|
"filename": "tk869-src.zip",
|
||||||
"dir": "",
|
"dir": "",
|
||||||
"version": "8.6.9",
|
"version": "8.6.9",
|
||||||
},
|
},
|
||||||
"webp": {
|
"webp": {
|
||||||
"url": "http://downloads.webmproject.org/releases/webp/libwebp-1.0.3.tar.gz",
|
"url": "http://downloads.webmproject.org/releases/webp/libwebp-1.0.3.tar.gz",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "libwebp-1.0.3.tar.gz",
|
"filename": "libwebp-1.0.3.tar.gz",
|
||||||
"dir": "libwebp-1.0.3",
|
"dir": "libwebp-1.0.3",
|
||||||
},
|
},
|
||||||
"openjpeg": {
|
"openjpeg": {
|
||||||
"url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz",
|
"url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "openjpeg-2.3.1.tar.gz",
|
"filename": "openjpeg-2.3.1.tar.gz",
|
||||||
"dir": "openjpeg-2.3.1",
|
"dir": "openjpeg-2.3.1",
|
||||||
},
|
},
|
||||||
"jpeg-turbo": {
|
"jpeg-turbo": {
|
||||||
"url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz",
|
"url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "libjpeg-turbo-2.0.3.tar.gz",
|
"filename": "libjpeg-turbo-2.0.3.tar.gz",
|
||||||
"dir": "libjpeg-turbo-2.0.3",
|
"dir": "libjpeg-turbo-2.0.3",
|
||||||
},
|
},
|
||||||
# ba653c8: Merge tag '2.12.5' into msvc
|
# ba653c8: Merge tag '2.12.5' into msvc
|
||||||
"imagequant": {
|
"imagequant": {
|
||||||
"url": "https://github.com/ImageOptim/libimagequant/archive/ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip", # noqa: E501
|
"url": "https://github.com/ImageOptim/libimagequant/archive/ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip", # noqa: E501
|
||||||
"filename": PILLOW_DEPENDS_DIR
|
"filename": "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip",
|
||||||
+ "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip",
|
|
||||||
"dir": "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971",
|
"dir": "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971",
|
||||||
},
|
},
|
||||||
"harfbuzz": {
|
"harfbuzz": {
|
||||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.1.zip",
|
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.1.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "harfbuzz-2.6.1.zip",
|
"filename": "harfbuzz-2.6.1.zip",
|
||||||
"dir": "harfbuzz-2.6.1",
|
"dir": "harfbuzz-2.6.1",
|
||||||
},
|
},
|
||||||
"fribidi": {
|
"fribidi": {
|
||||||
"url": "https://github.com/fribidi/fribidi/archive/v1.0.7.zip",
|
"url": "https://github.com/fribidi/fribidi/archive/v1.0.7.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "fribidi-1.0.7.zip",
|
"filename": "fribidi-1.0.7.zip",
|
||||||
"dir": "fribidi-1.0.7",
|
"dir": "fribidi-1.0.7",
|
||||||
},
|
},
|
||||||
"libraqm": {
|
"libraqm": {
|
||||||
"url": "https://github.com/HOST-Oman/libraqm/archive/v0.7.0.zip",
|
"url": "https://github.com/HOST-Oman/libraqm/archive/v0.7.0.zip",
|
||||||
"filename": PILLOW_DEPENDS_DIR + "libraqm-0.7.0.zip",
|
"filename": "libraqm-0.7.0.zip",
|
||||||
"dir": "libraqm-0.7.0",
|
"dir": "libraqm-0.7.0",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,37 @@ import sys
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
|
from config import libs
|
||||||
|
|
||||||
|
|
||||||
def fetch(url):
|
def fetch(url):
|
||||||
|
depends_filename = None
|
||||||
|
for lib in libs.values():
|
||||||
|
if lib["url"] == url:
|
||||||
|
depends_filename = lib["filename"]
|
||||||
|
break
|
||||||
|
if depends_filename and os.path.exists(depends_filename):
|
||||||
|
return depends_filename
|
||||||
name = urllib.parse.urlsplit(url)[2].split("/")[-1]
|
name = urllib.parse.urlsplit(url)[2].split("/")[-1]
|
||||||
|
|
||||||
if not os.path.exists(name):
|
if not os.path.exists(name):
|
||||||
print("Fetching", url)
|
|
||||||
|
def retrieve(request_url):
|
||||||
|
print("Fetching", request_url)
|
||||||
|
try:
|
||||||
|
return urllib.request.urlopen(request_url)
|
||||||
|
except urllib.error.URLError:
|
||||||
|
return urllib.request.urlopen(request_url)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = urllib.request.urlopen(url)
|
r = retrieve(url)
|
||||||
except urllib.error.URLError:
|
except urllib.error.HTTPError:
|
||||||
r = urllib.request.urlopen(url)
|
if depends_filename:
|
||||||
|
r = retrieve(
|
||||||
|
"https://github.com/python-pillow/pillow-depends/raw/master/"
|
||||||
|
+ depends_filename
|
||||||
|
)
|
||||||
|
name = depends_filename
|
||||||
content = r.read()
|
content = r.read()
|
||||||
with open(name, "wb") as fd:
|
with open(name, "wb") as fd:
|
||||||
fd.write(content)
|
fd.write(content)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user