mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +03:00 
			
		
		
		
	Restore legacy TIFF API.
To have the old API that always returns tuples, and fractions as pairs, set the `legacy_api` attribute of the IFD to True. This should alleviate concerns about backwards compatibility.
This commit is contained in:
		
							parent
							
								
									38f7e23495
								
							
						
					
					
						commit
						93abbd0caa
					
				| 
						 | 
					@ -247,10 +247,8 @@ class ImageFileDirectory(collections.MutableMapping):
 | 
				
			||||||
        * self._tagdata = {} Key: numerical tiff tag number
 | 
					        * self._tagdata = {} Key: numerical tiff tag number
 | 
				
			||||||
                             Value: undecoded byte string from file
 | 
					                             Value: undecoded byte string from file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Tags will be found in either self._tags or self._tagdata, but not
 | 
					    Tags will be found in the private attributes self._tagdata, and in
 | 
				
			||||||
    both.  The union of the two should contain all the tags from the Tiff
 | 
					    self._tags once decoded.
 | 
				
			||||||
    image file.  External classes shouldn't reference these unless they're
 | 
					 | 
				
			||||||
    really sure what they're doing.
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None):
 | 
					    def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None):
 | 
				
			||||||
| 
						 | 
					@ -275,9 +273,17 @@ class ImageFileDirectory(collections.MutableMapping):
 | 
				
			||||||
            raise SyntaxError("not a TIFF IFD")
 | 
					            raise SyntaxError("not a TIFF IFD")
 | 
				
			||||||
        self.reset()
 | 
					        self.reset()
 | 
				
			||||||
        self.next, = self._unpack("L", ifh[4:])
 | 
					        self.next, = self._unpack("L", ifh[4:])
 | 
				
			||||||
 | 
					        self._legacy_api = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    prefix = property(lambda self: self._prefix)
 | 
					    prefix = property(lambda self: self._prefix)
 | 
				
			||||||
    offset = property(lambda self: self._offset)
 | 
					    offset = property(lambda self: self._offset)
 | 
				
			||||||
 | 
					    legacy_api = property(lambda self: self._legacy_api)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @legacy_api.setter
 | 
				
			||||||
 | 
					    def legacy_api(self, value):
 | 
				
			||||||
 | 
					        if value != self._legacy_api:
 | 
				
			||||||
 | 
					            self._tags.clear()
 | 
				
			||||||
 | 
					        self._legacy_api = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def reset(self):
 | 
					    def reset(self):
 | 
				
			||||||
        self._tags = {}
 | 
					        self._tags = {}
 | 
				
			||||||
| 
						 | 
					@ -302,18 +308,18 @@ class ImageFileDirectory(collections.MutableMapping):
 | 
				
			||||||
                    for code, value in self.items())
 | 
					                    for code, value in self.items())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __len__(self):
 | 
					    def __len__(self):
 | 
				
			||||||
        return len(self._tagdata) + len(self._tags)
 | 
					        return len(set(self._tagdata) | set(self._tags))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __getitem__(self, tag):
 | 
					    def __getitem__(self, tag):
 | 
				
			||||||
        try:
 | 
					        if tag not in self._tags:  # unpack on the fly
 | 
				
			||||||
            return self._tags[tag]
 | 
					 | 
				
			||||||
        except KeyError:  # unpack on the fly
 | 
					 | 
				
			||||||
            data = self._tagdata[tag]
 | 
					            data = self._tagdata[tag]
 | 
				
			||||||
            typ = self.tagtype[tag]
 | 
					            typ = self.tagtype[tag]
 | 
				
			||||||
            size, handler = self._load_dispatch[typ]
 | 
					            size, handler = self._load_dispatch[typ]
 | 
				
			||||||
            self[tag] = handler(self, data)  # check type
 | 
					            self[tag] = handler(self, data)  # check type
 | 
				
			||||||
            del self._tagdata[tag]
 | 
					        val = self._tags[tag]
 | 
				
			||||||
            return self[tag]
 | 
					        if self.legacy_api and not isinstance(val, (tuple, bytes)):
 | 
				
			||||||
 | 
					            val = val,
 | 
				
			||||||
 | 
					        return val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __contains__(self, tag):
 | 
					    def __contains__(self, tag):
 | 
				
			||||||
        return tag in self._tags or tag in self._tagdata
 | 
					        return tag in self._tags or tag in self._tagdata
 | 
				
			||||||
