diff --git a/Tests/images/hopper_roundUp_2.tif b/Tests/images/hopper_float_dpi_2.tif similarity index 100% rename from Tests/images/hopper_roundUp_2.tif rename to Tests/images/hopper_float_dpi_2.tif diff --git a/Tests/images/hopper_roundUp_3.tif b/Tests/images/hopper_float_dpi_3.tif similarity index 100% rename from Tests/images/hopper_roundUp_3.tif rename to Tests/images/hopper_float_dpi_3.tif diff --git a/Tests/images/hopper_roundUp_None.tif b/Tests/images/hopper_float_dpi_None.tif similarity index 100% rename from Tests/images/hopper_roundUp_None.tif rename to Tests/images/hopper_float_dpi_None.tif diff --git a/Tests/images/hopper_roundDown_2.tif b/Tests/images/hopper_roundDown_2.tif deleted file mode 100644 index ac8cd057d61..00000000000 Binary files a/Tests/images/hopper_roundDown_2.tif and /dev/null differ diff --git a/Tests/images/hopper_roundDown_3.tif b/Tests/images/hopper_roundDown_3.tif deleted file mode 100644 index 0542fab9aa8..00000000000 Binary files a/Tests/images/hopper_roundDown_3.tif and /dev/null differ diff --git a/Tests/images/hopper_roundDown_None.tif b/Tests/images/hopper_roundDown_None.tif deleted file mode 100644 index 21c40e8fe6f..00000000000 Binary files a/Tests/images/hopper_roundDown_None.tif and /dev/null differ diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index c24438c4851..91506f98017 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -137,29 +137,26 @@ def test_int_resolution(self): im._setup() assert im.info["dpi"] == (71.0, 71.0) - def test_load_dpi_rounding(self): - for resolutionUnit, dpi in ((None, (72, 73)), (2, (72, 73)), (3, (183, 185))): - with Image.open( - "Tests/images/hopper_roundDown_" + str(resolutionUnit) + ".tif" - ) as im: - assert im.tag_v2.get(RESOLUTION_UNIT) == resolutionUnit - assert im.info["dpi"] == (dpi[0], dpi[0]) - - with Image.open( - "Tests/images/hopper_roundUp_" + str(resolutionUnit) + ".tif" - ) as im: - assert im.tag_v2.get(RESOLUTION_UNIT) == resolutionUnit - assert im.info["dpi"] == (dpi[1], dpi[1]) - - def test_save_dpi_rounding(self, tmp_path): + @pytest.mark.parametrize( + "resolutionUnit, dpi", + [(None, 72.8), (2, 72.8), (3, 184.912)], + ) + def test_load_float_dpi(self, resolutionUnit, dpi): + with Image.open( + "Tests/images/hopper_float_dpi_" + str(resolutionUnit) + ".tif" + ) as im: + assert im.tag_v2.get(RESOLUTION_UNIT) == resolutionUnit + for reloaded_dpi in im.info["dpi"]: + assert float(reloaded_dpi) == dpi + + def test_save_float_dpi(self, tmp_path): outfile = str(tmp_path / "temp.tif") with Image.open("Tests/images/hopper.tif") as im: - for dpi in (72.2, 72.8): - im.save(outfile, dpi=(dpi, dpi)) + im.save(outfile, dpi=(72.2, 72.2)) - with Image.open(outfile) as reloaded: - reloaded.load() - assert (round(dpi), round(dpi)) == reloaded.info["dpi"] + with Image.open(outfile) as reloaded: + for dpi in reloaded.info["dpi"]: + assert float(dpi) == 72.2 def test_save_setting_missing_resolution(self): b = BytesIO() diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 8c0f61f47da..0e779248fb7 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -358,6 +358,16 @@ def __eq__(self, other): other = other._val return self._val == other + def __getstate__(self): + return [self._val, self._numerator, self._denominator] + + def __setstate__(self, state): + IFDRational.__init__(self, 0) + _val, _numerator, _denominator = state + self._val = _val + self._numerator = _numerator + self._denominator = _denominator + def _delegate(op): def delegate(self, *args): return getattr(self._val, op)(*args) @@ -1284,11 +1294,11 @@ def _setup(self): if xres and yres: resunit = self.tag_v2.get(RESOLUTION_UNIT) if resunit == 2: # dots per inch - self.info["dpi"] = int(xres + 0.5), int(yres + 0.5) + self.info["dpi"] = (xres, yres) elif resunit == 3: # dots per centimeter. convert to dpi - self.info["dpi"] = int(xres * 2.54 + 0.5), int(yres * 2.54 + 0.5) + self.info["dpi"] = (xres * 2.54, yres * 2.54) elif resunit is None: # used to default to 1, but now 2) - self.info["dpi"] = int(xres + 0.5), int(yres + 0.5) + self.info["dpi"] = (xres, yres) # For backward compatibility, # we also preserve the old behavior self.info["resolution"] = xres, yres @@ -1521,8 +1531,8 @@ def _save(im, fp, filename): dpi = im.encoderinfo.get("dpi") if dpi: ifd[RESOLUTION_UNIT] = 2 - ifd[X_RESOLUTION] = int(dpi[0] + 0.5) - ifd[Y_RESOLUTION] = int(dpi[1] + 0.5) + ifd[X_RESOLUTION] = dpi[0] + ifd[Y_RESOLUTION] = dpi[1] if bits != (1,): ifd[BITSPERSAMPLE] = bits