Skip to content

Commit e8a55ec

Browse files
committed
Allow for IFDRational with negative numerator and zero denominator
1 parent 3c71559 commit e8a55ec

File tree

2 files changed

+19
-35
lines changed

2 files changed

+19
-35
lines changed

Tests/test_file_tiff_metadata.py

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -385,50 +385,34 @@ def test_ifd_unsigned_rational(tmp_path: Path) -> None:
385385
assert 1 == reloaded.tag_v2[41493].denominator
386386

387387

388-
def test_ifd_signed_rational(tmp_path: Path) -> None:
388+
@pytest.mark.parametrize(
389+
"numerator, denominator, expected",
390+
(
391+
(2**31 - 1, -(2**31), None), # pair of 4 byte signed longs
392+
(-(2**31), 2**31 - 1, None),
393+
(-(2**31) - 1, 1, (2**31 - 1, -1)), # out of bounds of 4 byte signed long
394+
(-1, 0, None), # IFDRational nan
395+
),
396+
)
397+
def test_ifd_signed_rational(
398+
numerator: int, denominator: int, expected: tuple[int, int] | None, tmp_path: Path
399+
) -> None:
389400
im = hopper()
390401
info = TiffImagePlugin.ImageFileDirectory_v2()
391402

392-
# pair of 4 byte signed longs
393-
numerator = 2**31 - 1
394-
denominator = -(2**31)
395-
396-
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
397-
398-
out = tmp_path / "temp.tiff"
399-
im.save(out, tiffinfo=info, compression="raw")
400-
401-
with Image.open(out) as reloaded:
402-
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
403-
assert numerator == reloaded.tag_v2[37380].numerator
404-
assert denominator == reloaded.tag_v2[37380].denominator
405-
406-
numerator = -(2**31)
407-
denominator = 2**31 - 1
408-
409-
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
410-
411-
out = tmp_path / "temp.tiff"
412-
im.save(out, tiffinfo=info, compression="raw")
413-
414-
with Image.open(out) as reloaded:
415-
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
416-
assert numerator == reloaded.tag_v2[37380].numerator
417-
assert denominator == reloaded.tag_v2[37380].denominator
418-
419-
# out of bounds of 4 byte signed long
420-
numerator = -(2**31) - 1
421-
denominator = 1
422-
423403
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
424404

425405
out = tmp_path / "temp.tiff"
426406
im.save(out, tiffinfo=info, compression="raw")
427407

428408
with Image.open(out) as reloaded:
429409
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
430-
assert 2**31 - 1 == reloaded.tag_v2[37380].numerator
431-
assert -1 == reloaded.tag_v2[37380].denominator
410+
if expected is None:
411+
expected = (numerator, denominator)
412+
assert (
413+
reloaded.tag_v2[37380].numerator,
414+
reloaded.tag_v2[37380].denominator,
415+
) == expected
432416

433417

434418
def test_ifd_signed_long(tmp_path: Path) -> None:

src/PIL/TiffImagePlugin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ def _setitem(self, tag: int, value: Any, legacy_api: bool) -> None:
688688
if all(isinstance(v, IFDRational) for v in values):
689689
for v in values:
690690
assert isinstance(v, IFDRational)
691-
if v < 0:
691+
if (float(v.numerator) < 0) != (float(v.denominator) < 0):
692692
self.tagtype[tag] = TiffTags.SIGNED_RATIONAL
693693
break
694694
else:

0 commit comments

Comments
 (0)