mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-07 22:04:46 +03:00
Replaced PSFile wrapper with standart Python open for universal line endings. DPI for non pixelbased eps files is set to 600. DPI for pixelbased eps files is calculated from file and scale is ignored.
This commit is contained in:
parent
7113721ad0
commit
85ecfe9daf
|
@ -23,6 +23,8 @@ __version__ = "0.5"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import io
|
import io
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
from PIL import Image, ImageFile, _binary
|
from PIL import Image, ImageFile, _binary
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -34,6 +36,8 @@ o32 = _binary.o32le
|
||||||
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
|
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
|
||||||
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
|
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
|
||||||
|
|
||||||
|
defaultDPI = 600.0
|
||||||
|
|
||||||
gs_windows_binary = None
|
gs_windows_binary = None
|
||||||
import sys
|
import sys
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
|
@ -51,6 +55,7 @@ if sys.platform.startswith('win'):
|
||||||
else:
|
else:
|
||||||
gs_windows_binary = False
|
gs_windows_binary = False
|
||||||
|
|
||||||
|
# UNUSED
|
||||||
def has_ghostscript():
|
def has_ghostscript():
|
||||||
if gs_windows_binary:
|
if gs_windows_binary:
|
||||||
return True
|
return True
|
||||||
|
@ -65,59 +70,44 @@ def has_ghostscript():
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def makeunicode( s, enc="latin-1", normalizer='NFD'):
|
||||||
|
try:
|
||||||
|
if type(s) != unicode:
|
||||||
|
s = unicode(s, enc)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return unicodedata.normalize(normalizer, s)
|
||||||
|
|
||||||
def Ghostscript(tile, size, fp, scale=1):
|
def Ghostscript(tile, size, fp):
|
||||||
"""Render an image using Ghostscript"""
|
"""Render an image using Ghostscript"""
|
||||||
|
|
||||||
|
# size is either pts or pixels
|
||||||
|
|
||||||
# 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
|
xpointsize = bbox[2] - bbox[0]
|
||||||
scale = int(scale) or 1
|
ypointsize = bbox[3] - bbox[1]
|
||||||
orig_size = size
|
xdpi = size[0] / (xpointsize / 72.0)
|
||||||
orig_bbox = bbox
|
ydpi = size[1] / (ypointsize / 72.0)
|
||||||
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)
|
|
||||||
|
|
||||||
import tempfile, os, subprocess
|
import tempfile, os, subprocess
|
||||||
|
|
||||||
out_fd, outfile = tempfile.mkstemp()
|
out_fd, outfile = tempfile.mkstemp()
|
||||||
os.close(out_fd)
|
os.close(out_fd)
|
||||||
in_fd, infile = tempfile.mkstemp()
|
|
||||||
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:
|
|
||||||
# fetch length of fp
|
|
||||||
fp.seek(0, 2)
|
|
||||||
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:
|
|
||||||
break
|
|
||||||
length -= len(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%fx%f" % res, # set input DPI (dots per inch)
|
"-r%fc%f" % (xdpi,ydpi), # 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
|
||||||
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
|
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
|
||||||
# adjust for image origin
|
# adjust for image origin
|
||||||
"-f", infile, # input file
|
"-f", fp.name
|
||||||
]
|
]
|
||||||
|
|
||||||
if gs_windows_binary is not None:
|
if gs_windows_binary is not None:
|
||||||
|
@ -136,50 +126,11 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
os.unlink(outfile)
|
os.unlink(outfile)
|
||||||
os.unlink(infile)
|
except:
|
||||||
except: pass
|
pass
|
||||||
|
|
||||||
return im
|
return im
|
||||||
|
|
||||||
|
|
||||||
class PSFile:
|
|
||||||
"""Wrapper that treats either CR or LF as end of line."""
|
|
||||||
def __init__(self, fp):
|
|
||||||
self.fp = fp
|
|
||||||
self.char = None
|
|
||||||
def __getattr__(self, id):
|
|
||||||
v = getattr(self.fp, id)
|
|
||||||
setattr(self, id, v)
|
|
||||||
return v
|
|
||||||
def seek(self, offset, whence=0):
|
|
||||||
self.char = None
|
|
||||||
self.fp.seek(offset, whence)
|
|
||||||
def read(self, count):
|
|
||||||
return self.fp.read(count).decode('latin-1')
|
|
||||||
def readbinary(self, count):
|
|
||||||
return self.fp.read(count)
|
|
||||||
def tell(self):
|
|
||||||
pos = self.fp.tell()
|
|
||||||
if self.char:
|
|
||||||
pos -= 1
|
|
||||||
return pos
|
|
||||||
def readline(self):
|
|
||||||
s = b""
|
|
||||||
if self.char:
|
|
||||||
c = self.char
|
|
||||||
self.char = None
|
|
||||||
else:
|
|
||||||
c = self.fp.read(1)
|
|
||||||
while c not in b"\r\n":
|
|
||||||
s = s + c
|
|
||||||
c = self.fp.read(1)
|
|
||||||
if c == b"\r":
|
|
||||||
self.char = self.fp.read(1)
|
|
||||||
if self.char == b"\n":
|
|
||||||
self.char = None
|
|
||||||
return s.decode('latin-1') + "\n"
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5
|
return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5
|
||||||
|
|
||||||
|
@ -194,55 +145,42 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
format_description = "Encapsulated Postscript"
|
format_description = "Encapsulated Postscript"
|
||||||
|
|
||||||
def _open(self):
|
def _open(self):
|
||||||
|
fp = open(self.fp.name, "Ur")
|
||||||
|
|
||||||
fp = PSFile(self.fp)
|
# HEAD
|
||||||
|
s = fp.read(512)
|
||||||
# FIX for: Some EPS file not handled correctly / issue #302
|
|
||||||
# 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()
|
||||||
offset = 0
|
elif i32(s) == 0xC6D3D0C5:
|
||||||
elif i32(sb[0:4]) == 0xC6D3D0C5:
|
offset = i32(s[4:])
|
||||||
offset = i32(sb[4:8])
|
length = i32(s[8:])
|
||||||
length = i32(sb[8:12])
|
fp.seek(offset)
|
||||||
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"
|
||||||
self.size = 1, 1 # FIXME: huh?
|
self.size = 1, 1 # FIXME: huh?
|
||||||
|
self.pixelsize = False
|
||||||
|
self.pointsize = False
|
||||||
|
self.bbox = False
|
||||||
|
|
||||||
#
|
#
|
||||||
# Load EPS header
|
# Load EPS header
|
||||||
|
|
||||||
s = fp.readline()
|
s = fp.readline()
|
||||||
|
s = s.rstrip("\r\n")
|
||||||
|
s = makeunicode(s)
|
||||||
|
|
||||||
while s:
|
while s:
|
||||||
|
|
||||||
if len(s) > 255:
|
if len(s) > 255:
|
||||||
raise SyntaxError("not an EPS file")
|
raise SyntaxError("not an EPS file")
|
||||||
|
|
||||||
if s[-2:] == '\r\n':
|
|
||||||
s = s[:-2]
|
|
||||||
elif s[-1:] == '\n':
|
|
||||||
s = s[:-1]
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
m = split.match(s)
|
m = split.match(s)
|
||||||
except re.error as v:
|
except re.error as v:
|
||||||
|
@ -256,10 +194,31 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
# Note: The DSC spec says that BoundingBox
|
# Note: The DSC spec says that BoundingBox
|
||||||
# fields should be integers, but some drivers
|
# fields should be integers, but some drivers
|
||||||
# put floating point values there anyway.
|
# 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.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))]
|
(length, box))]
|
||||||
|
self.pointsize = pointsize
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -284,14 +243,14 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
raise IOError("bad EPS header")
|
raise IOError("bad EPS header")
|
||||||
|
|
||||||
s = fp.readline()
|
s = fp.readline()
|
||||||
|
s = s.rstrip("\r\n")
|
||||||
|
s = makeunicode(s)
|
||||||
|
|
||||||
if s[:1] != "%":
|
if s[:1] != "%":
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Scan for an "ImageData" descriptor
|
# Scan for an "ImageData" descriptor
|
||||||
|
|
||||||
while s[0] == "%":
|
while s[0] == "%":
|
||||||
|
|
||||||
if len(s) > 255:
|
if len(s) > 255:
|
||||||
|
@ -303,11 +262,11 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
s = s[:-1]
|
s = s[:-1]
|
||||||
|
|
||||||
if s[:11] == "%ImageData:":
|
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] =\
|
x = int(x)
|
||||||
s[11:].split(None, 7)
|
y = int(y)
|
||||||
|
self.pixelsize = (x,y)
|
||||||
x = int(x); y = int(y)
|
|
||||||
|
|
||||||
bi = int(bi)
|
bi = int(bi)
|
||||||
mo = int(mo)
|
mo = int(mo)
|
||||||
|
@ -319,46 +278,66 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
elif en == 2:
|
elif en == 2:
|
||||||
decoder = "eps_hex"
|
decoder = "eps_hex"
|
||||||
else:
|
else:
|
||||||
break
|
pass
|
||||||
|
|
||||||
if bi != 8:
|
if bi != 8:
|
||||||
break
|
break
|
||||||
|
|
||||||
if mo == 1:
|
if mo == 1:
|
||||||
self.mode = "L"
|
self.mode = "L"
|
||||||
elif mo == 2:
|
elif mo == 2:
|
||||||
self.mode = "LAB"
|
self.mode = "LAB"
|
||||||
elif mo == 3:
|
elif mo == 3:
|
||||||
self.mode = "RGB"
|
self.mode = "RGB"
|
||||||
|
elif mo == 4:
|
||||||
|
self.mode = "CMYK"
|
||||||
else:
|
else:
|
||||||
break
|
pass
|
||||||
|
|
||||||
if id[:1] == id[-1:] == '"':
|
bbox = (0, 0, x, y)
|
||||||
id = id[1:-1]
|
if self.bbox:
|
||||||
|
bbox = self.bbox
|
||||||
|
|
||||||
# Scan forward to the actual image data
|
self.tile = [("eps",
|
||||||
while True:
|
(0, 0, x, y),
|
||||||
s = fp.readline()
|
0,
|
||||||
if not s:
|
(length, bbox))]
|
||||||
break
|
xdpi = round(x / (self.size[0] / 72.0))
|
||||||
if s[:len(id)] == id:
|
ydpi = round(y / (self.size[1] / 72.0))
|
||||||
self.size = x, y
|
self.info["dpi"] = (xdpi,ydpi)
|
||||||
self.tile2 = [(decoder,
|
return
|
||||||
(0, 0, x, y),
|
|
||||||
fp.tell(),
|
|
||||||
0)]
|
|
||||||
return
|
|
||||||
|
|
||||||
s = fp.readline()
|
s = fp.readline()
|
||||||
|
s = s.rstrip("\r\n")
|
||||||
|
s = makeunicode(s)
|
||||||
if not s:
|
if not s:
|
||||||
break
|
break
|
||||||
|
|
||||||
if not box:
|
if not box:
|
||||||
raise IOError("cannot determine EPS bounding 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
|
# Load EPS via Ghostscript
|
||||||
if not self.tile:
|
if not self.tile:
|
||||||
return
|
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.mode = self.im.mode
|
||||||
self.size = self.im.size
|
self.size = self.im.size
|
||||||
self.tile = []
|
self.tile = []
|
||||||
|
|
Loading…
Reference in New Issue
Block a user