- move PS font resources to new font.font module
[PyX/mjg.git] / pyx / font / font.py
blob99985cb313aae80cee3e102c8f301e3e4acbdc98
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
23 import t1file
25 try:
26 set()
27 except NameError:
28 # Python 2.3
29 from sets import Set as set
32 # PSresources
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"""
42 self.type = "t1file"
43 self.t1file = t1file
44 self.id = t1file.name
45 self.usedglyphs = set(glyphnames)
46 self.strip = 1
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)
53 if self.strip:
54 file.write("%%Included glyphs: %s\n" % " ".join(self.usedglyphs))
55 self.t1file.getstrippedfont(self.usedglyphs).outputPS(file, writer)
56 else:
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)
82 file.write("]\n")
83 if self.newfontmatrix:
84 file.write(str(self.newfontmatrix))
85 else:
86 file.write("0")
87 file.write(" ReEncodeFont\n")
88 file.write("%%EndResource\n")
91 _ReEncodeFont = pswriter.PSdefinition("ReEncodeFont", """{
92 6 dict
93 begin
94 /newfontmatrix exch def
95 /newencoding exch def
96 /newfontname exch def
97 /basefontname exch def
98 /basefontdict basefontname findfont def
99 /newfontdict basefontdict maxlength dict def
100 basefontdict {
101 exch dup dup /FID ne exch /Encoding ne and
102 { exch newfontdict 3 1 roll put }
103 { pop pop }
104 ifelse
105 } forall
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
111 }""")
114 class font:
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)
125 class T1font(font):
127 def __init__(self, pfbname=None):
128 self.t1file = t1file.PFBfile(pfbname)
131 class selectedfont:
133 def __init__(self, name, size):
134 self.name = name
135 self.size = 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):
147 self.font = font
148 self.x_pt = x_pt
149 self.y_pt = y_pt
150 self.glyphnames = glyphnames
151 self.size = size
152 self.slant = slant
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:
162 glyphsmissing = []
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)
171 return encodingname
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)])
175 return encodingname
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]
183 if self.slant:
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)
186 else:
187 newfontmatrix = None
188 newfontname = "%s-%s" % (self.font.t1file.name, encodingname)
190 # register resources
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))
207 else:
208 file.write("\\%03o" % codepoint)
209 file.write(") show\n")