Health fixes

This commit is contained in:
Andrew Murray 2016-02-05 09:57:13 +11:00
parent 0129c50933
commit 677b958a7f
20 changed files with 116 additions and 125 deletions

View File

@ -219,7 +219,7 @@ class DdsImageFile(ImageFile.ImageFile):
if len(header_bytes) != 120:
raise IOError("Incomplete header: %s bytes" % len(header_bytes))
header = BytesIO(header_bytes)
flags, height, width = struct.unpack("<3I", header.read(12))
self.size = (width, height)
self.mode = "RGBA"
@ -253,9 +253,8 @@ class DdsImageFile(ImageFile.ImageFile):
raise IOError("Truncated DDS file")
finally:
self.fp.close()
self.fp = BytesIO(decoded_data)
self.fp = BytesIO(decoded_data)
def load_seek(self, pos):
pass

View File

@ -62,7 +62,7 @@ class FtexImageFile(ImageFile.ImageFile):
mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))
self.mode = "RGB"
# Only support single-format files. I don't know of any multi-format file.
assert format_count == 1
@ -83,7 +83,6 @@ class FtexImageFile(ImageFile.ImageFile):
self.fp.close()
self.fp = BytesIO(data)
def load_seek(self, pos):
pass

View File

@ -30,7 +30,7 @@ i32 = _binary.i32be
def _accept(prefix):
return len(prefix) >= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1,2)
return len(prefix) >= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
##
@ -46,17 +46,17 @@ class GbrImageFile(ImageFile.ImageFile):
version = i32(self.fp.read(4))
if header_size < 20:
raise SyntaxError("not a GIMP brush")
if version not in (1,2):
raise SyntaxError("Unsupported GIMP brush version: %s" %version)
if version not in (1, 2):
raise SyntaxError("Unsupported GIMP brush version: %s" % version)
width = i32(self.fp.read(4))
height = i32(self.fp.read(4))
color_depth = i32(self.fp.read(4))
if width <= 0 or height <= 0:
if width <= 0 or height <= 0:
raise SyntaxError("not a GIMP brush")
if color_depth not in (1,4):
raise SyntaxError("Unsupported GMP brush color depth: %s" %color_depth)
if color_depth not in (1, 4):
raise SyntaxError("Unsupported GMP brush color depth: %s" % color_depth)
if version == 1:
comment_length = header_size-20
else:
@ -72,7 +72,7 @@ class GbrImageFile(ImageFile.ImageFile):
self.mode = "L"
else:
self.mode = 'RGBA'
self.size = width, height
self.info["comment"] = comment
@ -80,7 +80,7 @@ class GbrImageFile(ImageFile.ImageFile):
# Image might not be small
Image._decompression_bomb_check(self.size)
# Data is an uncompressed block of w * h * bytes/pixel
# Data is an uncompressed block of w * h * bytes/pixel
self._data_size = width * height * color_depth
def load(self):

View File

@ -448,7 +448,7 @@ def _getexif(self):
info = TiffImagePlugin.ImageFileDirectory_v1(head)
info.load(file)
exif[0x8825] = _fixup_dict(info)
return exif

View File

