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
22 from pyx
import canvas
, pswriter
, trafo
, unit
29 from sets
import Set
as set
36 class PST1file(pswriter
.PSresource
):
38 """ PostScript font definition included in the prolog """
40 def __init__(self
, t1file
, glyphnames
):
41 """ include type 1 font t1file stripped to the given glyphnames"""
45 self
.usedglyphs
= set(glyphnames
)
48 def merge(self
, other
):
49 self
.usedglyphs
.update(other
.usedglyphs
)
51 def output(self
, file, writer
, registry
):
52 file.write("%%%%BeginFont: %s\n" % self
.t1file
.name
)
54 file.write("%%Included glyphs: %s\n" % " ".join(self
.usedglyphs
))
55 self
.t1file
.getstrippedfont(self
.usedglyphs
).outputPS(file, writer
)
57 self
.t1file
.outputPS(file, writer
)
58 file.write("\n%%EndFont\n")
61 class PSencodefont(pswriter
.PSresource
):
63 """ encoded and transformed PostScript font"""
65 def __init__(self
, basefontname
, newfontname
, encoding
, newfontmatrix
):
66 """ include the font in the given encoding """
68 self
.type = "encodedfont"
69 self
.basefontname
= basefontname
70 self
.id = self
.newfontname
= newfontname
71 self
.encoding
= encoding
72 self
.newfontmatrix
= newfontmatrix
74 def output(self
, file, writer
, registry
):
75 file.write("%%%%BeginResource: %s\n" % self
.newfontname
)
76 file.write("/%s /%s [\n" % (self
.basefontname
, self
.newfontname
))
77 vector
= [None] * len(self
.encoding
)
78 for glyphname
, codepoint
in self
.encoding
.items():
79 vector
[codepoint
] = glyphname
80 for glyphname
in vector
:
81 file.write("/%s " % glyphname
)
83 if self
.newfontmatrix
:
84 file.write(str(self
.newfontmatrix
))
87 file.write(" ReEncodeFont\n")
88 file.write("%%EndResource\n")
91 _ReEncodeFont
= pswriter
.PSdefinition("ReEncodeFont", """{
94 /newfontmatrix exch def
97 /basefontname exch def
98 /basefontdict basefontname findfont def
99 /newfontdict basefontdict maxlength dict def
101 exch dup dup /FID ne exch /Encoding ne and
102 { exch newfontdict 3 1 roll put }
106 newfontdict /FontName newfontname put
107 newfontdict /Encoding newencoding put
108 0 newfontmatrix ne { newfontdict /FontMatrix newfontmatrix readonly put } if
109 newfontname newfontdict definefont pop
116 def text_pt(self
, x
, y
, text
, decoding
, size
, slant
=0, **features
):
117 # features: kerning, ligatures
118 glyphnames
= [decoding
[character
] for character
in text
]
119 return T1text_pt(self
, x
, y
, glyphnames
, size
, slant
)
121 def text(self
, x
, y
, text
, decoding
, size
, **features
):
122 return self
.text_pt(unit
.topt(x
), unit
.topt(y
), text
, decoding
, size
, **features
)
127 def __init__(self
, pfbname
=None):
128 self
.t1file
= t1file
.PFBfile(pfbname
)
133 def __init__(self
, name
, size
):
137 def __eq__(self
, other
):
138 return self
.name
== other
.name
and self
.size
== other
.size
140 def outputPS(self
, file, writer
):
141 file.write("/%s %f selectfont\n" % (self
.name
, self
.size
))
144 class T1text_pt(canvas
.canvasitem
):
146 def __init__(self
, font
, x_pt
, y_pt
, glyphnames
, size
, slant
):
150 self
.glyphnames
= glyphnames
154 def getencodingname(self
, encodings
):
155 """returns the name of the encoding (in encodings) mapping self.glyphnames to codepoints
156 If no such encoding can be found or extended, a new encoding is added to encodings
158 glyphnames
= set(self
.glyphnames
)
159 if len(glyphnames
) > 256:
160 raise ValueError("glyphs do not fit into one single encoding")
161 for encodingname
, encoding
in encodings
:
163 for glyphname
in glyphnames
:
164 if glyphname
not in encoding
.keys():
165 glyphsmissing
.append(glyphname
)
167 if glyphsmissing
+ len(encoding
) < 256:
168 # new glyphs fit in existing encoding which will thus be extended
169 for glyphname
in glyphsmissing
:
170 encoding
[glyphname
] = len(encoding
)
172 # create a new encoding for the glyphnames
173 encodingname
= "PyX%d" % len(encodings
)
174 encodings
[encodingname
] = dict([(glyphname
, i
) for i
, glyphname
in enumerate(glyphnames
)])
177 def processPS(self
, file, writer
, context
, registry
, bbox
):
178 # bbox += self.bbox()
180 encodingname
= self
.getencodingname(context
.encodings
.setdefault(self
.font
.t1file
.name
, {}))
181 encoding
= context
.encodings
[self
.font
.t1file
.name
][encodingname
]
184 newfontmatrix
= trafo
.trafo_pt(matrix
=((1, self
.slant
), (0, 1))) * self
.font
.t1file
.fontmatrix
185 newfontname
= "%s-%s-slant%f" % (self
.font
.t1file
.name
, encodingname
, self
.slant
)
188 newfontname
= "%s-%s" % (self
.font
.t1file
.name
, encodingname
)
191 if self
.font
.t1file
is not None:
192 registry
.add(PST1file(self
.font
.t1file
, self
.glyphnames
))
193 registry
.add(_ReEncodeFont
)
194 registry
.add(PSencodefont(self
.font
.t1file
.name
, newfontname
, encoding
, newfontmatrix
))
196 # select font if necessary
197 sf
= selectedfont(newfontname
, self
.size
)
198 if context
.selectedfont
is None or sf
!= context
.selectedfont
:
199 context
.selectedfont
= sf
200 sf
.outputPS(file, writer
)
202 file.write("%f %f moveto (" % (self
.x_pt
, self
.y_pt
))
203 for glyphname
in self
.glyphnames
:
204 codepoint
= encoding
[glyphname
]
205 if codepoint
> 32 and codepoint
< 127 and chr(codepoint
) not in "()[]<>\\":
206 file.write("%s" % chr(codepoint
))
208 file.write("\\%03o" % codepoint
)
209 file.write(") show\n")