1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2005-2007 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2005-2007 André Wobst <wobsta@users.sourceforge.net>
7 # This file is part of PyX (http://pyx.sourceforge.net/).
9 # PyX is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # PyX is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with PyX; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 from pyx
import bbox
, canvasitem
, pswriter
, trafo
, unit
30 from sets
import Set
as set
37 class PST1file(pswriter
.PSresource
):
39 """ PostScript font definition included in the prolog """
41 def __init__(self
, t1file
, glyphnames
, charcodes
):
42 """ include type 1 font t1file stripped to the given glyphnames"""
46 self
.glyphnames
= set(glyphnames
)
47 self
.charcodes
= set(charcodes
)
50 def merge(self
, other
):
51 self
.glyphnames
.update(other
.glyphnames
)
52 self
.charcodes
.update(other
.charcodes
)
54 def output(self
, file, writer
, registry
):
55 file.write("%%%%BeginFont: %s\n" % self
.t1file
.name
)
58 file.write("%%Included glyphs: %s\n" % " ".join(self
.glyphnames
))
60 file.write("%%Included charcodes: %s\n" % " ".join([str(charcode
) for charcode
in self
.charcodes
]))
61 self
.t1file
.getstrippedfont(self
.glyphnames
, self
.charcodes
).outputPS(file, writer
)
63 self
.t1file
.outputPS(file, writer
)
64 file.write("\n%%EndFont\n")
67 _ReEncodeFont
= pswriter
.PSdefinition("ReEncodeFont", """{
72 /basefontname exch def
73 /basefontdict basefontname findfont def
74 /newfontdict basefontdict maxlength dict def
76 exch dup dup /FID ne exch /Encoding ne and
77 { exch newfontdict 3 1 roll put }
81 newfontdict /FontName newfontname put
82 newfontdict /Encoding newencoding put
83 newfontname newfontdict definefont pop
88 class PSreencodefont(pswriter
.PSresource
):
90 """ reencoded PostScript font"""
92 def __init__(self
, basefontname
, newfontname
, encoding
):
93 """ reencode the font """
95 self
.type = "reencodefont"
96 self
.basefontname
= basefontname
97 self
.id = self
.newfontname
= newfontname
98 self
.encoding
= encoding
100 def output(self
, file, writer
, registry
):
101 file.write("%%%%BeginResource: %s\n" % self
.newfontname
)
102 file.write("/%s /%s\n[" % (self
.basefontname
, self
.newfontname
))
103 vector
= [None] * len(self
.encoding
)
104 for glyphname
, charcode
in self
.encoding
.items():
105 vector
[charcode
] = glyphname
106 for i
, glyphname
in enumerate(vector
):
112 file.write("/%s" % glyphname
)
114 file.write("ReEncodeFont\n")
115 file.write("%%EndResource\n")
118 _ChangeFontMatrix
= pswriter
.PSdefinition("ChangeFontMatrix", """{
121 /newfontmatrix exch def
122 /newfontname exch def
123 /basefontname exch def
124 /basefontdict basefontname findfont def
125 /newfontdict basefontdict maxlength dict def
127 exch dup dup /FID ne exch /FontMatrix ne and
128 { exch newfontdict 3 1 roll put }
132 newfontdict /FontName newfontname put
133 newfontdict /FontMatrix newfontmatrix readonly put
134 newfontname newfontdict definefont pop
139 class PSchangefontmatrix(pswriter
.PSresource
):
141 """ change font matrix of a PostScript font"""
143 def __init__(self
, basefontname
, newfontname
, newfontmatrix
):
144 """ change the font matrix """
146 self
.type = "changefontmatrix"
147 self
.basefontname
= basefontname
148 self
.id = self
.newfontname
= newfontname
149 self
.newfontmatrix
= newfontmatrix
151 def output(self
, file, writer
, registry
):
152 file.write("%%%%BeginResource: %s\n" % self
.newfontname
)
153 file.write("/%s /%s\n" % (self
.basefontname
, self
.newfontname
))
154 file.write(str(self
.newfontmatrix
))
155 file.write("\nChangeFontMatrix\n")
156 file.write("%%EndResource\n")
161 def text(self
, x
, y
, charcodes
, size_pt
, **kwargs
):
162 return self
.text_pt(unit
.topt(x
), unit
.topt(y
), charcodes
, size_pt
, **kwargs
)
167 def __init__(self
, t1file
, metric
):
169 self
.name
= t1file
.name
172 def text_pt(self
, x
, y
, charcodes
, size_pt
, **kwargs
):
173 return T1text_pt(self
, x
, y
, charcodes
, size_pt
, **kwargs
)
176 class T1builtinfont(T1font
):
178 def __init__(self
, name
, metric
):
186 def __init__(self
, name
, size_pt
):
188 self
.size_pt
= size_pt
190 def __eq__(self
, other
):
191 return self
.name
== other
.name
and self
.size_pt
== other
.size_pt
193 def outputPS(self
, file, writer
):
194 file.write("/%s %f selectfont\n" % (self
.name
, self
.size_pt
))
197 class text_pt(canvasitem
.canvasitem
):
202 class T1text_pt(text_pt
):
204 def __init__(self
, font
, x_pt
, y_pt
, charcodes
, size_pt
, decoding
=None, slant
=None, ignorebbox
=False): #, **features):
205 # features: kerning, ligatures
206 if decoding
is not None:
207 self
.glyphnames
= [decoding
[character
] for character
in charcodes
]
210 self
.charcodes
= charcodes
211 self
.reencode
= False
215 self
.size_pt
= size_pt
217 self
.ignorebbox
= ignorebbox
220 if self
.font
.metric
is None:
221 raise NotImplementedError("we don't yet have access to the metric")
222 return bbox
.bbox_pt(self
.x_pt
,
223 self
.y_pt
-self
.font
.metric
.depth_pt(self
.glyphnames
, self
.size_pt
),
224 self
.x_pt
+self
.font
.metric
.width_pt(self
.glyphnames
, self
.size_pt
),
225 self
.y_pt
+self
.font
.metric
.height_pt(self
.glyphnames
, self
.size_pt
))
227 def getencodingname(self
, encodings
):
228 """returns the name of the encoding (in encodings) mapping self.glyphnames to codepoints
229 If no such encoding can be found or extended, a new encoding is added to encodings
231 glyphnames
= set(self
.glyphnames
)
232 if len(glyphnames
) > 256:
233 raise ValueError("glyphs do not fit into one single encoding")
234 for encodingname
, encoding
in encodings
.items():
236 for glyphname
in glyphnames
:
237 if glyphname
not in encoding
.keys():
238 glyphsmissing
.append(glyphname
)
240 if len(glyphsmissing
) + len(encoding
) < 256:
241 # new glyphs fit in existing encoding which will thus be extended
242 for glyphname
in glyphsmissing
:
243 encoding
[glyphname
] = len(encoding
)
245 # create a new encoding for the glyphnames
246 encodingname
= "encoding%d" % len(encodings
)
247 encodings
[encodingname
] = dict([(glyphname
, i
) for i
, glyphname
in enumerate(glyphnames
)])
250 def processPS(self
, file, writer
, context
, registry
, bbox
):
251 if not self
.ignorebbox
:
255 if self
.font
.t1file
is not None:
257 registry
.add(PST1file(self
.font
.t1file
, self
.glyphnames
, []))
259 registry
.add(PST1file(self
.font
.t1file
, [], self
.charcodes
))
261 fontname
= self
.font
.name
263 encodingname
= self
.getencodingname(context
.encodings
.setdefault(self
.font
.name
, {}))
264 encoding
= context
.encodings
[self
.font
.name
][encodingname
]
265 newfontname
= "%s-%s" % (fontname
, encodingname
)
266 registry
.add(_ReEncodeFont
)
267 registry
.add(PSreencodefont(fontname
, newfontname
, encoding
))
268 fontname
= newfontname
271 newfontmatrix
= trafo
.trafo_pt(matrix
=((1, self
.slant
), (0, 1))) * self
.font
.t1file
.fontmatrix
272 newfontname
= "%s-slant%f" % (fontname
, self
.slant
)
273 registry
.add(_ChangeFontMatrix
)
274 registry
.add(PSchangefontmatrix(fontname
, newfontname
, newfontmatrix
))
275 fontname
= newfontname
278 # select font if necessary
279 sf
= selectedfont(fontname
, self
.size_pt
)
280 if context
.selectedfont
is None or sf
!= context
.selectedfont
:
281 context
.selectedfont
= sf
282 sf
.outputPS(file, writer
)
284 file.write("%f %f moveto (" % (self
.x_pt
, self
.y_pt
))
286 charcodes
= [encoding
[glyphname
] for glyphname
in self
.glyphnames
]
288 charcodes
= self
.charcodes
289 for charcode
in charcodes
:
290 if 32 < charcode
< 127 and chr(charcode
) not in "()[]<>\\":
291 file.write("%s" % chr(charcode
))
293 file.write("\\%03o" % charcode
)
294 file.write(") show\n")