Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed DPI rounding from BMP, JPEG and PNG loading #5476

Merged
merged 3 commits into from May 16, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file removed Tests/images/hopper_roundDown.bmp
Binary file not shown.
Binary file removed Tests/images/iptc_roundDown.jpg
Binary file not shown.
27 changes: 7 additions & 20 deletions Tests/test_file_bmp.py
Expand Up @@ -63,46 +63,33 @@ def test_dpi():

output.seek(0)
with Image.open(output) as reloaded:
assert reloaded.info["dpi"] == dpi
assert reloaded.info["dpi"] == (72.008961115161, 72.008961115161)


def test_save_bmp_with_dpi(tmp_path):
# Test for #1301
# Arrange
outfile = str(tmp_path / "temp.jpg")
with Image.open("Tests/images/hopper.bmp") as im:
assert im.info["dpi"] == (95.98654816726399, 95.98654816726399)

# Act
im.save(outfile, "JPEG", dpi=im.info["dpi"])

# Assert
with Image.open(outfile) as reloaded:
reloaded.load()
assert im.info["dpi"] == reloaded.info["dpi"]
assert im.size == reloaded.size
assert reloaded.info["dpi"] == (96, 96)
assert reloaded.size == im.size
assert reloaded.format == "JPEG"


def test_load_dpi_rounding():
# Round up
with Image.open("Tests/images/hopper.bmp") as im:
assert im.info["dpi"] == (96, 96)

# Round down
with Image.open("Tests/images/hopper_roundDown.bmp") as im:
assert im.info["dpi"] == (72, 72)


def test_save_dpi_rounding(tmp_path):
def test_save_float_dpi(tmp_path):
outfile = str(tmp_path / "temp.bmp")
with Image.open("Tests/images/hopper.bmp") as im:
im.save(outfile, dpi=(72.2, 72.2))
im.save(outfile, dpi=(72.21216100543306, 72.21216100543306))
with Image.open(outfile) as reloaded:
assert reloaded.info["dpi"] == (72, 72)

im.save(outfile, dpi=(72.8, 72.8))
with Image.open(outfile) as reloaded:
assert reloaded.info["dpi"] == (73, 73)
assert reloaded.info["dpi"] == (72.21216100543306, 72.21216100543306)


def test_load_dib():
Expand Down
9 changes: 0 additions & 9 deletions Tests/test_file_jpeg.py
Expand Up @@ -656,15 +656,6 @@ def test_save_tiff_with_dpi(self, tmp_path):
reloaded.load()
assert im.info["dpi"] == reloaded.info["dpi"]

def test_load_dpi_rounding(self):
# Round up
with Image.open("Tests/images/iptc_roundUp.jpg") as im:
assert im.info["dpi"] == (44, 44)

# Round down
with Image.open("Tests/images/iptc_roundDown.jpg") as im:
assert im.info["dpi"] == (2, 2)

def test_save_dpi_rounding(self, tmp_path):
outfile = str(tmp_path / "temp.jpg")
with Image.open("Tests/images/hopper.jpg") as im:
Expand Down
21 changes: 4 additions & 17 deletions Tests/test_file_png.py
Expand Up @@ -386,25 +386,12 @@ def test_roundtrip_dpi(self):
# Check dpi roundtripping

with Image.open(TEST_PNG_FILE) as im:
im = roundtrip(im, dpi=(100, 100))
assert im.info["dpi"] == (100, 100)
im = roundtrip(im, dpi=(100.33, 100.33))
assert im.info["dpi"] == (100.33, 100.33)

def test_load_dpi_rounding(self):
# Round up
def test_load_float_dpi(self):
with Image.open(TEST_PNG_FILE) as im:
assert im.info["dpi"] == (96, 96)

# Round down
with Image.open("Tests/images/icc_profile_none.png") as im:
assert im.info["dpi"] == (72, 72)

def test_save_dpi_rounding(self):
with Image.open(TEST_PNG_FILE) as im:
im = roundtrip(im, dpi=(72.2, 72.2))
assert im.info["dpi"] == (72, 72)

im = roundtrip(im, dpi=(72.8, 72.8))
assert im.info["dpi"] == (73, 73)
assert im.info["dpi"] == (95.9866, 95.9866)

def test_roundtrip_text(self):
# Check text roundtripping
Expand Down
4 changes: 1 addition & 3 deletions src/PIL/BmpImagePlugin.py
Expand Up @@ -115,9 +115,7 @@ def _bitmap(self, header=0, offset=0):
)
file_info["colors"] = i32(header_data, 28)
file_info["palette_padding"] = 4
self.info["dpi"] = tuple(
int(x / 39.3701 + 0.5) for x in file_info["pixels_per_meter"]
)
self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"])
if file_info["compression"] == self.BITFIELDS:
if len(header_data) >= 52:
for idx, mask in enumerate(
Expand Down
7 changes: 5 additions & 2 deletions src/PIL/JpegImagePlugin.py
Expand Up @@ -33,6 +33,7 @@
#
import array
import io
import math
import os
import struct
import subprocess
Expand Down Expand Up @@ -162,15 +163,17 @@ def APP(self, marker):
dpi = float(x_resolution[0]) / x_resolution[1]
except TypeError:
dpi = x_resolution
if math.isnan(dpi):
raise ValueError
if resolution_unit == 3: # cm
# 1 dpcm = 2.54 dpi
dpi *= 2.54
self.info["dpi"] = int(dpi + 0.5), int(dpi + 0.5)
self.info["dpi"] = dpi, dpi
except (KeyError, SyntaxError, ValueError, ZeroDivisionError):
# SyntaxError for invalid/unreadable EXIF
# KeyError for dpi not included
# ZeroDivisionError for invalid dpi rational value
# ValueError for x_resolution[0] being an invalid float
# ValueError for dpi being an invalid float
self.info["dpi"] = 72, 72


Expand Down
2 changes: 1 addition & 1 deletion src/PIL/PngImagePlugin.py
Expand Up @@ -500,7 +500,7 @@ def chunk_pHYs(self, pos, length):
px, py = i32(s, 0), i32(s, 4)
unit = s[8]
if unit == 1: # meter
dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5)
dpi = px * 0.0254, py * 0.0254
self.im_info["dpi"] = dpi
elif unit == 0:
self.im_info["aspect"] = px, py
Expand Down