Pillow/Tests/test_file_libtiff.py

849 lines
31 KiB
Python
Raw Normal View History

import base64
import distutils.version
2014-11-16 07:38:52 +03:00
import io
import itertools
import logging
import os
from collections import namedtuple
from ctypes import c_float
from PIL import Image, TiffImagePlugin, TiffTags, features
from .helper import PillowTestCase, hopper
logger = logging.getLogger(__name__)
class LibTiffTestCase(PillowTestCase):
2014-06-10 13:10:47 +04:00
def setUp(self):
2019-06-13 18:54:11 +03:00
if not features.check("libtiff"):
2014-06-10 13:10:47 +04:00
self.skipTest("tiff support not available")
2014-06-10 13:10:47 +04:00
def _assert_noerr(self, im):
"""Helper tests that assert basic sanity about the g4 tiff reading"""
# 1 bit
self.assertEqual(im.mode, "1")
2014-06-10 13:10:47 +04:00
# Does the data actually load
im.load()
im.getdata()
2014-06-10 13:10:47 +04:00
try:
2019-06-13 18:54:11 +03:00
self.assertEqual(im._compression, "group4")
2018-06-11 09:00:57 +03:00
except AttributeError:
2014-06-10 13:10:47 +04:00
print("No _compression")
2015-04-24 02:26:52 +03:00
print(dir(im))
2014-06-10 13:10:47 +04:00
# can we write it back out, in a different form.
out = self.tempfile("temp.png")
im.save(out)
2016-11-23 17:24:40 +03:00
out_bytes = io.BytesIO()
2019-06-13 18:54:11 +03:00
im.save(out_bytes, format="tiff", compression="group4")
2017-04-20 14:14:23 +03:00
class TestFileLibTiff(LibTiffTestCase):
2014-06-10 13:10:47 +04:00
def test_g4_tiff(self):
"""Test the ordinary file path load path"""
2015-04-02 11:45:24 +03:00
test_file = "Tests/images/hopper_g4_500.tif"
2019-11-25 23:03:23 +03:00
with Image.open(test_file) as im:
self.assertEqual(im.size, (500, 500))
self._assert_noerr(im)
2014-06-10 13:10:47 +04:00
def test_g4_large(self):
2015-04-24 11:24:52 +03:00
test_file = "Tests/images/pport_g4.tif"
2019-11-25 23:03:23 +03:00
with Image.open(test_file) as im:
self._assert_noerr(im)
2014-06-10 13:10:47 +04:00
def test_g4_tiff_file(self):
"""Testing the string load path"""
2015-04-02 11:45:24 +03:00
test_file = "Tests/images/hopper_g4_500.tif"
2019-06-13 18:54:11 +03:00
with open(test_file, "rb") as f:
2019-11-25 23:03:23 +03:00
with Image.open(f) as im:
self.assertEqual(im.size, (500, 500))
self._assert_noerr(im)
2014-06-10 13:10:47 +04:00
def test_g4_tiff_bytesio(self):
"""Testing the stringio loading code path"""
2015-04-02 11:45:24 +03:00
test_file = "Tests/images/hopper_g4_500.tif"
2014-11-16 07:38:52 +03:00
s = io.BytesIO()
2019-06-13 18:54:11 +03:00
with open(test_file, "rb") as f:
2014-06-10 13:10:47 +04:00
s.write(f.read())
s.seek(0)
2019-11-25 23:03:23 +03:00
with Image.open(s) as im:
self.assertEqual(im.size, (500, 500))
self._assert_noerr(im)
2014-06-10 13:10:47 +04:00
def test_g4_non_disk_file_object(self):
"""Testing loading from non-disk non-BytesIO file object"""
test_file = "Tests/images/hopper_g4_500.tif"
s = io.BytesIO()
with open(test_file, "rb") as f:
s.write(f.read())
s.seek(0)
r = io.BufferedReader(s)
2019-11-25 23:03:23 +03:00
with Image.open(r) as im:
self.assertEqual(im.size, (500, 500))
self._assert_noerr(im)
2014-06-10 13:10:47 +04:00
def test_g4_eq_png(self):
""" Checking that we're actually getting the data that we expect"""
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/hopper_bw_500.png") as png:
with Image.open("Tests/images/hopper_g4_500.tif") as g4:
self.assert_image_equal(g4, png)
2014-06-10 13:10:47 +04:00
# see https://github.com/python-pillow/Pillow/issues/279
def test_g4_fillorder_eq_png(self):
""" Checking that we're actually getting the data that we expect"""
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/g4-fillorder-test.png") as png:
with Image.open("Tests/images/g4-fillorder-test.tif") as g4:
self.assert_image_equal(g4, png)
2014-06-10 13:10:47 +04:00
def test_g4_write(self):
"""Checking to see that the saved image is the same as what we wrote"""
2015-04-02 11:45:24 +03:00
test_file = "Tests/images/hopper_g4_500.tif"
2019-11-25 23:03:23 +03:00
with Image.open(test_file) as orig:
out = self.tempfile("temp.tif")
rot = orig.transpose(Image.ROTATE_90)
self.assertEqual(rot.size, (500, 500))
rot.save(out)
2014-06-10 13:10:47 +04:00
2019-11-25 23:03:23 +03:00
with Image.open(out) as reread:
self.assertEqual(reread.size, (500, 500))
self._assert_noerr(reread)
self.assert_image_equal(reread, rot)
self.assertEqual(reread.info["compression"], "group4")
2014-06-10 13:10:47 +04:00
2019-11-25 23:03:23 +03:00
self.assertEqual(reread.info["compression"], orig.info["compression"])
2014-06-10 13:10:47 +04:00
2019-11-25 23:03:23 +03:00
self.assertNotEqual(orig.tobytes(), reread.tobytes())
2014-06-10 13:10:47 +04:00
def test_adobe_deflate_tiff(self):
2015-04-02 11:45:24 +03:00
test_file = "Tests/images/tiff_adobe_deflate.tif"
2019-11-25 23:03:23 +03:00
with Image.open(test_file) as im:
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (278, 374))
self.assertEqual(im.tile[0][:3], ("libtiff", (0, 0, 278, 374), 0))
im.load()
2014-06-10 13:10:47 +04:00
2019-11-25 23:03:23 +03:00
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
2014-06-10 13:10:47 +04:00
def test_write_metadata(self):
""" Test metadata writing through libtiff """
for legacy_api in [False, True]:
2019-06-13 18:54:11 +03:00
f = self.tempfile("temp.tiff")
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open("Tests/images/hopper_g4.tif") as img:
img.save(f, tiffinfo=img.tag)
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
if legacy_api:
original = img.tag.named()
else:
original = img.tag_v2.named()
# PhotometricInterpretation is set from SAVE_INFO,
# not the original image.
2019-06-13 18:54:11 +03:00
ignored = [
"StripByteCounts",
"RowsPerStrip",
"PageNumber",
"PhotometricInterpretation",
]
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open(f) as loaded:
if legacy_api:
reloaded = loaded.tag.named()
else:
reloaded = loaded.tag_v2.named()
2019-06-13 18:54:11 +03:00
for tag, value in itertools.chain(reloaded.items(), original.items()):
if tag not in ignored:
val = original[tag]
2019-06-13 18:54:11 +03:00
if tag.endswith("Resolution"):
if legacy_api:
self.assertEqual(
c_float(val[0][0] / val[0][1]).value,
c_float(value[0][0] / value[0][1]).value,
2019-06-13 18:54:11 +03:00
msg="%s didn't roundtrip" % tag,
)
else:
self.assertEqual(
2019-06-13 18:54:11 +03:00
c_float(val).value,
c_float(value).value,
msg="%s didn't roundtrip" % tag,
)
else:
2019-06-13 18:54:11 +03:00
self.assertEqual(val, value, msg="%s didn't roundtrip" % tag)
2014-06-10 13:10:47 +04:00
2015-12-14 23:24:01 +03:00
# https://github.com/python-pillow/Pillow/issues/1561
2019-06-13 18:54:11 +03:00
requested_fields = ["StripByteCounts", "RowsPerStrip", "StripOffsets"]
2015-12-14 23:24:01 +03:00
for field in requested_fields:
self.assertIn(field, reloaded, "%s not in metadata" % field)
2015-12-14 23:24:01 +03:00
2016-01-01 16:30:40 +03:00
def test_additional_metadata(self):
# these should not crash. Seriously dummy data, most of it doesn't make
# any sense, so we're running up against limits where we're asking
# libtiff to do stupid things.
2016-02-05 01:57:13 +03:00
2016-01-01 16:30:40 +03:00
# Get the list of the ones that we should be able to write
2019-06-13 18:54:11 +03:00
core_items = {
tag: info
for tag, info in ((s, TiffTags.lookup(s)) for s in TiffTags.LIBTIFF_CORE)
if info.type is not None
}
2016-02-05 01:57:13 +03:00
2016-08-04 09:40:12 +03:00
# Exclude ones that have special meaning
# that we're already testing them
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/hopper_g4.tif") as im:
for tag in im.tag_v2:
try:
del core_items[tag]
except KeyError:
pass
# Type codes:
# 2: "ascii",
# 3: "short",
# 4: "long",
# 5: "rational",
# 12: "double",
# Type: dummy value
values = {
2: "test",
3: 1,
4: 2 ** 20,
5: TiffImagePlugin.IFDRational(100, 1),
12: 1.05,
}
new_ifd = TiffImagePlugin.ImageFileDirectory_v2()
for tag, info in core_items.items():
if info.length == 1:
new_ifd[tag] = values[info.type]
if info.length == 0:
new_ifd[tag] = tuple(values[info.type] for _ in range(3))
else:
new_ifd[tag] = tuple(values[info.type] for _ in range(info.length))
2016-01-01 16:30:40 +03:00
2019-11-25 23:03:23 +03:00
# Extra samples really doesn't make sense in this application.
del new_ifd[338]
2016-01-01 16:30:40 +03:00
2019-11-25 23:03:23 +03:00
out = self.tempfile("temp.tif")
TiffImagePlugin.WRITE_LIBTIFF = True
2016-01-01 16:30:40 +03:00
2019-11-25 23:03:23 +03:00
im.save(out, tiffinfo=new_ifd)
2016-01-01 16:30:40 +03:00
2016-02-05 01:57:13 +03:00
TiffImagePlugin.WRITE_LIBTIFF = False
2016-01-01 16:30:40 +03:00
2018-10-25 11:45:13 +03:00
def test_custom_metadata(self):
tc = namedtuple("test_case", "value,type,supported_by_default")
2018-10-25 11:45:13 +03:00
custom = {
37000 + k: v
for k, v in enumerate(
[
tc(4, TiffTags.SHORT, True),
tc(123456789, TiffTags.LONG, True),
tc(-4, TiffTags.SIGNED_BYTE, False),
tc(-4, TiffTags.SIGNED_SHORT, False),
tc(-123456789, TiffTags.SIGNED_LONG, False),
tc(TiffImagePlugin.IFDRational(4, 7), TiffTags.RATIONAL, True),
tc(4.25, TiffTags.FLOAT, True),
tc(4.25, TiffTags.DOUBLE, True),
tc("custom tag value", TiffTags.ASCII, True),
tc(b"custom tag value", TiffTags.BYTE, True),
tc((4, 5, 6), TiffTags.SHORT, True),
tc((123456789, 9, 34, 234, 219387, 92432323), TiffTags.LONG, True),
tc((-4, 9, 10), TiffTags.SIGNED_BYTE, False),
tc((-4, 5, 6), TiffTags.SIGNED_SHORT, False),
tc(
(-123456789, 9, 34, 234, 219387, -92432323),
TiffTags.SIGNED_LONG,
False,
),
tc((4.25, 5.25), TiffTags.FLOAT, True),
tc((4.25, 5.25), TiffTags.DOUBLE, True),
# array of TIFF_BYTE requires bytes instead of tuple for backwards
# compatibility
tc(bytes([4]), TiffTags.BYTE, True),
tc(bytes((4, 9, 10)), TiffTags.BYTE, True),
]
)
2018-10-25 11:45:13 +03:00
}
libtiff_version = TiffImagePlugin._libtiff_version()
libtiffs = [False]
2019-06-13 18:54:11 +03:00
if distutils.version.StrictVersion(
libtiff_version
) >= distutils.version.StrictVersion("4.0"):
libtiffs.append(True)
for libtiff in libtiffs:
2018-10-25 11:45:13 +03:00
TiffImagePlugin.WRITE_LIBTIFF = libtiff
2019-02-22 15:17:26 +03:00
def check_tags(tiffinfo):
im = hopper()
2018-10-25 11:45:13 +03:00
2019-02-22 15:17:26 +03:00
out = self.tempfile("temp.tif")
im.save(out, tiffinfo=tiffinfo)
2018-10-25 11:45:13 +03:00
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open(out) as reloaded:
for tag, value in tiffinfo.items():
reloaded_value = reloaded.tag_v2[tag]
if (
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
and libtiff
):
# libtiff does not support real RATIONALS
self.assertAlmostEqual(float(reloaded_value), float(value))
continue
2019-02-22 15:17:26 +03:00
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
if libtiff and isinstance(value, bytes):
value = value.decode()
2019-02-22 15:17:26 +03:00
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
self.assertEqual(reloaded_value, value)
2019-02-22 15:17:26 +03:00
# Test with types
ifd = TiffImagePlugin.ImageFileDirectory_v2()
for tag, tagdata in custom.items():
ifd[tag] = tagdata.value
ifd.tagtype[tag] = tagdata.type
2019-02-22 15:17:26 +03:00
check_tags(ifd)
# Test without types. This only works for some types, int for example are
# always encoded as LONG and not SIGNED_LONG.
check_tags(
{
tag: tagdata.value
for tag, tagdata in custom.items()
if tagdata.supported_by_default
}
)
2019-02-22 15:17:26 +03:00
TiffImagePlugin.WRITE_LIBTIFF = False
2018-10-25 11:45:13 +03:00
2016-12-13 23:49:47 +03:00
def test_int_dpi(self):
# issue #1765
2019-06-13 18:54:11 +03:00
im = hopper("RGB")
out = self.tempfile("temp.tif")
2016-12-13 23:49:47 +03:00
TiffImagePlugin.WRITE_LIBTIFF = True
im.save(out, dpi=(72, 72))
TiffImagePlugin.WRITE_LIBTIFF = False
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open(out) as reloaded:
self.assertEqual(reloaded.info["dpi"], (72.0, 72.0))
2016-12-13 23:49:47 +03:00
2014-06-10 13:10:47 +04:00
def test_g3_compression(self):
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/hopper_g4_500.tif") as i:
out = self.tempfile("temp.tif")
i.save(out, compression="group3")
2014-06-10 13:10:47 +04:00
2019-11-25 23:03:23 +03:00
with Image.open(out) as reread:
self.assertEqual(reread.info["compression"], "group3")
self.assert_image_equal(reread, i)
2014-06-10 13:10:47 +04:00
def test_little_endian(self):
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/16bit.deflate.tif") as im:
self.assertEqual(im.getpixel((0, 0)), 480)
self.assertEqual(im.mode, "I;16")
b = im.tobytes()
# Bytes are in image native order (little endian)
self.assertEqual(b[0], ord(b"\xe0"))
self.assertEqual(b[1], ord(b"\x01"))
out = self.tempfile("temp.tif")
# out = "temp.le.tif"
im.save(out)
with Image.open(out) as reread:
self.assertEqual(reread.info["compression"], im.info["compression"])
self.assertEqual(reread.getpixel((0, 0)), 480)
2014-06-10 13:10:47 +04:00
# UNDONE - libtiff defaults to writing in native endian, so
# on big endian, we'll get back mode = 'I;16B' here.
def test_big_endian(self):
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/16bit.MM.deflate.tif") as im:
self.assertEqual(im.getpixel((0, 0)), 480)
self.assertEqual(im.mode, "I;16B")
2014-06-10 13:10:47 +04:00
2019-11-25 23:03:23 +03:00
b = im.tobytes()
2014-06-10 13:10:47 +04:00
2019-11-25 23:03:23 +03:00
# Bytes are in image native order (big endian)
self.assertEqual(b[0], ord(b"\x01"))
self.assertEqual(b[1], ord(b"\xe0"))
2014-06-10 13:10:47 +04:00
2019-11-25 23:03:23 +03:00
out = self.tempfile("temp.tif")
im.save(out)
with Image.open(out) as reread:
self.assertEqual(reread.info["compression"], im.info["compression"])
self.assertEqual(reread.getpixel((0, 0)), 480)
2014-06-10 13:10:47 +04:00
def test_g4_string_info(self):
"""Tests String data in info directory"""
2015-04-02 11:45:24 +03:00
test_file = "Tests/images/hopper_g4_500.tif"
2019-11-25 23:03:23 +03:00
with Image.open(test_file) as orig:
out = self.tempfile("temp.tif")
2014-06-10 13:10:47 +04:00
2019-11-25 23:03:23 +03:00
orig.tag[269] = "temp.tif"
orig.save(out)
2014-06-10 13:10:47 +04:00
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open(out) as reread:
self.assertEqual("temp.tif", reread.tag_v2[269])
self.assertEqual("temp.tif", reread.tag[269][0])
2014-06-10 13:10:47 +04:00
def test_12bit_rawmode(self):
""" Are we generating the same interpretation
of the image as Imagemagick is? """
TiffImagePlugin.READ_LIBTIFF = True
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/12bit.cropped.tif") as im:
im.load()
TiffImagePlugin.READ_LIBTIFF = False
# to make the target --
# convert 12bit.cropped.tif -depth 16 tmp.tif
# convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
# imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
# so we need to unshift so that the integer values are the same.
2014-06-10 13:10:47 +04:00
2019-11-25 23:03:23 +03:00
self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif")
2014-06-10 13:10:47 +04:00
def test_blur(self):
# test case from irc, how to do blur on b/w image
# and save to compressed tif.
from PIL import ImageFilter
2019-06-13 18:54:11 +03:00
out = self.tempfile("temp.tif")
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/pport_g4.tif") as im:
im = im.convert("L")
2014-06-10 13:10:47 +04:00
im = im.filter(ImageFilter.GaussianBlur(4))
2019-06-13 18:54:11 +03:00
im.save(out, compression="tiff_adobe_deflate")
2019-11-25 23:03:23 +03:00
with Image.open(out) as im2:
im2.load()
2013-10-22 21:09:25 +04:00
2019-11-25 23:03:23 +03:00
self.assert_image_equal(im, im2)
2014-06-10 13:10:47 +04:00
def test_compressions(self):
# Test various tiff compressions and assert similar image content but reduced
# file sizes.
2019-06-13 18:54:11 +03:00
im = hopper("RGB")
out = self.tempfile("temp.tif")
im.save(out)
size_raw = os.path.getsize(out)
2019-06-13 18:54:11 +03:00
for compression in ("packbits", "tiff_lzw"):
2014-06-10 13:10:47 +04:00
im.save(out, compression=compression)
size_compressed = os.path.getsize(out)
2019-11-25 23:03:23 +03:00
with Image.open(out) as im2:
self.assert_image_equal(im, im2)
2013-11-06 08:53:18 +04:00
2019-06-13 18:54:11 +03:00
im.save(out, compression="jpeg")
size_jpeg = os.path.getsize(out)
2019-11-25 23:03:23 +03:00
with Image.open(out) as im2:
self.assert_image_similar(im, im2, 30)
im.save(out, compression="jpeg", quality=30)
size_jpeg_30 = os.path.getsize(out)
2019-11-25 23:03:23 +03:00
with Image.open(out) as im3:
self.assert_image_similar(im2, im3, 30)
2019-06-30 11:13:02 +03:00
self.assertGreater(size_raw, size_compressed)
self.assertGreater(size_compressed, size_jpeg)
self.assertGreater(size_jpeg, size_jpeg_30)
def test_quality(self):
im = hopper("RGB")
out = self.tempfile("temp.tif")
self.assertRaises(ValueError, im.save, out, compression="tiff_lzw", quality=50)
self.assertRaises(ValueError, im.save, out, compression="jpeg", quality=-1)
self.assertRaises(ValueError, im.save, out, compression="jpeg", quality=101)
self.assertRaises(ValueError, im.save, out, compression="jpeg", quality="good")
im.save(out, compression="jpeg", quality=0)
im.save(out, compression="jpeg", quality=100)
2014-06-10 13:10:47 +04:00
def test_cmyk_save(self):
2019-06-13 18:54:11 +03:00
im = hopper("CMYK")
out = self.tempfile("temp.tif")
2014-06-10 13:10:47 +04:00
2019-06-13 18:54:11 +03:00
im.save(out, compression="tiff_adobe_deflate")
2019-11-25 23:03:23 +03:00
with Image.open(out) as im2:
self.assert_image_equal(im, im2)
def xtest_bw_compression_w_rgb(self):
2014-06-10 13:10:47 +04:00
""" This test passes, but when running all tests causes a failure due
to output on stderr from the error thrown by libtiff. We need to
capture that but not now"""
2019-06-13 18:54:11 +03:00
im = hopper("RGB")
out = self.tempfile("temp.tif")
2019-06-13 18:54:11 +03:00
self.assertRaises(IOError, im.save, out, compression="tiff_ccitt")
self.assertRaises(IOError, im.save, out, compression="group3")
self.assertRaises(IOError, im.save, out, compression="group4")
2014-06-10 13:10:47 +04:00
def test_fp_leak(self):
2014-09-23 17:16:04 +04:00
im = Image.open("Tests/images/hopper_g4_500.tif")
2014-06-10 13:10:47 +04:00
fn = im.fp.fileno()
2014-06-10 13:10:47 +04:00
os.fstat(fn)
im.load() # this should close it.
2017-09-01 14:05:40 +03:00
self.assertRaises(OSError, os.fstat, fn)
2014-06-10 13:10:47 +04:00
im = None # this should force even more closed.
2017-09-01 14:05:40 +03:00
self.assertRaises(OSError, os.fstat, fn)
self.assertRaises(OSError, os.close, fn)
2014-08-21 08:43:46 +04:00
def test_multipage(self):
# issue #862
TiffImagePlugin.READ_LIBTIFF = True
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open("Tests/images/multipage.tiff") as im:
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
2014-08-21 08:43:46 +04:00
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
im.seek(0)
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
self.assertTrue(im.tag.next)
2014-08-21 08:43:46 +04:00
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
im.seek(1)
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
self.assertTrue(im.tag.next)
2014-08-21 08:43:46 +04:00
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
im.seek(2)
self.assertFalse(im.tag.next)
self.assertEqual(im.size, (20, 20))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
2014-08-21 08:43:46 +04:00
TiffImagePlugin.READ_LIBTIFF = False
2016-11-17 15:43:11 +03:00
def test_multipage_nframes(self):
# issue #862
TiffImagePlugin.READ_LIBTIFF = True
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open("Tests/images/multipage.tiff") as im:
frames = im.n_frames
self.assertEqual(frames, 3)
for _ in range(frames):
im.seek(0)
# Should not raise ValueError: I/O operation on closed file
im.load()
2016-11-17 15:43:11 +03:00
TiffImagePlugin.READ_LIBTIFF = False
2014-08-21 08:43:46 +04:00
def test__next(self):
TiffImagePlugin.READ_LIBTIFF = True
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/hopper.tif") as im:
self.assertFalse(im.tag.next)
im.load()
self.assertFalse(im.tag.next)
2014-10-29 21:07:20 +03:00
def test_4bit(self):
# Arrange
test_file = "Tests/images/hopper_gray_4bpp.tif"
original = hopper("L")
# Act
TiffImagePlugin.READ_LIBTIFF = True
2019-11-25 23:03:23 +03:00
with Image.open(test_file) as im:
TiffImagePlugin.READ_LIBTIFF = False
2014-10-29 21:07:20 +03:00
2019-11-25 23:03:23 +03:00
# Assert
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.mode, "L")
self.assert_image_similar(im, original, 7.3)
2014-10-29 21:07:20 +03:00
def test_gray_semibyte_per_pixel(self):
test_files = (
(
2016-08-04 09:40:12 +03:00
24.8, # epsilon
( # group
"Tests/images/tiff_gray_2_4_bpp/hopper2.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper2I.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper2R.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif",
2019-06-13 18:54:11 +03:00
),
),
(
2016-08-04 09:40:12 +03:00
7.3, # epsilon
( # group
"Tests/images/tiff_gray_2_4_bpp/hopper4.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper4I.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper4R.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif",
2019-06-13 18:54:11 +03:00
),
),
)
original = hopper("L")
for epsilon, group in test_files:
2019-11-25 23:03:23 +03:00
with Image.open(group[0]) as im:
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.mode, "L")
self.assert_image_similar(im, original, epsilon)
for file in group[1:]:
2019-11-25 23:03:23 +03:00
with Image.open(file) as im2:
self.assertEqual(im2.size, (128, 128))
self.assertEqual(im2.mode, "L")
self.assert_image_equal(im, im2)
2014-11-16 07:38:52 +03:00
def test_save_bytesio(self):
# PR 1011
# Test TIFF saving to io.BytesIO() object.
2014-11-16 07:38:52 +03:00
TiffImagePlugin.WRITE_LIBTIFF = True
TiffImagePlugin.READ_LIBTIFF = True
# Generate test image
pilim = hopper()
def save_bytesio(compression=None):
2014-11-16 07:38:52 +03:00
buffer_io = io.BytesIO()
pilim.save(buffer_io, format="tiff", compression=compression)
buffer_io.seek(0)
2019-11-25 23:03:23 +03:00
with Image.open(buffer_io) as pilim_load:
self.assert_image_similar(pilim, pilim_load, 0)
save_bytesio()
2019-06-13 18:54:11 +03:00
save_bytesio("raw")
2014-11-16 07:38:52 +03:00
save_bytesio("packbits")
save_bytesio("tiff_lzw")
2014-11-16 07:38:52 +03:00
TiffImagePlugin.WRITE_LIBTIFF = False
TiffImagePlugin.READ_LIBTIFF = False
2014-10-29 21:07:20 +03:00
def test_crashing_metadata(self):
# issue 1597
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/rdf.tif") as im:
out = self.tempfile("temp.tif")
2019-11-25 23:03:23 +03:00
TiffImagePlugin.WRITE_LIBTIFF = True
# this shouldn't crash
im.save(out, format="TIFF")
TiffImagePlugin.WRITE_LIBTIFF = False
def test_page_number_x_0(self):
# Issue 973
# Test TIFF with tag 297 (Page Number) having value of 0 0.
# The first number is the current page number.
# The second is the total number of pages, zero means not available.
outfile = self.tempfile("temp.tif")
# Created by printing a page in Chrome to PDF, then:
# /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
# -dNOPAUSE /tmp/test.pdf -c quit
infile = "Tests/images/total-pages-zero.tif"
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open(infile) as im:
# Should not divide by zero
im.save(outfile)
2016-09-30 00:29:19 +03:00
def test_fd_duplication(self):
# https://github.com/python-pillow/Pillow/issues/1651
tmpfile = self.tempfile("temp.tif")
2019-06-13 18:54:11 +03:00
with open(tmpfile, "wb") as f:
with open("Tests/images/g4-multi.tiff", "rb") as src:
2016-09-30 00:29:19 +03:00
f.write(src.read())
im = Image.open(tmpfile)
2018-06-15 12:55:48 +03:00
im.n_frames
2016-09-30 00:29:19 +03:00
im.close()
# Should not raise PermissionError.
os.remove(tmpfile)
2016-09-30 00:29:19 +03:00
def test_read_icc(self):
with Image.open("Tests/images/hopper.iccprofile.tif") as img:
2019-06-13 18:54:11 +03:00
icc = img.info.get("icc_profile")
2018-08-04 21:08:40 +03:00
self.assertIsNotNone(icc)
TiffImagePlugin.READ_LIBTIFF = True
with Image.open("Tests/images/hopper.iccprofile.tif") as img:
2019-06-13 18:54:11 +03:00
icc_libtiff = img.info.get("icc_profile")
2018-08-04 21:08:40 +03:00
self.assertIsNotNone(icc_libtiff)
TiffImagePlugin.READ_LIBTIFF = False
self.assertEqual(icc, icc_libtiff)
def test_multipage_compression(self):
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open("Tests/images/compression.tif") as im:
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
im.seek(0)
self.assertEqual(im._compression, "tiff_ccitt")
self.assertEqual(im.size, (10, 10))
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
im.seek(1)
self.assertEqual(im._compression, "packbits")
self.assertEqual(im.size, (10, 10))
im.load()
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
im.seek(0)
self.assertEqual(im._compression, "tiff_ccitt")
self.assertEqual(im.size, (10, 10))
im.load()
def test_save_tiff_with_jpegtables(self):
# Arrange
outfile = self.tempfile("temp.tif")
# Created with ImageMagick: convert hopper.jpg hopper_jpg.tif
# Contains JPEGTables (347) tag
infile = "Tests/images/hopper_jpg.tif"
2019-11-25 23:03:23 +03:00
with Image.open(infile) as im:
# Act / Assert
# Should not raise UnicodeDecodeError or anything else
im.save(outfile)
2019-05-08 22:08:17 +03:00
def test_16bit_RGB_tiff(self):
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/tiff_16bit_RGB.tiff") as im:
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (100, 40))
self.assertEqual(
im.tile,
[
(
"libtiff",
(0, 0, 100, 40),
0,
("RGB;16N", "tiff_adobe_deflate", False, 8),
)
],
)
im.load()
2019-05-08 22:08:17 +03:00
2019-11-25 23:03:23 +03:00
self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png")
2019-05-08 22:08:17 +03:00
def test_16bit_RGBa_tiff(self):
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/tiff_16bit_RGBa.tiff") as im:
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (100, 40))
self.assertEqual(
im.tile,
[
(
"libtiff",
(0, 0, 100, 40),
0,
("RGBa;16N", "tiff_lzw", False, 38236),
)
],
)
im.load()
2019-11-25 23:03:23 +03:00
self.assert_image_equal_tofile(
im, "Tests/images/tiff_16bit_RGBa_target.png"
)
def test_gimp_tiff(self):
# Read TIFF JPEG images from GIMP [@PIL168]
codecs = dir(Image.core)
if "jpeg_decoder" not in codecs:
self.skipTest("jpeg support not available")
filename = "Tests/images/pil168.tif"
2019-11-25 23:03:23 +03:00
with Image.open(filename) as im:
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (256, 256))
self.assertEqual(
im.tile,
[("libtiff", (0, 0, 256, 256), 0, ("RGB", "jpeg", False, 5122))],
)
im.load()
2019-11-25 23:03:23 +03:00
self.assert_image_equal_tofile(im, "Tests/images/pil168.png")
def test_sampleformat(self):
# https://github.com/python-pillow/Pillow/issues/1466
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/copyleft.tiff") as im:
self.assertEqual(im.mode, "RGB")
2019-11-25 23:03:23 +03:00
self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode="RGB")
def test_lzw(self):
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/hopper_lzw.tif") as im:
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "TIFF")
im2 = hopper()
self.assert_image_similar(im, im2, 5)
def test_strip_cmyk_jpeg(self):
infile = "Tests/images/tiff_strip_cmyk_jpeg.tif"
2019-11-25 23:03:23 +03:00
with Image.open(infile) as im:
self.assert_image_similar_tofile(
im, "Tests/images/pil_sample_cmyk.jpg", 0.5
)
2019-04-30 17:42:30 +03:00
def test_strip_cmyk_16l_jpeg(self):
infile = "Tests/images/tiff_strip_cmyk_16l_jpeg.tif"
2019-11-25 23:03:23 +03:00
with Image.open(infile) as im:
self.assert_image_similar_tofile(
im, "Tests/images/pil_sample_cmyk.jpg", 0.5
)
2019-04-30 17:42:30 +03:00
def test_strip_ycbcr_jpeg_2x2_sampling(self):
infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif"
2019-11-25 23:03:23 +03:00
with Image.open(infile) as im:
self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
def test_strip_ycbcr_jpeg_1x1_sampling(self):
infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif"
2019-11-25 23:03:23 +03:00
with Image.open(infile) as im:
self.assert_image_equal_tofile(im, "Tests/images/flower2.jpg")
def test_tiled_cmyk_jpeg(self):
infile = "Tests/images/tiff_tiled_cmyk_jpeg.tif"
2019-11-25 23:03:23 +03:00
with Image.open(infile) as im:
self.assert_image_similar_tofile(
im, "Tests/images/pil_sample_cmyk.jpg", 0.5
)
def test_tiled_ycbcr_jpeg_1x1_sampling(self):
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif"
2019-11-25 23:03:23 +03:00
with Image.open(infile) as im:
self.assert_image_equal_tofile(im, "Tests/images/flower2.jpg")
def test_tiled_ycbcr_jpeg_2x2_sampling(self):
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif"
2019-11-25 23:03:23 +03:00
with Image.open(infile) as im:
self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
def test_old_style_jpeg(self):
infile = "Tests/images/old-style-jpeg-compression.tif"
2019-11-25 23:03:23 +03:00
with Image.open(infile) as im:
self.assert_image_equal_tofile(
im, "Tests/images/old-style-jpeg-compression.png"
)
2019-09-13 15:36:26 +03:00
2019-09-18 15:07:17 +03:00
def test_no_rows_per_strip(self):
# This image does not have a RowsPerStrip TIFF tag
infile = "Tests/images/no_rows_per_strip.tif"
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open(infile) as im:
im.load()
2019-09-18 15:07:17 +03:00
self.assertEqual(im.size, (950, 975))
2019-09-20 22:59:29 +03:00
2019-09-13 15:36:26 +03:00
def test_orientation(self):
2019-11-25 23:03:23 +03:00
with Image.open("Tests/images/g4_orientation_1.tif") as base_im:
for i in range(2, 9):
with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im:
im.load()
2019-09-13 15:36:26 +03:00
2019-11-25 23:03:23 +03:00
self.assert_image_similar(base_im, im, 0.7)
def test_sampleformat_not_corrupted(self):
# Assert that a TIFF image with SampleFormat=UINT tag is not corrupted
# when saving to a new file.
# Pillow 6.0 fails with "OSError: cannot identify image file".
tiff = io.BytesIO(
base64.b64decode(
b"SUkqAAgAAAAPAP4ABAABAAAAAAAAAAABBAABAAAAAQAAAAEBBAABAAAAAQAA"
b"AAIBAwADAAAAwgAAAAMBAwABAAAACAAAAAYBAwABAAAAAgAAABEBBAABAAAA"
b"4AAAABUBAwABAAAAAwAAABYBBAABAAAAAQAAABcBBAABAAAACwAAABoBBQAB"
b"AAAAyAAAABsBBQABAAAA0AAAABwBAwABAAAAAQAAACgBAwABAAAAAQAAAFMB"
b"AwADAAAA2AAAAAAAAAAIAAgACAABAAAAAQAAAAEAAAABAAAAAQABAAEAAAB4"
b"nGNgYAAAAAMAAQ=="
)
)
out = io.BytesIO()
with Image.open(tiff) as im:
im.save(out, format="tiff")
out.seek(0)
with Image.open(out) as im:
im.load()