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
34 changes: 31 additions & 3 deletions Tests/test_file_libtiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,6 @@ def check_tags(tiffinfo):
)
continue

if libtiff and isinstance(value, bytes):
value = value.decode()

assert reloaded_value == value

# Test with types
Expand All @@ -322,6 +319,17 @@ def check_tags(tiffinfo):
)
TiffImagePlugin.WRITE_LIBTIFF = False

def test_xmlpacket_tag(self, tmp_path):
TiffImagePlugin.WRITE_LIBTIFF = True

out = str(tmp_path / "temp.tif")
hopper().save(out, tiffinfo={700: b"xmlpacket tag"})
TiffImagePlugin.WRITE_LIBTIFF = False

with Image.open(out) as reloaded:
if 700 in reloaded.tag_v2:
assert reloaded.tag_v2[700] == b"xmlpacket tag"

def test_int_dpi(self, tmp_path):
# issue #1765
im = hopper("RGB")
Expand Down Expand Up @@ -667,6 +675,26 @@ def test_read_icc(self):
TiffImagePlugin.READ_LIBTIFF = False
assert icc == icc_libtiff

def test_write_icc(self, tmp_path):
def check_write(libtiff):
TiffImagePlugin.WRITE_LIBTIFF = libtiff

with Image.open("Tests/images/hopper.iccprofile.tif") as img:
icc_profile = img.info["icc_profile"]

out = str(tmp_path / "temp.tif")
img.save(out, icc_profile=icc_profile)
with Image.open(out) as reloaded:
assert icc_profile == reloaded.info["icc_profile"]

libtiffs = []
if Image.core.libtiff_support_custom_tags:
libtiffs.append(True)
libtiffs.append(False)

for libtiff in libtiffs:
check_write(libtiff)

def test_multipage_compression(self):
with Image.open("Tests/images/compression.tif") as im:

Expand Down
8 changes: 4 additions & 4 deletions Tests/test_file_tiff_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,13 +319,13 @@ def test_empty_values():

def test_PhotoshopInfo(tmp_path):
with Image.open("Tests/images/issue_2278.tif") as im:
assert len(im.tag_v2[34377]) == 1
assert isinstance(im.tag_v2[34377][0], bytes)
assert len(im.tag_v2[34377]) == 70
assert isinstance(im.tag_v2[34377], bytes)
out = str(tmp_path / "temp.tiff")
im.save(out)
with Image.open(out) as reloaded:
assert len(reloaded.tag_v2[34377]) == 1
assert isinstance(reloaded.tag_v2[34377][0], bytes)
assert len(reloaded.tag_v2[34377]) == 70
assert isinstance(reloaded.tag_v2[34377], bytes)


def test_too_many_entries():
Expand Down
22 changes: 13 additions & 9 deletions src/PIL/TiffImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,9 +553,10 @@ def _setitem(self, tag, value, legacy_api):
)
elif all(isinstance(v, float) for v in values):
self.tagtype[tag] = TiffTags.DOUBLE
else:
if all(isinstance(v, str) for v in values):
self.tagtype[tag] = TiffTags.ASCII
elif all(isinstance(v, str) for v in values):
self.tagtype[tag] = TiffTags.ASCII
elif all(isinstance(v, bytes) for v in values):
self.tagtype[tag] = TiffTags.BYTE

if self.tagtype[tag] == TiffTags.UNDEFINED:
values = [
Expand All @@ -573,8 +574,10 @@ def _setitem(self, tag, value, legacy_api):
# 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
if (
(info.length == 1)
or self.tagtype[tag] == TiffTags.BYTE
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 [
Expand Down Expand Up @@ -1546,16 +1549,17 @@ def _save(im, fp, filename):
# Custom items are supported for int, float, unicode, string and byte
# values. Other types and tuples require a tagtype.
if tag not in TiffTags.LIBTIFF_CORE:
if (
TiffTags.lookup(tag).type == TiffTags.UNDEFINED
or not Image.core.libtiff_support_custom_tags
):
if not Image.core.libtiff_support_custom_tags:
continue

if tag in ifd.tagtype:
types[tag] = ifd.tagtype[tag]
elif not (isinstance(value, (int, float, str, bytes))):
continue
else:
type = TiffTags.lookup(tag).type
if type:
types[tag] = type
if tag not in atts and tag not in blocklist:
if isinstance(value, str):
atts[tag] = value.encode("ascii", "replace") + b"\0"
Expand Down
32 changes: 9 additions & 23 deletions src/encode.c
Original file line number Diff line number Diff line change
Expand Up @@ -761,12 +761,6 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
}
}

if (PyBytes_Check(value) &&
(type == TIFF_BYTE || type == TIFF_UNDEFINED)) {
// For backwards compatibility
type = TIFF_ASCII;
}

if (PyTuple_Check(value)) {
Py_ssize_t len;
len = PyTuple_Size(value);
Expand All @@ -790,28 +784,24 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)

if (!is_core_tag) {
// Register field for non core tags.
if (type == TIFF_BYTE) {
is_var_length = 1;
}
if (ImagingLibTiffMergeFieldInfo(&encoder->state, type, key_int, is_var_length)) {
continue;
}
}

if (is_var_length) {
if (type == TIFF_BYTE || type == TIFF_UNDEFINED) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
PyBytes_Size(value), PyBytes_AsString(value));
} else if (is_var_length) {
Py_ssize_t len,i;
TRACE(("Setting from Tuple: %d \n", key_int));
len = PyTuple_Size(value);

if (type == TIFF_BYTE) {
UINT8 *av;
/* malloc check ok, calloc checks for overflow */
av = calloc(len, sizeof(UINT8));
if (av) {
for (i=0;i<len;i++) {
av[i] = (UINT8)PyLong_AsLong(PyTuple_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, len, av);
free(av);
}
} else if (type == TIFF_SHORT) {
if (type == TIFF_SHORT) {
UINT16 *av;
/* malloc check ok, calloc checks for overflow */
av = calloc(len, sizeof(UINT16));
Expand Down Expand Up @@ -914,10 +904,6 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(FLOAT64)PyFloat_AsDouble(value));
} else if (type == TIFF_BYTE) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
(UINT8)PyLong_AsLong(value));
} else if (type == TIFF_SBYTE) {
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) key_int,
Expand Down