Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion Tests/test_file_libtiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"
Expand Down
17 changes: 11 additions & 6 deletions src/PIL/TiffImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -1686,7 +1684,6 @@ def _save(im, fp, filename):
# SUBIFD may also cause a segfault.
blocklist += [
REFERENCEBLACKWHITE,
SAMPLEFORMAT,
STRIPBYTECOUNTS,
STRIPOFFSETS,
TRANSFERFUNCTION,
Expand All @@ -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
Expand All @@ -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.
Expand Down