@ -57,7 +57,7 @@ import struct
import sys
import warnings
from .TiffTags import TYPES, TagInfo
from .TiffTags import TYPES
__version__ = "1.3.5"
@ -227,6 +227,7 @@ def _limit_rational(val, max_val):
_load_dispatch = {}
_write_dispatch = {}
class IFDRational(Rational):
""" Implements a rational class where 0/0 is a legal value to match
the in the wild use of exif rationals.
@ -266,7 +267,6 @@ class IFDRational(Rational):
self._val = float('nan')
return
elif denominator == 1:
if sys.hexversion < 0x2070000 and type(value) == float:
# python 2.6 is different.
@ -284,7 +284,6 @@ class IFDRational(Rational):
def denominator(a):
return a._denominator
def limit_rational(self, max_denominator):
"""
@ -304,12 +303,12 @@ class IFDRational(Rational):
def __hash__(self):
return self._val.__hash__()
def __eq__(self,other):
def __eq__(self, other):
return self._val == other
def _delegate(op):
def delegate(self, *args):
return getattr(self._val,op)(*args)
return getattr(self._val, op)(*args)
return delegate
""" a = ['add','radd', 'sub', 'rsub','div', 'rdiv', 'mul', 'rmul',
@ -350,7 +349,6 @@ class IFDRational(Rational):
__round__ = _delegate('__round__')
class ImageFileDirectory_v2(collections.MutableMapping):
"""This class represents a TIFF tag directory. To speed things up, we
don't decode tags unless they're asked for.
@ -1126,8 +1124,8 @@ class TiffImageFile(ImageFile.ImageFile):
self.info["compression"] = self._compression
xres = self.tag_v2.get(X_RESOLUTION,1)
yres = self.tag_v2.get(Y_RESOLUTION,1)
xres = self.tag_v2.get(X_RESOLUTION, 1)
yres = self.tag_v2.get(Y_RESOLUTION, 1)
if xres and yres:
resunit = self.tag_v2.get(RESOLUTION_UNIT, 1)
@ -1416,14 +1414,15 @@ def _save(im, fp, filename):
if hasattr(im, 'tag'):
legacy_ifd = im.tag.to_v2()
for tag, value in itertools.chain(ifd.items(),
getattr(im, 'tag_v2', {}).items(),
legacy_ifd.items()):
getattr(im, 'tag_v2', {}).items(),
legacy_ifd.items()):
# Libtiff can only process certain core items without adding
# them to the custom dictionary. It will segfault if it attempts
# to add a custom tag without the dictionary entry
#
# UNDONE -- add code for the custom dictionary
if tag not in TiffTags.LIBTIFF_CORE: continue
if tag not in TiffTags.LIBTIFF_CORE:
continue
if tag not in atts and tag not in blocklist:
if isinstance(value, unicode if bytes is str else str):
atts[tag] = value.encode('ascii', 'replace') + b"\0"

View File

@ -30,6 +30,7 @@ class TagInfo(namedtuple("_TagInfo", "value name type length enum")):
def cvt_enum(self, value):
return self.enum.get(value, value)
def lookup(tag):
"""
:param tag: Integer tag number
@ -378,22 +379,22 @@ TYPES = {}
# some of these are not in our TAGS_V2 dict and were included from tiff.h
LIBTIFF_CORE = set ([255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
296, 297, 321, 320, 338, 32995, 322, 323, 32998,
32996, 339, 32997, 330, 531, 530, 301, 532, 333,
# as above
269 # this has been in our tests forever, and works
])
LIBTIFF_CORE = set([255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
296, 297, 321, 320, 338, 32995, 322, 323, 32998,
32996, 339, 32997, 330, 531, 530, 301, 532, 333,
# as above
269 # this has been in our tests forever, and works
])
LIBTIFF_CORE.remove(320) # Array of short, crashes
LIBTIFF_CORE.remove(301) # Array of short, crashes
LIBTIFF_CORE.remove(532) # Array of long, crashes
LIBTIFF_CORE.remove(320) # Array of short, crashes
LIBTIFF_CORE.remove(301) # Array of short, crashes
LIBTIFF_CORE.remove(532) # Array of long, crashes
LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes
LIBTIFF_CORE.remove(322) # We don't have support for tiled images in libtiff
LIBTIFF_CORE.remove(323) # Tiled images
LIBTIFF_CORE.remove(333) # Ink Names either
LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes
LIBTIFF_CORE.remove(322) # We don't have support for tiled images in libtiff
LIBTIFF_CORE.remove(323) # Tiled images
LIBTIFF_CORE.remove(333) # Ink Names either
# Note to advanced users: There may be combinations of these
# parameters and values that when added properly, will work and

View File

@ -10,7 +10,7 @@ class TestFliOverflow(PillowTestCase):
# this should not crash with a malloc error or access violation
im = Image.open(TEST_FILE)
im.load()
if __name__ == '__main__':
unittest.main()

View File

@ -3,6 +3,7 @@ from PIL import Image
TEST_FILE = "Tests/images/libtiff_segfault.tif"
class TestLibtiffSegfault(PillowTestCase):
def test_segfault(self):
""" This test should not segfault. It will on Pillow <= 3.1.0 and
@ -18,6 +19,5 @@ class TestLibtiffSegfault(PillowTestCase):
self.fail("Should have returned IOError")
if __name__ == '__main__':
unittest.main()

View File

