Skip to content

Commit 3ac0420

Browse files
committed
Fix: Set tag type accordingly if IFDRational with 0 denominator
1 parent 3c71559 commit 3ac0420

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

Tests/test_file_tiff_metadata.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import io
4+
import math
45
import struct
56
from pathlib import Path
67

@@ -280,6 +281,30 @@ def test_writing_other_types_to_undefined(
280281
assert reloaded.tag_v2[33723] == b"1"
281282

282283

284+
@pytest.mark.parametrize(
285+
"value, expected",
286+
(
287+
(IFDRational(1, 0), TiffTags.RATIONAL),
288+
(IFDRational(-1, 0), TiffTags.SIGNED_RATIONAL),
289+
),
290+
)
291+
def test_tagtype_on_zero_denominator(
292+
value: IFDRational, expected: int, tmp_path: Path
293+
) -> None:
294+
info = TiffImagePlugin.ImageFileDirectory_v2()
295+
296+
info[37380] = value
297+
assert info.tagtype[37380] == expected
298+
299+
im = hopper()
300+
out = tmp_path / "temp.tiff"
301+
im.save(out, tiffinfo=info)
302+
303+
with Image.open(out) as reloaded:
304+
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
305+
assert math.isnan(reloaded.tag_v2[37380])
306+
307+
283308
def test_undefined_zero(tmp_path: Path) -> None:
284309
# Check that the tag has not been changed since this test was created
285310
tag = TiffTags.TAGS_V2[45059]

Tests/test_tiff_ifdrational.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,14 @@ def test_ifd_rational_save(
7474
with Image.open(out) as reloaded:
7575
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
7676
assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282])
77+
78+
79+
@pytest.mark.parametrize(
80+
"numerator, denominator, expected_result",
81+
[
82+
(1, 1, 1.0),
83+
(1, 0, float("nan")),
84+
],
85+
)
86+
def test_float_cast(numerator, denominator, expected_result):
87+
float(IFDRational(numerator, denominator)) == expected_result

src/PIL/TiffImagePlugin.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,9 @@ def limit_rational(self, max_denominator: int) -> tuple[IntegralLike, int]:
402402
f = self._val.limit_denominator(max_denominator)
403403
return f.numerator, f.denominator
404404

405+
def __float__(self):
406+
return float(self._val)
407+
405408
def __repr__(self) -> str:
406409
return str(float(self._val))
407410

@@ -688,7 +691,7 @@ def _setitem(self, tag: int, value: Any, legacy_api: bool) -> None:
688691
if all(isinstance(v, IFDRational) for v in values):
689692
for v in values:
690693
assert isinstance(v, IFDRational)
691-
if v < 0:
694+
if v < 0 or (math.isnan(v) and v.numerator < 0):
692695
self.tagtype[tag] = TiffTags.SIGNED_RATIONAL
693696
break
694697
else:

0 commit comments

Comments
 (0)