home *** CD-ROM | disk | FTP | other *** search
- #
- # The Python Imaging Library.
- # $Id: EpsImagePlugin.py 2134 2004-10-06 08:55:20Z fredrik $
- #
- # EPS file handling
- #
- # History:
- # 1995-09-01 fl Created (0.1)
- # 1996-05-18 fl Don't choke on "atend" fields, Ghostscript interface (0.2)
- # 1996-08-22 fl Don't choke on floating point BoundingBox values
- # 1996-08-23 fl Handle files from Macintosh (0.3)
- # 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)
- #
- # Copyright (c) 1997-2003 by Secret Labs AB.
- # Copyright (c) 1995-2003 by Fredrik Lundh
- #
- # See the README file for information on usage and redistribution.
- #
-
- __version__ = "0.5"
-
- import re, string
- import Image, ImageFile
-
- #
- # --------------------------------------------------------------------
-
- def i32(c):
- return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24)
-
- def o32(i):
- return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255)
-
- split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
- field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
-
- def Ghostscript(tile, size, fp):
- """Render an image using Ghostscript (Unix only)"""
-
- # Unpack decoder tile
- decoder, tile, offset, data = tile[0]
- length, bbox = data
-
- import tempfile, os
-
- file = tempfile.mktemp()
-
- # Build ghostscript command
- command = ["gs",
- "-q", # quite mode
- "-g%dx%d" % size, # set output geometry (pixels)
- "-dNOPAUSE -dSAFER", # don't pause between pages, safe mode
- "-sDEVICE=ppmraw", # ppm driver
- "-sOutputFile=%s" % file,# output file
- "- >/dev/null 2>/dev/null"]
-
- command = string.join(command)
-
- # push data through ghostscript
- try:
- gs = os.popen(command, "w")
- # adjust for image origin
- if bbox[0] != 0 or bbox[1] != 0:
- gs.write("%d %d translate\n" % (-bbox[0], -bbox[1]))
- fp.seek(offset)
- while length > 0:
- s = fp.read(8192)
- if not s:
- break
- length = length - len(s)
- gs.write(s)
- status = gs.close()
- if status:
- raise IOError("gs failed (status %d)" % status)
- im = Image.core.open_ppm(file)
- finally:
- try: os.unlink(file)
- except: pass
-
- 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 tell(self):
- pos = self.fp.tell()
- if self.char:
- pos = pos - 1
- return pos
- def readline(self):
- s = ""
- if self.char:
- c = self.char
- self.char = None
- else:
- c = self.fp.read(1)
- while c not in "\r\n":
- s = s + c
- c = self.fp.read(1)
- if c == "\r":
- self.char = self.fp.read(1)
- if self.char == "\n":
- self.char = None
- return s + "\n"
-
-
- def _accept(prefix):
- return prefix[:4] == "%!PS" or i32(prefix) == 0xC6D3D0C5L
-
- ##
- # Image plugin for Encapsulated Postscript. This plugin supports only
- # a few variants of this format.
-
- class EpsImageFile(ImageFile.ImageFile):
- """EPS File Parser for the Python Imaging Library"""
-
- format = "EPS"
- format_description = "Encapsulated Postscript"
-
- def _open(self):
-
- # FIXME: should check the first 512 bytes to see if this
- # really is necessary (platform-dependent, though...)
-
- fp = PSFile(self.fp)
-
- # HEAD
- s = fp.read(512)
- if s[:4] == "%!PS":
- offset = 0
- fp.seek(0, 2)
- length = fp.tell()
- elif i32(s) == 0xC6D3D0C5L:
- offset = i32(s[4:])
- length = i32(s[8:])
- fp.seek(offset)
- else:
- raise SyntaxError, "not an EPS file"
-
- fp.seek(offset)
-
- box = None
-
- self.mode = "RGB"
- self.size = 1, 1 # FIXME: huh?
-
- #
- # Load EPS header
-
- s = fp.readline()
-
- 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, v:
- raise SyntaxError, "not an EPS file"
-
- if m:
- k, v = m.group(1, 2)
- self.info[k] = v
- if k == "BoundingBox":
- try:
- # Note: The DSC spec says that BoundingBox
- # fields should be integers, but some drivers
- # put floating point values there anyway.
- box = map(int, map(float, string.split(v)))
- self.size = box[2] - box[0], box[3] - box[1]
- self.tile = [("eps", (0,0) + self.size, offset,
- (length, box))]
- except:
- pass
-
- else:
-
- m = field.match(s)
-
- if m:
- k = m.group(1)
- if k == "EndComments":
- break
- if k[:8] == "PS-Adobe":
- self.info[k[:8]] = k[9:]
- else:
- self.info[k] = ""
- else:
- raise IOError, "bad EPS header"
-
- s = fp.readline()
-
- if s[:1] != "%":
- break
-
-
- #
- # Scan for an "ImageData" descriptor
-
- while s[0] == "%":
-
- 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]
-
- if s[:11] == "%ImageData:":
-
- [x, y, bi, mo, z3, z4, en, id] =\
- string.split(s[11:], maxsplit=7)
-
- x = int(x); y = int(y)
-
- bi = int(bi)
- mo = int(mo)
-
- en = int(en)
-
- if en == 1:
- decoder = "eps_binary"
- elif en == 2:
- decoder = "eps_hex"
- else:
- break
- if bi != 8:
- break
- if mo == 1:
- self.mode = "L"
- elif mo == 2:
- self.mode = "LAB"
- elif mo == 3:
- self.mode = "RGB"
- else:
- break
-
- if id[:1] == id[-1:] == '"':
- id = id[1:-1]
-
- # Scan forward to the actual image data
- while 1:
- 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
-
- s = fp.readline()
- if not s:
- break
-
- if not box:
- raise IOError, "cannot determine EPS bounding box"
-
- def load(self):
- # Load EPS via Ghostscript
- if not self.tile:
- return
- self.im = Ghostscript(self.tile, self.size, self.fp)
- self.mode = self.im.mode
- self.size = self.im.size
- self.tile = []
-
- #
- # --------------------------------------------------------------------
-
- def _save(im, fp, filename, eps=1):
- """EPS Writer for the Python Imaging Library."""
-
- #
- # make sure image data is available
- im.load()
-
- #
- # determine postscript image mode
- if im.mode == "L":
- operator = (8, 1, "image")
- elif im.mode == "RGB":
- operator = (8, 3, "false 3 colorimage")
- elif im.mode == "CMYK":
- operator = (8, 4, "false 4 colorimage")
- else:
- raise ValueError, "image mode is not supported"
-
- if eps:
- #
- # write EPS header
- fp.write("%!PS-Adobe-3.0 EPSF-3.0\n")
- fp.write("%%Creator: PIL 0.1 EpsEncode\n")
- #fp.write("%%CreationDate: %s"...)
- fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size)
- fp.write("%%Pages: 1\n")
- fp.write("%%EndComments\n")
- fp.write("%%Page: 1 1\n")
- fp.write("%%ImageData: %d %d " % im.size)
- fp.write("%d %d 0 1 1 \"%s\"\n" % operator)
-
- #
- # image header
- fp.write("gsave\n")
- fp.write("10 dict begin\n")
- fp.write("/buf %d string def\n" % (im.size[0] * operator[1]))
- fp.write("%d %d scale\n" % im.size)
- fp.write("%d %d 8\n" % im.size) # <= bits
- fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1]))
- fp.write("{ currentfile buf readhexstring pop } bind\n")
- fp.write("%s\n" % operator[2])
-
- ImageFile._save(im, fp, [("eps", (0,0)+im.size, 0, None)])
-
- fp.write("\n%%%%EndBinary\n")
- fp.write("grestore end\n")
- fp.flush()
-
- #
- # --------------------------------------------------------------------
-
- Image.register_open(EpsImageFile.format, EpsImageFile, _accept)
-
- Image.register_save(EpsImageFile.format, _save)
-
- Image.register_extension(EpsImageFile.format, ".ps")
- Image.register_extension(EpsImageFile.format, ".eps")
-
- Image.register_mime(EpsImageFile.format, "application/postscript")
-