From 103354facc79f48f6bdc140fb9a1f85c10ae9e18 Mon Sep 17 00:00:00 2001 From: gcq Date: Sat, 28 Jun 2014 22:18:47 +0200 Subject: [PATCH 1/4] BMP now uses a reasonable resolution, and customizable using the "dpi" option. --- PIL/BmpImagePlugin.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 436ca5dce..c20ba184a 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -203,30 +203,37 @@ def _save(im, fp, filename, check=0): if check: return check + info = im.encoderinfo + + dpi = info.get("dpi", (96, 96)) + + # 1 meter == 39.3701 inches + ppm = map(lambda x: int(x * 39.3701), dpi) + stride = ((im.size[0]*bits+7)//8+3)&(~3) header = 40 # or 64 for OS/2 version 2 offset = 14 + header + colors * 4 image = stride * im.size[1] # bitmap header - fp.write(b"BM" + # file type (magic) - o32(offset+image) + # file size - o32(0) + # reserved - o32(offset)) # image data offset + fp.write(b"BM" + # file type (magic) + o32(offset+image) + # file size + o32(0) + # reserved + o32(offset)) # image data offset # bitmap info header - fp.write(o32(header) + # info header size - o32(im.size[0]) + # width - o32(im.size[1]) + # height - o16(1) + # planes - o16(bits) + # depth - o32(0) + # compression (0=uncompressed) - o32(image) + # size of bitmap - o32(1) + o32(1) + # resolution - o32(colors) + # colors used - o32(colors)) # colors important + fp.write(o32(header) + # info header size + o32(im.size[0]) + # width + o32(im.size[1]) + # height + o16(1) + # planes + o16(bits) + # depth + o32(0) + # compression (0=uncompressed) + o32(image) + # size of bitmap + o32(ppm[0]) + o32(ppm[1]) + # resolution + o32(colors) + # colors used + o32(colors)) # colors important - fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) + fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) if im.mode == "1": for i in (0, 255): From 9318755a180a8e4012ca743865588faad416d2e7 Mon Sep 17 00:00:00 2001 From: gcq Date: Sat, 28 Jun 2014 23:21:22 +0200 Subject: [PATCH 2/4] Adds dpi to the Image info dictinoary. --- PIL/BmpImagePlugin.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index c20ba184a..e320feb7a 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -28,6 +28,7 @@ __version__ = "0.7" from PIL import Image, ImageFile, ImagePalette, _binary +import math i8 = _binary.i8 i16 = _binary.i16le @@ -88,6 +89,7 @@ class BmpImageFile(ImageFile.ImageFile): bits = i16(s[14:]) self.size = i32(s[4:]), i32(s[8:]) compression = i32(s[16:]) + pxperm = (i32(s[24:]), i32(s[28:])) # Pixels per meter lutsize = 4 colors = i32(s[32:]) direction = -1 @@ -162,6 +164,7 @@ class BmpImageFile(ImageFile.ImageFile): (rawmode, ((self.size[0]*bits+31)>>3)&(~3), direction))] self.info["compression"] = compression + self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), pxperm)) def _open(self): @@ -208,7 +211,7 @@ def _save(im, fp, filename, check=0): dpi = info.get("dpi", (96, 96)) # 1 meter == 39.3701 inches - ppm = map(lambda x: int(x * 39.3701), dpi) + ppm = tuple(map(lambda x: int(x * 39.3701), dpi)) stride = ((im.size[0]*bits+7)//8+3)&(~3) header = 40 # or 64 for OS/2 version 2 From 92b070ceaf453ef66e8cab91ddd5d59d13e1aa41 Mon Sep 17 00:00:00 2001 From: gcq Date: Sat, 28 Jun 2014 23:22:52 +0200 Subject: [PATCH 3/4] Addes tests --- Tests/test_file_bmp.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index 29ddb9824..2870aba04 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -37,6 +37,18 @@ class TestFileBmp(PillowTestCase): self.assertEqual(im.size, reloaded.size) self.assertEqual(reloaded.format, "BMP") + def test_dpi(self): + dpi = (72, 72) + + output = io.BytesIO() + im = lena() + im.save(output, "BMP", dpi=dpi) + + output.seek(0) + reloaded = Image.open(output) + + self.assertEqual(reloaded.info["dpi"], dpi) + if __name__ == '__main__': unittest.main() From 61be1d8b19452083bad9c897b30be6411d95bf4a Mon Sep 17 00:00:00 2001 From: gcq Date: Sat, 28 Jun 2014 23:56:22 +0200 Subject: [PATCH 4/4] dpi key should only be present when there is resolution info in the BMP header. --- PIL/BmpImagePlugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index e320feb7a..fae6bd391 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -97,6 +97,8 @@ class BmpImageFile(ImageFile.ImageFile): # upside-down storage self.size = self.size[0], 2**32 - self.size[1] direction = 0 + + self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), pxperm)) else: raise IOError("Unsupported BMP header type (%d)" % len(s)) @@ -164,7 +166,6 @@ class BmpImageFile(ImageFile.ImageFile): (rawmode, ((self.size[0]*bits+31)>>3)&(~3), direction))] self.info["compression"] = compression - self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), pxperm)) def _open(self):