1
"""Mac-only TrueType browser window."""
3
from fontTools import ttLib
4
from fontTools.ttLib import macUtils
12
from rf.views.wGlyphList import GlyphList
17
def __init__(self, path=None, ttFont=None, res_index=None):
21
self.filename = "????"
23
self.ttFont = ttLib.TTFont(path, res_index)
25
self.filename = os.path.basename(path)
27
self.filename = os.path.basename(path) + " - " + str(res_index)
28
self.currentglyph = None
32
def buildinterface(self):
38
tables = self.ttFont.keys()
40
self.w = w = W.Window((500, 300), title, minsize = (400, 200))
41
w.browsetablebutton = W.Button((hmargin, 32, buttonwidth, 16), "Browse table�",
43
w.browsefontbutton = W.Button((hmargin, vmargin, buttonwidth, 16), "Browse font�",
45
w.tablelist = W.List((hmargin, 56, buttonwidth, -128), tables, self.tablelisthit)
47
w.divline1 = W.VerticalLine((buttonwidth + 2 * hmargin, vmargin, 1, -vmargin))
49
gleft = buttonwidth + 3 * hmargin + 1
51
hasGlyfTable = self.ttFont.has_key('glyf')
53
glyphnames = self.ttFont.getGlyphNames2() # caselessly sorted glyph names
56
w.glyphlist = GlyphList((gleft, 56, glyphlistwidth, -vmargin),
57
glyphnames, self.glyphlisthit)
59
w.divline2 = W.VerticalLine((buttonwidth + glyphlistwidth + 4 * hmargin + 2,
60
vmargin, 1, -vmargin))
62
yMin = self.ttFont['head'].yMin
63
yMax = self.ttFont['head'].yMax
64
w.gviewer = GlyphViewer((buttonwidth + glyphlistwidth + 5 * hmargin + 3,
65
vmargin, -hmargin, -vmargin), yMin, yMax)
67
w.showpoints = W.CheckBox((gleft, vmargin, glyphlistwidth, 16), "Show points",
68
self.w.gviewer.toggleshowpoints)
69
w.showpoints.set(self.w.gviewer.showpoints)
70
w.showlines = W.CheckBox((gleft, vmargin + 24, glyphlistwidth, 16), "Show lines",
71
self.w.gviewer.toggleshowlines)
72
w.showlines.set(self.w.gviewer.showlines)
74
w.glyphlist = GlyphList((gleft, 56, glyphlistwidth, -vmargin),
76
w.noGlyphTable = W.TextBox((gleft, vmargin, -20, 20), "no 'glyf' table found")
79
w.setdefaultbutton(w.browsetablebutton)
81
w.tocurrentfont = W.Button((hmargin, -120, buttonwidth, 16), "Copy to current font", self.copytocurrentfont)
82
w.fromcurrentfont = W.Button((hmargin, -96, buttonwidth, 16), "Copy from current font", self.copyfromcurrentfont)
83
w.saveflat = W.Button((hmargin, -72, buttonwidth, 16), "Save as flat file�", self.saveflat)
84
w.savesuitcasebutton = W.Button((hmargin, -48, buttonwidth, 16), "Save as suitcase�", self.savesuitcase)
85
w.savexmlbutton = W.Button((hmargin, -24, buttonwidth, 16), "Save as XML�", self.saveXML)
88
w.browsetablebutton.enable(0)
90
def browsetable(self):
94
PyBrowser.Browser(self.ttFont)
96
def copytocurrentfont(self):
99
def copyfromcurrentfont(self):
103
path = putfile("Save font as flat file:", self.filename, ".TTF")
106
self.ttFont.save(path)
108
def savesuitcase(self):
109
path = putfile("Save font as suitcase:", self.filename, ".suit")
112
self.ttFont.save(path, 1)
115
path = putfile("Save font as XML text file:", self.filename, ".ttx")
118
pb = macUtils.ProgressBar("Saving %s as XML�" % self.filename)
120
self.ttFont.saveXML(path, pb)
124
def glyphlisthit(self, isDbl):
125
sel = self.w.glyphlist.getselectedobjects()
126
if not sel or sel[0] == self.currentglyph:
128
self.currentglyph = sel[0]
129
if self.glyphs.has_key(self.currentglyph):
130
g = self.glyphs[self.currentglyph]
132
g = Glyph(self.ttFont, self.currentglyph)
133
self.glyphs[self.currentglyph] = g
134
self.w.gviewer.setglyph(g)
136
def tablelisthit(self, isdbl):
138
for tag in self.w.tablelist.getselectedobjects():
139
table = self.ttFont[tag]
142
for glyphname in self.ttFont.getGlyphOrder():
144
glyph = table[glyphname]
146
pass # incomplete font, oh well.
147
PyBrowser.Browser(table)
149
sel = self.w.tablelist.getselection()
151
self.w.browsetablebutton.enable(1)
153
self.w.browsetablebutton.enable(0)
158
def __init__(self, ttFont, glyphName):
159
ttglyph = ttFont['glyf'][glyphName]
160
self.iscomposite = ttglyph.numberOfContours == -1
161
self.width, self.lsb = ttFont['hmtx'][glyphName]
162
if ttglyph.numberOfContours == 0:
166
self.xMin = ttglyph.xMin
167
coordinates, endPts, flags = ttglyph.getCoordinates(ttFont['glyf'])
172
self.contours.append(Numeric.array(coordinates[startpt:endpt+1]))
173
self.flags.append(flags[startpt:endpt+1])
176
def getcontours(self, scale, move):
178
for i in range(len(self.contours)):
179
contours.append(((self.contours[i] * Numeric.array(scale) + move), self.flags[i]))
183
class GlyphViewer(W.Widget):
185
def __init__(self, possize, yMin, yMax):
186
W.Widget.__init__(self, possize)
188
extra = 0.02 * (yMax-yMin)
189
self.yMin, self.yMax = yMin - extra, yMax + extra
193
def toggleshowpoints(self, onoff):
194
self.showpoints = onoff
198
def toggleshowlines(self, onoff):
199
self.showlines = onoff
203
def setglyph(self, glyph):
208
def draw(self, visRgn=None):
209
# This a HELL of a routine, but it's pretty damn fast...
211
if not self._visible:
213
Qd.EraseRect(Qd.InsetRect(self._bounds, 1, 1))
214
cliprgn = Qd.NewRgn()
215
savergn = Qd.NewRgn()
216
Qd.RectRgn(cliprgn, self._bounds)
221
l, t, r, b = Qd.InsetRect(self._bounds, 1, 1)
223
scale = float(height) / (self.yMax - self.yMin)
224
topoffset = t + scale * self.yMax
225
width = scale * self.glyph.width
226
lsb = scale * self.glyph.lsb
227
xMin = scale * self.glyph.xMin
228
# XXXX this is not correct when USE_MY_METRICS is set in component!
229
leftoffset = l + 0.5 * (r - l - width)
230
gleftoffset = leftoffset - xMin + lsb
232
Qd.RGBForeColor((0xafff, 0xafff, 0xafff))
234
Qd.MoveTo(leftoffset, t)
235
Qd.LineTo(leftoffset, b - 1)
237
Qd.MoveTo(leftoffset + width, t)
238
Qd.LineTo(leftoffset + width, b - 1)
240
Qd.MoveTo(l, topoffset)
241
Qd.LineTo(r - 1, topoffset)
244
Qd.RGBForeColor((0x5fff, 0, 0))
245
Qd.MoveTo(gleftoffset, topoffset - 16)
246
Qd.LineTo(gleftoffset, topoffset + 16)
248
Qd.RGBForeColor((0, 0, 0))
250
if self.glyph.iscomposite:
251
Qd.RGBForeColor((0x7fff, 0x7fff, 0x7fff))
254
contours = self.glyph.getcontours((scale, -scale), (gleftoffset, topoffset))
255
for contour, flags in contours:
259
nPoints = len(contour)
264
currentpoint = lineto(pt, done_moveto)
268
currentpoint = 0.5 * (contour[i-1] + pt)
270
currentpoint = contour[i-1]
271
if not flags[(i+1) % nPoints]:
272
endPt = 0.5 * (pt + contour[(i+1) % nPoints])
274
endPt = contour[(i+1) % nPoints]
277
currentpoint = qcurveto(currentpoint,
278
pt, endPt, done_moveto)
281
ATM.fillClosePathATM()
285
for contour, flags in contours:
286
Qd.RGBForeColor((0, 0xffff, 0))
287
for i in range(len(contour)):
289
onCurve = flags[i] & 0x1
291
Qd.PaintRect(Qd.InsetRect((x, y, x, y), -2, -2))
293
Qd.PaintOval(Qd.InsetRect((x, y, x, y), -2, -2))
294
Qd.RGBForeColor((0xffff, 0, 0))
295
Qd.RGBForeColor((0, 0, 0))
296
Qd.FrameRect(self._bounds)
299
Qd.DisposeRgn(cliprgn)
300
Qd.DisposeRgn(savergn)
303
extensions = [".suit", ".xml", ".ttx", ".TTF", ".ttf"]
305
def putfile(prompt, filename, newextension):
306
for ext in extensions:
307
if filename[-len(ext):] == ext:
308
filename = filename[:-len(ext)] + newextension
311
filename = filename + newextension
312
fss, ok = macfs.StandardPutFile(prompt, filename)
314
return fss.as_pathname()
317
def lineto(pt, done_moveto):
320
ATM.fillLineToATM((x, y))
322
ATM.fillMoveToATM((x, y))
325
def qcurveto(pt0, pt1, pt2, done_moveto):
328
ATM.fillMoveToATM((x0, y0))
329
x1a, y1a = pt0 + 0.6666666666667 * (pt1 - pt0)
330
x1b, y1b = pt2 + 0.6666666666667 * (pt1 - pt2)
332
ATM.fillCurveToATM((x1a, y1a), (x1b, y1b), (x2, y2))
337
fss, ok = macfs.StandardGetFile()
340
path = fss.as_pathname()
341
indices = macUtils.getSFNTResIndices(path)
344
TableBrowser(path, res_index=i)
349
if __name__ == "__main__":