| 
						 | 
					@ -355,6 +361,8 @@ class ImageFileDirectory(collections.MutableMapping):
 | 
				
			||||||
                      for value in values]
 | 
					                      for value in values]
 | 
				
			||||||
        values = tuple(info.cvt_enum(value) for value in values)
 | 
					        values = tuple(info.cvt_enum(value) for value in values)
 | 
				
			||||||
        if info.length == 1:
 | 
					        if info.length == 1:
 | 
				
			||||||
 | 
					            if self.legacy_api and self.tagtype[tag] in [5, 10]:
 | 
				
			||||||
 | 
					                values = values,
 | 
				
			||||||
            self._tags[tag], = values
 | 
					            self._tags[tag], = values
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            self._tags[tag] = values
 | 
					            self._tags[tag] = values
 | 
				
			||||||
| 
						 | 
					@ -364,7 +372,7 @@ class ImageFileDirectory(collections.MutableMapping):
 | 
				
			||||||
        self._tagdata.pop(tag, None)
 | 
					        self._tagdata.pop(tag, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __iter__(self):
 | 
					    def __iter__(self):
 | 
				
			||||||
        return itertools.chain(list(self._tags), list(self._tagdata))
 | 
					        return iter(set(self._tagdata) | set(self._tags))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _unpack(self, fmt, data):
 | 
					    def _unpack(self, fmt, data):
 | 
				
			||||||
        return struct.unpack(self._endian + fmt, data)
 | 
					        return struct.unpack(self._endian + fmt, data)
 | 
				
			||||||
| 
						 | 
					@ -398,10 +406,19 @@ class ImageFileDirectory(collections.MutableMapping):
 | 
				
			||||||
            b"".join(self._pack(fmt, value) for value in values))
 | 
					            b"".join(self._pack(fmt, value) for value in values))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    list(map(_register_basic,
 | 
					    list(map(_register_basic,
 | 
				
			||||||
             [(1, "B", "byte"), (3, "H", "short"), (4, "L", "long"),
 | 
					             [(3, "H", "short"), (4, "L", "long"),
 | 
				
			||||||
              (6, "b", "signed byte"), (8, "h", "signed short"),
 | 
					              (6, "b", "signed byte"), (8, "h", "signed short"),
 | 
				
			||||||
              (9, "l", "signed long"), (11, "f", "float"), (12, "d", "double")]))
 | 
					              (9, "l", "signed long"), (11, "f", "float"), (12, "d", "double")]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @_register_loader(1, 1) # Basic type, except for the legacy API.
 | 
				
			||||||
 | 
					    def load_byte(self, data):
 | 
				
			||||||
 | 
					        return (data if self.legacy_api else
 | 
				
			||||||
 | 
					                tuple(map(ord, data) if bytes is str else data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @_register_writer(1) # Basic type, except for the legacy API.
 | 
				
			||||||
 | 
					    def write_byte(self, data):
 | 
				
			||||||
 | 
					        return data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @_register_loader(2, 1)
 | 
					    @_register_loader(2, 1)
 | 
				
			||||||
    def load_string(self, data):
 | 
					    def load_string(self, data):
 | 
				
			||||||
        if data.endswith(b"\0"):
 | 
					        if data.endswith(b"\0"):
 | 
				
			||||||
| 
						 | 
					@ -418,7 +435,9 @@ class ImageFileDirectory(collections.MutableMapping):
 | 
				
			||||||
    @_register_loader(5, 8)
 | 
					    @_register_loader(5, 8)
 | 
				
			||||||
    def load_rational(self, data):
 | 
					    def load_rational(self, data):
 | 
				
			||||||
        vals = self._unpack("{0}L".format(len(data) // 4), data)
 | 
					        vals = self._unpack("{0}L".format(len(data) // 4), data)
 | 
				
			||||||
        return tuple(num / denom for num, denom in zip(vals[::2], vals[1::2]))
 | 
					        combine = lambda a, b: (a, b) if self.legacy_api else a / b
 | 
				
			||||||
 | 
					        return tuple(combine(num, denom)
 | 
				
			||||||
 | 
					                     for num, denom in zip(vals[::2], vals[1::2]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @_register_writer(5)
 | 
					    @_register_writer(5)
 | 
				
			||||||
    def write_rational(self, *values):
 | 
					    def write_rational(self, *values):
 | 
				
			||||||
| 
						 | 
					@ -436,7 +455,9 @@ class ImageFileDirectory(collections.MutableMapping):
 | 
				
			||||||
    @_register_loader(10, 8)
 | 
					    @_register_loader(10, 8)
 | 
				
			||||||
    def load_signed_rational(self, data):
 | 
					    def load_signed_rational(self, data):
 | 
				
			||||||
        vals = self._unpack("{0}l".format(len(data) // 4), data)
 | 
					        vals = self._unpack("{0}l".format(len(data) // 4), data)
 | 
				
			||||||
        return tuple(num / denom for num, denom in zip(vals[::2], vals[1::2]))
 | 
					        combine = lambda a, b: (a, b) if self.legacy_api else a / b
 | 
				
			||||||
 | 
					        return tuple(combine(num, denom)
 | 
				
			||||||
 | 
					                     for num, denom in zip(vals[::2], vals[1::2]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @_register_writer(10)
 | 
					    @_register_writer(10)
 | 
				
			||||||
    def write_signed_rational(self, *values):
 | 
					    def write_signed_rational(self, *values):
 | 
				
			||||||
| 
						 | 
					@ -1026,11 +1047,14 @@ def _save(im, fp, filename):
 | 
				
			||||||
    # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
 | 
					    # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
 | 
				
			||||||
    if hasattr(im, 'tag'):
 | 
					    if hasattr(im, 'tag'):
 | 
				
			||||||
        # preserve tags from original TIFF image file
 | 
					        # preserve tags from original TIFF image file
 | 
				
			||||||
 | 
					        orig_api = im.tag.legacy_api
 | 
				
			||||||
 | 
					        im.tag.legacy_api = False
 | 
				
			||||||
        for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION,
 | 
					        for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION,
 | 
				
			||||||
                    IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
 | 
					                    IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
 | 
				
			||||||
            if key in im.tag:
 | 
					            if key in im.tag:
 | 
				
			||||||
                ifd[key] = im.tag[key]
 | 
					                ifd[key] = im.tag[key]
 | 
				
			||||||
            ifd.tagtype[key] = im.tag.tagtype.get(key, None)
 | 
					            ifd.tagtype[key] = im.tag.tagtype.get(key, None)
 | 
				
			||||||
 | 
					        im.tag.legacy_api = orig_api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # preserve ICC profile (should also work when saving other formats
 | 
					        # preserve ICC profile (should also work when saving other formats
 | 
				
			||||||
        # which support profiles as TIFF) -- 2008-06-06 Florian Hoech
 | 
					        # which support profiles as TIFF) -- 2008-06-06 Florian Hoech
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,11 +43,6 @@ class PillowTestCase(unittest.TestCase):
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            print("=== orphaned temp file: %s" % path)
 | 
					            print("=== orphaned temp file: %s" % path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def assert_almost_equal(self, a, b, msg=None, eps=1e-6):
 | 
					 | 
				
			||||||
        self.assertLess(
 | 
					 | 
				
			||||||
            abs(a-b), eps,
 | 
					 | 
				
			||||||
            msg or "got %r, expected %r" % (a, b))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def assert_deep_equal(self, a, b, msg=None):
 | 
					    def assert_deep_equal(self, a, b, msg=None):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.assertEqual(
 | 
					            self.assertEqual(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,7 +80,7 @@ class TestImage(PillowTestCase):
 | 
				
			||||||
        ret = GimpGradientFile.sphere_increasing(middle, pos)
 | 
					        ret = GimpGradientFile.sphere_increasing(middle, pos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Assert
 | 
					        # Assert
 | 
				
			||||||
        self.assert_almost_equal(ret, 0.9682458365518543)
 | 
					        self.assertAlmostEqual(ret, 0.9682458365518543)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_sphere_decreasing(self):
 | 
					    def test_sphere_decreasing(self):
 | 
				
			||||||
        # Arrange
 | 
					        # Arrange
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,11 @@ from __future__ import print_function
 | 
				
			||||||
from helper import unittest, PillowTestCase, hopper, py3
 | 
					from helper import unittest, PillowTestCase, hopper, py3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import io
 | 
					import io
 | 
				
			||||||
 | 
					<<<<<<< HEAD
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
 | 
					=======
 | 
				
			||||||
 | 
					import itertools
 | 
				
			||||||
 | 
					>>>>>>> Restore legacy TIFF API.
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PIL import Image, TiffImagePlugin
 | 
					from PIL import Image, TiffImagePlugin
 | 
				
			||||||
| 
						 | 
					@ -123,41 +127,27 @@ class TestFileLibTiff(LibTiffTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_write_metadata(self):
 | 
					    def test_write_metadata(self):
 | 
				
			||||||
        """ Test metadata writing through libtiff """
 | 
					        """ Test metadata writing through libtiff """
 | 
				
			||||||
 | 
					        for legacy_api in [False, True]:
 | 
				
			||||||
            img = Image.open('Tests/images/hopper_g4.tif')
 | 
					            img = Image.open('Tests/images/hopper_g4.tif')
 | 
				
			||||||
 | 
					            img.tag.legacy_api = legacy_api
 | 
				
			||||||
            f = self.tempfile('temp.tiff')
 | 
					            f = self.tempfile('temp.tiff')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            img.save(f, tiffinfo=img.tag)
 | 
					            img.save(f, tiffinfo=img.tag)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        loaded = Image.open(f)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            original = img.tag.named()
 | 
					            original = img.tag.named()
 | 
				
			||||||
        reloaded = loaded.tag.named()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # PhotometricInterpretation is set from SAVE_INFO,
 | 
					            # PhotometricInterpretation is set from SAVE_INFO,
 | 
				
			||||||
            # not the original image.
 | 
					            # not the original image.
 | 
				
			||||||
        ignored = [
 | 
					            ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber',
 | 
				
			||||||
            'StripByteCounts', 'RowsPerStrip',
 | 
					                       'PhotometricInterpretation']
 | 
				
			||||||
            'PageNumber', 'PhotometricInterpretation']
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for tag, value in reloaded.items():
 | 
					            loaded = Image.open(f)
 | 
				
			||||||
            if tag not in ignored:
 | 
					            loaded.tag.legacy_api = legacy_api
 | 
				
			||||||
                if tag.endswith('Resolution'):
 | 
					            reloaded = loaded.tag.named()
 | 
				
			||||||
                    self.assert_almost_equal(
 | 
					 | 
				
			||||||
                        original[tag], value,
 | 
					 | 
				
			||||||
                        msg="%s didn't roundtrip" % tag)
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    self.assertEqual(
 | 
					 | 
				
			||||||
                        original[tag], value, "%s didn't roundtrip" % tag)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for tag, value in original.items():
 | 
					            for tag, value in itertools.chain(reloaded.items(), original.items()):
 | 
				
			||||||
                if tag not in ignored:
 | 
					                if tag not in ignored:
 | 
				
			||||||
                if tag.endswith('Resolution'):
 | 
					                    val = original[tag]
 | 
				
			||||||
                    self.assert_almost_equal(
 | 
					                    self.assertEqual(val, value, msg="%s didn't roundtrip" % tag)
 | 
				
			||||||
                        original[tag], value,
 | 
					 | 
				
			||||||
                        msg="%s didn't roundtrip" % tag)
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    self.assertEqual(
 | 
					 | 
				
			||||||
                        value, reloaded[tag], "%s didn't roundtrip" % tag)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_g3_compression(self):
 | 
					    def test_g3_compression(self):
 | 
				
			||||||
        i = Image.open('Tests/images/hopper_g4_500.tif')
 | 
					        i = Image.open('Tests/images/hopper_g4_500.tif')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,10 +73,16 @@ class TestFileTiff(PillowTestCase):
 | 
				
			||||||
    def test_xyres_tiff(self):
 | 
					    def test_xyres_tiff(self):
 | 
				
			||||||
        from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION
 | 
					        from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION
 | 
				
			||||||
        filename = "Tests/images/pil168.tif"
 | 
					        filename = "Tests/images/pil168.tif"
 | 
				
			||||||
 | 
					        for legacy_api in [False, True]:
 | 
				
			||||||
            im = Image.open(filename)
 | 
					            im = Image.open(filename)
 | 
				
			||||||
 | 
					            im.tag.legacy_api = legacy_api
 | 
				
			||||||
 | 
					            if legacy_api:
 | 
				
			||||||
 | 
					                assert isinstance(im.tag[X_RESOLUTION][0], tuple)
 | 
				
			||||||
 | 
					                assert isinstance(im.tag[Y_RESOLUTION][0], tuple)
 | 
				
			||||||
            # Try to read a file where X,Y_RESOLUTION are ints
 | 
					            # Try to read a file where X,Y_RESOLUTION are ints
 | 
				
			||||||
            im.tag[X_RESOLUTION] = (72,)
 | 
					            im.tag[X_RESOLUTION] = (72,)
 | 
				
			||||||
            im.tag[Y_RESOLUTION] = (72,)
 | 
					            im.tag[Y_RESOLUTION] = (72,)
 | 
				
			||||||
 | 
					            im.tag.legacy_api = False # _setup assumes the new API.
 | 
				
			||||||
            im._setup()
 | 
					            im._setup()
 | 
				
			||||||
            self.assertEqual(im.info['dpi'], (72., 72.))
 | 
					            self.assertEqual(im.info['dpi'], (72., 72.))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -204,7 +210,6 @@ class TestFileTiff(PillowTestCase):
 | 
				
			||||||
        self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 0, 255))
 | 
					        self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 0, 255))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test___str__(self):
 | 
					    def test___str__(self):
 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        filename = "Tests/images/pil136.tiff"
 | 
					        filename = "Tests/images/pil136.tiff"
 | 
				
			||||||
        im = Image.open(filename)
 | 
					        im = Image.open(filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -217,127 +222,81 @@ class TestFileTiff(PillowTestCase):
 | 
				
			||||||
    def test_as_dict(self):
 | 
					    def test_as_dict(self):
 | 
				
			||||||
        # Arrange
 | 
					        # Arrange
 | 
				
			||||||
        filename = "Tests/images/pil136.tiff"
 | 
					        filename = "Tests/images/pil136.tiff"
 | 
				
			||||||
 | 
					        for legacy_api in [False, True]:
 | 
				
			||||||
            im = Image.open(filename)
 | 
					            im = Image.open(filename)
 | 
				
			||||||
 | 
					            im.tag.legacy_api = legacy_api
 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        ret = im.ifd.as_dict()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        self.assertIsInstance(ret, dict)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.assertEqual(
 | 
					            self.assertEqual(
 | 
				
			||||||
            ret, {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1, 262: 2, 296: 2,
 | 
					                im.tag.as_dict(),
 | 
				
			||||||
                  273: (8,), 338: (1,), 277: 4, 279: (9460,),
 | 
					                {256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,),
 | 
				
			||||||
                  282: 72.0, 283: 72.0, 284: 1})
 | 
					                 262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,),
 | 
				
			||||||
 | 
					                 279: (9460,), 282: ((720000, 10000),),
 | 
				
			||||||
 | 
					                 283: ((720000, 10000),), 284: (1,)} if legacy_api else
 | 
				
			||||||
 | 
					                {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1,
 | 
				
			||||||
 | 
					                 262: 2, 296: 2, 273: (8,), 338: (1,), 277: 4,
 | 
				
			||||||
 | 
					                 279: (9460,), 282: 72.0, 283: 72.0, 284: 1})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test__delitem__(self):
 | 
					    def test__delitem__(self):
 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        filename = "Tests/images/pil136.tiff"
 | 
					        filename = "Tests/images/pil136.tiff"
 | 
				
			||||||
        im = Image.open(filename)
 | 
					        im = Image.open(filename)
 | 
				
			||||||
        len_before = len(im.ifd.as_dict())
 | 
					        len_before = len(im.ifd.as_dict())
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        del im.ifd[256]
 | 
					        del im.ifd[256]
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        len_after = len(im.ifd.as_dict())
 | 
					        len_after = len(im.ifd.as_dict())
 | 
				
			||||||
        self.assertEqual(len_before, len_after + 1)
 | 
					        self.assertEqual(len_before, len_after + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_load_byte(self):
 | 
					    def test_load_byte(self):
 | 
				
			||||||
        # Arrange
 | 
					        for legacy_api in [False, True]:
 | 
				
			||||||
            ifd = TiffImagePlugin.ImageFileDirectory()
 | 
					            ifd = TiffImagePlugin.ImageFileDirectory()
 | 
				
			||||||
 | 
					            ifd.legacy_api = legacy_api
 | 
				
			||||||
            data = b"abc"
 | 
					            data = b"abc"
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
            ret = ifd.load_byte(data)
 | 
					            ret = ifd.load_byte(data)
 | 
				
			||||||
 | 
					            self.assertEqual(ret, b"abc" if legacy_api else (97, 98, 99))
 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        self.assertEqual(ret, (97, 98, 99))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_load_string(self):
 | 
					    def test_load_string(self):
 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        ifd = TiffImagePlugin.ImageFileDirectory()
 | 
					        ifd = TiffImagePlugin.ImageFileDirectory()
 | 
				
			||||||
        data = b"abc\0"
 | 
					        data = b"abc\0"
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        ret = ifd.load_string(data)
 | 
					        ret = ifd.load_string(data)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        self.assertEqual(ret, "abc")
 | 
					        self.assertEqual(ret, "abc")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_load_float(self):
 | 
					    def test_load_float(self):
 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        ifd = TiffImagePlugin.ImageFileDirectory()
 | 
					        ifd = TiffImagePlugin.ImageFileDirectory()
 | 
				
			||||||
        data = b"abcdabcd"
 | 
					        data = b"abcdabcd"
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        ret = ifd.load_float(data)
 | 
					        ret = ifd.load_float(data)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        self.assertEqual(ret, (1.6777999408082104e+22, 1.6777999408082104e+22))
 | 
					        self.assertEqual(ret, (1.6777999408082104e+22, 1.6777999408082104e+22))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_load_double(self):
 | 
					    def test_load_double(self):
 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        ifd = TiffImagePlugin.ImageFileDirectory()
 | 
					        ifd = TiffImagePlugin.ImageFileDirectory()
 | 
				
			||||||
        data = b"abcdefghabcdefgh"
 | 
					        data = b"abcdefghabcdefgh"
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        ret = ifd.load_double(data)
 | 
					        ret = ifd.load_double(data)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        self.assertEqual(ret, (8.540883223036124e+194, 8.540883223036124e+194))
 | 
					        self.assertEqual(ret, (8.540883223036124e+194, 8.540883223036124e+194))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_seek(self):
 | 
					    def test_seek(self):
 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        filename = "Tests/images/pil136.tiff"
 | 
					        filename = "Tests/images/pil136.tiff"
 | 
				
			||||||
        im = Image.open(filename)
 | 
					        im = Image.open(filename)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        im.seek(-1)
 | 
					        im.seek(-1)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        self.assertEqual(im.tell(), 0)
 | 
					        self.assertEqual(im.tell(), 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_seek_eof(self):
 | 
					    def test_seek_eof(self):
 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        filename = "Tests/images/pil136.tiff"
 | 
					        filename = "Tests/images/pil136.tiff"
 | 
				
			||||||
        im = Image.open(filename)
 | 
					        im = Image.open(filename)
 | 
				
			||||||
        self.assertEqual(im.tell(), 0)
 | 
					        self.assertEqual(im.tell(), 0)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act / Assert
 | 
					 | 
				
			||||||
        self.assertRaises(EOFError, lambda: im.seek(1))
 | 
					        self.assertRaises(EOFError, lambda: im.seek(1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test__limit_rational_int(self):
 | 
					    def test__limit_rational_int(self):
 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        from PIL.TiffImagePlugin import _limit_rational
 | 
					        from PIL.TiffImagePlugin import _limit_rational
 | 
				
			||||||
        value = 34
 | 
					        value = 34
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        ret = _limit_rational(value, 65536)
 | 
					        ret = _limit_rational(value, 65536)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        self.assertEqual(ret, (34, 1))
 | 
					        self.assertEqual(ret, (34, 1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test__limit_rational_float(self):
 | 
					    def test__limit_rational_float(self):
 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        from PIL.TiffImagePlugin import _limit_rational
 | 
					        from PIL.TiffImagePlugin import _limit_rational
 | 
				
			||||||
        value = 22.3
 | 
					        value = 22.3
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        ret = _limit_rational(value, 65536)
 | 
					        ret = _limit_rational(value, 65536)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        self.assertEqual(ret, (223, 10))
 | 
					        self.assertEqual(ret, (223, 10))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_4bit(self):
 | 
					    def test_4bit(self):
 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        test_file = "Tests/images/hopper_gray_4bpp.tif"
 | 
					        test_file = "Tests/images/hopper_gray_4bpp.tif"
 | 
				
			||||||
        original = hopper("L")
 | 
					        original = hopper("L")
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        im = Image.open(test_file)
 | 
					        im = Image.open(test_file)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        self.assertEqual(im.size, (128, 128))
 | 
					        self.assertEqual(im.size, (128, 128))
 | 
				
			||||||
        self.assertEqual(im.mode, "L")
 | 
					        self.assertEqual(im.mode, "L")
 | 
				
			||||||
        self.assert_image_similar(im, original, 7.3)
 | 
					        self.assert_image_similar(im, original, 7.3)
 | 
				
			||||||
| 
						 | 
					@ -347,52 +306,45 @@ class TestFileTiff(PillowTestCase):
 | 
				
			||||||
        # Test TIFF with tag 297 (Page Number) having value of 0 0.
 | 
					        # Test TIFF with tag 297 (Page Number) having value of 0 0.
 | 
				
			||||||
        # The first number is the current page number.
 | 
					        # The first number is the current page number.
 | 
				
			||||||
        # The second is the total number of pages, zero means not available.
 | 
					        # The second is the total number of pages, zero means not available.
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Arrange
 | 
					 | 
				
			||||||
        outfile = self.tempfile("temp.tif")
 | 
					        outfile = self.tempfile("temp.tif")
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Created by printing a page in Chrome to PDF, then:
 | 
					        # Created by printing a page in Chrome to PDF, then:
 | 
				
			||||||
        # /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
 | 
					        # /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
 | 
				
			||||||
        # -dNOPAUSE /tmp/test.pdf -c quit
 | 
					        # -dNOPAUSE /tmp/test.pdf -c quit
 | 
				
			||||||
        infile = "Tests/images/total-pages-zero.tif"
 | 
					        infile = "Tests/images/total-pages-zero.tif"
 | 
				
			||||||
        im = Image.open(infile)
 | 
					        im = Image.open(infile)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act / Assert
 | 
					 | 
				
			||||||
        # Should not divide by zero
 | 
					        # Should not divide by zero
 | 
				
			||||||
        im.save(outfile)
 | 
					        im.save(outfile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_with_underscores(self):
 | 
					    def test_with_underscores(self):
 | 
				
			||||||
        # Arrange: use underscores
 | 
					 | 
				
			||||||
        kwargs = {'resolution_unit': 'inch',
 | 
					        kwargs = {'resolution_unit': 'inch',
 | 
				
			||||||
                  'x_resolution': 72,
 | 
					                  'x_resolution': 72,
 | 
				
			||||||
                  'y_resolution': 36}
 | 
					                  'y_resolution': 36}
 | 
				
			||||||
        filename = self.tempfile("temp.tif")
 | 
					        filename = self.tempfile("temp.tif")
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        hopper("RGB").save(filename, **kwargs)
 | 
					        hopper("RGB").save(filename, **kwargs)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION
 | 
					        from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION
 | 
				
			||||||
 | 
					        for legacy_api in [False, True]:
 | 
				
			||||||
            im = Image.open(filename)
 | 
					            im = Image.open(filename)
 | 
				
			||||||
        self.assertEqual(im.tag[X_RESOLUTION], 72)
 | 
					            im.tag.legacy_api = legacy_api
 | 
				
			||||||
        self.assertEqual(im.tag[Y_RESOLUTION], 36)
 | 
					            self.assertEqual(im.tag[X_RESOLUTION][0][0] if legacy_api
 | 
				
			||||||
 | 
					                             else im.tag[X_RESOLUTION], 72)
 | 
				
			||||||
 | 
					            self.assertEqual(im.tag[Y_RESOLUTION][0][0] if legacy_api
 | 
				
			||||||
 | 
					                             else im.tag[Y_RESOLUTION], 36)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_deprecation_warning_with_spaces(self):
 | 
					    def test_deprecation_warning_with_spaces(self):
 | 
				
			||||||
        # Arrange: use spaces
 | 
					 | 
				
			||||||
        kwargs = {'resolution unit': 'inch',
 | 
					        kwargs = {'resolution unit': 'inch',
 | 
				
			||||||
                  'x resolution': 36,
 | 
					                  'x resolution': 36,
 | 
				
			||||||
                  'y resolution': 72}
 | 
					                  'y resolution': 72}
 | 
				
			||||||
        filename = self.tempfile("temp.tif")
 | 
					        filename = self.tempfile("temp.tif")
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Act
 | 
					 | 
				
			||||||
        self.assert_warning(DeprecationWarning,
 | 
					        self.assert_warning(DeprecationWarning,
 | 
				
			||||||
                            lambda: hopper("RGB").save(filename, **kwargs))
 | 
					                            lambda: hopper("RGB").save(filename, **kwargs))
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Assert
 | 
					 | 
				
			||||||
        from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION
 | 
					        from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION
 | 
				
			||||||
 | 
					        for legacy_api in [False, True]:
 | 
				
			||||||
            im = Image.open(filename)
 | 
					            im = Image.open(filename)
 | 
				
			||||||
        self.assertEqual(im.tag[X_RESOLUTION], 36)
 | 
					            im.tag.legacy_api = legacy_api
 | 
				
			||||||
        self.assertEqual(im.tag[Y_RESOLUTION], 72)
 | 
					            self.assertEqual(im.tag[X_RESOLUTION][0][0] if legacy_api
 | 
				
			||||||
 | 
					                             else im.tag[X_RESOLUTION], 36)
 | 
				
			||||||
 | 
					            self.assertEqual(im.tag[Y_RESOLUTION][0][0] if legacy_api
 | 
				
			||||||
 | 
					                             else im.tag[Y_RESOLUTION], 72)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,18 +34,46 @@ class TestFileTiffMetadata(PillowTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        img.save(f, tiffinfo=info)
 | 
					        img.save(f, tiffinfo=info)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for legacy_api in [False, True]:
 | 
				
			||||||
            loaded = Image.open(f)
 | 
					            loaded = Image.open(f)
 | 
				
			||||||
 | 
					            loaded.tag.legacy_api = legacy_api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(loaded.tag[50838], (len(basetextdata + " ?"),))
 | 
					            self.assertEqual(loaded.tag[50838],
 | 
				
			||||||
 | 
					                             (len(basetextdata + " ?"),) if legacy_api else len(basetextdata + " ?"))
 | 
				
			||||||
            self.assertEqual(loaded.tag[50839], basetextdata + " ?")
 | 
					            self.assertEqual(loaded.tag[50839], basetextdata + " ?")
 | 
				
			||||||
        self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']][0], floatdata,
 | 
					            loaded_float = loaded.tag[tag_ids['RollAngle']]
 | 
				
			||||||
                               places=5)
 | 
					            if legacy_api:
 | 
				
			||||||
        self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']], doubledata)
 | 
					                loaded_float = loaded_float[0]
 | 
				
			||||||
 | 
					            self.assertAlmostEqual(loaded_float, floatdata, places=5)
 | 
				
			||||||
 | 
					            loaded_double = loaded.tag[tag_ids['YawAngle']]
 | 
				
			||||||
 | 
					            if legacy_api:
 | 
				
			||||||
 | 
					                loaded_double = loaded_double[0]
 | 
				
			||||||
 | 
					            self.assertAlmostEqual(loaded_double, doubledata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_read_metadata(self):
 | 
					    def test_read_metadata(self):
 | 
				
			||||||
 | 
					        for legacy_api in [False, True]:
 | 
				
			||||||
            img = Image.open('Tests/images/hopper_g4.tif')
 | 
					            img = Image.open('Tests/images/hopper_g4.tif')
 | 
				
			||||||
 | 
					            img.tag.legacy_api = legacy_api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        known = {'YResolution': 4294967295 / 113653537,
 | 
					            known = {'YResolution': ((4294967295, 113653537),),
 | 
				
			||||||
 | 
					                     'PlanarConfiguration': (1,),
 | 
				
			||||||
 | 
					                     'BitsPerSample': (1,),
 | 
				
			||||||
 | 
					                     'ImageLength': (128,),
 | 
				
			||||||
 | 
					                     'Compression': (4,),
 | 
				
			||||||
 | 
					                     'FillOrder': (1,),
 | 
				
			||||||
 | 
					                     'RowsPerStrip': (128,),
 | 
				
			||||||
 | 
					                     'ResolutionUnit': (3,),
 | 
				
			||||||
 | 
					                     'PhotometricInterpretation': (0,),
 | 
				
			||||||
 | 
					                     'PageNumber': (0, 1),
 | 
				
			||||||
 | 
					                     'XResolution': ((4294967295, 113653537),),
 | 
				
			||||||
 | 
					                     'ImageWidth': (128,),
 | 
				
			||||||
 | 
					                     'Orientation': (1,),
 | 
				
			||||||
 | 
					                     'StripByteCounts': (1968,),
 | 
				
			||||||
 | 
					                     'SamplesPerPixel': (1,),
 | 
				
			||||||
 | 
					                     'StripOffsets': (8,)
 | 
				
			||||||
 | 
					                    } if legacy_api else {
 | 
				
			||||||
 | 
					                     'YResolution': 4294967295 / 113653537,
 | 
				
			||||||
                     'PlanarConfiguration': 1,
 | 
					                     'PlanarConfiguration': 1,
 | 
				
			||||||
                     'BitsPerSample': (1,),
 | 
					                     'BitsPerSample': (1,),
 | 
				
			||||||
                     'ImageLength': 128,
 | 
					                     'ImageLength': 128,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user