@ -17,12 +17,12 @@ class TestFileDds(PillowTestCase):
im = Image.open(TEST_FILE_DXT1)
im.load()
self.assertEqual(im.format, "DDS")
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (256, 256))
# This target image is from the test set of images, and is exact.
# This target image is from the test set of images, and is exact.
self.assert_image_equal(target.convert('RGBA'), im)
def test_sanity_dxt5(self):
@ -42,7 +42,7 @@ class TestFileDds(PillowTestCase):
# a little brighter. The 0,0 pixel is (00,6c,f8,ff) by our code,
# and by the target image for the DXT1, and the imagemagick .png
# is giving (00, 6d, ff, ff). So, assert similar, pretty tight
# I'm currently seeing about a 3 for the epsilon.
# I'm currently seeing about a 3 for the epsilon.
self.assert_image_similar(target, im, 5)
def test_sanity_dxt3(self):
@ -78,18 +78,18 @@ class TestFileDds(PillowTestCase):
img_file = f.read()
def short_header():
im = Image.open(BytesIO(img_file[:119]))
Image.open(BytesIO(img_file[:119]))
self.assertRaises(IOError, short_header)
def test_short_file(self):
""" Check that the appropriate error is thrown for a short file"""
with open(TEST_FILE_DXT5, 'rb') as f:
img_file = f.read()
def short_file():
im = Image.open(BytesIO(img_file[:-100]))
Image.open(BytesIO(img_file[:-100]))
self.assertRaises(IOError, short_file)

View File

@ -1,6 +1,7 @@
from helper import unittest, PillowTestCase
from helper import PillowTestCase
from PIL import Image
class TestFileFtex(PillowTestCase):
def test_load_raw(self):

View File

@ -11,7 +11,6 @@ class TestFileGbr(PillowTestCase):
self.assertRaises(SyntaxError,
lambda: GbrImagePlugin.GbrImageFile(invalid_file))
def test_gbr_file(self):
im = Image.open('Tests/images/gbr.gbr')

View File

@ -191,26 +191,26 @@ class TestFileJpeg(PillowTestCase):
def test_exif_rollback(self):
# rolling back exif support in 3.1 to pre-3.0 formatting.
# expected from 2.9, with b/u qualifiers switched for 3.2 compatibility
# this test passes on 2.9 and 3.1, but not 3.0
# this test passes on 2.9 and 3.1, but not 3.0
expected_exif = {34867: 4294967295,
258: (24, 24, 24),
36867: '2099:09:29 10:10:10',
34853: {0: b'\x00\x00\x00\x01',
2: (4294967295, 1),
5: b'\x01',
30: 65535,
29: '1999:99:99 99:99:99'},
296: 65535,
34665: 185,
41994: 65535,
514: 4294967295,
258: (24, 24, 24),
36867: '2099:09:29 10:10:10',
34853: {0: b'\x00\x00\x00\x01',
2: (4294967295, 1),
5: b'\x01',
30: 65535,
29: '1999:99:99 99:99:99'},
296: 65535,
34665: 185,
41994: 65535,
514: 4294967295,
271: 'Make',
272: 'XXX-XXX',
305: 'PIL',
42034: ((1, 1), (1, 1), (1, 1), (1, 1)),
42035: 'LensMake',
34856: b'\xaa\xaa\xaa\xaa\xaa\xaa',
282: (4294967295, 1),
272: 'XXX-XXX',
305: 'PIL',
42034: ((1, 1), (1, 1), (1, 1), (1, 1)),
42035: 'LensMake',
34856: b'\xaa\xaa\xaa\xaa\xaa\xaa',
282: (4294967295, 1),
33434: (4294967295, 1)}
im = Image.open('Tests/images/exif_gps.jpg')
@ -218,7 +218,6 @@ class TestFileJpeg(PillowTestCase):
for tag, value in expected_exif.items():
self.assertEqual(value, exif[tag])
def test_exif_gps_typeerror(self):
im = Image.open('Tests/images/exif_gps_typeerror.jpg')

View File

