mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-06-26 07:53:20 +03:00
Merge pull request #827 from hugovk/test_file_tiff
More tests for TiffImagePlugin
This commit is contained in:
commit
ab1bbb07c3
|
@ -49,7 +49,8 @@ from PIL import _binary
|
|||
from PIL._util import isStringType
|
||||
|
||||
import warnings
|
||||
import array, sys
|
||||
import array
|
||||
import sys
|
||||
import collections
|
||||
import itertools
|
||||
import os
|
||||
|
@ -58,8 +59,8 @@ import os
|
|||
READ_LIBTIFF = False
|
||||
WRITE_LIBTIFF = False
|
||||
|
||||
II = b"II" # little-endian (intel-style)
|
||||
MM = b"MM" # big-endian (motorola-style)
|
||||
II = b"II" # little-endian (Intel style)
|
||||
MM = b"MM" # big-endian (Motorola style)
|
||||
|
||||
i8 = _binary.i8
|
||||
o8 = _binary.o8
|
||||
|
@ -164,7 +165,7 @@ OPEN_INFO = {
|
|||
(II, 2, 1, 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"),
|
||||
(II, 2, 1, 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"),
|
||||
(II, 2, 1, 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"),
|
||||
(II, 2, 1, 1, (8,8,8,8), (999,)): ("RGBA", "RGBA"), # corel draw 10
|
||||
(II, 2, 1, 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
|
||||
(II, 3, 1, 1, (1,), ()): ("P", "P;1"),
|
||||
(II, 3, 1, 2, (1,), ()): ("P", "P;1R"),
|
||||
(II, 3, 1, 1, (2,), ()): ("P", "P;2"),
|
||||
|
@ -196,7 +197,7 @@ OPEN_INFO = {
|
|||
(MM, 2, 1, 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"),
|
||||
(MM, 2, 1, 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"),
|
||||
(MM, 2, 1, 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"),
|
||||
(MM, 2, 1, 1, (8,8,8,8), (999,)): ("RGBA", "RGBA"), # corel draw 10
|
||||
(MM, 2, 1, 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
|
||||
(MM, 3, 1, 1, (1,), ()): ("P", "P;1"),
|
||||
(MM, 3, 1, 2, (1,), ()): ("P", "P;1R"),
|
||||
(MM, 3, 1, 1, (2,), ()): ("P", "P;2"),
|
||||
|
@ -214,9 +215,11 @@ OPEN_INFO = {
|
|||
|
||||
PREFIXES = [b"MM\000\052", b"II\052\000", b"II\xBC\000"]
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[:4] in PREFIXES
|
||||
|
||||
|
||||
##
|
||||
# Wrapper for TIFF IFDs.
|
||||
|
||||
|
@ -287,7 +290,9 @@ class ImageFileDirectory(collections.MutableMapping):
|
|||
return dict(self.items())
|
||||
|
||||
def named(self):
|
||||
"""Returns the complete tag dictionary, with named tags where posible."""
|
||||
"""
|
||||
Returns the complete tag dictionary, with named tags where posible.
|
||||
"""
|
||||
from PIL import TiffTags
|
||||
result = {}
|
||||
for tag_code, value in self.items():
|
||||
|
@ -295,7 +300,6 @@ class ImageFileDirectory(collections.MutableMapping):
|
|||
result[tag_name] = value
|
||||
return result
|
||||
|
||||
|
||||
# dictionary API
|
||||
|
||||
def __len__(self):
|
||||
|
@ -449,14 +453,17 @@ class ImageFileDirectory(collections.MutableMapping):
|
|||
data = ifd[8:8+size]
|
||||
|
||||
if len(data) != size:
|
||||
warnings.warn("Possibly corrupt EXIF data. Expecting to read %d bytes but only got %d. Skipping tag %s" % (size, len(data), tag))
|
||||
warnings.warn("Possibly corrupt EXIF data. "
|
||||
"Expecting to read %d bytes but only got %d. "
|
||||
"Skipping tag %s" % (size, len(data), tag))
|
||||
continue
|
||||
|
||||
self.tagdata[tag] = data
|
||||
self.tagtype[tag] = typ
|
||||
|
||||
if Image.DEBUG:
|
||||
if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP):
|
||||
if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK,
|
||||
ICCPROFILE, XMP):
|
||||
print("- value: <table: %d bytes>" % size)
|
||||
else:
|
||||
print("- value:", self[tag])
|
||||
|
@ -541,7 +548,8 @@ class ImageFileDirectory(collections.MutableMapping):
|
|||
typname = TiffTags.TYPES.get(typ, "unknown")
|
||||
print("save: %s (%d)" % (tagname, tag), end=' ')
|
||||
print("- type: %s (%d)" % (typname, typ), end=' ')
|
||||
if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP):
|
||||
if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK,
|
||||
ICCPROFILE, XMP):
|
||||
size = len(data)
|
||||
print("- value: <table: %d bytes>" % size)
|
||||
else:
|
||||
|
@ -586,6 +594,7 @@ class ImageFileDirectory(collections.MutableMapping):
|
|||
|
||||
return offset
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for TIFF files.
|
||||
|
||||
|
@ -694,9 +703,11 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
if not len(self.tile) == 1:
|
||||
raise IOError("Not exactly one tile")
|
||||
|
||||
# (self._compression, (extents tuple), 0, (rawmode, self._compression, fp))
|
||||
# (self._compression, (extents tuple),
|
||||
# 0, (rawmode, self._compression, fp))
|
||||
ignored, extents, ignored_2, args = self.tile[0]
|
||||
decoder = Image._getdecoder(self.mode, 'libtiff', args, self.decoderconfig)
|
||||
decoder = Image._getdecoder(self.mode, 'libtiff', args,
|
||||
self.decoderconfig)
|
||||
try:
|
||||
decoder.setimage(self.im, extents)
|
||||
except ValueError:
|
||||
|
@ -706,12 +717,12 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
# We've got a stringio like thing passed in. Yay for all in memory.
|
||||
# The decoder needs the entire file in one shot, so there's not
|
||||
# a lot we can do here other than give it the entire file.
|
||||
# unless we could do something like get the address of the underlying
|
||||
# string for stringio.
|
||||
# unless we could do something like get the address of the
|
||||
# underlying string for stringio.
|
||||
#
|
||||
# Rearranging for supporting byteio items, since they have a fileno
|
||||
# that returns an IOError if there's no underlying fp. Easier to deal
|
||||
# with here by reordering.
|
||||
# that returns an IOError if there's no underlying fp. Easier to
|
||||
# dea. with here by reordering.
|
||||
if Image.DEBUG:
|
||||
print ("have getvalue. just sending in a string from getvalue")
|
||||
n, err = decoder.decode(self.fp.getvalue())
|
||||
|
@ -720,7 +731,8 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
if Image.DEBUG:
|
||||
print ("have fileno, calling fileno version of the decoder.")
|
||||
self.fp.seek(0)
|
||||
n,err = decoder.decode(b"fpfp") # 4 bytes, otherwise the trace might error out
|
||||
# 4 bytes, otherwise the trace might error out
|
||||
n, err = decoder.decode(b"fpfp")
|
||||
else:
|
||||
# we have something else.
|
||||
if Image.DEBUG:
|
||||
|
@ -728,7 +740,6 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
# UNDONE -- so much for that buffer size thing.
|
||||
n, err = decoder.decode(self.fp.read())
|
||||
|
||||
|
||||
self.tile = []
|
||||
self.readonly = 0
|
||||
# libtiff closed the fp in a, we need to close self.fp, if possible
|
||||
|
@ -825,13 +836,16 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
offsets = self.tag[STRIPOFFSETS]
|
||||
h = getscalar(ROWSPERSTRIP, ysize)
|
||||
w = self.size[0]
|
||||
if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3", "group4",
|
||||
"tiff_jpeg", "tiff_adobe_deflate",
|
||||
"tiff_thunderscan", "tiff_deflate",
|
||||
"tiff_sgilog", "tiff_sgilog24",
|
||||
if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3",
|
||||
"group4", "tiff_jpeg",
|
||||
"tiff_adobe_deflate",
|
||||
"tiff_thunderscan",
|
||||
"tiff_deflate",
|
||||
"tiff_sgilog",
|
||||
"tiff_sgilog24",
|
||||
"tiff_raw_16"]:
|
||||
## if Image.DEBUG:
|
||||
## print "Activating g4 compression for whole file"
|
||||
# if Image.DEBUG:
|
||||
# print "Activating g4 compression for whole file"
|
||||
|
||||
# Decoder expects entire file as one tile.
|
||||
# There's a buffer size limit in load (64k)
|
||||
|
@ -850,7 +864,8 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
|
||||
# libtiff closes the file descriptor, so pass in a dup.
|
||||
try:
|
||||
fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno())
|
||||
fp = hasattr(self.fp, "fileno") and \
|
||||
os.dup(self.fp.fileno())
|
||||
except IOError:
|
||||
# io.BytesIO have a fileno, but returns an IOError if
|
||||
# it doesn't use a file descriptor.
|
||||
|
@ -937,10 +952,12 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
# --------------------------------------------------------------------
|
||||
# Write TIFF files
|
||||
|
||||
# little endian is default except for image modes with explict big endian byte-order
|
||||
# little endian is default except for image modes with
|
||||
# explict big endian byte-order
|
||||
|
||||
SAVE_INFO = {
|
||||
# mode => rawmode, byteorder, photometrics, sampleformat, bitspersample, extra
|
||||
# mode => rawmode, byteorder, photometrics,
|
||||
# sampleformat, bitspersample, extra
|
||||
"1": ("1", II, 1, 1, (1,), None),
|
||||
"L": ("L", II, 1, 1, (8,), None),
|
||||
"LA": ("LA", II, 1, 1, (8, 8), 2),
|
||||
|
@ -963,6 +980,7 @@ SAVE_INFO = {
|
|||
"F;32BF": ("F;32BF", MM, 1, 3, (32,), None),
|
||||
}
|
||||
|
||||
|
||||
def _cvt_res(value):
|
||||
# convert value to TIFF rational number -- (numerator, denominator)
|
||||
if isinstance(value, collections.Sequence):
|
||||
|
@ -973,6 +991,7 @@ def _cvt_res(value):
|
|||
value = float(value)
|
||||
return (int(value * 65536), 65536)
|
||||
|
||||
|
||||
def _save(im, fp, filename):
|
||||
|
||||
try:
|
||||
|
@ -982,7 +1001,8 @@ def _save(im, fp, filename):
|
|||
|
||||
ifd = ImageFileDirectory(prefix)
|
||||
|
||||
compression = im.encoderinfo.get('compression',im.info.get('compression','raw'))
|
||||
compression = im.encoderinfo.get('compression', im.info.get('compression',
|
||||
'raw'))
|
||||
|
||||
libtiff = WRITE_LIBTIFF or compression != 'raw'
|
||||
|
||||
|
@ -1010,7 +1030,6 @@ def _save(im, fp, filename):
|
|||
except:
|
||||
pass # might not be an IFD, Might not have populated type
|
||||
|
||||
|
||||
# additions written by Greg Couch, gregc@cgl.ucsf.edu
|
||||
# inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
|
||||
if hasattr(im, 'tag'):
|
||||
|
@ -1078,7 +1097,8 @@ def _save(im, fp, filename):
|
|||
ifd[ROWSPERSTRIP] = im.size[1]
|
||||
ifd[STRIPBYTECOUNTS] = stride * im.size[1]
|
||||
ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer
|
||||
ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression,1) # no compression by default
|
||||
# no compression by default:
|
||||
ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1)
|
||||
|
||||
if libtiff:
|
||||
if Image.DEBUG:
|
||||
|
@ -1089,23 +1109,27 @@ def _save(im, fp, filename):
|
|||
fp.seek(0)
|
||||
_fp = os.dup(fp.fileno())
|
||||
|
||||
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes.
|
||||
# ICC Profile crashes.
|
||||
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE]
|
||||
atts = {}
|
||||
# bits per sample is a single short in the tiff directory, not a list.
|
||||
atts[BITSPERSAMPLE] = bits[0]
|
||||
# Merge the ones that we have with (optional) more bits from
|
||||
# the original file, e.g x,y resolution so that we can
|
||||
# save(load('')) == original file.
|
||||
for k,v in itertools.chain(ifd.items(), getattr(im, 'ifd', {}).items()):
|
||||
for k, v in itertools.chain(ifd.items(),
|
||||
getattr(im, 'ifd', {}).items()):
|
||||
if k not in atts and k not in blocklist:
|
||||
if type(v[0]) == tuple and len(v) > 1:
|
||||
# A tuple of more than one rational tuples
|
||||
# flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL
|
||||
# flatten to floats,
|
||||
# following tiffcp.c->cpTag->TIFF_RATIONAL
|
||||
atts[k] = [float(elt[0])/float(elt[1]) for elt in v]
|
||||
continue
|
||||
if type(v[0]) == tuple and len(v) == 1:
|
||||
# A tuple of one rational tuples
|
||||
# flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL
|
||||
# flatten to floats,
|
||||
# following tiffcp.c->cpTag->TIFF_RATIONAL
|
||||
atts[k] = float(v[0][0])/float(v[0][1])
|
||||
continue
|
||||
if type(v) == tuple and len(v) > 2:
|
||||
|
@ -1115,7 +1139,8 @@ def _save(im, fp, filename):
|
|||
continue
|
||||
if type(v) == tuple and len(v) == 2:
|
||||
# one rational tuple
|
||||
# flatten to float, following tiffcp.c->cpTag->TIFF_RATIONAL
|
||||
# flatten to float,
|
||||
# following tiffcp.c->cpTag->TIFF_RATIONAL
|
||||
atts[k] = float(v[0])/float(v[1])
|
||||
continue
|
||||
if type(v) == tuple and len(v) == 1:
|
||||
|
@ -1143,7 +1168,8 @@ def _save(im, fp, filename):
|
|||
e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig)
|
||||
e.setimage(im.im, (0, 0)+im.size)
|
||||
while True:
|
||||
l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock
|
||||
# undone, change to self.decodermaxblock:
|
||||
l, s, d = e.encode(16*1024)
|
||||
if not _fp:
|
||||
fp.write(d)
|
||||
if s:
|
||||
|
@ -1158,7 +1184,6 @@ def _save(im, fp, filename):
|
|||
("raw", (0, 0)+im.size, offset, (rawmode, stride, 1))
|
||||
])
|
||||
|
||||
|
||||
# -- helper for multi-page save --
|
||||
if "_debug_multipage" in im.encoderinfo:
|
||||
# just to access o32 and o16 (using correct byte order)
|
||||
|
|
|
@ -141,6 +141,137 @@ class TestFileTiff(PillowTestCase):
|
|||
self.assertEqual(
|
||||
im.getextrema(), (-3.140936851501465, 3.140684127807617))
|
||||
|
||||
def test___str__(self):
|
||||
# Arrange
|
||||
file = "Tests/images/pil136.tiff"
|
||||
im = Image.open(file)
|
||||
|
||||
# Act
|
||||
ret = str(im.ifd)
|
||||
|
||||
# Assert
|
||||
self.assertIsInstance(ret, str)
|
||||
self.assertEqual(
|
||||
ret,
|
||||
'{256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), '
|
||||
'262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), '
|
||||
'279: (9460,), 282: ((720000, 10000),), '
|
||||
'283: ((720000, 10000),), 284: (1,)}')
|
||||
|
||||
def test__delitem__(self):
|
||||
# Arrange
|
||||
file = "Tests/images/pil136.tiff"
|
||||
im = Image.open(file)
|
||||
len_before = len(im.ifd.as_dict())
|
||||
|
||||
# Act
|
||||
del im.ifd[256]
|
||||
|
||||
# Assert
|
||||
len_after = len(im.ifd.as_dict())
|
||||
self.assertEqual(len_before, len_after + 1)
|
||||
|
||||
def test_load_byte(self):
|
||||
# Arrange
|
||||
from PIL import TiffImagePlugin
|
||||
ifd = TiffImagePlugin.ImageFileDirectory()
|
||||
data = b"abc"
|
||||
|
||||
# Act
|
||||
ret = ifd.load_byte(data)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(ret, b"abc")
|
||||
|
||||
def test_load_string(self):
|
||||
# Arrange
|
||||
from PIL import TiffImagePlugin
|
||||
ifd = TiffImagePlugin.ImageFileDirectory()
|
||||
data = b"abc\0"
|
||||
|
||||
# Act
|
||||
ret = ifd.load_string(data)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(ret, "abc")
|
||||
|
||||
def test_load_float(self):
|
||||
# Arrange
|
||||
from PIL import TiffImagePlugin
|
||||
ifd = TiffImagePlugin.ImageFileDirectory()
|
||||
data = b"abcdabcd"
|
||||
|
||||
# Act
|
||||
ret = ifd.load_float(data)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(ret, (1.6777999408082104e+22, 1.6777999408082104e+22))
|
||||
|
||||
def test_load_double(self):
|
||||
# Arrange
|
||||
from PIL import TiffImagePlugin
|
||||
ifd = TiffImagePlugin.ImageFileDirectory()
|
||||
data = b"abcdefghabcdefgh"
|
||||
|
||||
# Act
|
||||
ret = ifd.load_double(data)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(ret, (8.540883223036124e+194, 8.540883223036124e+194))
|
||||
|
||||
def test_seek(self):
|
||||
# Arrange
|
||||
file = "Tests/images/pil136.tiff"
|
||||
im = Image.open(file)
|
||||
|
||||
# Act
|
||||
im.seek(-1)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(im.tell(), 0)
|
||||
|
||||
def test_seek_eof(self):
|
||||
# Arrange
|
||||
file = "Tests/images/pil136.tiff"
|
||||
im = Image.open(file)
|
||||
self.assertEqual(im.tell(), 0)
|
||||
|
||||
# Act / Assert
|
||||
self.assertRaises(EOFError, lambda: im.seek(1))
|
||||
|
||||
def test__cvt_res_int(self):
|
||||
# Arrange
|
||||
from PIL.TiffImagePlugin import _cvt_res
|
||||
value = 34
|
||||
|
||||
# Act
|
||||
ret = _cvt_res(value)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(ret, (34, 1))
|
||||
|
||||
def test__cvt_res_float(self):
|
||||
# Arrange
|
||||
from PIL.TiffImagePlugin import _cvt_res
|
||||
value = 22.3
|
||||
|
||||
# Act
|
||||
ret = _cvt_res(value)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(ret, (1461452, 65536))
|
||||
|
||||
def test__cvt_res_sequence(self):
|
||||
# Arrange
|
||||
from PIL.TiffImagePlugin import _cvt_res
|
||||
value = [0, 1]
|
||||
|
||||
# Act
|
||||
ret = _cvt_res(value)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(ret, [0, 1])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in New Issue
Block a user