# # The Python Imaging Library. # $Id$ # # 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) # 2014-05-07 e Handling of EPS with binary preview and fixed resolution # resizing # # 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. # import io import os import re import subprocess import sys import tempfile from . import Image, ImageFile from ._binary import i32le as i32 # # -------------------------------------------------------------------- split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") gs_windows_binary = None if sys.platform.startswith("win"): import shutil for binary in ("gswin32c", "gswin64c", "gs"): if shutil.which(binary) is not None: gs_windows_binary = binary break else: gs_windows_binary = False def has_ghostscript(): if gs_windows_binary: return True if not sys.platform.startswith("win"): try: subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL) return True except OSError: # No Ghostscript pass return False def Ghostscript(tile, size, fp, scale=1): """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 dependent on bbox and size res = ( 72.0 * size[0] / (bbox[2] - bbox[0]), 72.0 * size[1] / (bbox[3] - bbox[1]), ) out_fd, outfile = tempfile.mkstemp() os.close(out_fd) infile_temp = None if hasattr(fp, "name") and os.path.exists(fp.name): infile = fp.name else: in_fd, infile_temp = tempfile.mkstemp() os.close(in_fd) infile = infile_temp # Ignore length and offset! # Ghostscript can read it # Copy whole file to read in Ghostscript with open(infile_temp, "wb") as f: # fetch length of fp fp.seek(0, io.SEEK_END) 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 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) "-dBATCH", # exit after processing "-dNOPAUSE", # don't pause between pages "-dSAFER", # safe mode "-sDEVICE=ppmraw", # ppm driver f"-sOutputFile={outfile}", # output file # adjust for image origin "-c", f"{-bbox[0]} {-bbox[1]} translate", "-f", infile, # input file # showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272) "-c", "showpage", ] if gs_windows_binary is not None: if not gs_windows_binary: raise OSError("Unable to locate Ghostscript on paths") command[0] = gs_windows_binary # push data through Ghostscript try: startupinfo = None if sys.platform.startswith("win"): startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW subprocess.check_call(command, startupinfo=startupinfo) out_im = Image.open(outfile) out_im.load() finally: try: os.unlink(outfile) if infile_temp: os.unlink(infile_temp) except OSError: pass im = out_im.im.copy() out_im.close() return im class PSFile: """ Wrapper for bytesio object that treats either CR or LF as end of line. """ def __init__(self, fp): self.fp = fp self.char = None def seek(self, offset, whence=io.SEEK_SET): self.char = None self.fp.seek(offset, whence) def readline(self): s = self.char or b"" self.char = None c = self.fp.read(1) while c not in b"\r\n": s = s + c c = self.fp.read(1) self.char = self.fp.read(1) # line endings can be 1 or 2 of \r \n, in either order if self.char in b"\r\n": self.char = None return s.decode("latin-1") def _accept(prefix): return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5) ## # 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" mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"} def _open(self): (length, offset) = self._find_offset(self.fp) # Rewrap the open file pointer in something that will # convert line endings and decode to latin-1. fp = PSFile(self.fp) # go to offset - start of "%!PS" fp.seek(offset) box = None self.mode = "RGB" self._size = 1, 1 # FIXME: huh? # # Load EPS header s_raw = fp.readline() s = s_raw.strip("\r\n") while s_raw: if s: if len(s) > 255: raise SyntaxError("not an EPS file") try: m = split.match(s) except re.error as e: raise SyntaxError("not an EPS file") from e 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 = [int(float(i)) for i in v.split()] self._size = box[2] - box[0], box[3] - box[1] self.tile = [ ("eps", (0, 0) + self.size, offset, (length, box)) ] except Exception: 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] = "" elif s[0] == "%": # handle non-DSC PostScript comments that some # tools mistakenly put in the Comments section pass else: raise OSError("bad EPS header") s_raw = fp.readline() s = s_raw.strip("\r\n") if s and s[:1] != "%": break # # Scan for an "ImageData" descriptor while s[:1] == "%": if len(s) > 255: raise SyntaxError("not an EPS file") if s[:11] == "%ImageData:": # Encoded bitmapped image. x, y, bi, mo = s[11:].split(None, 7)[:4] if int(bi) != 8: break try: self.mode = self.mode_map[int(mo)] except ValueError: break self._size = int(x), int(y) return s = fp.readline().strip("\r\n") if not s: break if not box: raise OSError("cannot determine EPS bounding box") def _find_offset(self, fp): s = fp.read(160) if s[:4] == b"%!PS": # for HEAD without binary preview fp.seek(0, io.SEEK_END) length = fp.tell() offset = 0 elif i32(s[0:4]) == 0xC6D3D0C5: # FIX for: Some EPS file not handled correctly / issue #302 # EPS can contain binary data # or start directly with latin coding # more info see: # https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf offset = i32(s[4:8]) length = i32(s[8:12]) else: raise SyntaxError("not an EPS file") return (length, offset) def load(self, scale=1): # Load EPS via Ghostscript if not self.tile: return self.im = Ghostscript(self.tile, self.size, self.fp, scale) self.mode = self.im.mode self._size = self.im.size self.tile = [] def load_seek(self, *args, **kwargs): # we can't incrementally load, so force ImageFile.parser to # use our custom load method by defining this method. pass # # -------------------------------------------------------------------- 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") base_fp = fp wrapped_fp = False if fp != sys.stdout: fp = io.TextIOWrapper(fp, encoding="latin-1") wrapped_fp = True try: 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(f"/buf {im.size[0] * operator[1]} string def\n") fp.write("%d %d scale\n" % im.size) fp.write("%d %d 8\n" % im.size) # <= bits fp.write(f"[{im.size[0]} 0 0 -{im.size[1]} 0 {im.size[1]}]\n") fp.write("{ currentfile buf readhexstring pop } bind\n") fp.write(operator[2] + "\n") if hasattr(fp, "flush"): fp.flush() ImageFile._save(im, base_fp, [("eps", (0, 0) + im.size, 0, None)]) fp.write("\n%%%%EndBinary\n") fp.write("grestore end\n") if hasattr(fp, "flush"): fp.flush() finally: if wrapped_fp: fp.detach() # # -------------------------------------------------------------------- Image.register_open(EpsImageFile.format, EpsImageFile, _accept) Image.register_save(EpsImageFile.format, _save) Image.register_extensions(EpsImageFile.format, [".ps", ".eps"]) Image.register_mime(EpsImageFile.format, "application/postscript")
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__pycache__ | Folder | 2755 |
|
|
BdfFontFile.py | File | 2.75 KB | 0644 |
|
BlpImagePlugin.py | File | 14 KB | 0644 |
|
BmpImagePlugin.py | File | 13.92 KB | 0644 |
|
BufrStubImagePlugin.py | File | 1.48 KB | 0644 |
|
ContainerIO.py | File | 2.82 KB | 0644 |
|
CurImagePlugin.py | File | 1.68 KB | 0644 |
|
DcxImagePlugin.py | File | 2.09 KB | 0644 |
|
DdsImagePlugin.py | File | 5.34 KB | 0644 |
|
EpsImagePlugin.py | File | 11.82 KB | 0644 |
|
ExifTags.py | File | 8.8 KB | 0644 |
|
FitsStubImagePlugin.py | File | 1.59 KB | 0644 |
|
FliImagePlugin.py | File | 4.23 KB | 0644 |
|
FontFile.py | File | 2.7 KB | 0644 |
|
FpxImagePlugin.py | File | 6.53 KB | 0644 |
|
FtexImagePlugin.py | File | 3.23 KB | 0644 |
|
GbrImagePlugin.py | File | 2.73 KB | 0644 |
|
GdImageFile.py | File | 2.47 KB | 0644 |
|
GifImagePlugin.py | File | 28.25 KB | 0644 |
|
GimpGradientFile.py | File | 3.27 KB | 0644 |
|
GimpPaletteFile.py | File | 1.24 KB | 0644 |
|
GribStubImagePlugin.py | File | 1.51 KB | 0644 |
|
Hdf5StubImagePlugin.py | File | 1.48 KB | 0644 |
|
IcnsImagePlugin.py | File | 11.44 KB | 0644 |
|
IcoImagePlugin.py | File | 9.94 KB | 0644 |
|
ImImagePlugin.py | File | 10.53 KB | 0644 |
|
Image.py | File | 113.4 KB | 0644 |
|
ImageChops.py | File | 7.13 KB | 0644 |
|
ImageCms.py | File | 36.22 KB | 0644 |
|
ImageColor.py | File | 8.44 KB | 0644 |
|
ImageDraw.py | File | 29.94 KB | 0644 |
|
ImageDraw2.py | File | 4.9 KB | 0644 |
|
ImageEnhance.py | File | 3.12 KB | 0644 |
|
ImageFile.py | File | 20.74 KB | 0644 |
|
ImageFilter.py | File | 15.46 KB | 0644 |
|
ImageFont.py | File | 43.49 KB | 0644 |
|
ImageGrab.py | File | 3.54 KB | 0644 |
|
ImageMath.py | File | 6.88 KB | 0644 |
|
ImageMode.py | File | 1.6 KB | 0644 |
|
ImageMorph.py | File | 7.67 KB | 0644 |
|
ImageOps.py | File | 18.03 KB | 0644 |
|
ImagePalette.py | File | 6.2 KB | 0644 |
|
ImagePath.py | File | 336 B | 0644 |
|
ImageQt.py | File | 5.67 KB | 0644 |
|
ImageSequence.py | File | 1.81 KB | 0644 |
|
ImageShow.py | File | 6.15 KB | 0644 |
|
ImageStat.py | File | 3.81 KB | 0644 |
|
ImageTk.py | File | 9.11 KB | 0644 |
|
ImageTransform.py | File | 2.78 KB | 0644 |
|
ImageWin.py | File | 7.02 KB | 0644 |
|
ImtImagePlugin.py | File | 2.15 KB | 0644 |
|
IptcImagePlugin.py | File | 5.6 KB | 0644 |
|
Jpeg2KImagePlugin.py | File | 8.52 KB | 0644 |
|
JpegImagePlugin.py | File | 27.16 KB | 0644 |
|
JpegPresets.py | File | 12.41 KB | 0644 |
|
McIdasImagePlugin.py | File | 1.71 KB | 0644 |
|
MicImagePlugin.py | File | 2.54 KB | 0644 |
|
MpegImagePlugin.py | File | 1.76 KB | 0644 |
|
MpoImagePlugin.py | File | 4.14 KB | 0644 |
|
MspImagePlugin.py | File | 5.43 KB | 0644 |
|
PSDraw.py | File | 6.51 KB | 0644 |
|
PaletteFile.py | File | 1.08 KB | 0644 |
|
PalmImagePlugin.py | File | 8.89 KB | 0644 |
|
PcdImagePlugin.py | File | 1.47 KB | 0644 |
|
PcfFontFile.py | File | 6.2 KB | 0644 |
|
PcxImagePlugin.py | File | 5.41 KB | 0644 |
|
PdfImagePlugin.py | File | 7.49 KB | 0644 |
|
PdfParser.py | File | 33.58 KB | 0644 |
|
PixarImagePlugin.py | File | 1.61 KB | 0644 |
|
PngImagePlugin.py | File | 42.79 KB | 0644 |
|
PpmImagePlugin.py | File | 4.34 KB | 0644 |
|
PsdImagePlugin.py | File | 7.56 KB | 0644 |
|
PyAccess.py | File | 9.37 KB | 0644 |
|
SgiImagePlugin.py | File | 5.96 KB | 0644 |
|
SpiderImagePlugin.py | File | 9.31 KB | 0644 |
|
SunImagePlugin.py | File | 4.2 KB | 0644 |
|
TarIO.py | File | 1.41 KB | 0644 |
|
TgaImagePlugin.py | File | 6.18 KB | 0644 |
|
TiffImagePlugin.py | File | 66.86 KB | 0644 |
|
TiffTags.py | File | 14.22 KB | 0644 |
|
WalImageFile.py | File | 5.4 KB | 0644 |
|
WebPImagePlugin.py | File | 10.54 KB | 0644 |
|
WmfImagePlugin.py | File | 4.56 KB | 0644 |
|
XVThumbImagePlugin.py | File | 1.9 KB | 0644 |
|
XbmImagePlugin.py | File | 2.37 KB | 0644 |
|
XpmImagePlugin.py | File | 3 KB | 0644 |
|
__init__.py | File | 3.18 KB | 0644 |
|
__main__.py | File | 41 B | 0644 |
|
_binary.py | File | 1.75 KB | 0644 |
|
_imaging.cpython-36m-x86_64-linux-gnu.so | File | 650.13 KB | 0755 |
|
_imagingcms.cpython-36m-x86_64-linux-gnu.so | File | 38.05 KB | 0755 |
|
_imagingft.cpython-36m-x86_64-linux-gnu.so | File | 41.98 KB | 0755 |
|
_imagingmath.cpython-36m-x86_64-linux-gnu.so | File | 24.43 KB | 0755 |
|
_imagingmorph.cpython-36m-x86_64-linux-gnu.so | File | 8.12 KB | 0755 |
|
_imagingtk.cpython-36m-x86_64-linux-gnu.so | File | 9.37 KB | 0755 |
|
_tkinter_finder.py | File | 224 B | 0644 |
|
_util.py | File | 359 B | 0644 |
|
_version.py | File | 50 B | 0644 |
|
_webp.cpython-36m-x86_64-linux-gnu.so | File | 40.82 KB | 0755 |
|
features.py | File | 8.8 KB | 0644 |
|