mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-07 22:04:46 +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 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'):
|
||||||
|
@ -64,23 +68,29 @@ def has_ghostscript():
|
||||||
# no ghostscript
|
# no ghostscript
|
||||||
pass
|
pass
|
||||||
return False
|
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"""
|
"""Render an image using Ghostscript"""
|
||||||
|
|
||||||
# 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
|
# bbox is in points == 1/72 inch
|
||||||
scale = int(scale) or 1
|
# size is in pixels, a device unit
|
||||||
orig_size = size
|
xpointsize = bbox[2] - bbox[0]
|
||||||
orig_bbox = bbox
|
ypointsize = bbox[3] - bbox[1]
|
||||||
size = (size[0] * scale, size[1] * scale)
|
xdpi = size[0] / (xpointsize / 72.0)
|
||||||
# resolution is dependend on bbox and size
|
ydpi = size[1] / (ypointsize / 72.0)
|
||||||
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
|
||||||
|
|
||||||
|
@ -90,28 +100,27 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
os.close(in_fd)
|
os.close(in_fd)
|
||||||
|
|
||||||
# ignore length and offset!
|
# ignore length and offset!
|
||||||
# ghostscript can read it
|
# ghostscript can read it
|
||||||
# copy whole file to read in ghostscript
|
# copy whole file to read in ghostscript
|
||||||
with open(infile, 'wb') as f:
|
with open(infile, 'wb') as f:
|
||||||
# fetch length of fp
|
# fetch length of fp
|
||||||
fp.seek(0, 2)
|
fp.seek(0, 2)
|
||||||
fsize = fp.tell()
|
lengthfile = fp.tell()
|
||||||
# ensure start position
|
# ensure start position
|
||||||
# go back
|
# go back
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
lengthfile = fsize
|
|
||||||
while lengthfile > 0:
|
while lengthfile > 0:
|
||||||
s = fp.read(min(lengthfile, 100*1024))
|
s = fp.read( 4*1024*1024 )
|
||||||
if not s:
|
if not s:
|
||||||
break
|
break
|
||||||
length -= len(s)
|
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%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
|
"-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
|
||||||
|
@ -119,7 +128,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')
|
||||||
|
@ -210,10 +219,10 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
sb = fp.readbinary(160)
|
sb = fp.readbinary(160)
|
||||||
|
|
||||||
|
offset = 0
|
||||||
if s[:4] == "%!PS":
|
if s[:4] == "%!PS":
|
||||||
fp.seek(0, 2)
|
fp.seek(0, 2)
|
||||||
length = fp.tell()
|
length = fp.tell()
|
||||||
offset = 0
|
|
||||||
elif i32(sb[0:4]) == 0xC6D3D0C5:
|
elif i32(sb[0:4]) == 0xC6D3D0C5:
|
||||||
offset = i32(sb[4:8])
|
offset = i32(sb[4:8])
|
||||||
length = i32(sb[8:12])
|
length = i32(sb[8:12])
|
||||||
|
@ -222,27 +231,26 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# go to offset - start of "%!PS"
|
# 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 +264,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,11 +313,12 @@ 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
|
||||||
|
|
||||||
|
@ -303,12 +333,12 @@ 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 +349,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 = []
|
||||||
|
|
|
@ -27,13 +27,13 @@ class TestFileEps(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
# Regular scale
|
# Regular scale
|
||||||
image1 = Image.open(file1)
|
image1 = Image.open(file1)
|
||||||
image1.load()
|
image1.load(scale=1.0)
|
||||||
self.assertEqual(image1.mode, "RGB")
|
self.assertEqual(image1.mode, "RGB")
|
||||||
self.assertEqual(image1.size, (460, 352))
|
self.assertEqual(image1.size, (460, 352))
|
||||||
self.assertEqual(image1.format, "EPS")
|
self.assertEqual(image1.format, "EPS")
|
||||||
|
|
||||||
image2 = Image.open(file2)
|
image2 = Image.open(file2)
|
||||||
image2.load()
|
image2.load(scale=1.0)
|
||||||
self.assertEqual(image2.mode, "RGB")
|
self.assertEqual(image2.mode, "RGB")
|
||||||
self.assertEqual(image2.size, (360, 252))
|
self.assertEqual(image2.size, (360, 252))
|
||||||
self.assertEqual(image2.format, "EPS")
|
self.assertEqual(image2.format, "EPS")
|
||||||
|
@ -71,14 +71,14 @@ class TestFileEps(PillowTestCase):
|
||||||
|
|
||||||
# Zero bounding box
|
# Zero bounding box
|
||||||
image1_scale1 = Image.open(file1)
|
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 = Image.open(file1_compare).convert("RGB")
|
||||||
image1_scale1_compare.load()
|
image1_scale1_compare.load()
|
||||||
self.assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
self.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(scale=1.0)
|
||||||
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()
|
||||||
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user