EMF: support negative bounding box coordinates (#2249)

* EMF: support negative bounding box coordinates

Similar to placeable WMF, bounding box coordinates
should be interpreted as signed integer, otherwise
opening EMF file with negative (x0,y0) fails.

* Basic load tests for WMF and EMF formats

* WMF/WMF tests: just test open(), not load()

Not sure why load() fails on Debian build. Well, at least we can test
open().

* WMF/EMF: Unpack signed integers using unpack()

* WMF/EMF: Compare to reference PNG rendering

* EMF/WMF comparison: use assert_image_similar()

* Use similarity epsilon 0.5 for WMF, as vector rendering looks different across Windows platforms

* Trigger rebuild
This commit is contained in:
glexey 2016-11-27 08:03:51 -08:00 committed by wiredfool
parent fdeff998df
commit 0116c9240e
7 changed files with 62 additions and 16 deletions

View File

@ -14,6 +14,10 @@
#
# See the README file for information on usage and redistribution.
#
# WMF/EMF reference documentation:
# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf
# http://wvware.sourceforge.net/caolan/index.html
# http://wvware.sourceforge.net/caolan/ora-wmf.html
from PIL import Image, ImageFile, _binary
@ -56,16 +60,9 @@ if hasattr(Image.core, "drawwmf"):
# --------------------------------------------------------------------
word = _binary.i16le
def short(c, o=0):
v = word(c, o)
if v >= 32768:
v -= 65536
return v
short = _binary.si16le
dword = _binary.i32le
_long = _binary.si32le
#
# --------------------------------------------------------------------
@ -121,13 +118,13 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# enhanced metafile
# get bounding box
x0 = dword(s, 8)
y0 = dword(s, 12)
x1 = dword(s, 16)
y1 = dword(s, 20)
x0 = _long(s, 8)
y0 = _long(s, 12)
x1 = _long(s, 16)
y1 = _long(s, 20)
# get frame (in 0.01 millimeter units)
frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36)
frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36)
# normalize size to 72 dots per inch
size = x1 - x0, y1 - y0

View File

@ -31,23 +31,41 @@ else:
# TODO: replace with more readable struct.unpack equivalent
def i16le(c, o=0):
"""
Converts a 2-bytes (16 bits) string to an integer.
Converts a 2-bytes (16 bits) string to an unsigned integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
return unpack("<H", c[o:o+2])[0]
def si16le(c, o=0):
"""
Converts a 2-bytes (16 bits) string to a signed integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
return unpack("<h", c[o:o+2])[0]
def i32le(c, o=0):
"""
Converts a 4-bytes (32 bits) string to an integer.
Converts a 4-bytes (32 bits) string to an unsigned integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
return unpack("<I", c[o:o+4])[0]
def si32le(c, o=0):
"""
Converts a 4-bytes (32 bits) string to a signed integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
return unpack("<i", c[o:o+4])[0]
def i16be(c, o=0):
return unpack(">H", c[o:o+2])[0]

BIN
Tests/images/drawing.emf Normal file

Binary file not shown.

BIN
Tests/images/drawing.wmf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

31
Tests/test_file_wmf.py Normal file
View File

@ -0,0 +1,31 @@
from helper import unittest, PillowTestCase
from PIL import Image
from io import BytesIO
class TestFileWmf(PillowTestCase):
def test_load_raw(self):
# Test basic EMF open and rendering
im = Image.open('Tests/images/drawing.emf')
if hasattr(Image.core, "drawwmf"):
# Currently, support for WMF/EMF is Windows-only
im.load()
# Compare to reference rendering
imref = Image.open('Tests/images/drawing_emf_ref.png')
imref.load()
self.assert_image_similar(im, imref, 0)
# Test basic WMF open and rendering
im = Image.open('Tests/images/drawing.wmf')
if hasattr(Image.core, "drawwmf"):
# Currently, support for WMF/EMF is Windows-only
im.load()
# Compare to reference rendering
imref = Image.open('Tests/images/drawing_wmf_ref.png')
imref.load()
self.assert_image_similar(im, imref, 0.5)
if __name__ == '__main__':
unittest.main()