@ -176,19 +176,20 @@ class TestFileLibTiff(LibTiffTestCase):
# 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.
# Get the list of the ones that we should be able to write
core_items = dict((tag, info) for tag, info in [(s,TiffTags.lookup(s)) for s
core_items = dict((tag, info) for tag, info in [(s, TiffTags.lookup(s)) for s
in TiffTags.LIBTIFF_CORE]
if info.type is not None)
# Exclude ones that have special meaning that we're already testing them
im = Image.open('Tests/images/hopper_g4.tif')
for tag in im.tag_v2.keys():
try:
del(core_items[tag])
except: pass
except:
pass
# Type codes:
# 2: "ascii",
@ -197,12 +198,11 @@ class TestFileLibTiff(LibTiffTestCase):
# 5: "rational",
# 12: "double",
# type: dummy value
values = { 2: 'test',
3: 1,
4: 2**20,
5: TiffImagePlugin.IFDRational(100,1),
12: 1.05 }
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():
@ -213,18 +213,16 @@ class TestFileLibTiff(LibTiffTestCase):
else:
new_ifd[tag] = tuple(values[info.type] for _ in range(info.length))
# Extra samples really doesn't make sense in this application.
# Extra samples really doesn't make sense in this application.
del(new_ifd[338])
out = self.tempfile("temp.tif")
TiffImagePlugin.WRITE_LIBTIFF = True
im.save(out, tiffinfo=new_ifd)
TiffImagePlugin.WRITE_LIBTIFF = False
def test_g3_compression(self):
i = Image.open('Tests/images/hopper_g4_500.tif')
out = self.tempfile("temp.tif")
@ -459,7 +457,6 @@ class TestFileLibTiff(LibTiffTestCase):
TiffImagePlugin.WRITE_LIBTIFF = False
if __name__ == '__main__':
unittest.main()

View File

@ -1,22 +1,22 @@
from helper import unittest, PillowTestCase, hopper
from helper import unittest, PillowTestCase
from PIL import Image
class TestFilePcd(PillowTestCase):
def test_load_raw(self):
im = Image.open('Tests/images/hopper.pcd')
im.load() # should not segfault.
im.load() # should not segfault.
# Note that this image was created with a resized hopper
# image, which was then converted to pcd with imagemagick
# and the colors are wonky in Pillow. It's unclear if this
# is a pillow or a convert issue, as other images not generated
# from convert look find on pillow and not imagemagick.
#target = hopper().resize((768,512))
#self.assert_image_similar(im, target, 10)
# target = hopper().resize((768,512))
# self.assert_image_similar(im, target, 10)
if __name__ == '__main__':
unittest.main()

View File

@ -84,7 +84,7 @@ class TestFileTiff(PillowTestCase):
self.assertIsInstance(im.tag[X_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[Y_RESOLUTION], TiffImagePlugin.IFDRational)

View File

@ -122,21 +122,22 @@ class TestFileTiffMetadata(PillowTestCase):
original = img.tag_v2.named()
reloaded = loaded.tag_v2.named()
for k,v in original.items():
for k, v in original.items():
if type(v) == IFDRational:
original[k] = IFDRational(*_limit_rational(v,2**31))
original[k] = IFDRational(*_limit_rational(v, 2**31))
if type(v) == tuple and \
type(v[0]) == IFDRational:
type(v[0]) == IFDRational:
original[k] = tuple([IFDRational(
*_limit_rational(elt, 2**31)) for elt in v])
*_limit_rational(elt, 2**31)) for elt in v])
ignored = ['StripByteCounts', 'RowsPerStrip',
'PageNumber', 'StripOffsets']
for tag, value in reloaded.items():
if tag in ignored: continue
if tag in ignored:
continue
if (type(original[tag]) == tuple
and type(original[tag][0]) == IFDRational):
and type(original[tag][0]) == IFDRational):
# Need to compare element by element in the tuple,
# not comparing tuples of object references
self.assert_deep_equal(original[tag],
@ -188,7 +189,7 @@ class TestFileTiffMetadata(PillowTestCase):
def test_exif_div_zero(self):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
info[41988] = TiffImagePlugin.IFDRational(0,0)
info[41988] = TiffImagePlugin.IFDRational(0, 0)
out = self.tempfile('temp.tiff')
im.save(out, tiffinfo=info, compression='raw')
@ -198,8 +199,6 @@ class TestFileTiffMetadata(PillowTestCase):
self.assertEqual(0, reloaded.tag_v2[41988][0].denominator)
if __name__ == '__main__':
unittest.main()

View File

@ -1,6 +1,7 @@
from helper import unittest, PillowTestCase, hopper, py3
import sys
class TestImageGetIm(PillowTestCase):
def test_sanity(self):
@ -10,7 +11,6 @@ class TestImageGetIm(PillowTestCase):
if py3:
self.assertIn("PyCapsule", type_repr)
if sys.hexversion < 0x2070000:
# py2.6 x64, windows
target_types = (int, long)

View File

@ -1,15 +1,16 @@
from helper import unittest, PillowTestCase, hopper
from PIL import Image
class TestImagingCoreResize(PillowTestCase):
#see https://github.com/python-pillow/Pillow/issues/1710
# see https://github.com/python-pillow/Pillow/issues/1710
def test_overflow(self):
im = hopper('L')
xsize = 0x100000008 // 4
ysize = 1000 # unimportant
ysize = 1000 # unimportant
try:
# any resampling filter will do here
im.im.resize((xsize, ysize), Image.LINEAR)
im.im.resize((xsize, ysize), Image.LINEAR)
self.fail("Resize should raise MemoryError on invalid xsize")
except MemoryError:
self.assertTrue(True, "Should raise MemoryError")
@ -17,17 +18,17 @@ class TestImagingCoreResize(PillowTestCase):
def test_invalid_size(self):
im = hopper()
im.resize((100,100))
im.resize((100, 100))
self.assertTrue(True, "Should not Crash")
try:
im.resize((-100,100))
im.resize((-100, 100))
self.fail("Resize should raise a value error on x negative size")
except ValueError:
self.assertTrue(True, "Should raise ValueError")
try:
im.resize((100,-100))
im.resize((100, -100))
self.fail("Resize should raise a value error on y negative size")
except ValueError:
self.assertTrue(True, "Should raise ValueError")

View File

@ -7,8 +7,8 @@ from PIL.TiffImagePlugin import IFDRational
from fractions import Fraction
class Test_IFDRational(PillowTestCase):
class Test_IFDRational(PillowTestCase):
def _test_equal(self, num, denom, target):
@ -20,17 +20,16 @@ class Test_IFDRational(PillowTestCase):
def test_sanity(self):
self._test_equal(1, 1, 1)
self._test_equal(1, 1, Fraction(1,1))
self._test_equal(1, 1, Fraction(1, 1))
self._test_equal(2, 2, 1)
self._test_equal(1.0, 1, Fraction(1,1))
self._test_equal(1.0, 1, Fraction(1, 1))
self._test_equal(Fraction(1,1), 1, Fraction(1,1))
self._test_equal(IFDRational(1,1), 1, 1)
self._test_equal(Fraction(1, 1), 1, Fraction(1, 1))
self._test_equal(IFDRational(1, 1), 1, 1)
self._test_equal(1, 2, Fraction(1,2))
self._test_equal(1, 2, IFDRational(1,2))
self._test_equal(1, 2, Fraction(1, 2))
self._test_equal(1, 2, IFDRational(1, 2))
def test_nonetype(self):
" Fails if the _delegate function doesn't return a valid function"
@ -45,17 +44,15 @@ class Test_IFDRational(PillowTestCase):
self.assertTrue(xres and 1)
self.assertTrue(xres and yres)
def test_ifd_rational_save(self):
for libtiff in (True, False):
TiffImagePlugin.WRITE_LIBTIFF = libtiff
im = hopper()
out = self.tempfile('temp.tiff')
res = IFDRational(301,1)
im.save(out, dpi=(res,res), compression='raw')
res = IFDRational(301, 1)
im.save(out, dpi=(res, res), compression='raw')
reloaded = Image.open(out)
self.assertEqual(float(IFDRational(301,1)),
self.assertEqual(float(IFDRational(301, 1)),
float(reloaded.tag_v2[282]))

View File

@ -47,14 +47,14 @@ When used in a ``ImageFileDirectory_v1``, a 2 item tuple is returned
of the numerator and denominator, as was done previously.
This class should be used when adding a rational value to an
ImageFileDirectory for saving to image metadata.
ImageFileDirectory for saving to image metadata.
JpegImagePlugin._getexif
++++++++++++++++++++++++
In Pillow 3.0, the dictionary returned from the private, experimental,
but generally widely used ``_getexif`` function changed to reflect the
ImageFileDirectory_v2 format, without a fallback to the previous format.
ImageFileDirectory_v2 format, without a fallback to the previous format.
In Pillow 3.1, ``_getexif`` now returns a dictionary compatible with
Pillow 2.9 and earlier, built with
@ -70,6 +70,6 @@ Out of Spec Metadata
In Pillow 3.0 and 3.1, images that contain metadata that is internally
consistent but not in agreement with the TIFF spec may cause an
exception when reading the metadata. This can happen when a tag that
is specified to have a single value is stored with an array of values.
is specified to have a single value is stored with an array of values.
It is anticipated that this behavior will change in future releases.
It is anticipated that this behavior will change in future releases.