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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 import re
, tempfile
, os
, binascii
29 import bbox
, canvas
, pswriter
, pdfwriter
, t1strip
34 # fallback implementation for Python 2.2 and below
36 return zip(xrange(len(list)), list)
39 # _PFB_ASCII = "\200\1"
41 # _PFB_DONE = "\200\3"
44 _StandardEncodingMatch
= re
.compile(r
"\b/Encoding\s+StandardEncoding\s+def\b")
45 #_FontBBoxMatch = re.compile(r"\bFontBBox\s*\{\s*(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s*\}\s*readonly\s+def\b")
46 #_ItalicAngle = re.compile(r"\bItalicAngle\s+(-?\d+)\b")
51 raise ValueError("invalid string length")
55 ord(s
[3])*256*256*256)
58 """ ascii file containing tokens separated by spaces.
60 Comments beginning with % are ignored. Strings containing spaces
61 are not handled correctly
64 def __init__(self
, filename
):
65 self
.file = open(filename
, "r")
69 """ return next token or None if EOF """
71 line
= self
.file.readline()
74 self
.line
= line
.split("%")[0].split()
76 self
.line
= self
.line
[1:]
85 def __init__(self
, name
, filename
):
86 """ font encoding contained in filename """
88 self
.filename
= filename
93 def __init__(self
, name
, filename
):
95 encfile
= _tokenfile(filename
)
98 self
.encname
= encfile
.gettoken()
99 token
= encfile
.gettoken()
101 raise RuntimeError("cannot parse encoding file '%s', expecting '[' got '%s'" % (filename
, token
))
104 token
= encfile
.gettoken()
105 if token
is None or token
=="]":
106 raise RuntimeError("not enough charcodes in encoding file '%s'" % filename
)
107 self
.encvector
.append(token
)
108 if encfile
.gettoken() != "]":
109 raise RuntimeError("too many charcodes in encoding file '%s'" % filename
)
110 token
= encfile
.gettoken()
112 raise RuntimeError("cannot parse encoding file '%s', expecting 'def' got '%s'" % (filename
, token
))
113 token
= encfile
.gettoken()
115 raise RuntimeError("encoding file '%s' too long" % filename
)
118 # def decode(self, charcode):
119 # return self.encvector[charcode]
121 def outputPS(self
, file, writer
, registry
):
122 file.write("%%%%BeginProcSet: %s\n" % self
.name
)
125 for i
, glyphname
in enumerate(self
.encvector
):
126 if i
and not (i
% 8):
130 file.write(glyphname
)
131 file.write(" ] def\n"
134 def outputPDF(self
, file, writer
, registry
):
139 for i
, glyphname
in enumerate(self
.encvector
):
140 if i
and not (i
% 8):
144 file.write(glyphname
)
151 def __init__(self
, basefontname
, filename
, encoding
, metric
):
152 self
.basefontname
= basefontname
153 self
.filename
= filename
154 self
.encoding
= encoding
158 self
.name
= basefontname
160 self
.name
= "%s-%s" % (basefontname
, encoding
.name
)
165 # TODO: own stripping using glyph names instead of char codes; completely remove encoding
167 def __init__(self
, fontname
, fontfilename
, usedchars
, encodingfilename
):
168 self
.fontname
= fontname
169 self
.fontfilename
= fontfilename
170 self
.usedchars
= [0]*256
171 for charcode
in usedchars
.keys():
172 self
.usedchars
[charcode
] = 1
173 self
.encodingfilename
= encodingfilename
176 # As a simple heuristics we assume non-symbolic fonts if and only
177 # if the Adobe standard encoding is used. All other font flags are
178 # not specified here.
179 fontfile
= open(self
.fontfilename
, "rb")
180 fontdata
= fontfile
.read()
182 if _StandardEncodingMatch
.search(fontdata
):
186 def outputPS(self
, file, writer
, registry
):
187 file.write("%%%%BeginFont: %s\n" % self
.fontname
)
188 file.write("%Included char codes:")
189 for i
in range(len(self
.usedchars
)):
190 if self
.usedchars
[i
]:
191 file.write(" %d" % i
)
193 if self
.encodingfilename
is not None:
194 t1strip
.t1strip(file, self
.fontfilename
, self
.usedchars
, self
.encodingfilename
)
196 t1strip
.t1strip(file, self
.fontfilename
, self
.usedchars
)
197 file.write("%%EndFont\n")
199 def outputPDF(self
, file, writer
, registry
):
200 strippedfontfilename
= tempfile
.mktemp()
201 strippedfontfile
= open(strippedfontfilename
, "w")
203 if self
.encodingfilename
is not None:
204 t1strip
.t1strip(strippedfontfile
, self
.fontfilename
, self
.usedchars
, self
.encodingfilename
)
206 t1strip
.t1strip(strippedfontfile
, self
.fontfilename
, self
.usedchars
)
207 strippedfontfile
.close()
208 strippedfontfile
= open(strippedfontfilename
, "r")
209 fontdata
= strippedfontfile
.read()
210 strippedfontfile
.close()
211 os
.unlink(strippedfontfilename
)
213 # split the font into its three parts
214 length1
= fontdata
.index("currentfile eexec") + 18
215 length2
= fontdata
.index("0"*20)
216 fontdata1
= fontdata
[:length1
]
217 fontdata2
= fontdata
[length1
:length2
]
218 fontdata3
= fontdata
[length2
:]
219 length3
= len(fontdata3
)
222 fontdata2
= binascii
.a2b_hex(fontdata2
.replace(" ", "").replace("\r", "").replace("\n", "").replace("\t", ""))
223 length2
= len(fontdata2
)
225 # we might be allowed to skip the third part ...
226 if (fontdata3
.replace("\n", "")
229 .replace(" ", "")) == "0"*512 + "cleartomark":
233 data
= fontdata1
+ fontdata2
+ fontdata3
235 data
= zlib
.compress(fontdata1
+ fontdata2
+ fontdata3
)
241 "/Length3 %d\n" % (len(data
), length1
, length2
, length3
))
243 file.write("/Filter /FlateDecode\n")
251 class text_pt(canvas
.canvasitem
):
253 def __init__(self
, x_pt
, y_pt
, font
):
262 def addchar(self
, char
):
263 metric
= self
.font
.metric
264 self
.width_pt
+= metric
.getwidth_pt(char
)
265 cheight_pt
= metric
.getwidth_pt(char
)
266 if cheight_pt
> self
.height_pt
:
267 self
.height_pt
= cheight_pt
268 cdepth_pt
= metric
.getdepth_pt(char
)
269 if cdepth_pt
> self
.depth_pt
:
270 self
.depth_pt
= cdepth_pt
271 self
.chars
.append(char
)
274 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
)
276 def registerPS(self
, registry
):
277 # note that we don't register PSfont as it is just a helper resource
278 # which registers the needed components
279 pswriter
.PSfont(self
.font
, self
.chars
, registry
)
281 def registerPDF(self
, registry
):
282 registry
.add(pdfwriter
.PDFfont(self
.font
, self
.chars
, registry
))
284 def outputPS(self
, file, writer
, context
):
285 if ( context
.font
is None or
286 context
.font
.name
!= self
.font
.name
or
287 context
.font
.metric
.getsize_pt() != self
.font
.metric
.getsize_pt() ):
288 file.write("/%s %f selectfont\n" % (self
.font
.name
, self
.font
.metric
.getsize_pt()))
289 context
.font
= self
.font
291 for char
in self
.chars
:
292 if char
> 32 and char
< 127 and chr(char
) not in "()[]<>\\":
293 ascii
= "%s" % chr(char
)
295 ascii
= "\\%03o" % char
297 file.write("%g %g moveto (%s) show\n" % (self
.x_pt
, self
.y_pt
, outstring
))
300 def outputPDF(self
, file, writer
, context
):
301 if ( context
.font
is None or
302 context
.font
.name
!= self
.font
.name
or
303 context
.font
.metric
.getsize_pt() != self
.font
.metric
.getsize_pt() ):
304 file.write("/%s %f Tf\n" % (self
.font
.name
, self
.font
.metric
.getsize_pt()))
305 context
.font
= self
.font
307 for char
in self
.chars
:
308 if 32 <= char
<= 127 and chr(char
) not in "()[]<>\\":
309 ascii
= "%s" % chr(char
)
311 ascii
= "\\%03o" % char
313 file.write("1 0 0 1 %f %f Tm (%s) Tj\n" % (self
.x_pt
, self
.y_pt
, outstring
))