home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 April / enter-2004-04.iso / files / EVE_1424_100181.exe / EpsImagePlugin.py < prev    next >
Encoding:
Python Source  |  2004-04-20  |  9.6 KB  |  346 lines

  1. #
  2. # The Python Imaging Library.
  3. # $Id: //modules/pil/PIL/EpsImagePlugin.py#2 $
  4. #
  5. # EPS file handling
  6. #
  7. # History:
  8. # 1995-09-01 fl   Created (0.1)
  9. # 1996-05-18 fl   Don't choke on "atend" fields, Ghostscript interface (0.2)
  10. # 1996-08-22 fl   Don't choke on floating point BoundingBox values
  11. # 1996-08-23 fl   Handle files from Macintosh (0.3)
  12. # 2001-02-17 fl   Use 're' instead of 'regex' (Python 2.1) (0.4)
  13. #
  14. # Copyright (c) Secret Labs AB 1997-2001.
  15. # Copyright (c) Fredrik Lundh 1995-96-2001
  16. #
  17. # See the README file for information on usage and redistribution.
  18. #
  19.  
  20.  
  21. __version__ = "0.4"
  22.  
  23.  
  24. import re, string
  25. import Image, ImageFile
  26.  
  27. #
  28. # --------------------------------------------------------------------
  29.  
  30. def i32(c):
  31.     return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24)
  32.  
  33. def o32(i):
  34.     return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255)
  35.  
  36. split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
  37. field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
  38.  
  39. def Ghostscript(tile, size, fp):
  40.     """Render an image using Ghostscript (Unix only)"""
  41.  
  42.     # Unpack decoder tile
  43.     decoder, tile, offset, data = tile[0]
  44.     length, bbox = data
  45.  
  46.     import tempfile, os
  47.  
  48.     file = tempfile.mktemp()
  49.  
  50.     # Build ghostscript command
  51.     command = ["gs",
  52.                "-q",                    # quite mode
  53.                "-g%dx%d" % size,        # set output geometry (pixels)
  54.                "-dNOPAUSE -dSAFER",     # don't pause between pages, safe mode
  55.                "-sDEVICE=ppmraw",       # ppm driver
  56.                "-sOutputFile=%s" % file,# output file
  57.                "- >/dev/tty 2>/dev/tty"]
  58.  
  59.     command = string.join(command)
  60.  
  61.     # push data through ghostscript
  62.     try:
  63.         gs = os.popen(command, "w")
  64.         # adjust for image origin
  65.         if bbox[0] != 0 or bbox[1] != 0:
  66.             gs.write("%d %d translate\n" % (-bbox[0], -bbox[1]))
  67.         fp.seek(offset)
  68.         while length > 0:
  69.             s = fp.read(8192)
  70.             if not s:
  71.                 break
  72.             length = length - len(s)
  73.             gs.write(s)
  74.         gs.close()
  75.         im = Image.core.open_ppm(file)
  76.     finally:
  77.         try: os.unlink(file)
  78.         except: pass
  79.  
  80.     return im
  81.  
  82.  
  83. class PSFile:
  84.     """Wrapper that treats either CR or LF as end of line."""
  85.     def __init__(self, fp):
  86.         self.fp = fp
  87.         self.char = None
  88.     def __getattr__(self, id):
  89.         v = getattr(self.fp, id)
  90.         setattr(self, id, v)
  91.         return v
  92.     def seek(self, offset, whence=0):
  93.         self.char = None
  94.         self.fp.seek(offset, whence)
  95.     def tell(self):
  96.         pos = self.fp.tell()
  97.         if self.char:
  98.             pos = pos - 1
  99.         return pos
  100.     def readline(self):
  101.         s = ""
  102.         if self.char:
  103.             c = self.char
  104.             self.char = None
  105.         else:
  106.             c = self.fp.read(1)
  107.         while c not in "\r\n":
  108.             s = s + c
  109.             c = self.fp.read(1)
  110.         if c == "\r":
  111.             self.char = self.fp.read(1)
  112.             if self.char == "\n":
  113.                 self.char = None
  114.         return s + "\n"
  115.  
  116.  
  117. def _accept(prefix):
  118.     return prefix[:4] == "%!PS" or i32(prefix) == 0xC6D3D0C5
  119.  
  120.  
  121. class EpsImageFile(ImageFile.ImageFile):
  122.     """EPS File Parser for the Python Imaging Library"""
  123.  
  124.     format = "EPS"
  125.     format_description = "Encapsulated Postscript"
  126.  
  127.     def _open(self):
  128.  
  129.         # FIXME: should check the first 512 bytes to see if this
  130.         # really is necessary (platform-dependent, though...)
  131.  
  132.         fp = PSFile(self.fp)
  133.  
  134.         # HEAD
  135.         s = fp.read(512)
  136.         if s[:4] == "%!PS":
  137.             offset = 0
  138.             fp.seek(0, 2)
  139.             length = fp.tell()
  140.         elif i32(s) == 0xC6D3D0C5:
  141.             offset = i32(s[4:])
  142.             length = i32(s[8:])
  143.             fp.seek(offset)
  144.         else:
  145.             raise SyntaxError, "not an EPS file"
  146.  
  147.         fp.seek(offset)
  148.  
  149.         box = None
  150.  
  151.         self.mode = "RGB"
  152.         self.size = 1, 1 # FIXME: huh?
  153.  
  154.         #
  155.         # Load EPS header
  156.  
  157.         s = fp.readline()
  158.  
  159.         while s:
  160.  
  161.             if len(s) > 255:
  162.                 raise SyntaxError, "not an EPS file"
  163.  
  164.             if s[-2:] == '\r\n':
  165.                 s = s[:-2]
  166.             elif s[-1:] == '\n':
  167.                 s = s[:-1]
  168.  
  169.             try:
  170.                 m = split.match(s)
  171.             except re.error, v:
  172.                 raise SyntaxError, "not an EPS file"
  173.  
  174.             if m:
  175.                 k, v = m.group(1, 2)
  176.                 self.info[k] = v
  177.                 if k == "BoundingBox":
  178.                     try:
  179.                         # Note: The DSC spec says that BoundingBox
  180.                         # fields should be integers, but some drivers
  181.                         # put floating point values there anyway.
  182.                         box = map(int, map(string.atof, string.split(v)))
  183.                         self.size = box[2] - box[0], box[3] - box[1]
  184.                         self.tile = [("eps", (0,0) + self.size, offset,
  185.                                       (length, box))]
  186.                     except:
  187.                         pass
  188.  
  189.             else:
  190.  
  191.                 m = field.match(s)
  192.  
  193.                 if m:
  194.                     k = m.group(1)
  195.                     if k == "EndComments":
  196.                         break
  197.                     if k[:8] == "PS-Adobe":
  198.                         self.info[k[:8]] = k[9:]
  199.                     else:
  200.                         self.info[k] = ""
  201.                 else:
  202.                     raise IOError, "bad EPS header"
  203.  
  204.             s = fp.readline()
  205.  
  206.             if s[:1] != "%":
  207.                 break
  208.  
  209.  
  210.         #
  211.         # Scan for an "ImageData" descriptor
  212.  
  213.         while s[0] == "%":
  214.  
  215.             if len(s) > 255:
  216.                 raise SyntaxError, "not an EPS file"
  217.  
  218.             if s[-2:] == '\r\n':
  219.                 s = s[:-2]
  220.             elif s[-1:] == '\n':
  221.                 s = s[:-1]
  222.  
  223.             if s[:11] == "%ImageData:":
  224.  
  225.                 [x, y, bi, mo, z3, z4, en, id] =\
  226.                     string.split(s[11:], maxsplit=7)
  227.  
  228.                 x = int(x); y = int(y)
  229.  
  230.                 bi = int(bi)
  231.                 mo = int(mo)
  232.  
  233.                 en = int(en)
  234.  
  235.                 if en == 1:
  236.                     decoder = "eps_binary"
  237.                 elif en == 2:
  238.                     decoder = "eps_hex"
  239.                 else:
  240.                     break
  241.                 if bi != 8:
  242.                     break
  243.                 if mo == 1:
  244.                     self.mode = "L"
  245.                 elif mo == 2:
  246.                     self.mode = "LAB"
  247.                 elif mo == 3:
  248.                     self.mode = "RGB"
  249.                 else:
  250.                     break
  251.  
  252.                 if id[:1] == id[-1:] == '"':
  253.                     id = id[1:-1]
  254.  
  255.                 # Scan forward to the actual image data
  256.                 while 1:
  257.                     s = fp.readline()
  258.                     if not s:
  259.                         break
  260.                     if s[:len(id)] == id:
  261.                         self.size = x, y
  262.                         self.tile2 = [(decoder,
  263.                                        (0, 0, x, y),
  264.                                        fp.tell(),
  265.                                        0)]
  266.                         return
  267.  
  268.             s = fp.readline()
  269.             if not s:
  270.                 break
  271.  
  272.         if not box:
  273.             raise IOError, "cannot determine EPS bounding box"
  274.  
  275.     def load(self):
  276.         # Load EPS via Ghostscript
  277.         if not self.tile:
  278.             return
  279.         self.im = Ghostscript(self.tile, self.size, self.fp)
  280.         self.mode = self.im.mode
  281.         self.size = self.im.size
  282.         self.tile = []
  283.  
  284. #
  285. # --------------------------------------------------------------------
  286.  
  287. def _save(im, fp, filename, eps=1):
  288.     """EPS Writer for the Python Imaging Library."""
  289.  
  290.     #
  291.     # make sure image data is available
  292.     im.load()
  293.  
  294.     #
  295.     # determine postscript image mode
  296.     if im.mode == "L":
  297.         operator = (8, 1, "image")
  298.     elif im.mode == "RGB":
  299.         operator = (8, 3, "false 3 colorimage")
  300.     elif im.mode == "CMYK":
  301.         operator = (8, 4, "false 4 colorimage")
  302.     else:
  303.         raise ValueError, "image mode is not supported"
  304.  
  305.     if eps:
  306.         #
  307.         # write EPS header
  308.         fp.write("%!PS-Adobe-3.0 EPSF-3.0\n")
  309.         fp.write("%%Creator: PIL 0.1 EpsEncode\n")
  310.         #fp.write("%%CreationDate: %s"...)
  311.         fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size)
  312.         fp.write("%%Pages: 1\n")
  313.         fp.write("%%EndComments\n")
  314.         fp.write("%%Page: 1 1\n")
  315.         fp.write("%%ImageData: %d %d " % im.size)
  316.         fp.write("%d %d 0 1 1 \"%s\"\n" % operator)
  317.  
  318.     #
  319.     # image header
  320.     fp.write("gsave\n")
  321.     fp.write("10 dict begin\n")
  322.     fp.write("/buf %d string def\n" % (im.size[0] * operator[1]))
  323.     fp.write("%d %d scale\n" % im.size)
  324.     fp.write("%d %d 8\n" % im.size) # <= bits
  325.     fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1]))
  326.     fp.write("{ currentfile buf readhexstring pop } bind\n")
  327.     fp.write("%s\n" % operator[2])
  328.  
  329.     ImageFile._save(im, fp, [("eps", (0,0)+im.size, 0, None)])
  330.  
  331.     fp.write("\n%%%%EndBinary\n")
  332.     fp.write("grestore end\n")
  333.     fp.flush()
  334.  
  335. #
  336. # --------------------------------------------------------------------
  337.  
  338. Image.register_open(EpsImageFile.format, EpsImageFile, _accept)
  339.  
  340. Image.register_save(EpsImageFile.format, _save)
  341.  
  342. Image.register_extension(EpsImageFile.format, ".ps")
  343. Image.register_extension(EpsImageFile.format, ".eps")
  344.  
  345. Image.register_mime(EpsImageFile.format, "application/postscript")
  346.