mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-27 09:44:31 +03:00
Merge pull request #619 from eliempje/master
Bugfix: EPS thumbnail failed
This commit is contained in:
commit
3f18e0adfe
|
@ -11,6 +11,7 @@
|
||||||
# 1996-08-23 fl Handle files from Macintosh (0.3)
|
# 1996-08-23 fl Handle files from Macintosh (0.3)
|
||||||
# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4)
|
# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4)
|
||||||
# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5)
|
# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5)
|
||||||
|
# 2014-05-07 e Handling of EPS with binary preview and fixed resolution resizing
|
||||||
#
|
#
|
||||||
# Copyright (c) 1997-2003 by Secret Labs AB.
|
# Copyright (c) 1997-2003 by Secret Labs AB.
|
||||||
# Copyright (c) 1995-2003 by Fredrik Lundh
|
# Copyright (c) 1995-2003 by Fredrik Lundh
|
||||||
|
@ -71,14 +72,15 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
# Unpack decoder tile
|
# Unpack decoder tile
|
||||||
decoder, tile, offset, data = tile[0]
|
decoder, tile, offset, data = tile[0]
|
||||||
length, bbox = data
|
length, bbox = data
|
||||||
|
|
||||||
#Hack to support hi-res rendering
|
#Hack to support hi-res rendering
|
||||||
scale = int(scale) or 1
|
scale = int(scale) or 1
|
||||||
orig_size = size
|
orig_size = size
|
||||||
orig_bbox = bbox
|
orig_bbox = bbox
|
||||||
size = (size[0] * scale, size[1] * scale)
|
size = (size[0] * scale, size[1] * scale)
|
||||||
bbox = [bbox[0], bbox[1], bbox[2] * scale, bbox[3] * scale]
|
# resolution is dependend on bbox and size
|
||||||
#print("Ghostscript", scale, size, orig_size, bbox, orig_bbox)
|
res = ( float((72.0 * size[0]) / (bbox[2]-bbox[0])), float((72.0 * size[1]) / (bbox[3]-bbox[1])) )
|
||||||
|
#print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res)
|
||||||
|
|
||||||
import tempfile, os, subprocess
|
import tempfile, os, subprocess
|
||||||
|
|
||||||
|
@ -86,21 +88,30 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
os.close(out_fd)
|
os.close(out_fd)
|
||||||
in_fd, infile = tempfile.mkstemp()
|
in_fd, infile = tempfile.mkstemp()
|
||||||
os.close(in_fd)
|
os.close(in_fd)
|
||||||
|
|
||||||
|
# ignore length and offset!
|
||||||
|
# ghostscript can read it
|
||||||
|
# copy whole file to read in ghostscript
|
||||||
with open(infile, 'wb') as f:
|
with open(infile, 'wb') as f:
|
||||||
fp.seek(offset)
|
# fetch length of fp
|
||||||
while length >0:
|
fp.seek(0, 2)
|
||||||
s = fp.read(100*1024)
|
fsize = fp.tell()
|
||||||
|
# ensure start position
|
||||||
|
# go back
|
||||||
|
fp.seek(0)
|
||||||
|
lengthfile = fsize
|
||||||
|
while lengthfile > 0:
|
||||||
|
s = fp.read(min(lengthfile, 100*1024))
|
||||||
if not s:
|
if not s:
|
||||||
break
|
break
|
||||||
length = length - len(s)
|
lengthfile = lengthfile - len(s)
|
||||||
f.write(s)
|
f.write(s)
|
||||||
|
|
||||||
# Build ghostscript command
|
# Build ghostscript command
|
||||||
command = ["gs",
|
command = ["gs",
|
||||||
"-q", # quiet mode
|
"-q", # quiet mode
|
||||||
"-g%dx%d" % size, # set output geometry (pixels)
|
"-g%dx%d" % size, # set output geometry (pixels)
|
||||||
"-r%d" % (72*scale), # set input DPI (dots per inch)
|
"-r%fx%f" % res, # set input DPI (dots per inch)
|
||||||
"-dNOPAUSE -dSAFER", # don't pause between pages, safe mode
|
"-dNOPAUSE -dSAFER", # don't pause between pages, safe mode
|
||||||
"-sDEVICE=ppmraw", # ppm driver
|
"-sDEVICE=ppmraw", # ppm driver
|
||||||
"-sOutputFile=%s" % outfile, # output file
|
"-sOutputFile=%s" % outfile, # output file
|
||||||
|
@ -108,7 +119,7 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
# adjust for image origin
|
# adjust for image origin
|
||||||
"-f", infile, # input file
|
"-f", infile, # input file
|
||||||
]
|
]
|
||||||
|
|
||||||
if gs_windows_binary is not None:
|
if gs_windows_binary is not None:
|
||||||
if not gs_windows_binary:
|
if not gs_windows_binary:
|
||||||
raise WindowsError('Unable to locate Ghostscript on paths')
|
raise WindowsError('Unable to locate Ghostscript on paths')
|
||||||
|
@ -127,7 +138,7 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
os.unlink(outfile)
|
os.unlink(outfile)
|
||||||
os.unlink(infile)
|
os.unlink(infile)
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
return im
|
return im
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,6 +156,8 @@ class PSFile:
|
||||||
self.fp.seek(offset, whence)
|
self.fp.seek(offset, whence)
|
||||||
def read(self, count):
|
def read(self, count):
|
||||||
return self.fp.read(count).decode('latin-1')
|
return self.fp.read(count).decode('latin-1')
|
||||||
|
def readbinary(self, count):
|
||||||
|
return self.fp.read(count)
|
||||||
def tell(self):
|
def tell(self):
|
||||||
pos = self.fp.tell()
|
pos = self.fp.tell()
|
||||||
if self.char:
|
if self.char:
|
||||||
|
@ -182,26 +195,34 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
def _open(self):
|
def _open(self):
|
||||||
|
|
||||||
# FIXME: should check the first 512 bytes to see if this
|
|
||||||
# really is necessary (platform-dependent, though...)
|
|
||||||
|
|
||||||
fp = PSFile(self.fp)
|
fp = PSFile(self.fp)
|
||||||
|
|
||||||
# HEAD
|
# FIX for: Some EPS file not handled correctly / issue #302
|
||||||
s = fp.read(512)
|
# EPS can contain binary data
|
||||||
|
# or start directly with latin coding
|
||||||
|
# read header in both ways to handle both
|
||||||
|
# file types
|
||||||
|
# more info see http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
|
||||||
|
|
||||||
|
# for HEAD without binary preview
|
||||||
|
s = fp.read(4)
|
||||||
|
# for HEAD with binary preview
|
||||||
|
fp.seek(0)
|
||||||
|
sb = fp.readbinary(160)
|
||||||
|
|
||||||
if s[:4] == "%!PS":
|
if s[:4] == "%!PS":
|
||||||
offset = 0
|
|
||||||
fp.seek(0, 2)
|
fp.seek(0, 2)
|
||||||
length = fp.tell()
|
length = fp.tell()
|
||||||
elif i32(s) == 0xC6D3D0C5:
|
offset = 0
|
||||||
offset = i32(s[4:])
|
elif i32(sb[0:4]) == 0xC6D3D0C5:
|
||||||
length = i32(s[8:])
|
offset = i32(sb[4:8])
|
||||||
fp.seek(offset)
|
length = i32(sb[8:12])
|
||||||
else:
|
else:
|
||||||
raise SyntaxError("not an EPS file")
|
raise SyntaxError("not an EPS file")
|
||||||
|
|
||||||
|
# go to offset - start of "%!PS"
|
||||||
fp.seek(offset)
|
fp.seek(offset)
|
||||||
|
|
||||||
box = None
|
box = None
|
||||||
|
|
||||||
self.mode = "RGB"
|
self.mode = "RGB"
|
||||||
|
@ -211,7 +232,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
# Load EPS header
|
# Load EPS header
|
||||||
|
|
||||||
s = fp.readline()
|
s = fp.readline()
|
||||||
|
|
||||||
while s:
|
while s:
|
||||||
|
|
||||||
if len(s) > 255:
|
if len(s) > 255:
|
||||||
|
|
BIN
Tests/images/binary_preview_map.eps
Executable file
BIN
Tests/images/binary_preview_map.eps
Executable file
Binary file not shown.
|
@ -1,25 +1,27 @@
|
||||||
from tester import *
|
from tester import *
|
||||||
|
|
||||||
from PIL import Image, EpsImagePlugin
|
from PIL import Image, EpsImagePlugin
|
||||||
import sys
|
|
||||||
import io
|
import io
|
||||||
|
|
||||||
if not EpsImagePlugin.has_ghostscript():
|
if not EpsImagePlugin.has_ghostscript():
|
||||||
skip()
|
skip()
|
||||||
|
|
||||||
#Our two EPS test files (they are identical except for their bounding boxes)
|
# Our two EPS test files (they are identical except for their bounding boxes)
|
||||||
file1 = "Tests/images/zero_bb.eps"
|
file1 = "Tests/images/zero_bb.eps"
|
||||||
file2 = "Tests/images/non_zero_bb.eps"
|
file2 = "Tests/images/non_zero_bb.eps"
|
||||||
|
|
||||||
#Due to palletization, we'll need to convert these to RGB after load
|
# Due to palletization, we'll need to convert these to RGB after load
|
||||||
file1_compare = "Tests/images/zero_bb.png"
|
file1_compare = "Tests/images/zero_bb.png"
|
||||||
file1_compare_scale2 = "Tests/images/zero_bb_scale2.png"
|
file1_compare_scale2 = "Tests/images/zero_bb_scale2.png"
|
||||||
|
|
||||||
file2_compare = "Tests/images/non_zero_bb.png"
|
file2_compare = "Tests/images/non_zero_bb.png"
|
||||||
file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png"
|
file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png"
|
||||||
|
|
||||||
|
# EPS test files with binary preview
|
||||||
|
file3 = "Tests/images/binary_preview_map.eps"
|
||||||
|
|
||||||
def test_sanity():
|
def test_sanity():
|
||||||
#Regular scale
|
# Regular scale
|
||||||
image1 = Image.open(file1)
|
image1 = Image.open(file1)
|
||||||
image1.load()
|
image1.load()
|
||||||
assert_equal(image1.mode, "RGB")
|
assert_equal(image1.mode, "RGB")
|
||||||
|
@ -32,7 +34,7 @@ def test_sanity():
|
||||||
assert_equal(image2.size, (360, 252))
|
assert_equal(image2.size, (360, 252))
|
||||||
assert_equal(image2.format, "EPS")
|
assert_equal(image2.format, "EPS")
|
||||||
|
|
||||||
#Double scale
|
# Double scale
|
||||||
image1_scale2 = Image.open(file1)
|
image1_scale2 = Image.open(file1)
|
||||||
image1_scale2.load(scale=2)
|
image1_scale2.load(scale=2)
|
||||||
assert_equal(image1_scale2.mode, "RGB")
|
assert_equal(image1_scale2.mode, "RGB")
|
||||||
|
@ -45,54 +47,96 @@ def test_sanity():
|
||||||
assert_equal(image2_scale2.size, (720, 504))
|
assert_equal(image2_scale2.size, (720, 504))
|
||||||
assert_equal(image2_scale2.format, "EPS")
|
assert_equal(image2_scale2.format, "EPS")
|
||||||
|
|
||||||
|
|
||||||
def test_file_object():
|
def test_file_object():
|
||||||
#issue 479
|
# issue 479
|
||||||
image1 = Image.open(file1)
|
image1 = Image.open(file1)
|
||||||
with open(tempfile('temp_file.eps'), 'wb') as fh:
|
with open(tempfile('temp_file.eps'), 'wb') as fh:
|
||||||
image1.save(fh, 'EPS')
|
image1.save(fh, 'EPS')
|
||||||
|
|
||||||
|
|
||||||
def test_iobase_object():
|
def test_iobase_object():
|
||||||
#issue 479
|
# issue 479
|
||||||
image1 = Image.open(file1)
|
image1 = Image.open(file1)
|
||||||
with io.open(tempfile('temp_iobase.eps'), 'wb') as fh:
|
with io.open(tempfile('temp_iobase.eps'), 'wb') as fh:
|
||||||
image1.save(fh, 'EPS')
|
image1.save(fh, 'EPS')
|
||||||
|
|
||||||
|
|
||||||
def test_render_scale1():
|
def test_render_scale1():
|
||||||
#We need png support for these render test
|
# We need png support for these render test
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
|
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
|
||||||
skip("zip/deflate support not available")
|
skip("zip/deflate support not available")
|
||||||
|
|
||||||
#Zero bounding box
|
# Zero bounding box
|
||||||
image1_scale1 = Image.open(file1)
|
image1_scale1 = Image.open(file1)
|
||||||
image1_scale1.load()
|
image1_scale1.load()
|
||||||
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
|
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
|
||||||
image1_scale1_compare.load()
|
image1_scale1_compare.load()
|
||||||
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
||||||
|
|
||||||
#Non-Zero bounding box
|
# Non-Zero bounding box
|
||||||
image2_scale1 = Image.open(file2)
|
image2_scale1 = Image.open(file2)
|
||||||
image2_scale1.load()
|
image2_scale1.load()
|
||||||
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
|
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
|
||||||
image2_scale1_compare.load()
|
image2_scale1_compare.load()
|
||||||
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||||
|
|
||||||
|
|
||||||
def test_render_scale2():
|
def test_render_scale2():
|
||||||
#We need png support for these render test
|
# We need png support for these render test
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
|
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
|
||||||
skip("zip/deflate support not available")
|
skip("zip/deflate support not available")
|
||||||
|
|
||||||
#Zero bounding box
|
# Zero bounding box
|
||||||
image1_scale2 = Image.open(file1)
|
image1_scale2 = Image.open(file1)
|
||||||
image1_scale2.load(scale=2)
|
image1_scale2.load(scale=2)
|
||||||
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
|
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
|
||||||
image1_scale2_compare.load()
|
image1_scale2_compare.load()
|
||||||
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
||||||
|
|
||||||
#Non-Zero bounding box
|
# Non-Zero bounding box
|
||||||
image2_scale2 = Image.open(file2)
|
image2_scale2 = Image.open(file2)
|
||||||
image2_scale2.load(scale=2)
|
image2_scale2.load(scale=2)
|
||||||
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
|
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
|
||||||
image2_scale2_compare.load()
|
image2_scale2_compare.load()
|
||||||
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
||||||
|
|
||||||
|
|
||||||
|
def test_resize():
|
||||||
|
# Arrange
|
||||||
|
image1 = Image.open(file1)
|
||||||
|
image2 = Image.open(file2)
|
||||||
|
new_size = (100, 100)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
image1 = image1.resize(new_size)
|
||||||
|
image2 = image2.resize(new_size)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert_equal(image1.size, new_size)
|
||||||
|
assert_equal(image2.size, new_size)
|
||||||
|
|
||||||
|
|
||||||
|
def test_thumbnail():
|
||||||
|
# Issue #619
|
||||||
|
# Arrange
|
||||||
|
image1 = Image.open(file1)
|
||||||
|
image2 = Image.open(file2)
|
||||||
|
new_size = (100, 100)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
image1.thumbnail(new_size)
|
||||||
|
image2.thumbnail(new_size)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert_equal(max(image1.size), max(new_size))
|
||||||
|
assert_equal(max(image2.size), max(new_size))
|
||||||
|
|
||||||
|
def test_read_binary_preview():
|
||||||
|
# Issue 302
|
||||||
|
# open image with binary preview
|
||||||
|
image1 = Image.open(file3)
|
||||||
|
|
||||||
|
# End of file
|
||||||
|
|
Loading…
Reference in New Issue
Block a user