mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-27 09:44:31 +03:00
Merge pull request #2719 from wiredfool/issue_2278
Fixes for Issues #2278 and #2006, value error in exif/tiff ifd
This commit is contained in:
commit
a0ce5740d5
|
@ -550,11 +550,28 @@ class ImageFileDirectory_v2(collections.MutableMapping):
|
||||||
|
|
||||||
dest = self._tags_v1 if legacy_api else self._tags_v2
|
dest = self._tags_v1 if legacy_api else self._tags_v2
|
||||||
|
|
||||||
if info.length == 1:
|
# Three branches:
|
||||||
if legacy_api and self.tagtype[tag] in [5, 10]:
|
# Spec'd length == 1, Actual length 1, store as element
|
||||||
|
# Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed.
|
||||||
|
# No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple.
|
||||||
|
# Don't mess with the legacy api, since it's frozen.
|
||||||
|
if ((info.length == 1) or
|
||||||
|
(info.length is None and len(values) == 1 and not legacy_api)):
|
||||||
|
# Don't mess with the legacy api, since it's frozen.
|
||||||
|
if legacy_api and self.tagtype[tag] in [5, 10]: # rationals
|
||||||
values = values,
|
values = values,
|
||||||
dest[tag], = values
|
try:
|
||||||
|
dest[tag], = values
|
||||||
|
except ValueError:
|
||||||
|
# We've got a builtin tag with 1 expected entry
|
||||||
|
warnings.warn(
|
||||||
|
"Metadata Warning, tag %s had too many entries: %s, expected 1" % (
|
||||||
|
tag, len(values)))
|
||||||
|
dest[tag] = values[0]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# Spec'd length > 1 or undefined
|
||||||
|
# Unspec'd, and length > 1
|
||||||
dest[tag] = values
|
dest[tag] = values
|
||||||
|
|
||||||
def __delitem__(self, tag):
|
def __delitem__(self, tag):
|
||||||
|
@ -1011,8 +1028,10 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
args = rawmode, ""
|
args = rawmode, ""
|
||||||
if JPEGTABLES in self.tag_v2:
|
if JPEGTABLES in self.tag_v2:
|
||||||
# Hack to handle abbreviated JPEG headers
|
# Hack to handle abbreviated JPEG headers
|
||||||
# FIXME This will fail with more than one value
|
# Definition of JPEGTABLES is that the count
|
||||||
self.tile_prefix, = self.tag_v2[JPEGTABLES]
|
# is the number of bytes in the tables datastream
|
||||||
|
# so, it should always be 1 in our tag info
|
||||||
|
self.tile_prefix = self.tag_v2[JPEGTABLES]
|
||||||
elif compression == "packbits":
|
elif compression == "packbits":
|
||||||
args = rawmode
|
args = rawmode
|
||||||
elif compression == "tiff_lzw":
|
elif compression == "tiff_lzw":
|
||||||
|
|
|
@ -23,7 +23,7 @@ from collections import namedtuple
|
||||||
class TagInfo(namedtuple("_TagInfo", "value name type length enum")):
|
class TagInfo(namedtuple("_TagInfo", "value name type length enum")):
|
||||||
__slots__ = []
|
__slots__ = []
|
||||||
|
|
||||||
def __new__(cls, value=None, name="unknown", type=None, length=0, enum=None):
|
def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None):
|
||||||
return super(TagInfo, cls).__new__(
|
return super(TagInfo, cls).__new__(
|
||||||
cls, value, name, type, length, enum or {})
|
cls, value, name, type, length, enum or {})
|
||||||
|
|
||||||
|
@ -142,6 +142,8 @@ TAGS_V2 = {
|
||||||
341: ("SMaxSampleValue", DOUBLE, 0),
|
341: ("SMaxSampleValue", DOUBLE, 0),
|
||||||
342: ("TransferRange", SHORT, 6),
|
342: ("TransferRange", SHORT, 6),
|
||||||
|
|
||||||
|
347: ("JPEGTables", UNDEFINED, 1),
|
||||||
|
|
||||||
# obsolete JPEG tags
|
# obsolete JPEG tags
|
||||||
512: ("JPEGProc", SHORT, 1),
|
512: ("JPEGProc", SHORT, 1),
|
||||||
513: ("JPEGInterchangeFormat", LONG, 1),
|
513: ("JPEGInterchangeFormat", LONG, 1),
|
||||||
|
@ -158,7 +160,10 @@ TAGS_V2 = {
|
||||||
531: ("YCbCrPositioning", SHORT, 1),
|
531: ("YCbCrPositioning", SHORT, 1),
|
||||||
532: ("ReferenceBlackWhite", LONG, 0),
|
532: ("ReferenceBlackWhite", LONG, 0),
|
||||||
|
|
||||||
|
700: ('XMP', BYTE, 1),
|
||||||
|
|
||||||
33432: ("Copyright", ASCII, 1),
|
33432: ("Copyright", ASCII, 1),
|
||||||
|
34377: ('PhotoshopInfo', BYTE, 1),
|
||||||
|
|
||||||
# FIXME add more tags here
|
# FIXME add more tags here
|
||||||
34665: ("ExifIFD", SHORT, 1),
|
34665: ("ExifIFD", SHORT, 1),
|
||||||
|
@ -188,8 +193,8 @@ TAGS_V2 = {
|
||||||
|
|
||||||
50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}),
|
50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}),
|
||||||
50780: ("BestQualityScale", RATIONAL, 1),
|
50780: ("BestQualityScale", RATIONAL, 1),
|
||||||
50838: ("ImageJMetaDataByteCounts", LONG, 1),
|
50838: ("ImageJMetaDataByteCounts", LONG, 0), # Can be more than one
|
||||||
50839: ("ImageJMetaData", UNDEFINED, 1)
|
50839: ("ImageJMetaData", UNDEFINED, 1) # see Issue #2006
|
||||||
}
|
}
|
||||||
|
|
||||||
# Legacy Tags structure
|
# Legacy Tags structure
|
||||||
|
|
BIN
Tests/images/issue_2278.tif
Normal file
BIN
Tests/images/issue_2278.tif
Normal file
Binary file not shown.
|
@ -56,7 +56,7 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
loaded = Image.open(f)
|
loaded = Image.open(f)
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),))
|
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),))
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], len(bindata))
|
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),))
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
|
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
|
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
|
||||||
|
@ -69,6 +69,16 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
loaded_double = loaded.tag[tag_ids['YawAngle']][0]
|
loaded_double = loaded.tag[tag_ids['YawAngle']][0]
|
||||||
self.assertAlmostEqual(loaded_double, doubledata)
|
self.assertAlmostEqual(loaded_double, doubledata)
|
||||||
|
|
||||||
|
# check with 2 element ImageJMetaDataByteCounts, issue #2006
|
||||||
|
|
||||||
|
info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8)
|
||||||
|
img.save(f, tiffinfo=info)
|
||||||
|
loaded = Image.open(f)
|
||||||
|
|
||||||
|
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
|
||||||
|
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
|
||||||
|
|
||||||
|
|
||||||
def test_read_metadata(self):
|
def test_read_metadata(self):
|
||||||
img = Image.open('Tests/images/hopper_g4.tif')
|
img = Image.open('Tests/images/hopper_g4.tif')
|
||||||
|
|
||||||
|
@ -202,8 +212,8 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
im.save(out, tiffinfo=info, compression='raw')
|
im.save(out, tiffinfo=info, compression='raw')
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
reloaded = Image.open(out)
|
||||||
self.assertEqual(0, reloaded.tag_v2[41988][0].numerator)
|
self.assertEqual(0, reloaded.tag_v2[41988].numerator)
|
||||||
self.assertEqual(0, reloaded.tag_v2[41988][0].denominator)
|
self.assertEqual(0, reloaded.tag_v2[41988].denominator)
|
||||||
|
|
||||||
def test_expty_values(self):
|
def test_expty_values(self):
|
||||||
data = io.BytesIO(
|
data = io.BytesIO(
|
||||||
|
@ -220,6 +230,27 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
self.fail("Should not be struct value error there.")
|
self.fail("Should not be struct value error there.")
|
||||||
self.assertIn(33432, info)
|
self.assertIn(33432, info)
|
||||||
|
|
||||||
|
def test_PhotoshopInfo(self):
|
||||||
|
im = Image.open('Tests/images/issue_2278.tif')
|
||||||
|
|
||||||
|
self.assertIsInstance(im.tag_v2[34377], bytes)
|
||||||
|
out = self.tempfile('temp.tiff')
|
||||||
|
im.save(out)
|
||||||
|
reloaded = Image.open(out)
|
||||||
|
self.assertIsInstance(reloaded.tag_v2[34377], bytes)
|
||||||
|
|
||||||
|
def test_too_many_entries(self):
|
||||||
|
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
|
|
||||||
|
# 277: ("SamplesPerPixel", SHORT, 1),
|
||||||
|
ifd._tagdata[277] = struct.pack('hh', 4,4)
|
||||||
|
ifd.tagtype[277] = TiffTags.SHORT
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.assert_warning(UserWarning, lambda: ifd[277])
|
||||||
|
except ValueError:
|
||||||
|
self.fail("Invalid Metadata count should not cause a Value Error.")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -46,6 +46,22 @@ This release contains several performance improvements:
|
||||||
using a recent version of libjpeg-turbo.
|
using a recent version of libjpeg-turbo.
|
||||||
|
|
||||||
|
|
||||||
|
TIFF Metadata Changes
|
||||||
|
=====================
|
||||||
|
|
||||||
|
* TIFF tags with unknown type/quantity now default to being bare
|
||||||
|
values if they are 1 element, where previously they would be a
|
||||||
|
single element tuple. This is only with the new api, not the legacy
|
||||||
|
api. This normalizes the handling of fields, so that the metadata
|
||||||
|
with inferred or image specified counts are handled the same as
|
||||||
|
metadata with count specified in the TIFF spec.
|
||||||
|
* The ``PhotoshopInfo``, ``XMP``, and ``JPEGTables`` tags now have a
|
||||||
|
defined type (bytes) and a count of 1.
|
||||||
|
* The ``ImageJMetaDataByteCounts`` tag now has an arbitrary number of
|
||||||
|
items, as there can be multiple items, one for UTF-8, and one for
|
||||||
|
UTF-16.
|
||||||
|
|
||||||
|
|
||||||
Core Image API Changes
|
Core Image API Changes
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user