mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-07 13:54:45 +03:00
Merge 689fa83f86
into 408e7335b3
This commit is contained in:
commit
abdf466d84
156
PIL/EpsImagePlugin.py
Normal file → Executable file
156
PIL/EpsImagePlugin.py
Normal file → Executable file
|
@ -23,6 +23,8 @@ __version__ = "0.5"
|
|||
|
||||
import re
|
||||
import io
|
||||
import unicodedata
|
||||
|
||||
from PIL import Image, ImageFile, _binary
|
||||
|
||||
#
|
||||
|
@ -34,6 +36,8 @@ o32 = _binary.o32le
|
|||
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
|
||||
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
|
||||
|
||||
defaultDPI = 600.0
|
||||
|
||||
gs_windows_binary = None
|
||||
import sys
|
||||
if sys.platform.startswith('win'):
|
||||
|
@ -64,23 +68,29 @@ def has_ghostscript():
|
|||
# no ghostscript
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def Ghostscript(tile, size, fp, scale=1):
|
||||
def makeunicode( s, enc="latin-1", normalizer='NFC'):
|
||||
"""return a normalized unicode string"""
|
||||
try:
|
||||
if type(s) != unicode:
|
||||
s = unicode(s, enc)
|
||||
except:
|
||||
pass
|
||||
return unicodedata.normalize(normalizer, s)
|
||||
|
||||
def Ghostscript(tile, size, fp):
|
||||
"""Render an image using Ghostscript"""
|
||||
|
||||
# Unpack decoder tile
|
||||
decoder, tile, offset, data = tile[0]
|
||||
length, bbox = data
|
||||
|
||||
#Hack to support hi-res rendering
|
||||
scale = int(scale) or 1
|
||||
orig_size = size
|
||||
orig_bbox = bbox
|
||||
size = (size[0] * scale, size[1] * scale)
|
||||
# resolution is dependend on bbox and size
|
||||
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)
|
||||
|
||||
# bbox is in points == 1/72 inch
|
||||
# size is in pixels, a device unit
|
||||
xpointsize = bbox[2] - bbox[0]
|
||||
ypointsize = bbox[3] - bbox[1]
|
||||
xdpi = size[0] / (xpointsize / 72.0)
|
||||
ydpi = size[1] / (ypointsize / 72.0)
|
||||
|
||||
import tempfile, os, subprocess
|
||||
|
||||
|
@ -90,28 +100,27 @@ def Ghostscript(tile, size, fp, scale=1):
|
|||
os.close(in_fd)
|
||||
|
||||
# ignore length and offset!
|
||||
# ghostscript can read it
|
||||
# ghostscript can read it
|
||||
# copy whole file to read in ghostscript
|
||||
with open(infile, 'wb') as f:
|
||||
# fetch length of fp
|
||||
fp.seek(0, 2)
|
||||
fsize = fp.tell()
|
||||
lengthfile = fp.tell()
|
||||
# ensure start position
|
||||
# go back
|
||||
fp.seek(0)
|
||||
lengthfile = fsize
|
||||
while lengthfile > 0:
|
||||
s = fp.read(min(lengthfile, 100*1024))
|
||||
s = fp.read( 4*1024*1024 )
|
||||
if not s:
|
||||
break
|
||||
length -= len(s)
|
||||
lengthfile -= len(s)
|
||||
f.write(s)
|
||||
|
||||
# Build ghostscript command
|
||||
command = ["gs",
|
||||
"-q", # quiet mode
|
||||
"-g%dx%d" % size, # set output geometry (pixels)
|
||||
"-r%fx%f" % res, # set input DPI (dots per inch)
|
||||
"-r%fx%f" % (xdpi,ydpi), # set input DPI (dots per inch)
|
||||
"-dNOPAUSE -dSAFER", # don't pause between pages, safe mode
|
||||
"-sDEVICE=ppmraw", # ppm driver
|
||||
"-sOutputFile=%s" % outfile, # output file
|
||||
|
@ -119,7 +128,7 @@ def Ghostscript(tile, size, fp, scale=1):
|
|||
# adjust for image origin
|
||||
"-f", infile, # input file
|
||||
]
|
||||
|
||||
|
||||
if gs_windows_binary is not None:
|
||||
if not gs_windows_binary:
|
||||
raise WindowsError('Unable to locate Ghostscript on paths')
|
||||
|
@ -210,10 +219,10 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
fp.seek(0)
|
||||
sb = fp.readbinary(160)
|
||||
|
||||
offset = 0
|
||||
if s[:4] == "%!PS":
|
||||
fp.seek(0, 2)
|
||||
length = fp.tell()
|
||||
offset = 0
|
||||
elif i32(sb[0:4]) == 0xC6D3D0C5:
|
||||
offset = i32(sb[4:8])
|
||||
length = i32(sb[8:12])
|
||||
|
@ -222,27 +231,26 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
|
||||
# go to offset - start of "%!PS"
|
||||
fp.seek(offset)
|
||||
|
||||
|
||||
box = None
|
||||
|
||||
self.mode = "RGB"
|
||||
self.size = 1, 1 # FIXME: huh?
|
||||
self.pixelsize = False
|
||||
self.pointsize = False
|
||||
self.bbox = False
|
||||
|
||||
#
|
||||
# Load EPS header
|
||||
|
||||
s = fp.readline()
|
||||
|
||||
s = s.rstrip("\r\n")
|
||||
s = makeunicode(s)
|
||||
|
||||
while s:
|
||||
|
||||
if len(s) > 255:
|
||||
raise SyntaxError("not an EPS file")
|
||||
|
||||
if s[-2:] == '\r\n':
|
||||
s = s[:-2]
|
||||
elif s[-1:] == '\n':
|
||||
s = s[:-1]
|
||||
|
||||
try:
|
||||
m = split.match(s)
|
||||
except re.error as v:
|
||||
|
@ -256,10 +264,31 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
# Note: The DSC spec says that BoundingBox
|
||||
# fields should be integers, but some drivers
|
||||
# put floating point values there anyway.
|
||||
box = [int(float(s)) for s in v.split()]
|
||||
|
||||
# if self.pointsize is not False, we already have a hiresbbox
|
||||
if not self.pointsize:
|
||||
box = [int(float(s)) for s in v.split()]
|
||||
self.bbox = box
|
||||
pointsize = box[2] - box[0], box[3] - box[1]
|
||||
self.size = box[2] - box[0], box[3] - box[1]
|
||||
self.tile = [ ("eps",
|
||||
(0,0) + self.size,
|
||||
offset,
|
||||
(length, box))]
|
||||
self.pointsize = pointsize
|
||||
except:
|
||||
pass
|
||||
if k == "HiResBoundingBox":
|
||||
try:
|
||||
box = [float(s) for s in v.split()]
|
||||
self.bbox = box
|
||||
pointsize = box[2] - box[0], box[3] - box[1]
|
||||
self.size = box[2] - box[0], box[3] - box[1]
|
||||
self.tile = [("eps", (0,0) + self.size, offset,
|
||||
self.tile = [ ("eps",
|
||||
(0,0) + self.size,
|
||||
offset,
|
||||
(length, box))]
|
||||
self.pointsize = pointsize
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -284,11 +313,12 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
raise IOError("bad EPS header")
|
||||
|
||||
s = fp.readline()
|
||||
s = s.rstrip("\r\n")
|
||||
s = makeunicode(s)
|
||||
|
||||
if s[:1] != "%":
|
||||
break
|
||||
|
||||
|
||||
#
|
||||
# Scan for an "ImageData" descriptor
|
||||
|
||||
|
@ -303,12 +333,12 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
s = s[:-1]
|
||||
|
||||
if s[:11] == "%ImageData:":
|
||||
[x, y, bi, mo, z3, z4, en, starttag] = s[11:].split(None, 7)
|
||||
|
||||
[x, y, bi, mo, z3, z4, en, id] =\
|
||||
s[11:].split(None, 7)
|
||||
|
||||
x = int(x); y = int(y)
|
||||
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
self.pixelsize = (x,y)
|
||||
|
||||
bi = int(bi)
|
||||
mo = int(mo)
|
||||
|
||||
|
@ -319,46 +349,66 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
elif en == 2:
|
||||
decoder = "eps_hex"
|
||||
else:
|
||||
break
|
||||
pass
|
||||
|
||||
if bi != 8:
|
||||
break
|
||||
|
||||
if mo == 1:
|
||||
self.mode = "L"
|
||||
elif mo == 2:
|
||||
self.mode = "LAB"
|
||||
elif mo == 3:
|
||||
self.mode = "RGB"
|
||||
elif mo == 4:
|
||||
self.mode = "CMYK"
|
||||
else:
|
||||
break
|
||||
pass
|
||||
|
||||
if id[:1] == id[-1:] == '"':
|
||||
id = id[1:-1]
|
||||
bbox = (0, 0, x, y)
|
||||
if self.bbox:
|
||||
bbox = self.bbox
|
||||
|
||||
# Scan forward to the actual image data
|
||||
while True:
|
||||
s = fp.readline()
|
||||
if not s:
|
||||
break
|
||||
if s[:len(id)] == id:
|
||||
self.size = x, y
|
||||
self.tile2 = [(decoder,
|
||||
(0, 0, x, y),
|
||||
fp.tell(),
|
||||
0)]
|
||||
return
|
||||
self.tile = [("eps",
|
||||
(0, 0, x, y),
|
||||
0,
|
||||
(length, bbox))]
|
||||
xdpi = round(x / (self.size[0] / 72.0))
|
||||
ydpi = round(y / (self.size[1] / 72.0))
|
||||
self.info["dpi"] = (xdpi,ydpi)
|
||||
return
|
||||
|
||||
s = fp.readline()
|
||||
s = s.rstrip("\r\n")
|
||||
s = makeunicode(s)
|
||||
if not s:
|
||||
break
|
||||
|
||||
if not box:
|
||||
raise IOError("cannot determine EPS bounding box")
|
||||
|
||||
def load(self, scale=1):
|
||||
if not self.pixelsize:
|
||||
self.info["dpi"] = (defaultDPI, defaultDPI)
|
||||
|
||||
|
||||
def load(self, scale=None):
|
||||
# Load EPS via Ghostscript
|
||||
if not self.tile:
|
||||
return
|
||||
self.im = Ghostscript(self.tile, self.size, self.fp, scale)
|
||||
|
||||
size = self.size
|
||||
if self.pixelsize:
|
||||
# pixel based eps
|
||||
# size is imagesize in pixels
|
||||
size = self.pixelsize
|
||||
else:
|
||||
# generic eps
|
||||
# size is in points (== 72dpi uglyness)
|
||||
if not scale:
|
||||
scale = (defaultDPI/72.0)
|
||||
size = ( size[0] * scale, size[1] * scale)
|
||||
|
||||
self.im = Ghostscript(self.tile, size, self.fp)
|
||||
self.mode = self.im.mode
|
||||
self.size = self.im.size
|
||||
self.tile = []
|
||||
|
|
|
@ -27,13 +27,13 @@ class TestFileEps(PillowTestCase):
|
|||
def test_sanity(self):
|
||||
# Regular scale
|
||||
image1 = Image.open(file1)
|
||||
image1.load()
|
||||
image1.load(scale=1.0)
|
||||
self.assertEqual(image1.mode, "RGB")
|
||||
self.assertEqual(image1.size, (460, 352))
|
||||
self.assertEqual(image1.format, "EPS")
|
||||
|
||||
image2 = Image.open(file2)
|
||||
image2.load()
|
||||
image2.load(scale=1.0)
|
||||
self.assertEqual(image2.mode, "RGB")
|
||||
self.assertEqual(image2.size, (360, 252))
|
||||
self.assertEqual(image2.format, "EPS")
|
||||
|
@ -71,14 +71,14 @@ class TestFileEps(PillowTestCase):
|
|||
|
||||
# Zero bounding box
|
||||
image1_scale1 = Image.open(file1)
|
||||
image1_scale1.load()
|
||||
image1_scale1.load(scale=1.0)
|
||||
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
|
||||
image1_scale1_compare.load()
|
||||
self.assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
||||
|
||||
# Non-Zero bounding box
|
||||
image2_scale1 = Image.open(file2)
|
||||
image2_scale1.load()
|
||||
image2_scale1.load(scale=1.0)
|
||||
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
|
||||
image2_scale1_compare.load()
|
||||
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||
|
|
Loading…
Reference in New Issue
Block a user