# # THIS IS WORK IN PROGRESS. # # The Python Imaging Library. # $Id$ # # WMF support for PIL # # history: # 96-12-14 fl Created # # notes: # This code currently supports placable metafiles only, and # just a few graphics operations are implemented. # # Copyright (c) Secret Labs AB 1997. # Copyright (c) Fredrik Lundh 1996. # # See the README file for information on usage and redistribution. # __version__ = "0.1" import Image, ImageDraw, ImageFile import string # # -------------------------------------------------------------------- def i16(c): return ord(c[0]) + (ord(c[1])<<8) def i32(c): return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) # -------------------------------------------------------------------- # The following codes are taken from the wingdi.h header file. # Copyright (c) 1985-1996, Microsoft Corp. All rights reserved. META_ANIMATEPALETTE = 0x0436 META_ARC = 0x0817 META_BITBLT = 0x0922 META_CHORD = 0x0830 META_CREATEBRUSHINDIRECT = 0x02FC META_CREATEFONTINDIRECT = 0x02FB META_CREATEPALETTE = 0x00f7 META_CREATEPATTERNBRUSH = 0x01F9 META_CREATEPENINDIRECT = 0x02FA META_CREATEREGION = 0x06FF META_DELETEOBJECT = 0x01f0 META_DIBBITBLT = 0x0940 META_DIBCREATEPATTERNBRUSH = 0x0142 META_DIBSTRETCHBLT = 0x0b41 META_ELLIPSE = 0x0418 META_ESCAPE = 0x0626 META_EXCLUDECLIPRECT = 0x0415 META_EXTFLOODFILL = 0x0548 META_EXTTEXTOUT = 0x0a32 META_FILLREGION = 0x0228 META_FLOODFILL = 0x0419 META_FRAMEREGION = 0x0429 META_INTERSECTCLIPRECT = 0x0416 META_INVERTREGION = 0x012A META_LINETO = 0x0213 META_MOVETO = 0x0214 META_OFFSETCLIPRGN = 0x0220 META_OFFSETVIEWPORTORG = 0x0211 META_OFFSETWINDOWORG = 0x020F META_PAINTREGION = 0x012B META_PATBLT = 0x061D META_PIE = 0x081A META_POLYGON = 0x0324 META_POLYLINE = 0x0325 META_POLYPOLYGON = 0x0538 META_REALIZEPALETTE = 0x0035 META_RECTANGLE = 0x041B META_RESIZEPALETTE = 0x0139 META_RESTOREDC = 0x0127 META_ROUNDRECT = 0x061C META_SAVEDC = 0x001E META_SCALEVIEWPORTEXT = 0x0412 META_SCALEWINDOWEXT = 0x0410 META_SELECTCLIPREGION = 0x012C META_SELECTOBJECT = 0x012D META_SELECTPALETTE = 0x0234 META_SETBKCOLOR = 0x0201 META_SETBKMODE = 0x0102 META_SETDIBTODEV = 0x0d33 META_SETMAPMODE = 0x0103 META_SETMAPPERFLAGS = 0x0231 META_SETPALENTRIES = 0x0037 META_SETPIXEL = 0x041F META_SETPOLYFILLMODE = 0x0106 META_SETRELABS = 0x0105 META_SETROP2 = 0x0104 META_SETSTRETCHBLTMODE = 0x0107 META_SETTEXTALIGN = 0x012E META_SETTEXTCHAREXTRA = 0x0108 META_SETTEXTCOLOR = 0x0209 META_SETTEXTJUSTIFICATION = 0x020A META_SETVIEWPORTEXT = 0x020E META_SETVIEWPORTORG = 0x020D META_SETWINDOWEXT = 0x020C META_SETWINDOWORG = 0x020B META_STRETCHBLT = 0x0B23 META_STRETCHDIB = 0x0f43 META_TEXTOUT = 0x0521 # create a code to name dictionary (for debugging) NAME = {} for k, v in vars().items(): if k[:5] == "META_": NAME[v] = k[5:] # # -------------------------------------------------------------------- # Read WMF file def _accept(prefix): return prefix[:6] == "\327\315\306\232\000\000" class WmfImageFile(ImageFile.ImageFile): format = "WMF" format_description = "Windows Metafile" def _open(self): # check placable header s = self.fp.read(22) if s[:6] != "\327\315\306\232\000\000": raise SyntaxError, "Not a placable WMF file" # position on output device bbox = i16(s[6:8]), i16(s[8:10]), i16(s[10:12]), i16(s[12:14]) # FIXME: should take the scale into account self.mode = "P" self.size = (bbox[2]-bbox[0]) / 20, (bbox[3]-bbox[1]) / 20 # FIXME: while hacking self.size = (bbox[2] + bbox[0])/10, (bbox[3] + bbox[1])/10 self.bbox = bbox # check standard header s = self.fp.read(18) if s[:6] != "\001\000\011\000\000\003": raise SyntaxError, "Not a WMF file" def _ink(self, rgb): # lookup colour in current palette try: return self.palette[rgb] except KeyError: # hmm. what if the palette becomes full? ink = len(self.palette) self.palette[rgb] = ink return ink def load(self): if self.im: return # # windows standard palette self.palette = { '\000\000\000': 0, '\200\000\000': 1, '\000\200\000': 2, '\200\200\000': 3, '\000\000\200': 4, '\200\000\200': 5, '\000\200\200': 6, '\300\300\300': 7, '\300\334\300': 8, '\246\312\360': 9, '\377\373\360': 246, '\240\240\244': 247, '\200\200\200': 248, '\377\000\000': 249, '\000\377\000': 250, '\377\377\000': 251, '\000\000\377': 252, '\377\000\377': 253, '\000\377\377': 254, '\377\377\377': 255, } fill = 0 pen = brush = self._ink("\000\000\000") paper = self._ink("\377\377\377") self.im = Image.core.fill(self.mode, self.size, paper) # # render metafile into image, using the standard palette id = ImageDraw.ImageDraw(self) while 1: s = self.fp.read(6) size = i32(s)*2 func = i16(s[4:]) if not func: break s = self.fp.read(size-6) if func == META_SETPOLYFILLMODE: fill = i16(s) id.setfill(fill) elif func == META_CREATEBRUSHINDIRECT: brush = self._ink(s[2:5]) elif func == META_CREATEPENINDIRECT: pen = self._ink(s[6:9]) elif func == META_POLYGON: xy = map(lambda i,s=s: i16(s[i:i+2])/10, range(2, len(s), 2)) if fill: id.setink(brush) id.polygon(xy) id.setink(pen) id.setfill(0) id.polygon(xy) id.setfill(1) else: id.setink(pen) id.polygon(xy) elif func == META_POLYLINE: xy = map(lambda i,s=s: i16(s[i:i+2])/10, range(2, len(s), 2)) id.setink(pen) id.line(xy) elif func == META_RECTANGLE: xy = (i16(s[2:4])/10, i16(s[0:2])/10, i16(s[6:8])/10, i16(s[4:6])/10) if fill: id.setink(brush) id.rectangle(xy) id.setink(pen) id.setfill(0) id.rectangle(xy) id.setfill(1) else: id.setink(pen) id.rectangle(xy) else: if Image.DEBUG: print size, hex(func), NAME[func] pass # # attach palette to image palette = ["\0\0\0"] * 256 for rgb, i in self.palette.items(): if i < 256: palette[i] = rgb self.im.putpalette("RGB", string.join(palette, "")) # # -------------------------------------------------------------------- # Registry stuff Image.register_open("WMF", WmfImageFile, _accept) Image.register_extension("WMF", ".wmf")