"""Mac-only TrueType browser window.""" from fontTools import ttLib from fontTools.ttLib import macUtils import macfs import PyBrowser import W, Lists import os import ATM import Numeric 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(Numeric.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] * Numeric.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()