diff options
author | Christian Robertson <robertsonc@google.com> | 2014-05-19 16:09:20 -0700 |
---|---|---|
committer | Christian Robertson <robertsonc@google.com> | 2014-05-19 16:09:20 -0700 |
commit | 7051939fa74b5f8f453be605f5dc3b4c23e1d1d9 (patch) | |
tree | 65cb31b34a64b2b07f9f6fd1bcb58d9a4945a92d /scripts/lib/fontbuild/mix.py |
Importing Roboto 2.0
Diffstat (limited to 'scripts/lib/fontbuild/mix.py')
-rwxr-xr-x | scripts/lib/fontbuild/mix.py | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/scripts/lib/fontbuild/mix.py b/scripts/lib/fontbuild/mix.py new file mode 100755 index 0000000..86bf4ed --- /dev/null +++ b/scripts/lib/fontbuild/mix.py @@ -0,0 +1,334 @@ +from FL import * +from numpy import array, append +import copy + +class FFont: + "Font wrapper for floating point operations" + + def __init__(self,f=None): + self.glyphs = {} + self.hstems = [] + self.vstems = [] + if isinstance(f,FFont): + #self.glyphs = [g.copy() for g in f.glyphs] + for key,g in f.glyphs.iteritems(): + self.glyphs[key] = g.copy() + self.hstems = list(f.hstems) + self.vstems = list(f.vstems) + elif f != None: + self.copyFromFont(f) + + def copyFromFont(self,f): + for g in f.glyphs: + self.glyphs[g.name] = FGlyph(g) + self.hstems = [s for s in f.stem_snap_h[0]] + self.vstems = [s for s in f.stem_snap_v[0]] + + + def copyToFont(self,f): + for g in f.glyphs: + try: + gF = self.glyphs[g.name] + gF.copyToGlyph(g) + except: + print "Copy to glyph failed for" + g.name + f.stem_snap_h[0] = self.hstems + f.stem_snap_v[0] = self.vstems + + def getGlyph(self, gname): + try: + return self.glyphs[gname] + except: + return None + + def setGlyph(self, gname, glyph): + self.glyphs[gname] = glyph + + def addDiff(self,b,c): + newFont = FFont(self) + for key,g in newFont.glyphs.iteritems(): + gB = b.getGlyph(key) + gC = c.getGlyph(key) + try: + newFont.glyphs[key] = g.addDiff(gB,gC) + except: + print "Add diff failed for '%s'" %key + return newFont + +class FGlyph: + "provides a temporary floating point compatible glyph data structure" + + def __init__(self, g=None): + self.nodes = [] + self.width = 0. + self.components = [] + self.kerning = [] + self.anchors = [] + if g != None: + self.copyFromGlyph(g) + + def copyFromGlyph(self,g): + self.name = g.name + valuesX = [] + valuesY = [] + self.width = len(valuesX) + valuesX.append(g.width) + for c in g.components: + self.components.append((len(valuesX),len(valuesY))) + valuesX.append(c.scale.x) + valuesY.append(c.scale.y) + valuesX.append(c.delta.x) + valuesY.append(c.delta.y) + + for a in g.anchors: + self.anchors.append((len(valuesX), len(valuesY))) + valuesX.append(a.x) + valuesY.append(a.y) + + for i in range(len(g.nodes)): + self.nodes.append([]) + for j in range (len(g.nodes[i])): + self.nodes[i].append( (len(valuesX), len(valuesY)) ) + valuesX.append(g.nodes[i][j].x) + valuesY.append(g.nodes[i][j].y) + + for k in g.kerning: + self.kerning.append(KerningPair(k)) + + self.dataX = array(valuesX) + self.dataY = array(valuesY) + + def copyToGlyph(self,g): + g.width = self._derefX(self.width) + if len(g.components) == len(self.components): + for i in range(len(self.components)): + g.components[i].scale.x = self._derefX( self.components[i][0] + 0) + g.components[i].scale.y = self._derefY( self.components[i][1] + 0) + g.components[i].deltas[0].x = self._derefX( self.components[i][0] + 1) + g.components[i].deltas[0].y = self._derefY( self.components[i][1] + 1) + g.kerning = [] + if len(g.anchors) == len(self.anchors): + for i in range(len(self.anchors)): + g.anchors[i].x = self._derefX( self.anchors[i][0]) + g.anchors[i].y = self._derefY( self.anchors[i][1]) + for k in self.kerning: + g.kerning.append(KerningPair(k)) + for i in range( len(g.nodes)) : + for j in range (len(g.nodes[i])): + g.nodes[i][j].x = self._derefX( self.nodes[i][j][0] ) + g.nodes[i][j].y = self._derefY( self.nodes[i][j][1] ) + + def isCompatible(self,g): + return len(self.dataX) == len(g.dataX) and len(self.dataY) == len(g.dataY) and len(g.nodes) == len(self.nodes) + + def __add__(self,g): + if self.isCompatible(g): + newGlyph = self.copy() + newGlyph.dataX = self.dataX + g.dataX + newGlyph.dataY = self.dataY + g.dataY + return newGlyph + else: + print "Add failed for '%s'" %(self.name) + raise Exception + + def __sub__(self,g): + if self.isCompatible(g): + newGlyph = self.copy() + newGlyph.dataX = self.dataX - g.dataX + newGlyph.dataY = self.dataY - g.dataY + return newGlyph + else: + print "Subtract failed for '%s'" %(self.name) + raise Exception + + def __mul__(self,scalar): + newGlyph = self.copy() + newGlyph.dataX = self.dataX * scalar + newGlyph.dataY = self.dataY * scalar + return newGlyph + + def scaleX(self,scalar): + newGlyph = self.copy() + if len(self.dataX) > 0: + newGlyph.dataX = self.dataX * scalar + for i in range(len(newGlyph.components)): + newGlyph.dataX[newGlyph.components[i][0]] = self.dataX[newGlyph.components[i][0]] + return newGlyph + + def shift(self,ammount): + newGlyph = self.copy() + newGlyph.dataX = self.dataX + ammount + for i in range(len(newGlyph.components)): + newGlyph.dataX[newGlyph.components[i][0]] = self.dataX[newGlyph.components[i][0]] + return newGlyph + + def interp(self, g, v): + gF = self.copy() + if not self.isCompatible(g): + print "Interpolate failed for '%s'; outlines incompatible" %(self.name) + raise Exception + + gF.dataX += (g.dataX - gF.dataX) * v.x + gF.dataY += (g.dataY - gF.dataY) * v.y + gF.kerning = interpolateKerns(self,g,v) + return gF + + def copy(self): + ng = FGlyph() + ng.nodes = list(self.nodes) + ng.width = self.width + ng.components = list(self.components) + ng.kerning = list(self.kerning) + ng.anchors = list(self.anchors) + ng.dataX = self.dataX.copy() + ng.dataY = self.dataY.copy() + ng.name = self.name + return ng + + def _derefX(self,id): + return int(round(self.dataX[id])) + + def _derefY(self,id): + return int(round(self.dataY[id])) + + def addDiff(self,gB,gC): + newGlyph = self + (gB - gC) + return newGlyph + + + +class Master: + + + def __init__(self,font=None,v=0,ifont=None, kernlist=None, overlay=None): + if isinstance(font,FFont): + self.font = None + self.ffont = font + elif isinstance(font,str): + self.openFont(font,overlay) + elif isinstance(font,Mix): + self.font = font + else: + self.font = font + self.ifont = ifont + self.ffont = FFont(font) + if isinstance(v,float) or isinstance(v,int): + self.v = Point(v,v) + else: + self.v = v + if kernlist != None: + kerns = [i.strip().split() for i in open(kernlist).readlines()] + + self.kernlist = [{'left':k[0], 'right':k[1], 'value': k[2]} + for k in kerns + if not k[0].startswith("#") + and not k[0] == ""] + #TODO implement class based kerning / external kerning file + + def openFont(self, path, overlayPath=None): + fl.Open(path,True) + self.ifont = fl.ifont + self.font = fl.font + if overlayPath != None: + fl.Open(overlayPath,True) + ifont = self.ifont + font = self.font + overlayIfont = fl.ifont + overlayFont = fl.font + + for overlayGlyph in overlayFont.glyphs: + glyphIndex = font.FindGlyph(overlayGlyph.name) + if glyphIndex != -1: + oldGlyph = Glyph(font.glyphs[glyphIndex]) + kernlist = [KerningPair(k) for k in oldGlyph.kerning] + font.glyphs[glyphIndex] = Glyph(overlayGlyph) + font.glyphs[glyphIndex].kerning = kernlist + else: + font.glyphs.append(overlayGlyph) + fl.UpdateFont(ifont) + fl.Close(overlayIfont) + self.ffont = FFont(self.font) + + +class Mix: + def __init__(self,masters,v): + self.masters = masters + if isinstance(v,float) or isinstance(v,int): + self.v = Point(v,v) + else: + self.v = v + + def getFGlyph(self, master, gname): + if isinstance(master.font, Mix): + return font.mixGlyphs(gname) + return master.ffont.getGlyph(gname) + + def getGlyphMasters(self,gname): + masters = self.masters + if len(masters) <= 2: + return self.getFGlyph(masters[0], gname), self.getFGlyph(masters[-1], gname) + + def generateFFont(self): + ffont = FFont(self.masters[0].ffont) + for key,g in ffont.glyphs.iteritems(): + ffont.glyphs[key] = self.mixGlyphs(key) + return ffont + + def generateFont(self, baseFont): + newFont = Font(baseFont) + #self.mixStems(newFont) todo _ fix stems code + for g in newFont.glyphs: + gF = self.mixGlyphs(g.name) + if gF == None: + g.mark = True + else: + # FIX THIS #print gF.name, g.name, len(gF.nodes),len(g.nodes),len(gF.components),len(g.components) + try: + gF.copyToGlyph(g) + except: + "Nodes incompatible" + return newFont + + def mixGlyphs(self,gname): + gA,gB = self.getGlyphMasters(gname) + try: + return gA.interp(gB,self.v) + except: + print "mixglyph failed for %s" %(gname) + if gA != None: + return gA.copy() + +def narrowFLGlyph(g, gThin, factor=.75): + gF = FGlyph(g) + if not isinstance(gThin,FGlyph): + gThin = FGlyph(gThin) + gCondensed = gThin.scaleX(factor) + try: + gNarrow = gF + (gCondensed - gThin) + gNarrow.copyToGlyph(g) + except: + print "No dice for: " + g.name + +def interpolate(a,b,v,e=0): + if e == 0: + return a+(b-a)*v + qe = (b-a)*v*v*v + a #cubic easing + le = a+(b-a)*v # linear easing + return le + (qe-le) * e + +def interpolateKerns(gA,gB,v): + kerns = [] + for kA in gA.kerning: + key = kA.key + matchedKern = None + for kB in gA.kerning: + if key == kB.key: + matchedKern = kB + break + # if matchedkern == None: + # matchedkern = Kern(kA) + # matchedkern.value = 0 + if matchedKern != None: + kernValue = interpolate(kA.value, matchedKern.value, v.x) + kerns.append(KerningPair(kA.key,kernValue)) + return kerns
\ No newline at end of file |