fix various deprecation warnings
[PyX/mjg.git] / pyx / type1font.py
blobcf310798a30371f2ca963b81a0327b677470332e
1 #!/usr/bin/env python
2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2005 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2005 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 import re, tempfile, os, binascii
25 try:
26 import zlib
27 except:
28 pass
29 import bbox, canvas, pswriter, pdfwriter, t1strip
31 # _PFB_ASCII = "\200\1"
32 # _PFB_BIN = "\200\2"
33 # _PFB_DONE = "\200\3"
34 # _PFA = "%!"
36 _StandardEncodingMatch = re.compile(r"\b/Encoding\s+StandardEncoding\s+def\b")
37 #_FontBBoxMatch = re.compile(r"\bFontBBox\s*\{\s*(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s*\}\s*readonly\s+def\b")
38 #_ItalicAngle = re.compile(r"\bItalicAngle\s+(-?\d+)\b")
41 def _pfblength(s):
42 if len(s) != 4:
43 raise ValueError("invalid string length")
44 return (ord(s[0]) +
45 ord(s[1])*256 +
46 ord(s[2])*256*256 +
47 ord(s[3])*256*256*256)
49 class _tokenfile:
50 """ ascii file containing tokens separated by spaces.
52 Comments beginning with % are ignored. Strings containing spaces
53 are not handled correctly
54 """
56 def __init__(self, filename):
57 self.file = open(filename, "r")
58 self.line = None
60 def gettoken(self):
61 """ return next token or None if EOF """
62 while not self.line:
63 line = self.file.readline()
64 if line == "":
65 return None
66 self.line = line.split("%")[0].split()
67 token = self.line[0]
68 self.line = self.line[1:]
69 return token
71 def close(self):
72 self.file.close()
75 class encoding:
77 def __init__(self, name, filename):
78 """ font encoding contained in filename """
79 self.name = name
80 self.filename = filename
83 class encodingfile:
85 def __init__(self, name, filename):
86 self.name = name
87 encfile = _tokenfile(filename)
89 # name of encoding
90 self.encname = encfile.gettoken()
91 token = encfile.gettoken()
92 if token != "[":
93 raise RuntimeError("cannot parse encoding file '%s', expecting '[' got '%s'" % (filename, token))
94 self.encvector = []
95 for i in range(256):
96 token = encfile.gettoken()
97 if token is None or token=="]":
98 raise RuntimeError("not enough charcodes in encoding file '%s'" % filename)
99 self.encvector.append(token)
100 if encfile.gettoken() != "]":
101 raise RuntimeError("too many charcodes in encoding file '%s'" % filename)
102 token = encfile.gettoken()
103 if token != "def":
104 raise RuntimeError("cannot parse encoding file '%s', expecting 'def' got '%s'" % (filename, token))
105 token = encfile.gettoken()
106 if token != None:
107 raise RuntimeError("encoding file '%s' too long" % filename)
108 encfile.close()
110 # def decode(self, charcode):
111 # return self.encvector[charcode]
113 def outputPS(self, file, writer, registry):
114 file.write("%%%%BeginProcSet: %s\n" % self.name)
115 file.write("/%s\n"
116 "[" % self.name)
117 for i, glyphname in enumerate(self.encvector):
118 if i and not (i % 8):
119 file.write("\n")
120 else:
121 file.write(" ")
122 file.write(glyphname)
123 file.write(" ] def\n"
124 "%%EndProcSet\n")
126 def outputPDF(self, file, writer, registry):
127 file.write("<<\n"
128 "/Type /Encoding\n"
129 "/Differences\n"
130 "[ 0")
131 for i, glyphname in enumerate(self.encvector):
132 if i and not (i % 8):
133 file.write("\n")
134 else:
135 file.write(" ")
136 file.write(glyphname)
137 file.write(" ]\n"
138 ">>\n")
141 class font:
143 def __init__(self, basefontname, filename, encoding, metric):
144 self.basefontname = basefontname
145 self.filename = filename
146 self.encoding = encoding
147 self.metric = metric
149 if encoding is None:
150 self.name = basefontname
151 else:
152 self.name = "%s-%s" % (basefontname, encoding.name)
155 class fontfile:
157 # TODO: own stripping using glyph names instead of char codes; completely remove encoding
159 def __init__(self, fontname, fontfilename, usedchars, encodingfilename):
160 self.fontname = fontname
161 self.fontfilename = fontfilename
162 self.usedchars = [0]*256
163 for charcode in usedchars.keys():
164 self.usedchars[charcode] = 1
165 self.encodingfilename = encodingfilename
167 def getflags(self):
168 # As a simple heuristics we assume non-symbolic fonts if and only
169 # if the Adobe standard encoding is used. All other font flags are
170 # not specified here.
171 fontfile = open(self.fontfilename, "rb")
172 fontdata = fontfile.read()
173 fontfile.close()
174 if _StandardEncodingMatch.search(fontdata):
175 return 32
176 return 4
178 def outputPS(self, file, writer, registry):
179 file.write("%%%%BeginFont: %s\n" % self.fontname)
180 file.write("%Included char codes:")
181 for i in range(len(self.usedchars)):
182 if self.usedchars[i]:
183 file.write(" %d" % i)
184 file.write("\n")
185 if self.encodingfilename is not None:
186 t1strip.t1strip(file, self.fontfilename, self.usedchars, self.encodingfilename)
187 else:
188 t1strip.t1strip(file, self.fontfilename, self.usedchars)
189 file.write("%%EndFont\n")
191 def outputPDF(self, file, writer, registry):
192 strippedfontfilename = tempfile.mktemp()
193 strippedfontfile = open(strippedfontfilename, "w")
194 if self.usedchars:
195 if self.encodingfilename is not None:
196 t1strip.t1strip(strippedfontfile, self.fontfilename, self.usedchars, self.encodingfilename)
197 else:
198 t1strip.t1strip(strippedfontfile, self.fontfilename, self.usedchars)
199 strippedfontfile.close()
200 strippedfontfile = open(strippedfontfilename, "r")
201 fontdata = strippedfontfile.read()
202 strippedfontfile.close()
203 os.unlink(strippedfontfilename)
205 # split the font into its three parts
206 length1 = fontdata.index("currentfile eexec") + 18
207 length2 = fontdata.index("0"*20)
208 fontdata1 = fontdata[:length1]
209 fontdata2 = fontdata[length1:length2]
210 fontdata3 = fontdata[length2:]
211 length3 = len(fontdata3)
213 # convert to pfa
214 fontdata2 = binascii.a2b_hex(fontdata2.replace(" ", "").replace("\r", "").replace("\n", "").replace("\t", ""))
215 length2 = len(fontdata2)
217 # we might be allowed to skip the third part ...
218 if (fontdata3.replace("\n", "")
219 .replace("\r", "")
220 .replace("\t", "")
221 .replace(" ", "")) == "0"*512 + "cleartomark":
222 length3 = 0
223 fontdata3 = ""
225 data = fontdata1 + fontdata2 + fontdata3
226 if writer.compress:
227 data = zlib.compress(fontdata1 + fontdata2 + fontdata3)
229 file.write("<<\n"
230 "/Length %d\n"
231 "/Length1 %d\n"
232 "/Length2 %d\n"
233 "/Length3 %d\n" % (len(data), length1, length2, length3))
234 if writer.compress:
235 file.write("/Filter /FlateDecode\n")
236 file.write(">>\n"
237 "stream\n")
238 file.write(data)
239 file.write("\nendstream\n")
242 class text_pt(canvas.canvasitem):
244 def __init__(self, x_pt, y_pt, font):
245 self.font = font
246 self.x_pt = x_pt
247 self.y_pt = y_pt
248 self.width_pt = 0
249 self.height_pt = 0
250 self.depth_pt = 0
251 self.chars = []
253 def addchar(self, char):
254 metric = self.font.metric
255 self.width_pt += metric.getwidth_pt(char)
256 cheight_pt = metric.getwidth_pt(char)
257 if cheight_pt > self.height_pt:
258 self.height_pt = cheight_pt
259 cdepth_pt = metric.getdepth_pt(char)
260 if cdepth_pt > self.depth_pt:
261 self.depth_pt = cdepth_pt
262 self.chars.append(char)
264 def bbox(self):
265 return bbox.bbox_pt(self.x_pt, self.y_pt-self.depth_pt, self.x_pt+self.width_pt, self.y_pt+self.height_pt)
267 def registerPS(self, registry):
268 # note that we don't register PSfont as it is just a helper resource
269 # which registers the needed components
270 pswriter.PSfont(self.font, self.chars, registry)
272 def registerPDF(self, registry):
273 registry.add(pdfwriter.PDFfont(self.font, self.chars, registry))
275 def outputPS(self, file, writer, context):
276 if ( context.font is None or
277 context.font.name != self.font.name or
278 context.font.metric.getsize_pt() != self.font.metric.getsize_pt() ):
279 file.write("/%s %f selectfont\n" % (self.font.name, self.font.metric.getsize_pt()))
280 context.font = self.font
281 outstring = ""
282 for char in self.chars:
283 if char > 32 and char < 127 and chr(char) not in "()[]<>\\":
284 ascii = "%s" % chr(char)
285 else:
286 ascii = "\\%03o" % char
287 outstring += ascii
288 file.write("%g %g moveto (%s) show\n" % (self.x_pt, self.y_pt, outstring))
291 def outputPDF(self, file, writer, context):
292 if ( context.font is None or
293 context.font.name != self.font.name or
294 context.font.metric.getsize_pt() != self.font.metric.getsize_pt() ):
295 file.write("/%s %f Tf\n" % (self.font.name, self.font.metric.getsize_pt()))
296 context.font = self.font
297 outstring = ""
298 for char in self.chars:
299 if char > 32 and char < 127 and chr(char) not in "()[]<>\\":
300 ascii = "%s" % chr(char)
301 else:
302 ascii = "\\%03o" % char
303 outstring += ascii
304 file.write("1 0 0 1 %f %f Tm (%s) Tj\n" % (self.x_pt, self.y_pt, outstring))