diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index d9fa1e4b450..e40a19394bf 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -9,7 +9,7 @@ import pytest from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags, features -from PIL.TiffImagePlugin import STRIPOFFSETS, SUBIFD +from PIL.TiffImagePlugin import SAMPLEFORMAT, STRIPOFFSETS, SUBIFD from .helper import ( assert_image_equal, @@ -825,6 +825,17 @@ def test_sampleformat(self): assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode="RGB") + def test_sampleformat_write(self, tmp_path): + im = Image.new("F", (1, 1)) + out = str(tmp_path / "temp.tif") + TiffImagePlugin.WRITE_LIBTIFF = True + im.save(out) + TiffImagePlugin.WRITE_LIBTIFF = False + + with Image.open(out) as reloaded: + assert reloaded.mode == "F" + assert reloaded.getexif()[SAMPLEFORMAT] == 3 + def test_lzw(self): with Image.open("Tests/images/hopper_lzw.tif") as im: assert im.mode == "RGB" diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index a6a842b1d8b..5df5c4f4cd5 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1676,8 +1676,6 @@ def _save(im, fp, filename): # optional types for non core tags types = {} - # SAMPLEFORMAT is determined by the image format and should not be copied - # from legacy_ifd. # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library # based on the data in the strip. # The other tags expect arrays with a certain length (fixed or depending on @@ -1686,7 +1684,6 @@ def _save(im, fp, filename): # SUBIFD may also cause a segfault. blocklist += [ REFERENCEBLACKWHITE, - SAMPLEFORMAT, STRIPBYTECOUNTS, STRIPOFFSETS, TRANSFERFUNCTION, @@ -1702,9 +1699,14 @@ def _save(im, fp, filename): legacy_ifd = {} 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() - ): + + # SAMPLEFORMAT is determined by the image format and should not be copied + # from legacy_ifd. + supplied_tags = {**getattr(im, "tag_v2", {}), **legacy_ifd} + if SAMPLEFORMAT in supplied_tags: + del supplied_tags[SAMPLEFORMAT] + + for tag, value in itertools.chain(ifd.items(), supplied_tags.items()): # Libtiff can only process certain core items without adding # them to the custom dictionary. # Custom items are supported for int, float, unicode, string and byte @@ -1729,6 +1731,9 @@ def _save(im, fp, filename): else: atts[tag] = value + if SAMPLEFORMAT in atts and len(atts[SAMPLEFORMAT]) == 1: + atts[SAMPLEFORMAT] = atts[SAMPLEFORMAT][0] + logger.debug("Converted items: %s" % sorted(atts.items())) # libtiff always expects the bytes in native order.