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
29 import bbox
, canvas
, pswriter
, pdfwriter
, t1strip
31 # _PFB_ASCII = "\200\1"
33 # _PFB_DONE = "\200\3"
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")
43 raise ValueError("invalid string length")
47 ord(s
[3])*256*256*256)
50 """ ascii file containing tokens separated by spaces.
52 Comments beginning with % are ignored. Strings containing spaces
53 are not handled correctly
56 def __init__(self
, filename
):
57 self
.file = open(filename
, "r")
61 """ return next token or None if EOF """
63 line
= self
.file.readline()
66 self
.line
= line
.split("%")[0].split()
68 self
.line
= self
.line
[1:]
77 def __init__(self
, name
, filename
):
78 """ font encoding contained in filename """
80 self
.filename
= filename
85 def __init__(self
, name
, filename
):
87 encfile
= _tokenfile(filename
)
90 self
.encname
= encfile
.gettoken()
91 token
= encfile
.gettoken()
93 raise RuntimeError("cannot parse encoding file '%s', expecting '[' got '%s'" % (filename
, token
))
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()
104 raise RuntimeError("cannot parse encoding file '%s', expecting 'def' got '%s'" % (filename
, token
))
105 token
= encfile
.gettoken()
107 raise RuntimeError("encoding file '%s' too long" % filename
)
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
)
117 for i
, glyphname
in enumerate(self
.encvector
):
118 if i
and not (i
% 8):
122 file.write(glyphname
)
123 file.write(" ] def\n"
126 def outputPDF(self
, file, writer
, registry
):
131 for i
, glyphname
in enumerate(self
.encvector
):
132 if i
and not (i
% 8):
136 file.write(glyphname
)
143 def __init__(self
, basefontname
, filename
, encoding
, metric
):
144 self
.basefontname
= basefontname
145 self
.filename
= filename
146 self
.encoding
= encoding
150 self
.name
= basefontname
152 self
.name
= "%s-%s" % (basefontname
, encoding
.name
)
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
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()
174 if _StandardEncodingMatch
.search(fontdata
):
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
)
185 if self
.encodingfilename
is not None:
186 t1strip
.t1strip(file, self
.fontfilename
, self
.usedchars
, self
.encodingfilename
)
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")
195 if self
.encodingfilename
is not None:
196 t1strip
.t1strip(strippedfontfile
, self
.fontfilename
, self
.usedchars
, self
.encodingfilename
)
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
)
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", "")
221 .replace(" ", "")) == "0"*512 + "cleartomark":
225 data
= fontdata1
+ fontdata2
+ fontdata3
227 data
= zlib
.compress(fontdata1
+ fontdata2
+ fontdata3
)
233 "/Length3 %d\n" % (len(data
), length1
, length2
, length3
))
235 file.write("/Filter /FlateDecode\n")
239 file.write("\nendstream\n")
242 class text_pt(canvas
.canvasitem
):
244 def __init__(self
, x_pt
, y_pt
, font
):
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
)
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
282 for char
in self
.chars
:
283 if char
> 32 and char
< 127 and chr(char
) not in "()[]<>\\":
284 ascii
= "%s" % chr(char
)
286 ascii
= "\\%03o" % char
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
298 for char
in self
.chars
:
299 if char
> 32 and char
< 127 and chr(char
) not in "()[]<>\\":
300 ascii
= "%s" % chr(char
)
302 ascii
= "\\%03o" % char
304 file.write("1 0 0 1 %f %f Tm (%s) Tj\n" % (self
.x_pt
, self
.y_pt
, outstring
))