352 lines
9.7 KiB
Python
352 lines
9.7 KiB
Python
"""Mac-OS9-only TrueType browser window, deprecated and no longer maintained."""
|
|
|
|
from fontTools import ttLib
|
|
from fontTools.ttLib import macUtils
|
|
import macfs
|
|
import PyBrowser
|
|
import W, Lists
|
|
import os
|
|
import ATM
|
|
import numpy
|
|
import Qd
|
|
from rf.views.wGlyphList import GlyphList
|
|
|
|
|
|
class TableBrowser:
|
|
|
|
def __init__(self, path=None, ttFont=None, res_index=None):
|
|
W.SetCursor('watch')
|
|
if path is None:
|
|
self.ttFont = ttFont
|
|
self.filename = "????"
|
|
else:
|
|
self.ttFont = ttLib.TTFont(path, res_index)
|
|
if res_index is None:
|
|
self.filename = os.path.basename(path)
|
|
else:
|
|
self.filename = os.path.basename(path) + " - " + str(res_index)
|
|
self.currentglyph = None
|
|
self.glyphs = {}
|
|
self.buildinterface()
|
|
|
|
def buildinterface(self):
|
|
buttonwidth = 120
|
|
glyphlistwidth = 150
|
|
hmargin = 10
|
|
vmargin = 8
|
|
title = self.filename
|
|
tables = self.ttFont.keys()
|
|
tables.sort()
|
|
self.w = w = W.Window((500, 300), title, minsize = (400, 200))
|
|
w.browsetablebutton = W.Button((hmargin, 32, buttonwidth, 16), "Browse table",
|
|
self.browsetable)
|
|
w.browsefontbutton = W.Button((hmargin, vmargin, buttonwidth, 16), "Browse font",
|
|
self.browsefont)
|
|
w.tablelist = W.List((hmargin, 56, buttonwidth, -128), tables, self.tablelisthit)
|
|
|
|
w.divline1 = W.VerticalLine((buttonwidth + 2 * hmargin, vmargin, 1, -vmargin))
|
|
|
|
gleft = buttonwidth + 3 * hmargin + 1
|
|
|
|
hasGlyfTable = self.ttFont.has_key('glyf')
|
|
|
|
glyphnames = self.ttFont.getGlyphNames2() # caselessly sorted glyph names
|
|
|
|
if hasGlyfTable:
|
|
w.glyphlist = GlyphList((gleft, 56, glyphlistwidth, -vmargin),
|
|
glyphnames, self.glyphlisthit)
|
|
|
|
w.divline2 = W.VerticalLine((buttonwidth + glyphlistwidth + 4 * hmargin + 2,
|
|
vmargin, 1, -vmargin))
|
|
|
|
yMin = self.ttFont['head'].yMin
|
|
yMax = self.ttFont['head'].yMax
|
|
w.gviewer = GlyphViewer((buttonwidth + glyphlistwidth + 5 * hmargin + 3,
|
|
vmargin, -hmargin, -vmargin), yMin, yMax)
|
|
|
|
w.showpoints = W.CheckBox((gleft, vmargin, glyphlistwidth, 16), "Show points",
|
|
self.w.gviewer.toggleshowpoints)
|
|
w.showpoints.set(self.w.gviewer.showpoints)
|
|
w.showlines = W.CheckBox((gleft, vmargin + 24, glyphlistwidth, 16), "Show lines",
|
|
self.w.gviewer.toggleshowlines)
|
|
w.showlines.set(self.w.gviewer.showlines)
|
|
else:
|
|
w.glyphlist = GlyphList((gleft, 56, glyphlistwidth, -vmargin),
|
|
glyphnames)
|
|
w.noGlyphTable = W.TextBox((gleft, vmargin, -20, 20), "no 'glyf' table found")
|
|
|
|
|
|
w.setdefaultbutton(w.browsetablebutton)
|
|
|
|
w.tocurrentfont = W.Button((hmargin, -120, buttonwidth, 16), "Copy to current font", self.copytocurrentfont)
|
|
w.fromcurrentfont = W.Button((hmargin, -96, buttonwidth, 16), "Copy from current font", self.copyfromcurrentfont)
|
|
w.saveflat = W.Button((hmargin, -72, buttonwidth, 16), "Save as flat file", self.saveflat)
|
|
w.savesuitcasebutton = W.Button((hmargin, -48, buttonwidth, 16), "Save as suitcase", self.savesuitcase)
|
|
w.savexmlbutton = W.Button((hmargin, -24, buttonwidth, 16), "Save as XML", self.saveXML)
|
|
|
|
w.open()
|
|
w.browsetablebutton.enable(0)
|
|
|
|
def browsetable(self):
|
|
self.tablelisthit(1)
|
|
|
|
def browsefont(self):
|
|
PyBrowser.Browser(self.ttFont)
|
|
|
|
def copytocurrentfont(self):
|
|
pass
|
|
|
|
def copyfromcurrentfont(self):
|
|
pass
|
|
|
|
def saveflat(self):
|
|
path = putfile("Save font as flat file:", self.filename, ".TTF")
|
|
if path:
|
|
W.SetCursor('watch')
|
|
self.ttFont.save(path)
|
|
|
|
def savesuitcase(self):
|
|
path = putfile("Save font as suitcase:", self.filename, ".suit")
|
|
if path:
|
|
W.SetCursor('watch')
|
|
self.ttFont.save(path, 1)
|
|
|
|
def saveXML(self):
|
|
path = putfile("Save font as XML text file:", self.filename, ".ttx")
|
|
if path:
|
|
W.SetCursor('watch')
|
|
pb = macUtils.ProgressBar("Saving %s as XML" % self.filename)
|
|
try:
|
|
self.ttFont.saveXML(path, pb)
|
|
finally:
|
|
pb.close()
|
|
|
|
def glyphlisthit(self, isDbl):
|
|
sel = self.w.glyphlist.getselectedobjects()
|
|
if not sel or sel[0] == self.currentglyph:
|
|
return
|
|
self.currentglyph = sel[0]
|
|
if self.glyphs.has_key(self.currentglyph):
|
|
g = self.glyphs[self.currentglyph]
|
|
else:
|
|
g = Glyph(self.ttFont, self.currentglyph)
|
|
self.glyphs[self.currentglyph] = g
|
|
self.w.gviewer.setglyph(g)
|
|
|
|
def tablelisthit(self, isdbl):
|
|
if isdbl:
|
|
for tag in self.w.tablelist.getselectedobjects():
|
|
table = self.ttFont[tag]
|
|
if tag == 'glyf':
|
|
W.SetCursor('watch')
|
|
for glyphname in self.ttFont.getGlyphOrder():
|
|
try:
|
|
glyph = table[glyphname]
|
|
except KeyError:
|
|
pass # incomplete font, oh well.
|
|
PyBrowser.Browser(table)
|
|
else:
|
|
sel = self.w.tablelist.getselection()
|
|
if sel:
|
|
self.w.browsetablebutton.enable(1)
|
|
else:
|
|
self.w.browsetablebutton.enable(0)
|
|
|
|
|
|
class Glyph:
|
|
|
|
def __init__(self, ttFont, glyphName):
|
|
ttglyph = ttFont['glyf'][glyphName]
|
|
self.iscomposite = ttglyph.numberOfContours == -1
|
|
self.width, self.lsb = ttFont['hmtx'][glyphName]
|
|
if ttglyph.numberOfContours == 0:
|
|
self.xMin = 0
|
|
self.contours = []
|
|
return
|
|
self.xMin = ttglyph.xMin
|
|
coordinates, endPts, flags = ttglyph.getCoordinates(ttFont['glyf'])
|
|
self.contours = []
|
|
self.flags = []
|
|
startpt = 0
|
|
for endpt in endPts:
|
|
self.contours.append(numpy.array(coordinates[startpt:endpt+1]))
|
|
self.flags.append(flags[startpt:endpt+1])
|
|
startpt = endpt + 1
|
|
|
|
def getcontours(self, scale, move):
|
|
contours = []
|
|
for i in range(len(self.contours)):
|
|
contours.append(((self.contours[i] * numpy.array(scale) + move), self.flags[i]))
|
|
return contours
|
|
|
|
|
|
class GlyphViewer(W.Widget):
|
|
|
|
def __init__(self, possize, yMin, yMax):
|
|
W.Widget.__init__(self, possize)
|
|
self.glyph = None
|
|
extra = 0.02 * (yMax-yMin)
|
|
self.yMin, self.yMax = yMin - extra, yMax + extra
|
|
self.showpoints = 1
|
|
self.showlines = 1
|
|
|
|
def toggleshowpoints(self, onoff):
|
|
self.showpoints = onoff
|
|
self.SetPort()
|
|
self.draw()
|
|
|
|
def toggleshowlines(self, onoff):
|
|
self.showlines = onoff
|
|
self.SetPort()
|
|
self.draw()
|
|
|
|
def setglyph(self, glyph):
|
|
self.glyph = glyph
|
|
self.SetPort()
|
|
self.draw()
|
|
|
|
def draw(self, visRgn=None):
|
|
# This a HELL of a routine, but it's pretty damn fast...
|
|
import Qd
|
|
if not self._visible:
|
|
return
|
|
Qd.EraseRect(Qd.InsetRect(self._bounds, 1, 1))
|
|
cliprgn = Qd.NewRgn()
|
|
savergn = Qd.NewRgn()
|
|
Qd.RectRgn(cliprgn, self._bounds)
|
|
Qd.GetClip(savergn)
|
|
Qd.SetClip(cliprgn)
|
|
try:
|
|
if self.glyph:
|
|
l, t, r, b = Qd.InsetRect(self._bounds, 1, 1)
|
|
height = b - t
|
|
scale = float(height) / (self.yMax - self.yMin)
|
|
topoffset = t + scale * self.yMax
|
|
width = scale * self.glyph.width
|
|
lsb = scale * self.glyph.lsb
|
|
xMin = scale * self.glyph.xMin
|
|
# XXXX this is not correct when USE_MY_METRICS is set in component!
|
|
leftoffset = l + 0.5 * (r - l - width)
|
|
gleftoffset = leftoffset - xMin + lsb
|
|
if self.showlines:
|
|
Qd.RGBForeColor((0xafff, 0xafff, 0xafff))
|
|
# left sidebearing
|
|
Qd.MoveTo(leftoffset, t)
|
|
Qd.LineTo(leftoffset, b - 1)
|
|
# right sidebearing
|
|
Qd.MoveTo(leftoffset + width, t)
|
|
Qd.LineTo(leftoffset + width, b - 1)
|
|
# baseline
|
|
Qd.MoveTo(l, topoffset)
|
|
Qd.LineTo(r - 1, topoffset)
|
|
|
|
# origin
|
|
Qd.RGBForeColor((0x5fff, 0, 0))
|
|
Qd.MoveTo(gleftoffset, topoffset - 16)
|
|
Qd.LineTo(gleftoffset, topoffset + 16)
|
|
# reset color
|
|
Qd.RGBForeColor((0, 0, 0))
|
|
|
|
if self.glyph.iscomposite:
|
|
Qd.RGBForeColor((0x7fff, 0x7fff, 0x7fff))
|
|
|
|
ATM.startFillATM()
|
|
contours = self.glyph.getcontours((scale, -scale), (gleftoffset, topoffset))
|
|
for contour, flags in contours:
|
|
currentpoint = None
|
|
done_moveto = 0
|
|
i = 0
|
|
nPoints = len(contour)
|
|
while i < nPoints:
|
|
pt = contour[i]
|
|
if flags[i]:
|
|
# onCurve
|
|
currentpoint = lineto(pt, done_moveto)
|
|
else:
|
|
if not currentpoint:
|
|
if not flags[i-1]:
|
|
currentpoint = 0.5 * (contour[i-1] + pt)
|
|
else:
|
|
currentpoint = contour[i-1]
|
|
if not flags[(i+1) % nPoints]:
|
|
endPt = 0.5 * (pt + contour[(i+1) % nPoints])
|
|
else:
|
|
endPt = contour[(i+1) % nPoints]
|
|
i = i + 1
|
|
# offCurve
|
|
currentpoint = qcurveto(currentpoint,
|
|
pt, endPt, done_moveto)
|
|
done_moveto = 1
|
|
i = i + 1
|
|
ATM.fillClosePathATM()
|
|
ATM.endFillATM()
|
|
# draw point markers
|
|
if self.showpoints:
|
|
for contour, flags in contours:
|
|
Qd.RGBForeColor((0, 0xffff, 0))
|
|
for i in range(len(contour)):
|
|
(x, y) = contour[i]
|
|
onCurve = flags[i] & 0x1
|
|
if onCurve:
|
|
Qd.PaintRect(Qd.InsetRect((x, y, x, y), -2, -2))
|
|
else:
|
|
Qd.PaintOval(Qd.InsetRect((x, y, x, y), -2, -2))
|
|
Qd.RGBForeColor((0xffff, 0, 0))
|
|
Qd.RGBForeColor((0, 0, 0))
|
|
Qd.FrameRect(self._bounds)
|
|
finally:
|
|
Qd.SetClip(savergn)
|
|
Qd.DisposeRgn(cliprgn)
|
|
Qd.DisposeRgn(savergn)
|
|
|
|
|
|
extensions = [".suit", ".xml", ".ttx", ".TTF", ".ttf"]
|
|
|
|
def putfile(prompt, filename, newextension):
|
|
for ext in extensions:
|
|
if filename[-len(ext):] == ext:
|
|
filename = filename[:-len(ext)] + newextension
|
|
break
|
|
else:
|
|
filename = filename + newextension
|
|
fss, ok = macfs.StandardPutFile(prompt, filename)
|
|
if ok:
|
|
return fss.as_pathname()
|
|
|
|
|
|
def lineto(pt, done_moveto):
|
|
x, y = pt
|
|
if done_moveto:
|
|
ATM.fillLineToATM((x, y))
|
|
else:
|
|
ATM.fillMoveToATM((x, y))
|
|
return pt
|
|
|
|
def qcurveto(pt0, pt1, pt2, done_moveto):
|
|
if not done_moveto:
|
|
x0, y0 = pt0
|
|
ATM.fillMoveToATM((x0, y0))
|
|
x1a, y1a = pt0 + 0.6666666666667 * (pt1 - pt0)
|
|
x1b, y1b = pt2 + 0.6666666666667 * (pt1 - pt2)
|
|
x2, y2 = pt2
|
|
ATM.fillCurveToATM((x1a, y1a), (x1b, y1b), (x2, y2))
|
|
return pt2
|
|
|
|
|
|
def browseTTFont():
|
|
fss, ok = macfs.StandardGetFile()
|
|
if not ok:
|
|
return
|
|
path = fss.as_pathname()
|
|
indices = macUtils.getSFNTResIndices(path)
|
|
if indices:
|
|
for i in indices:
|
|
TableBrowser(path, res_index=i)
|
|
else:
|
|
TableBrowser(path)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
browseTTFont()
|
|
|