From 86cdcddb8b5e95e0b918f8d1e2e9c6d867a8a489 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Tue, 3 Nov 2015 13:46:47 -0800 Subject: Correct dates in sources These dates seem to be incorrectly exported by vfb2ufo. The new dates are taken from the currently available webfont binaries. --- src/v2/README.md | 6 +++++- src/v2/Roboto_Bold.ufo/fontinfo.plist | 2 +- src/v2/Roboto_Regular.ufo/fontinfo.plist | 2 +- src/v2/Roboto_Thin.ufo/fontinfo.plist | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/v2/README.md b/src/v2/README.md index b2362d6..892d3b3 100644 --- a/src/v2/README.md +++ b/src/v2/README.md @@ -18,8 +18,12 @@ wine [path-to-vfb2ufo]/exe/vfb2ufo.exe [roboto]/src/v2/Roboto_Bold.vfb The converter should work both ways, so it is possible to convert altered UFOs back into VFBs which can be opened in FontLab. -**Note:** There is currently an issue when converting via vfb2ufo, in which +### Notes +There is currently an issue when converting via vfb2ufo, in which some anchors are dropped from glyphs. For now it is also necessary to run the script `get_dropped_anchors.py` through FontLab after updating the VFBs, in order to extract these dropped anchors into an external resource which is then re-incorporated into the masters during the build. + +vfb2ufo may output UFOs with bogus timestamps. Verify the openTypeNameCreated +field in the output `fontinfo.plist` files. diff --git a/src/v2/Roboto_Bold.ufo/fontinfo.plist b/src/v2/Roboto_Bold.ufo/fontinfo.plist index 8e4af9c..cad5eeb 100644 --- a/src/v2/Roboto_Bold.ufo/fontinfo.plist +++ b/src/v2/Roboto_Bold.ufo/fontinfo.plist @@ -15,7 +15,7 @@ italicAngle 0 openTypeHeadCreated - 2074/09/13 05:29:33 + 2008/09/12 12:29:34 openTypeHeadFlags diff --git a/src/v2/Roboto_Regular.ufo/fontinfo.plist b/src/v2/Roboto_Regular.ufo/fontinfo.plist index 82b4505..58edbfa 100644 --- a/src/v2/Roboto_Regular.ufo/fontinfo.plist +++ b/src/v2/Roboto_Regular.ufo/fontinfo.plist @@ -15,7 +15,7 @@ italicAngle 0 openTypeHeadCreated - 2074/09/13 05:29:33 + 2008/09/12 12:29:34 openTypeHeadFlags diff --git a/src/v2/Roboto_Thin.ufo/fontinfo.plist b/src/v2/Roboto_Thin.ufo/fontinfo.plist index c779e7e..1aac006 100644 --- a/src/v2/Roboto_Thin.ufo/fontinfo.plist +++ b/src/v2/Roboto_Thin.ufo/fontinfo.plist @@ -15,7 +15,7 @@ italicAngle 0 openTypeHeadCreated - 2074/09/13 05:29:33 + 2008/09/12 12:29:34 openTypeHeadFlags -- cgit v1.2.3 From 0d5d786360b64d4e789b4311fe54f1180a6c302a Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 13 Nov 2015 13:37:56 -0800 Subject: [italics] Fix whitespace --- scripts/lib/fontbuild/alignpoints.py | 27 +++++++---- scripts/lib/fontbuild/curveFitPen.py | 91 ++++++++++++++++++------------------ scripts/lib/fontbuild/italics.py | 30 ++++++++---- 3 files changed, 83 insertions(+), 65 deletions(-) diff --git a/scripts/lib/fontbuild/alignpoints.py b/scripts/lib/fontbuild/alignpoints.py index 1133716..76581a5 100644 --- a/scripts/lib/fontbuild/alignpoints.py +++ b/scripts/lib/fontbuild/alignpoints.py @@ -28,7 +28,7 @@ def alignCorners(glyph, va, subsegments): # if seg.type == "line": # subIndex = subsegmentIndex(i,j,subsegments) # out[subIndex] = alignPoints(va[subIndex]) - + for i,c in enumerate(subsegments): segmentCount = len(glyph.contours[i].segments) n = len(c) @@ -66,7 +66,7 @@ def alignCorners(glyph, va, subsegments): def subsegmentIndex(contourIndex, segmentIndex, subsegments): # This whole thing is so dumb. Need a better data model for subsegments - + contourOffset = 0 for i,c in enumerate(subsegments): if i == contourIndex: @@ -77,10 +77,11 @@ def subsegmentIndex(contourIndex, segmentIndex, subsegments): startIndex = subsegments[contourIndex][segmentIndex-1][0] segmentCount = subsegments[contourIndex][segmentIndex][1] endIndex = (startIndex + segmentCount + 1) % (n) - + indices = np.array([(startIndex + i) % (n) + contourOffset for i in range(segmentCount + 1)]) return indices + def alignPoints(pts, start=None, end=None): if start == None or end == None: start, end = fitLine(pts) @@ -89,6 +90,7 @@ def alignPoints(pts, start=None, end=None): out[i] = nearestPoint(start, end, p) return out + def findCorner(pp, nn): if len(pp) < 4 or len(nn) < 4: assert 0, "line too short to fit" @@ -96,34 +98,36 @@ def findCorner(pp, nn): nStart,nEnd = fitLine(nn) prev = pEnd - pStart next = nEnd - nStart - # print int(np.arctan2(prev[1],prev[0]) / math.pi * 180), + # print int(np.arctan2(prev[1],prev[0]) / math.pi * 180), # print int(np.arctan2(next[1],next[0]) / math.pi * 180) # if lines are parallel, return simple average of end and start points - if np.dot(prev / np.linalg.norm(prev), + if np.dot(prev / np.linalg.norm(prev), next / np.linalg.norm(next)) > .999999: # print "parallel lines", np.arctan2(prev[1],prev[0]), np.arctan2(next[1],next[0]) # print prev, next assert 0, "parallel lines" return lineIntersect(pStart, pEnd, nStart, nEnd) + def lineIntersect((x1,y1),(x2,y2),(x3,y3),(x4,y4)): x12 = x1 - x2 x34 = x3 - x4 y12 = y1 - y2 y34 = y3 - y4 - + det = x12 * y34 - y12 * x34 if det == 0: print "parallel!" - + a = x1 * y2 - y1 * x2 b = x3 * y4 - y3 * x4 - + x = (a * x34 - b * x12) / det y = (a * y34 - b * y12) / det - + return (x,y) + def fitLineLSQ(pts): "returns a line fit with least squares. Fails for vertical lines" n = len(pts) @@ -133,6 +137,7 @@ def fitLineLSQ(pts): line = lstsq(a,pts[:,1])[0] return line + def fitLine(pts): """returns a start vector and direction vector Assumes points segments that already form a somewhat smooth line @@ -147,7 +152,8 @@ def fitLine(pts): direction = np.mean(a[1:-1], axis=0) start = np.mean(pts[1:-1], axis=0) return start, start+direction - + + def nearestPoint(a,b,c): "nearest point to point c on line a_b" magnitude = np.linalg.norm(b-a) @@ -155,6 +161,7 @@ def nearestPoint(a,b,c): raise Exception, "Line segment cannot be 0 length" return (b-a) * np.dot((c-a) / magnitude, (b-a) / magnitude) + a + # pts = np.array([[1,1],[2,2],[3,3],[4,4]]) # pts2 = np.array([[1,0],[2,0],[3,0],[4,0]]) # print alignPoints(pts2, start = pts[0], end = pts[0]+pts[0]) diff --git a/scripts/lib/fontbuild/curveFitPen.py b/scripts/lib/fontbuild/curveFitPen.py index 7c232c0..6ef42da 100644 --- a/scripts/lib/fontbuild/curveFitPen.py +++ b/scripts/lib/fontbuild/curveFitPen.py @@ -17,6 +17,7 @@ __all__ = ["SubsegmentPen","SubsegmentsToCurvesPen", "segmentGlyph", "fitGlyph"] + from fontTools.pens.basePen import BasePen from fontTools.misc import bezierTools from robofab.pens.pointPen import AbstractPointPen @@ -27,15 +28,17 @@ from numpy import array as v from random import random from robofab.pens.pointPen import BasePointToSegmentPen + + class SubsegmentsToCurvesPointPen(BasePointToSegmentPen): def __init__(self, glyph, subsegmentGlyph, subsegments): BasePointToSegmentPen.__init__(self) self.glyph = glyph self.subPen = SubsegmentsToCurvesPen(None, glyph.getPen(), subsegmentGlyph, subsegments) - + def setMatchTangents(self, b): self.subPen.matchTangents = b - + def _flushContour(self, segments): # # adapted from robofab.pens.adapterPens.rfUFOPointPen @@ -56,13 +59,13 @@ class SubsegmentsToCurvesPointPen(BasePointToSegmentPen): self.subPen.setLastSmooth(True) if segmentType == 'line': del segments[-1] - + self.subPen.moveTo(movePt) - + # do the rest of the segments for segmentType, points in segments: isSmooth = True in [smooth for pt, smooth, name, kwargs in points] - pp = [pt for pt, smooth, name, kwargs in points] + pp = [pt for pt, smooth, name, kwargs in points] if segmentType == "line": assert len(pp) == 1 if isSmooth: @@ -73,17 +76,18 @@ class SubsegmentsToCurvesPointPen(BasePointToSegmentPen): assert len(pp) == 3 if isSmooth: self.subPen.smoothCurveTo(*pp) - else: + else: self.subPen.curveTo(*pp) elif segmentType == "qcurve": assert 0, "qcurve not supported" else: assert 0, "illegal segmentType: %s" % segmentType self.subPen.closePath() - + def addComponent(self, glyphName, transform): self.subPen.addComponent(glyphName, transform) + class SubsegmentsToCurvesPen(BasePen): def __init__(self, glyphSet, otherPen, subsegmentGlyph, subsegments): BasePen.__init__(self, None) @@ -95,10 +99,10 @@ class SubsegmentsToCurvesPen(BasePen): self.lastPoint = (0,0) self.lastSmooth = False self.nextSmooth = False - + def setLastSmooth(self, b): self.lastSmooth = b - + def _moveTo(self, (x, y)): self.contourIndex += 1 self.segmentIndex = 0 @@ -106,7 +110,7 @@ class SubsegmentsToCurvesPen(BasePen): p = self.ssglyph.contours[self.contourIndex][0].points[0] self.otherPen.moveTo((p.x, p.y)) self.lastPoint = (x,y) - + def _lineTo(self, (x, y)): self.segmentIndex += 1 index = self.subsegments[self.contourIndex][self.segmentIndex][0] @@ -114,39 +118,39 @@ class SubsegmentsToCurvesPen(BasePen): self.otherPen.lineTo((p.x, p.y)) self.lastPoint = (x,y) self.lastSmooth = False - + def smoothLineTo(self, (x, y)): self.lineTo((x,y)) self.lastSmooth = True - + def smoothCurveTo(self, (x1, y1), (x2, y2), (x3, y3)): self.nextSmooth = True self.curveTo((x1, y1), (x2, y2), (x3, y3)) self.nextSmooth = False self.lastSmooth = True - - def _curveToOne(self, (x1, y1), (x2, y2), (x3, y3)): + + def _curveToOne(self, (x1, y1), (x2, y2), (x3, y3)): self.segmentIndex += 1 c = self.ssglyph.contours[self.contourIndex] n = len(c) startIndex = (self.subsegments[self.contourIndex][self.segmentIndex-1][0]) segmentCount = (self.subsegments[self.contourIndex][self.segmentIndex][1]) endIndex = (startIndex + segmentCount + 1) % (n) - + indices = [(startIndex + i) % (n) for i in range(segmentCount + 1)] points = np.array([(c[i].points[0].x, c[i].points[0].y) for i in indices]) prevPoint = (c[(startIndex - 1)].points[0].x, c[(startIndex - 1)].points[0].y) nextPoint = (c[(endIndex) % n].points[0].x, c[(endIndex) % n].points[0].y) prevTangent = prevPoint - points[0] nextTangent = nextPoint - points[-1] - + tangent1 = points[1] - points[0] tangent3 = points[-2] - points[-1] prevTangent /= np.linalg.norm(prevTangent) nextTangent /= np.linalg.norm(nextTangent) tangent1 /= np.linalg.norm(tangent1) tangent3 /= np.linalg.norm(tangent3) - + tangent1, junk = self.smoothTangents(tangent1, prevTangent, self.lastSmooth) tangent3, junk = self.smoothTangents(tangent3, nextTangent, self.nextSmooth) if self.matchTangents == True: @@ -162,7 +166,7 @@ class SubsegmentsToCurvesPen(BasePen): self.otherPen.curveTo((cp[1,0], cp[1,1]), (cp[2,0], cp[2,1]), (cp[3,0], cp[3,1])) self.lastPoint = (x3, y3) self.lastSmooth = False - + def smoothTangents(self,t1,t2,forceSmooth = False): if forceSmooth or (abs(t1.dot(t2)) > .95 and norm(t1-t2) > 1): # print t1,t2, @@ -170,15 +174,13 @@ class SubsegmentsToCurvesPen(BasePen): t2 = -t1 # print t1,t2 return t1 / norm(t1), t2 / norm(t2) - - + def _closePath(self): self.otherPen.closePath() - + def _endPath(self): self.otherPen.endPath() - - + def addComponent(self, glyphName, transformation): self.otherPen.addComponent(glyphName, transformation) @@ -189,10 +191,10 @@ class SubsegmentPointPen(BasePointToSegmentPen): self.glyph = glyph self.resolution = resolution self.subPen = SubsegmentPen(None, glyph.getPen()) - + def getSubsegments(self): return self.subPen.subsegments[:] - + def _flushContour(self, segments): # # adapted from robofab.pens.adapterPens.rfUFOPointPen @@ -210,9 +212,9 @@ class SubsegmentPointPen(BasePointToSegmentPen): movePt, smooth, name, kwargs = points[-1] if segmentType == 'line': del segments[-1] - + self.subPen.moveTo(movePt) - + # do the rest of the segments for segmentType, points in segments: points = [pt for pt, smooth, name, kwargs in points] @@ -227,12 +229,13 @@ class SubsegmentPointPen(BasePointToSegmentPen): else: assert 0, "illegal segmentType: %s" % segmentType self.subPen.closePath() - + def addComponent(self, glyphName, transform): self.subPen.addComponent(glyphName, transform) + class SubsegmentPen(BasePen): - + def __init__(self, glyphSet, otherPen, resolution=25): BasePen.__init__(self,glyphSet) self.resolution = resolution @@ -240,7 +243,7 @@ class SubsegmentPen(BasePen): self.subsegments = [] self.startContour = (0,0) self.contourIndex = -1 - + def _moveTo(self, (x, y)): self.contourIndex += 1 self.segmentIndex = 0 @@ -250,7 +253,7 @@ class SubsegmentPen(BasePen): self.startContour = (x,y) self.lastPoint = (x,y) self.otherPen.moveTo((x,y)) - + def _lineTo(self, (x, y)): count = self.stepsForSegment((x,y),self.lastPoint) if count < 1: @@ -262,7 +265,7 @@ class SubsegmentPen(BasePen): y1 = self.lastPoint[1] + (y - self.lastPoint[1]) * i/float(count) self.otherPen.lineTo((x1,y1)) self.lastPoint = (x,y) - + def _curveToOne(self, (x1, y1), (x2, y2), (x3, y3)): count = self.stepsForSegment((x3,y3),self.lastPoint) if count < 2: @@ -277,46 +280,45 @@ class SubsegmentPen(BasePen): for i in range(count): self.otherPen.lineTo((x[i],y[i])) self.lastPoint = (x3,y3) - + def _closePath(self): if not (self.lastPoint[0] == self.startContour[0] and self.lastPoint[1] == self.startContour[1]): self._lineTo(self.startContour) self.otherPen.closePath() - + def _endPath(self): self.otherPen.endPath() - + def addComponent(self, glyphName, transformation): self.otherPen.addComponent(glyphName, transformation) - + def stepsForSegment(self, p1, p2): dist = np.linalg.norm(v(p1) - v(p2)) out = int(dist / self.resolution) return out - + def renderCurve(self,p,count): curvePoints = [] t = 1.0 / float(count) temp = t * t - + f = p[0] fd = 3 * (p[1] - p[0]) * t fdd_per_2 = 3 * (p[0] - 2 * p[1] + p[2]) * temp fddd_per_2 = 3 * (3 * (p[1] - p[2]) + p[3] - p[0]) * temp * t - + fddd = fddd_per_2 + fddd_per_2 fdd = fdd_per_2 + fdd_per_2 fddd_per_6 = fddd_per_2 * (1.0 / 3) - + for i in range(count): f = f + fd + fdd_per_2 + fddd_per_6 fd = fd + fdd + fddd_per_2 fdd = fdd + fddd fdd_per_2 = fdd_per_2 + fddd_per_2 curvePoints.append(f) - - return curvePoints + return curvePoints def fitBezierSimple(pts): @@ -363,14 +365,14 @@ def fitBezier(pts,tangent0=None,tangent3=None): pout = pts.copy() pout[:,0] -= (T[:,0] * pts[0,0]) + (T[:,3] * pts[-1,0]) pout[:,1] -= (T[:,0] * pts[0,1]) + (T[:,3] * pts[-1,1]) - + TT = np.zeros((n*2,4)) for i in range(n): for j in range(2): TT[i*2,j*2] = T[i,j+1] TT[i*2+1,j*2+1] = T[i,j+1] pout = pout.reshape((n*2,1),order="C") - + if tangent0 != None and tangent3 != None: tangentConstraintsT = np.array([ [tangent0[1], -tangent0[0], 0, 0], @@ -414,4 +416,3 @@ if __name__ == '__main__': [1,1] ]) print np.array(p.renderCurve(pts,10)) * 10 - diff --git a/scripts/lib/fontbuild/italics.py b/scripts/lib/fontbuild/italics.py index c889bd5..b1db984 100644 --- a/scripts/lib/fontbuild/italics.py +++ b/scripts/lib/fontbuild/italics.py @@ -33,11 +33,11 @@ def italicizeGlyph(f, g, angle=10, stemWidth=185): m = Transform(1, 0, slope, 1, 0, 0) xoffset, junk = m.transformPoint((0, MEAN_YCENTER)) m = Transform(.97, 0, slope, 1, xoffset, 0) - + if len(glyph) > 0: g2 = italicize(f[g.name], angle, xoffset=xoffset, stemWidth=stemWidth) f.insertGlyph(g2, g.name) - + transformFLGlyphMembers(f[g.name], m) if unic > 0xFFFF: #restore unicode @@ -53,12 +53,12 @@ def italicize(glyph, angle=12, stemWidth=180, xoffset=-50): grad = mapEdges(lambda a,(p,n): normalize(p-a), va, e) cornerWeights = mapEdges(lambda a,(p,n): normalize(p-a).dot(normalize(a-n)), grad, e)[:,0].reshape((-1,1)) smooth = np.ones((n,1)) * CURVE_CORRECTION_WEIGHT - + controlPoints = findControlPointsInMesh(glyph, va, subsegments) smooth[controlPoints > 0] = 1 smooth[cornerWeights < .6] = CORNER_WEIGHT # smooth[cornerWeights >= .9999] = 1 - + out = va.copy() hascurves = False for c in glyph.contours: @@ -80,13 +80,13 @@ def italicize(glyph, angle=12, stemWidth=180, xoffset=-50): centerSkew = skewMesh(center.dot(np.array([[.97,0],[0,1]])), angle * .9) out = outCorrected + (centerSkew - center) out[:,1] = outCorrected[:,1] - + smooth = np.ones((n,1)) * .1 out = alignCorners(glyph, out, subsegments) out = copyMeshDetails(skewMesh(va, angle), out, e, 7, smooth=smooth) # grad = mapEdges(lambda a,(p,n): normalize(p-a), skewMesh(outCorrected, angle*.9), e) # out = recompose(out, grad, e, smooth=smooth) - + out = skewMesh(out, angle * .1) out[:,0] += xoffset # out[:,1] = outCorrected[:,1] @@ -132,6 +132,7 @@ def glyphToMesh(g): offset += len(c) return np.array(points), edges + def meshToGlyph(points, g): g1 = g.copy() j = 0 @@ -144,6 +145,7 @@ def meshToGlyph(points, g): j += 1 return g1 + def quantizeGradient(grad, book=None): if book == None: book = np.array([(1,0),(0,1),(0,-1),(-1,0)]) @@ -153,6 +155,7 @@ def quantizeGradient(grad, book=None): out[i] = normalize(v) return out + def findControlPointsInMesh(glyph, va, subsegments): controlPointIndices = np.zeros((len(va),1)) index = 0 @@ -166,7 +169,6 @@ def findControlPointsInMesh(glyph, va, subsegments): return controlPointIndices - def recompose(v, grad, e, smooth=1, P=None, distance=None): n = len(v) if distance == None: @@ -183,6 +185,7 @@ def recompose(v, grad, e, smooth=1, P=None, distance=None): out[:,i] = cg(P, f[:,i])[0] return out + def mP(v,e): n = len(v) M = np.zeros((n,n)) @@ -193,18 +196,21 @@ def mP(v,e): M[i,i] = 2 return M + def normalize(v): n = np.linalg.norm(v) if n == 0: return v return v/n + def mapEdges(func,v,e,*args): b = v.copy() for i, edges in e.iteritems(): b[i] = func(v[i], [v[j] for j in edges], *args) return b + def getNormal(a,b,c): "Assumes TT winding direction" p = np.roll(normalize(b - a), 1) @@ -214,19 +220,23 @@ def getNormal(a,b,c): # print p, n, normalize((p + n) * .5) return normalize((p + n) * .5) + def edgeNormals(v,e): "Assumes a mesh where each vertex has exactly least two edges" return mapEdges(lambda a,(p,n) : getNormal(a,p,n),v,e) + def rangePrevNext(count): c = np.arange(count,dtype=int) r = np.vstack((c, np.roll(c, 1), np.roll(c, -1))) return r.T + def skewMesh(v,angle): slope = np.tanh([math.pi * angle / 180]) return v.dot(np.array([[1,0],[slope,1]])) + def labelConnected(e): label = 0 labels = np.zeros((len(e),1)) @@ -236,6 +246,7 @@ def labelConnected(e): label += 1 return labels + def copyGradDetails(a,b,e,scale=15): n = len(a) labels = labelConnected(e) @@ -245,6 +256,7 @@ def copyGradDetails(a,b,e,scale=15): out[mask,:] = gaussian(b[mask,:], scale, mode="wrap", axis=0) + a[mask,:] - gaussian(a[mask,:], scale, mode="wrap", axis=0) return out + def copyMeshDetails(va,vb,e,scale=5,smooth=.01): gradA = mapEdges(lambda a,(p,n): normalize(p-a), va, e) gradB = mapEdges(lambda a,(p,n): normalize(p-a), vb, e) @@ -253,8 +265,6 @@ def copyMeshDetails(va,vb,e,scale=5,smooth=.01): return recompose(vb, grad, e, smooth=smooth) - - def condenseGlyph(glyph, scale=.8, stemWidth=185): ga, subsegments = segmentGlyph(glyph, 25) va, e = glyphToMesh(ga) @@ -273,7 +283,7 @@ def condenseGlyph(glyph, scale=.8, stemWidth=185): # cornerWeights = mapEdges(lambda a,(p,n): normalize(p-a).dot(normalize(a-n)), grad, e)[:,0].reshape((-1,1)) # smooth = np.ones((n,1)) * .1 # smooth[cornerWeights < .6] = 10 - # + # # grad2 = quantizeGradient(grad).astype(float) # grad2 = copyGradDetails(grad, grad2, e, scale=10) # grad2 = mapEdges(lambda a,e: normalize(a), grad2, e) -- cgit v1.2.3 From ceb4ae1b523cb0160dbf2ac57b770db0761c68d9 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 13 Nov 2015 13:39:25 -0800 Subject: [italics] Fix import order, remove unused imports --- scripts/lib/fontbuild/alignpoints.py | 4 +++- scripts/lib/fontbuild/curveFitPen.py | 8 ++------ scripts/lib/fontbuild/italics.py | 20 ++++++++++---------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/scripts/lib/fontbuild/alignpoints.py b/scripts/lib/fontbuild/alignpoints.py index 76581a5..e3bb539 100644 --- a/scripts/lib/fontbuild/alignpoints.py +++ b/scripts/lib/fontbuild/alignpoints.py @@ -13,9 +13,11 @@ # limitations under the License. +import math + import numpy as np from numpy.linalg import lstsq -import math + def alignCorners(glyph, va, subsegments): out = va.copy() diff --git a/scripts/lib/fontbuild/curveFitPen.py b/scripts/lib/fontbuild/curveFitPen.py index 6ef42da..c2b90ac 100644 --- a/scripts/lib/fontbuild/curveFitPen.py +++ b/scripts/lib/fontbuild/curveFitPen.py @@ -19,14 +19,10 @@ __all__ = ["SubsegmentPen","SubsegmentsToCurvesPen", "segmentGlyph", "fitGlyph"] from fontTools.pens.basePen import BasePen -from fontTools.misc import bezierTools -from robofab.pens.pointPen import AbstractPointPen -from robofab.pens.adapterPens import PointToSegmentPen, GuessSmoothPointPen import numpy as np -from numpy.linalg import norm from numpy import array as v -from random import random - +from numpy.linalg import norm +from robofab.pens.adapterPens import GuessSmoothPointPen from robofab.pens.pointPen import BasePointToSegmentPen diff --git a/scripts/lib/fontbuild/italics.py b/scripts/lib/fontbuild/italics.py index b1db984..83b123f 100644 --- a/scripts/lib/fontbuild/italics.py +++ b/scripts/lib/fontbuild/italics.py @@ -13,12 +13,18 @@ # limitations under the License. +import math + from fontTools.misc.transform import Transform -from robofab.world import RFont -from time import clock import numpy as np -import math -from alignpoints import alignCorners +from numpy.linalg import norm +from scipy.sparse.linalg import cg +from scipy.ndimage.filters import gaussian_filter1d as gaussian +from scipy.cluster.vq import vq, whiten + +from fontbuild.alignpoints import alignCorners +from fontbuild.curveFitPen import fitGlyph, segmentGlyph + def italicizeGlyph(f, g, angle=10, stemWidth=185): unic = g.unicode #save unicode @@ -111,13 +117,7 @@ def transformFLGlyphMembers(g, m, transformAnchors = True): a.x = aa[0] # a.x,a.y = (aa[0] - p[0], aa[1] - p[1]) # a.x = a.x - m[4] - -from curveFitPen import fitGlyph,segmentGlyph -from numpy.linalg import norm -from scipy.sparse.linalg import cg -from scipy.ndimage.filters import gaussian_filter1d as gaussian -from scipy.cluster.vq import vq, kmeans2, whiten def glyphToMesh(g): points = [] -- cgit v1.2.3 From 8b5f2c40e043ac354b74481919d25b85f1136d41 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Thu, 19 Nov 2015 15:23:24 -0800 Subject: [italics] Work around RoboFab-related issues This is (hopefully) a temporary hack until a more robust solution to the issue is found. Basically the problem is that RoboFab pens do float-to-float comparisons to decide whether to remove duplicate points from contours, and breaks with BooleanOperations output (which can include point coordinates with very small fractional components). The italicizing code assumes certain behavior from RoboFab pens and can't handle duplicate points being kept. --- scripts/lib/fontbuild/Build.py | 14 ++------------ scripts/lib/fontbuild/curveFitPen.py | 8 ++++++++ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py index d1e870a..5d92705 100644 --- a/scripts/lib/fontbuild/Build.py +++ b/scripts/lib/fontbuild/Build.py @@ -120,18 +120,8 @@ class FontProject: if g.name == "uniFFFD": continue - - # if i < 24: - # continue - # if i > 86: - # for i,g in enumerate(fl.font.glyphs): - # fl.UpdateGlyph(i) - # # break - # assert False - - # print g.name - # if self.thinfont != None: - # narrowFLGlyph(g,self.thinfont.getGlyph(g.name),factor=narrowAmmount) + + removeGlyphOverlap(g) if g.name in self.lessItalic: italicizeGlyph(f, g, 9, stemWidth=stemWidth) diff --git a/scripts/lib/fontbuild/curveFitPen.py b/scripts/lib/fontbuild/curveFitPen.py index c2b90ac..f7c0cae 100644 --- a/scripts/lib/fontbuild/curveFitPen.py +++ b/scripts/lib/fontbuild/curveFitPen.py @@ -280,6 +280,14 @@ class SubsegmentPen(BasePen): def _closePath(self): if not (self.lastPoint[0] == self.startContour[0] and self.lastPoint[1] == self.startContour[1]): self._lineTo(self.startContour) + + # round values used by otherPen (a RoboFab SegmentToPointPen) to decide + # whether to delete duplicate points at start and end of contour + #TODO(jamesgk) figure out why we have to do this hack, then remove it + c = self.otherPen.contour + for i in [0, -1]: + c[i] = [[round(n, 5) for n in c[i][0]]] + list(c[i][1:]) + self.otherPen.closePath() def _endPath(self): -- cgit v1.2.3 From b9db24ac21becece78cdf011921d1416046dc159 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Thu, 19 Nov 2015 15:42:48 -0800 Subject: [italics] Add some comments It's not much, but maybe a start.... --- scripts/lib/fontbuild/italics.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/lib/fontbuild/italics.py b/scripts/lib/fontbuild/italics.py index 83b123f..51d1f96 100644 --- a/scripts/lib/fontbuild/italics.py +++ b/scripts/lib/fontbuild/italics.py @@ -53,6 +53,8 @@ def italicizeGlyph(f, g, angle=10, stemWidth=185): def italicize(glyph, angle=12, stemWidth=180, xoffset=-50): CURVE_CORRECTION_WEIGHT = .03 CORNER_WEIGHT = 10 + + # decompose the glyph into smaller segments ga, subsegments = segmentGlyph(glyph,25) va, e = glyphToMesh(ga) n = len(va) @@ -79,14 +81,19 @@ def italicize(glyph, angle=12, stemWidth=180, xoffset=-50): # out = copyMeshDetails(va, out, e, 6) else: outCorrected = out + + # create a transform for italicizing normals = edgeNormals(out, e) center = va + normals * stemWidth * .4 if stemWidth > 130: center[:, 0] = va[:, 0] * .7 + center[:,0] * .3 centerSkew = skewMesh(center.dot(np.array([[.97,0],[0,1]])), angle * .9) + + # apply the transform out = outCorrected + (centerSkew - center) out[:,1] = outCorrected[:,1] + # make some corrections smooth = np.ones((n,1)) * .1 out = alignCorners(glyph, out, subsegments) out = copyMeshDetails(skewMesh(va, angle), out, e, 7, smooth=smooth) @@ -101,6 +108,8 @@ def italicize(glyph, angle=12, stemWidth=180, xoffset=-50): # gOut.width *= .97 # gOut.width += 10 # return gOut + + # recompose the glyph into original segments return fitGlyph(glyph, gOut, subsegments) -- cgit v1.2.3 From 21ef39becd62519e32accdf2f07554571fc46bfa Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Wed, 14 Oct 2015 11:28:53 -0700 Subject: Fix import order in Build.py --- scripts/lib/fontbuild/Build.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py index 5d92705..f29c727 100644 --- a/scripts/lib/fontbuild/Build.py +++ b/scripts/lib/fontbuild/Build.py @@ -13,23 +13,25 @@ # limitations under the License. +import ConfigParser +import os +import sys + from booleanOperations import BooleanOperationManager +from fontTools.misc.transform import Transform from robofab.world import OpenFont -from fontbuild.mix import Mix,Master,narrowFLGlyph -from fontbuild.instanceNames import setNamesRF -from fontbuild.italics import italicizeGlyph + from fontbuild.convertCurves import glyphCurvesToQuadratic -from fontbuild.mitreGlyph import mitreGlyph +from fontbuild.decomposeGlyph import decomposeGlyph +from fontbuild.features import readFeatureFile, writeFeatureFile from fontbuild.generateGlyph import generateGlyph -from fontTools.misc.transform import Transform +from fontbuild.instanceNames import setNamesRF +from fontbuild.italics import italicizeGlyph from fontbuild.kerning import makeKernFeature -from fontbuild.features import readFeatureFile, writeFeatureFile from fontbuild.markFeature import GenerateFeature_mark +from fontbuild.mitreGlyph import mitreGlyph +from fontbuild.mix import Mix,Master,narrowFLGlyph from fontbuild.mkmkFeature import GenerateFeature_mkmk -from fontbuild.decomposeGlyph import decomposeGlyph -import ConfigParser -import os -import sys class FontProject: -- cgit v1.2.3 From e8c9488af391731a94e9c47b63e926f3d7e14c6e Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Tue, 3 Nov 2015 13:28:11 -0800 Subject: Generate TTFs with ufo2ft --- scripts/build-v2.py | 9 ++- scripts/lib/fontbuild/Build.py | 74 ++++++++------------ scripts/lib/fontbuild/kerning.py | 112 ----------------------------- scripts/lib/fontbuild/markFeature.py | 132 ++++++++--------------------------- scripts/lib/fontbuild/mkmkFeature.py | 87 ----------------------- 5 files changed, 66 insertions(+), 348 deletions(-) delete mode 100644 scripts/lib/fontbuild/kerning.py delete mode 100755 scripts/lib/fontbuild/mkmkFeature.py diff --git a/scripts/build-v2.py b/scripts/build-v2.py index 58842d6..0ef86e9 100644 --- a/scripts/build-v2.py +++ b/scripts/build-v2.py @@ -91,8 +91,7 @@ proj = FontProject(rg.font, BASEDIR, "res/roboto.cfg", th.ffont) FAMILYNAME = "Roboto" proj.buildOTF = True -#proj.autohintOTF = True -proj.buildTTF = True +#proj.compatible = True proj.generateFont(th.font, "%s/Thin/Regular/Th"%FAMILYNAME) proj.generateFont(Mix([th, rg], 0.45), "%s/Light/Regular/Lt"%FAMILYNAME) @@ -119,6 +118,10 @@ proj.generateFont(Mix([rg, bd], RPoint(1.125, 1.0)), "%s/Black Italic/Bold Italic/Bk"%FAMILYNAME, italic=True, stemWidth=290) +# unfortunately some condensed forms (*.cn) of glyphs are not compatible with +# their original forms, so we can't convert all fonts together compatibly +proj.generateTTFs() + thcn1 = Master(condenseFont(th.font, .84, 40)) cn1 = Master(rg.ffont.addDiff(thcn1.ffont, th.ffont)) bdcn1 = Master(bd.ffont.addDiff(thcn1.ffont, th.ffont)) @@ -143,4 +146,4 @@ proj.generateFont(Mix([cn1, bdcn1], RPoint(0.75, 0.75)), "%s Condensed/Bold Italic/Bold Italic/Rg"%FAMILYNAME, italic=True, swapSuffixes=[".cn"], stemWidth=240) -sys.exit(0) +proj.generateTTFs() diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py index f29c727..34a672e 100644 --- a/scripts/lib/fontbuild/Build.py +++ b/scripts/lib/fontbuild/Build.py @@ -18,20 +18,19 @@ import os import sys from booleanOperations import BooleanOperationManager +from convert_curves import fonts_to_quadratic from fontTools.misc.transform import Transform from robofab.world import OpenFont +from ufo2ft import compileOTF, compileTTF -from fontbuild.convertCurves import glyphCurvesToQuadratic from fontbuild.decomposeGlyph import decomposeGlyph from fontbuild.features import readFeatureFile, writeFeatureFile from fontbuild.generateGlyph import generateGlyph from fontbuild.instanceNames import setNamesRF from fontbuild.italics import italicizeGlyph -from fontbuild.kerning import makeKernFeature -from fontbuild.markFeature import GenerateFeature_mark +from fontbuild.markFeature import RobotoFeatureCompiler from fontbuild.mitreGlyph import mitreGlyph from fontbuild.mix import Mix,Master,narrowFLGlyph -from fontbuild.mkmkFeature import GenerateFeature_mkmk class FontProject: @@ -68,8 +67,8 @@ class FontProject: self.buildnumber = self.loadBuildNumber() self.buildOTF = False - self.autohintOTF = False - self.buildTTF = False + self.compatible = False + self.generatedFonts = [] def loadBuildNumber(self): @@ -155,33 +154,38 @@ class FontProject: setNamesRF(f, n, foundry=self.config.get('main', 'foundry'), version=self.config.get('main', 'version')) - cleanCurves(f) + if not self.compatible: + cleanCurves(f) deleteGlyphs(f, self.deleteList) if kern: log(">> Generating kern classes") readFeatureFile(f, self.ot_kerningclasses) - makeKernFeature(f, self.ot_kerningclasses) log(">> Generating font files") - GenerateFeature_mark(f) - GenerateFeature_mkmk(f) ufoName = self.generateOutputPath(f, "ufo") f.save(ufoName) + self.generatedFonts.append(ufoName) if self.buildOTF: log(">> Generating OTF file") newFont = OpenFont(ufoName) otfName = self.generateOutputPath(f, "otf") - builtSuccessfully = saveOTF(newFont, otfName, autohint=self.autohintOTF) - if not builtSuccessfully: - sys.exit(1) + saveOTF(newFont, otfName) - if self.buildTTF: - log(">> Generating TTF file") - import fontforge - otFont = fontforge.open(otfName) - otFont.generate(self.generateOutputPath(f, "ttf")) + def generateTTFs(self): + """Build TTF for each font generated since last call to generateTTFs.""" + + fonts = [OpenFont(ufo) for ufo in self.generatedFonts] + log(">> Converting curves to quadratic") + fonts_to_quadratic(fonts, self.compatible) + + log(">> Generating TTF files") + for font in fonts: + ttfName = self.generateOutputPath(font, "ttf") + log(os.path.basename(ttfName)) + saveOTF(font, ttfName, truetype=True) + self.generatedFonts = [] def transformGlyphMembers(g, m): @@ -266,30 +270,12 @@ def removeGlyphOverlap(glyph): manager.union(contours, glyph.getPointPen()) -def saveOTF(font, destFile, autohint=False): - """Save a RoboFab font as an OTF binary using ufo2fdk. - - Returns True on success, False otherwise. - """ - - from ufo2fdk import OTFCompiler - - # glyphs with multiple unicode values must be split up, due to FontTool's - # use of a name -> UV dictionary during cmap compilation - for glyph in font: - if len(glyph.unicodes) > 1: - newUV = glyph.unicodes.pop() - newGlyph = font.newGlyph("uni%04X" % newUV) - newGlyph.appendComponent(glyph.name) - newGlyph.unicode = newUV - newGlyph.width = glyph.width - - compiler = OTFCompiler() - reports = compiler.compile(font, destFile, autohint=autohint) - if autohint: - print reports["autohint"] - print reports["makeotf"] +def saveOTF(font, destFile, truetype=False): + """Save a RoboFab font as an OTF binary using ufo2fdk.""" - successMsg = ("makeotfexe [NOTE] Wrote new font file '%s'." % - os.path.basename(destFile)) - return successMsg in reports["makeotf"] + if truetype: + compiler = compileTTF + else: + compiler = compileOTF + otf = compiler(font, featureCompilerClass=RobotoFeatureCompiler) + otf.save(destFile) diff --git a/scripts/lib/fontbuild/kerning.py b/scripts/lib/fontbuild/kerning.py deleted file mode 100644 index c93e303..0000000 --- a/scripts/lib/fontbuild/kerning.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright 2015 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from feaTools import parser -from feaTools.writers.baseWriter import AbstractFeatureWriter - - -class KernFeatureWriter(AbstractFeatureWriter): - """Generates a kerning feature based on glyph class definitions. - - Uses the kerning rules contained in an RFont's kerning attribute, as well as - glyph classes from parsed OTF text. Class-based rules are set based on the - existing rules for their key glyphs. - """ - - def __init__(self, font): - self.kerning = font.kerning - self.leftClasses = [] - self.rightClasses = [] - self.classSizes = {} - - def write(self, linesep="\n"): - """Write kern feature.""" - - # maintain collections of different rule types - leftClassKerning, rightClassKerning, classPairKerning = {}, {}, {} - for leftName, leftContents in self.leftClasses: - leftKey = leftContents[0] - - # collect rules with two classes - for rightName, rightContents in self.rightClasses: - rightKey = rightContents[0] - pair = leftKey, rightKey - kerningVal = self.kerning[pair] - if kerningVal is None: - continue - classPairKerning[leftName, rightName] = kerningVal - self.kerning.remove(pair) - - # collect rules with left class and right glyph - for pair, kerningVal in self.kerning.getLeft(leftKey): - leftClassKerning[leftName, pair[1]] = kerningVal - self.kerning.remove(pair) - - # collect rules with left glyph and right class - for rightName, rightContents in self.rightClasses: - rightKey = rightContents[0] - for pair, kerningVal in self.kerning.getRight(rightKey): - rightClassKerning[pair[0], rightName] = kerningVal - self.kerning.remove(pair) - - # write the feature - self.ruleCount = 0 - lines = ["feature kern {"] - lines.append(self._writeKerning(self.kerning, linesep)) - lines.append(self._writeKerning(leftClassKerning, linesep, True)) - lines.append(self._writeKerning(rightClassKerning, linesep, True)) - lines.append(self._writeKerning(classPairKerning, linesep)) - lines.append("} kern;") - return linesep.join(lines) - - def _writeKerning(self, kerning, linesep, enum=False): - """Write kerning rules for a mapping of pairs to values.""" - - lines = [] - enum = "enum " if enum else "" - pairs = kerning.items() - pairs.sort() - for (left, right), val in pairs: - if enum: - rulesAdded = (self.classSizes.get(left, 1) * - self.classSizes.get(right, 1)) - else: - rulesAdded = 1 - self.ruleCount += rulesAdded - if self.ruleCount > 1024: - lines.append(" subtable;") - self.ruleCount = rulesAdded - lines.append(" %spos %s %s %d;" % (enum, left, right, val)) - return linesep.join(lines) - - def classDefinition(self, name, contents): - """Store a class definition as either a left- or right-hand class.""" - - if not name.startswith("@_"): - return - info = (name, contents) - if name.endswith("_L"): - self.leftClasses.append(info) - elif name.endswith("_R"): - self.rightClasses.append(info) - self.classSizes[name] = len(contents) - - -def makeKernFeature(font, text): - """Add a kern feature to the font, using a KernFeatureWriter.""" - - writer = KernFeatureWriter(font) - parser.parseFeatures(writer, text) - font.features.text += writer.write() diff --git a/scripts/lib/fontbuild/markFeature.py b/scripts/lib/fontbuild/markFeature.py index b617ef6..945cef0 100755 --- a/scripts/lib/fontbuild/markFeature.py +++ b/scripts/lib/fontbuild/markFeature.py @@ -13,105 +13,33 @@ # limitations under the License. -from fontbuild.features import updateFeature - - -aliases = [["uni0430", "a"], ["uni0435", "e"], ["uni0440", "p"], ["uni0441", "c"], ["uni0445", "x"], ["uni0455", "s"], ["uni0456", "i"], ["uni0471", "psi"]] - -def GetAliaseName(gname): - for i in range (len(aliases)): - if (gname == aliases[i][1]): - return aliases[i][0] - return None - -def CreateAccNameList(font, acc_anchor_name, bCombAccentOnly = True): - #combrange = range(0x0300,0x0370) + range(0x1AB0,0x1ABF) + range(0x1DC0,0x1DE0) - lst = [] - for g in font: - if bCombAccentOnly and g.width != 0: #((g.unicode < 0x0300) or (g.unicode > 0x362)): - continue - for anchor in g.anchors: - if acc_anchor_name == anchor.name: - lst.append(g.name) - return lst - -def CreateAccGlyphList(font, acc_list, acc_anchor_name): - g_list = [] - for g in font: - if g.name in acc_list: - for anchor in g.anchors: - if acc_anchor_name == anchor.name: - g_list.append([g.name, anchor.x, anchor.y]) - break - return g_list - - -def CreateGlyphList(font, acc_list, anchor_name): - g_list = [] - for g in font: - if g.name in acc_list: - continue - for anchor in g.anchors: - if anchor_name == anchor.name: - g_list.append([g.name, anchor.x, anchor.y]) - break - return g_list - -def Create_mark_lookup(accent_g_list, base_g_list, lookupname, acc_class, lookAliases = True): - txt = "lookup " + lookupname + " {\n" - - for acc in accent_g_list: - txt += " markClass " + acc[0] + " " + acc_class +";\n" - - for base in base_g_list: - txt += " pos base " + base[0] + " mark " + acc_class + ";\n" - if (lookAliases): - base2 = GetAliaseName(base[0]) - if (None == base2): - continue - txt += " pos base " + base2 + " mark " + acc_class + ";\n" - - txt += "} " + lookupname + ";\n" - - return txt - -##### main ############## -def GenerateFeature_mark(font): - - combination_anchor_list = [ - ["top", "_marktop", True, True], - ["bottom", "_markbottom", True, True], - ["top_dd", "_marktop_dd", True, False], - ["bottom_dd", "_markbottom_dd", True, False], - ["rhotichook", "_markrhotichook", False, False], - ["top0315", "_marktop0315", False, False], - ["parent_top", "_markparent_top", False, False], - ["parenthesses.w1", "_markparenthesses.w1", False, False], - ["parenthesses.w2", "_markparenthesses.w2", False, False], - ["parenthesses.w3", "_markparenthesses.w3", False, False] - ] - - text = "feature mark {\n" - - for n in range(len(combination_anchor_list)): - - accent_name_list = [] - accent_mark_list = [] - base_mark_list = [] - - anchors_pair = combination_anchor_list[n] - anchor_name = anchors_pair[0] - acc_anchor_name = anchors_pair[1] - comb_accent_only = anchors_pair[2] - expand_to_composits = anchors_pair[3] - lookupname = "mark"+`n+1` - classname = "@MC_" + anchor_name - - accent_name_list = CreateAccNameList(font, acc_anchor_name, comb_accent_only) - accent_mark_list = CreateAccGlyphList(font, accent_name_list, acc_anchor_name) - base_mark_list = CreateGlyphList(font, accent_name_list, anchor_name) - text += Create_mark_lookup(accent_mark_list, base_mark_list, lookupname, classname, expand_to_composits) - - text += "} mark;\n" - - updateFeature(font, "mark", text) +from ufo2ft.kernFeatureWriter import KernFeatureWriter +from ufo2ft.makeotfParts import FeatureOTFCompiler + + +class RobotoFeatureCompiler(FeatureOTFCompiler): + def precompile(self): + self.overwriteFeatures = True + + def setupAnchorPairs(self): + self.anchorPairs = [ + ["top", "_marktop", True, True], + ["bottom", "_markbottom", True, True], + ["top_dd", "_marktop_dd", True, False], + ["bottom_dd", "_markbottom_dd", True, False], + ["rhotichook", "_markrhotichook", False, False], + ["top0315", "_marktop0315", False, False], + ["parent_top", "_markparent_top", False, False], + ["parenthesses.w1", "_markparenthesses.w1", False, False], + ["parenthesses.w2", "_markparenthesses.w2", False, False], + ["parenthesses.w3", "_markparenthesses.w3", False, False]] + + self.mkmkAnchorPairs = [ + ["mkmktop", "_marktop"], + ["mkmkbottom_acc", "_markbottom"]] + + def setupAliases(self): + self.aliases = [ + ["a", "uni0430"], ["e", "uni0435"], ["p", "uni0440"], + ["c", "uni0441"], ["x", "uni0445"], ["s", "uni0455"], + ["i", "uni0456"], ["psi", "uni0471"]] diff --git a/scripts/lib/fontbuild/mkmkFeature.py b/scripts/lib/fontbuild/mkmkFeature.py deleted file mode 100755 index 16f9313..0000000 --- a/scripts/lib/fontbuild/mkmkFeature.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright 2015 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from fontbuild.features import updateFeature - - -def CreateAccNameList(font, acc_anchor_name): - lst = [] - for g in font: - for anchor in g.anchors: - if acc_anchor_name == anchor.name: - lst.append(g.name) - return lst - -def CreateAccGlyphList(font, acc_list, acc_anchor_name): - g_list = [] - for g in font: - if g.name in acc_list: - for anchor in g.anchors: - if acc_anchor_name == anchor.name: - g_list.append([g.name, anchor.x, anchor.y]) - break - return g_list - - -def CreateGlyphList(font, acc_list, anchor_name): - g_list = [] - for g in font: - for anchor in g.anchors: - if anchor_name == anchor.name: - g_list.append([g.name, anchor.x, anchor.y]) - break - return g_list - -def Create_mkmk1(accent_g_list, base_g_list, lookupname, acc_class): - txt = "lookup " + lookupname + " {\n" - #acc_class = "@MC_mkmk" - for acc in accent_g_list: - txt += " markClass " + acc[0] + " " + acc_class +";\n" - - for base in base_g_list: - txt += " pos mark " + base[0] + " mark " + acc_class + ";\n" - - txt += "} " + lookupname + ";\n" - - return txt - - -##### main ############## -def GenerateFeature_mkmk(font): - text = "feature mkmk {\n" - - accent_name_list = [] - accent_mark_list = [] - base_mark_list = [] - anchor_name = "mkmktop" - acc_anchor_name = "_marktop" - accent_name_list = CreateAccNameList(font, acc_anchor_name) - accent_mark_list = CreateAccGlyphList(font, accent_name_list, acc_anchor_name) - base_mark_list = CreateGlyphList(font, accent_name_list, anchor_name) - text += Create_mkmk1(accent_mark_list, base_mark_list, "mkmk1", "@MC_mkmk_top") - - accent_name_list = [] - accent_mark_list = [] - base_mark_list = [] - anchor_name = "mkmkbottom_acc" - acc_anchor_name = "_markbottom" - accent_name_list = CreateAccNameList(font, acc_anchor_name) - accent_mark_list = CreateAccGlyphList(font, accent_name_list, acc_anchor_name) - base_mark_list = CreateGlyphList(font, accent_name_list, anchor_name) - text += Create_mkmk1(accent_mark_list, base_mark_list, "mkmk2", "@MC_mkmk_bottom") - - text += "} mkmk;\n" - - updateFeature(font, "mkmk", text) -- cgit v1.2.3 From d1d422cd292addb4b59e85e2af1971635f1d923c Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Thu, 19 Nov 2015 16:13:57 -0800 Subject: Update curve conversion calls based on upstream --- scripts/lib/fontbuild/Build.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py index 34a672e..a8451f0 100644 --- a/scripts/lib/fontbuild/Build.py +++ b/scripts/lib/fontbuild/Build.py @@ -18,7 +18,7 @@ import os import sys from booleanOperations import BooleanOperationManager -from convert_curves import fonts_to_quadratic +from cu2qu.rf import fonts_to_quadratic from fontTools.misc.transform import Transform from robofab.world import OpenFont from ufo2ft import compileOTF, compileTTF @@ -177,15 +177,20 @@ class FontProject: """Build TTF for each font generated since last call to generateTTFs.""" fonts = [OpenFont(ufo) for ufo in self.generatedFonts] + self.generatedFonts = [] + log(">> Converting curves to quadratic") - fonts_to_quadratic(fonts, self.compatible) + if self.compatible: + fonts_to_quadratic(*fonts, dump_report=True) + else: + for font in fonts: + fonts_to_quadratic(font, dump_report=True) log(">> Generating TTF files") for font in fonts: ttfName = self.generateOutputPath(font, "ttf") log(os.path.basename(ttfName)) saveOTF(font, ttfName, truetype=True) - self.generatedFonts = [] def transformGlyphMembers(g, m): -- cgit v1.2.3 From 09eb72c1feecd1c997c8cccc47532440717e4a5f Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 20 Nov 2015 17:24:29 -0800 Subject: Change error bound when converting curves This is a small change which passes the threshold at which dots will have eight points instead of four, which makes a noticeable difference in shape. --- scripts/lib/fontbuild/Build.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py index a8451f0..041a7ec 100644 --- a/scripts/lib/fontbuild/Build.py +++ b/scripts/lib/fontbuild/Build.py @@ -180,11 +180,14 @@ class FontProject: self.generatedFonts = [] log(">> Converting curves to quadratic") + # using a slightly higher max error (e.g. 0.0025), dots will have fewer + # control points and look noticeably different + max_err = 0.002 if self.compatible: - fonts_to_quadratic(*fonts, dump_report=True) + fonts_to_quadratic(*fonts, max_err=max_err, dump_report=True) else: for font in fonts: - fonts_to_quadratic(font, dump_report=True) + fonts_to_quadratic(font, max_err=max_err, dump_report=True) log(">> Generating TTF files") for font in fonts: -- cgit v1.2.3 From cc9999078ad8dd324b6e38baa9e390510361b189 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Mon, 23 Nov 2015 14:40:51 -0800 Subject: Update readme --- README.md | 40 +++++++--------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 039db7e..37ca75a 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ cd $HOME/roboto-src ```bash git clone https://github.com/google/roboto.git git clone https://github.com/behdad/fonttools.git +git clone https://github.com/jamesgk/ufo2ft.git git clone https://github.com/robofab-developers/robofab.git git clone https://github.com/typesupply/feaTools.git git clone https://github.com/typemytype/booleanOperations.git @@ -39,11 +40,7 @@ via: sudo apt-get install cython ``` -##### For OTF generation: - -```bash -git clone https://github.com/typesupply/ufo2fdk.git -``` +##### For OTF/TTF generation: To build the FDK yourself: @@ -59,12 +56,6 @@ If you're not building the FDK yourself, download the pre-built version [here](http://www.adobe.com/devnet/opentype/afdko.html) and unzip it into the current directory. -##### For TTF generation, on Ubuntu: - -```bash -sudo apt-get install fontforge python-fontforge -``` - ##### For post-production: ```bash @@ -85,6 +76,8 @@ You can install the necessary modules at the sytem level: ```bash cd fonttools sudo python setup.py install +cd ../ufo2ft +sudo python setup.py install cd ../robofab sudo python setup.py install cd ../feaTools @@ -100,6 +93,7 @@ Or set `$PYTHONPATH` locally before running `make`: ```bash PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/fonttools/Lib" +PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/ufo2ft/Lib" PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/robofab/Lib" PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/feaTools/Lib" PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/booleanOperations/Lib" @@ -107,18 +101,6 @@ PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/booleanOperations/Lib" ##### For OTF generation: -```bash -cd ufo2fdk -sudo python setup.py install -cd .. -``` - -Or: - -```bash -PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/ufo2fdk/Lib" -``` - If building the FDK yourself, follow the instructions in `afdko/FDK/FDK Build Notes.txt`: ```bash @@ -201,10 +183,10 @@ The Roboto build toolchain depends on: glyph overlap removal. - (requires Cython to install: http://cython.org/) -## OTF Generation +## OTF/TTF Generation OTF generation depends on: -- ufo2fdk (https://github.com/typesupply/ufo2fdk) +- ufo2ft (https://github.com/jamesgk/ufo2tk) - Open-source portions of the AFDKO (https://github.com/adobe-type-tools/afdko/releases) @@ -213,14 +195,6 @@ the variety which includes closed-source tools (http://www.adobe.com/devnet/opentype/afdko.html), though these closed-source portions are not used to build Roboto. -## TTF Generation -TTF generation depends on: - -- FontForge (https://github.com/fontforge/fontforge) - -Whose Python interface should be available on Ubuntu by default via `apt-get -install fontforge python-fontforge`. - ## Post-Production Post-production scripts (most of the code outside of the `fontbuild` directory, e.g. for testing output) depend on: -- cgit v1.2.3 From bf552b4e6581f955f932ed26f09f22a087255650 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Mon, 23 Nov 2015 14:57:22 -0800 Subject: Add cu2qu dependency note to readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 37ca75a..e9bab88 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ cd $HOME/roboto-src ```bash git clone https://github.com/google/roboto.git git clone https://github.com/behdad/fonttools.git +git clone https://github.com/googlei18n/cu2qu.git git clone https://github.com/jamesgk/ufo2ft.git git clone https://github.com/robofab-developers/robofab.git git clone https://github.com/typesupply/feaTools.git @@ -76,6 +77,8 @@ You can install the necessary modules at the sytem level: ```bash cd fonttools sudo python setup.py install +cd ../cu2qu +sudo python setup.py install cd ../ufo2ft sudo python setup.py install cd ../robofab @@ -93,6 +96,7 @@ Or set `$PYTHONPATH` locally before running `make`: ```bash PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/fonttools/Lib" +PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/cu2qu/Lib" PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/ufo2ft/Lib" PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/robofab/Lib" PYTHONPATH="$PYTHONPATH:$HOME/roboto-src/feaTools/Lib" @@ -187,6 +191,7 @@ The Roboto build toolchain depends on: OTF generation depends on: - ufo2ft (https://github.com/jamesgk/ufo2tk) +- cu2qu (https://github.com/googlei18n/cu2qu) - Open-source portions of the AFDKO (https://github.com/adobe-type-tools/afdko/releases) -- cgit v1.2.3 From d2d238585968c4f91f73801a36d8d20ee3c3f424 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Mon, 23 Nov 2015 15:34:48 -0800 Subject: Fix typo in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e9bab88..08681f6 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ The Roboto build toolchain depends on: ## OTF/TTF Generation OTF generation depends on: -- ufo2ft (https://github.com/jamesgk/ufo2tk) +- ufo2ft (https://github.com/jamesgk/ufo2ft) - cu2qu (https://github.com/googlei18n/cu2qu) - Open-source portions of the AFDKO (https://github.com/adobe-type-tools/afdko/releases) -- cgit v1.2.3 From 5b0dcd91995fe60d235bde7c7bb6bdbf0b910e81 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Mon, 23 Nov 2015 18:54:25 -0800 Subject: Reverse contours before generating TTFs The UFOs store contours in ccw order, and the TrueType spec seems to say they should be cw. The old binaries are cw. --- scripts/lib/fontbuild/Build.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py index 041a7ec..5bb0ed6 100644 --- a/scripts/lib/fontbuild/Build.py +++ b/scripts/lib/fontbuild/Build.py @@ -193,6 +193,9 @@ class FontProject: for font in fonts: ttfName = self.generateOutputPath(font, "ttf") log(os.path.basename(ttfName)) + for glyph in font: + for contour in glyph: + contour.reverseContour() saveOTF(font, ttfName, truetype=True) -- cgit v1.2.3 From 7d38014a38dae2303960adac770cd1399f7af032 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Tue, 24 Nov 2015 15:03:18 -0800 Subject: Another update to curve conversion calls cu2qu is a new library without a currently well-defined API, so these calls have changed and will probably change again. --- scripts/lib/fontbuild/Build.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py index 5bb0ed6..be0b79c 100644 --- a/scripts/lib/fontbuild/Build.py +++ b/scripts/lib/fontbuild/Build.py @@ -180,14 +180,14 @@ class FontProject: self.generatedFonts = [] log(">> Converting curves to quadratic") - # using a slightly higher max error (e.g. 0.0025), dots will have fewer - # control points and look noticeably different + # using a slightly higher max error (e.g. 0.0025 em), dots will have + # fewer control points and look noticeably different max_err = 0.002 if self.compatible: - fonts_to_quadratic(*fonts, max_err=max_err, dump_report=True) + fonts_to_quadratic(*fonts, max_err_em=max_err, dump_report=True) else: for font in fonts: - fonts_to_quadratic(font, max_err=max_err, dump_report=True) + fonts_to_quadratic(font, max_err_em=max_err, dump_report=True) log(">> Generating TTF files") for font in fonts: -- cgit v1.2.3 From 0b96a9418ddec3c573f1a5e8705fe9e4d4324904 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Wed, 25 Nov 2015 12:12:47 -0800 Subject: [fontcrunch] Incorporate some upstream changes These are changes from https://github.com/googlefonts/fontcrunch. Not included is the removal of the main function in quadopt.cc, which we obviously need. --- third_party/fontcrunch/quadopt.cc | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/third_party/fontcrunch/quadopt.cc b/third_party/fontcrunch/quadopt.cc index 59f3523..a76da84 100644 --- a/third_party/fontcrunch/quadopt.cc +++ b/third_party/fontcrunch/quadopt.cc @@ -119,8 +119,8 @@ class ArclenFunctor { public: ArclenFunctor(const Quad& q) : dx0(2 * (q.p[1].x - q.p[0].x)) - , dx1(2 * (q.p[2].x - q.p[1].x)) , dy0(2 * (q.p[1].y - q.p[0].y)) + , dx1(2 * (q.p[2].x - q.p[1].x)) , dy1(2 * (q.p[2].y - q.p[1].y)) { } void operator()(double dydx[1], double t, const double y[1]) { Point p(deriv(t)); @@ -224,7 +224,6 @@ Point Thetas::dir(double s) const { // L1 angle norm, 2, L2 angle norm, 0.05 // L1 distance norm, 200 -double penalty = 1; double dist_factor = .005; double angle_factor = 5; @@ -272,7 +271,9 @@ private: // of angle mismatch double measureQuad(const Thetas& curve, double s0, double s1, const Quad& q) { ArclenFunctor derivs(q); - double ss = (s1 - s0) / q.arclen(); + double ss = 0; + if (q.arclen() != 0) + ss = (s1 - s0) / q.arclen(); MeasureFunctor err(curve, s0, ss, derivs, q); const int n = 10; double dt = 1./n; @@ -293,13 +294,13 @@ struct Break { }; struct Statelet { - void combine(const Statelet* prev, double score, Quad q); + void combine(const Statelet* prev, double score, Quad q, double penalty); const Statelet* prev; double score; Quad q; }; -void Statelet::combine(const Statelet* newprev, double newscore, Quad newq) { +void Statelet::combine(const Statelet* newprev, double newscore, Quad newq, double penalty) { prev = newprev; double pmul = 2; if (newq.isLine()) { @@ -313,18 +314,18 @@ void Statelet::combine(const Statelet* newprev, double newscore, Quad newq) { } struct State { - void combine(const State* prev, double score, Quad q); + void combine(const State* prev, double score, Quad q, double penalty); vector sts; bool init; }; -void State::combine(const State* prev, double score, Quad q) { +void State::combine(const State* prev, double score, Quad q, double penalty) { const Statelet* prevsl = prev->sts.empty() ? 0 : &prev->sts[0]; if (prevsl == 0 && !prev->init) { return; } Statelet sl; - sl.combine(prevsl, score, q); + sl.combine(prevsl, score, q, penalty); if (sts.empty()) { sts.push_back(sl); } else { @@ -379,7 +380,7 @@ void findBreaks(vector* breaks, const Thetas& curve) { bool intersect(Point* result, Point p0, Point dir0, Point p1, Point dir1) { double det = dir0.x * dir1.y - dir0.y * dir1.x; - if (std::abs(det) < 1e-6) return false; + if (std::abs(det) < 1e-6 || std::isnan(det)) return false; det = 1 / det; double a = p0.y * dir0.x - p0.x * dir0.y; double b = p1.y * dir1.x - p1.x * dir1.y; @@ -389,47 +390,47 @@ bool intersect(Point* result, Point p0, Point dir0, Point p1, Point dir1) { } void tryQuad(const State* prev, State* st, const Thetas& curve, - const Break& bk0, const Break& bk1, const Quad& q) { + const Break& bk0, const Break& bk1, const Quad& q, double penalty) { double score = measureQuad(curve, bk0.s, bk1.s, q); - st->combine(prev, score, q); + st->combine(prev, score, q, penalty); } void tryLineQuad(const State* prev, State* st, const Thetas& curve, - const Break& bk0, const Break& bk1) { + const Break& bk0, const Break& bk1, double penalty) { if (isInt(bk0.xy.x) && isInt(bk0.xy.y)) { Quad line(bk0.xy, lerp(0.5, bk0.xy, bk1.xy), bk1.xy); - tryQuad(prev, st, curve, bk0, bk1, line); + tryQuad(prev, st, curve, bk0, bk1, line, penalty); } Point pmid; if (intersect(&pmid, bk0.xy, bk0.dir, bk1.xy, bk1.dir)) { Quad q(bk0.xy, round(pmid), bk1.xy); if (okForHalf(prev, q)) { - tryQuad(prev, st, curve, bk0, bk1, q); + tryQuad(prev, st, curve, bk0, bk1, q, penalty); } } } -vector optimize(const Thetas& curve) { +vector optimize(const Thetas& curve, double penalty=1) { vector breaks; findBreaks(&breaks, curve); int n = breaks.size() - 1; vector states; states.resize(n + 1); states[0].init = true; - tryLineQuad(&states[0], &states[n], curve, breaks[0], breaks[n]); + tryLineQuad(&states[0], &states[n], curve, breaks[0], breaks[n], penalty); if (states[n].sts[0].score <= 3 * penalty) { goto done; } for (int i = 1; i < n; i++) { - tryLineQuad(&states[0], &states[i], curve, breaks[0], breaks[i]); - tryLineQuad(&states[i], &states[n], curve, breaks[i], breaks[n]); + tryLineQuad(&states[0], &states[i], curve, breaks[0], breaks[i], penalty); + tryLineQuad(&states[i], &states[n], curve, breaks[i], breaks[n], penalty); } if (states[n].sts[0].score <= 4 * penalty) { goto done; } for (int i = 1; i <= n; i++) { for (int j = i - 1; j >= 0; j--) { - tryLineQuad(&states[j], &states[i], curve, breaks[j], breaks[i]); + tryLineQuad(&states[j], &states[i], curve, breaks[j], breaks[i], penalty); } } done: @@ -447,9 +448,8 @@ void readBzs(vector* result, std::istream& is) { result->push_back(Quad(Point(x0, y0), Point(x1, y1), Point(x2, y2))); } // Round the endpoints, they must be on integers - (*result)[0].p[0] = round((*result)[0].p[0]); - Quad* lastq = &(*result)[(*result).size()]; - lastq->p[2] = round(lastq->p[2]); + result->front().p[0] = round(result->front().p[0]); + result->back().p[2] = round(result->back().p[2]); } int main(int argc, char** argv) { -- cgit v1.2.3 From 5283037a5e665164ae9ca0fb8261758c7c1cc9d8 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Wed, 25 Nov 2015 12:23:20 -0800 Subject: [fontcrunch] Stricter compilation of quadopt I feel like we may as well have these flags, since it compiles fine with them. --- third_party/fontcrunch/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/third_party/fontcrunch/Makefile b/third_party/fontcrunch/Makefile index 2090d39..3a073ec 100644 --- a/third_party/fontcrunch/Makefile +++ b/third_party/fontcrunch/Makefile @@ -20,7 +20,7 @@ OPT = $(patsubst %.bez, %.bezopt, $(SRC)) dummy: $(OPT) quadopt: quadopt.cc - $(CXX) $< -std=c++0x -O3 -o $@ + $(CXX) $< -std=c++0x -O3 -Werror -Wall -o $@ %.bezopt: %.bez quadopt ./quadopt $< $@ @@ -29,4 +29,4 @@ clean: rm -f quadopt find . -name '*.bez' -delete find . -name '*.bezopt' -delete - rmdir ?? \ No newline at end of file + rmdir ?? -- cgit v1.2.3 From a66d29b00655b3638620c0c726a3ef7e97e46559 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Thu, 10 Dec 2015 18:56:52 -0800 Subject: Decompose U+2050 to avoid a backwards contour If we wait until the UFO glyph is drawn to a FontTools glyph, the contours (one of which is a reflected component) will have opposite directions. When decomposed in the build scripts, the glyph's contours both have the correct direction. --- res/roboto.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/res/roboto.cfg b/res/roboto.cfg index 205cd46..4531bd2 100644 --- a/res/roboto.cfg +++ b/res/roboto.cfg @@ -36,6 +36,7 @@ decompose: integral product florin Tbar tbar Hbar hbar Eng eng uogonek Uogonek.smcp Aogonek.smcp Eogonek.smcp uni0524 uni0525 uni0526 uni0527 uni052E uni052F Hdesc hdesc uni2C69 uni2C6A uni2C6B uni2C6C Ndesc ndesc uni0498.smcp uni04A2.smcp uni04AA.smcp uni04B6.smcp nbspace uni202F uni205F erev uni1AB5 + uni2050 predecompose: uni04B4 uni04B5 dcroat uni040F uni045F uni0490 uni0491 OE oe Oslash oslash uni04A6 uni04A7 uni0492 uni0493 uni04BC uni04BD gamma Ohorn -- cgit v1.2.3 From 5397958ba23710b7146b9351d06ce96da66a19bd Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 11 Dec 2015 11:57:25 -0800 Subject: Update calls to cu2qu This is hopefully the last time we have to do this. In response to https://github.com/googlei18n/cu2qu/commit/56f36a1b2ab673c25ac81756d5c251909a7d41e2 --- scripts/lib/fontbuild/Build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py index be0b79c..cf07d55 100644 --- a/scripts/lib/fontbuild/Build.py +++ b/scripts/lib/fontbuild/Build.py @@ -184,10 +184,10 @@ class FontProject: # fewer control points and look noticeably different max_err = 0.002 if self.compatible: - fonts_to_quadratic(*fonts, max_err_em=max_err, dump_report=True) + fonts_to_quadratic(fonts, max_err_em=max_err, dump_stats=True) else: for font in fonts: - fonts_to_quadratic(font, max_err_em=max_err, dump_report=True) + fonts_to_quadratic([font], max_err_em=max_err, dump_stats=True) log(">> Generating TTF files") for font in fonts: -- cgit v1.2.3 From a40611066facdeab2ba726411d8f1acbf907132b Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 11 Dec 2015 12:00:22 -0800 Subject: Update use of nototools' unit tests Due to changes in https://github.com/jamesgk/nototools/commit/7c07e2b2485b29638d9141b809d7eadd547f1b01 https://github.com/jamesgk/nototools/commit/995fe3c706d1e9fb76d5506e36a6ef06f2ea2dfe --- scripts/run_general_tests.py | 18 ++++++++++++++++-- scripts/run_web_tests.py | 4 ++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/scripts/run_general_tests.py b/scripts/run_general_tests.py index ddfae17..1a007d2 100755 --- a/scripts/run_general_tests.py +++ b/scripts/run_general_tests.py @@ -96,8 +96,22 @@ class TestVerticalMetrics(font_tests.TestVerticalMetrics): class TestGlyphAreas(font_tests.TestGlyphAreas): - loaded_fonts = UFOS - masters = UFO_MASTERS + master_weights_to_test = ['Thin', 'Bold'] + instance_weights_to_test = ['Thin', 'Regular', 'Bold'] + exclude = ['Condensed', 'Italic'] + + master_glyph_sets = [ + f.replace('_', '-') for f in UFO_MASTERS[0]], UFO_MASTERS[1] + instance_glyph_sets = FONTS[0], [f.getGlyphSet() for f in FONTS[1]] + + master_glyphs_to_test = UFO_MASTERS[1][0].keys() + instance_glyphs_to_test = FONTS[1][0].getGlyphOrder() + + #TODO maybe fix masters so that whitelisting isn't necessary + whitelist = [ + 'uni0488', # offset 20 units b/w masters, interpolated points are off + 'uni2050' # has flipped component, so contour is backwards in master + ] if __name__ == '__main__': diff --git a/scripts/run_web_tests.py b/scripts/run_web_tests.py index 4f051bf..5d3fd34 100755 --- a/scripts/run_web_tests.py +++ b/scripts/run_web_tests.py @@ -53,8 +53,8 @@ class TestNames(font_tests.TestNames): mark_heavier_as_bold = True expected_copyright = 'Copyright 2011 Google Inc. All Rights Reserved.' - def expected_unique_id(self, full_name): - return full_name + def expected_unique_id(self, family, style): + return family + ' ' + style class TestDigitWidths(font_tests.TestDigitWidths): -- cgit v1.2.3 From f9116ecf24f80e3c9161cd5189e8e9a90c03e908 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 11 Dec 2015 12:04:37 -0800 Subject: Don't set Black weight as bold outside of Android There's no documented reason why we were doing this before, so let's output these fonts for now and see if they work on other platforms. Android can come next. --- scripts/build-v2.py | 4 ++-- scripts/run_general_tests.py | 3 ++- scripts/touchup_for_android.py | 11 +++++++++++ scripts/touchup_for_web.py | 6 ------ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/scripts/build-v2.py b/scripts/build-v2.py index 0ef86e9..6026939 100644 --- a/scripts/build-v2.py +++ b/scripts/build-v2.py @@ -101,7 +101,7 @@ proj.generateFont(Mix([rg, bd], 0.35), "%s/Medium/Regular/Lt"%FAMILYNAME) proj.generateFont(Mix([rg, bd], RPoint(0.73, 0.73)), "%s/Bold/Bold/Rg"%FAMILYNAME) proj.generateFont(Mix([rg, bd], RPoint(1.125, 1.0)), - "%s/Black/Bold/Bk"%FAMILYNAME) + "%s/Black/Regular/Bk"%FAMILYNAME) proj.generateFont(th.font, "%s/Thin Italic/Italic/Th"%FAMILYNAME, italic=True, stemWidth=80) @@ -115,7 +115,7 @@ proj.generateFont(Mix([rg, bd], RPoint(0.73, 0.73)), "%s/Bold Italic/Bold Italic/Rg"%FAMILYNAME, italic=True, stemWidth=290) proj.generateFont(Mix([rg, bd], RPoint(1.125, 1.0)), - "%s/Black Italic/Bold Italic/Bk"%FAMILYNAME, + "%s/Black Italic/Italic/Bk"%FAMILYNAME, italic=True, stemWidth=290) # unfortunately some condensed forms (*.cn) of glyphs are not compatible with diff --git a/scripts/run_general_tests.py b/scripts/run_general_tests.py index 1a007d2..3321fa3 100755 --- a/scripts/run_general_tests.py +++ b/scripts/run_general_tests.py @@ -44,12 +44,13 @@ class TestItalicAngle(font_tests.TestItalicAngle): class TestMetaInfo(font_tests.TestMetaInfo): """Bugs: + https://github.com/google/roboto/issues/142 https://code.google.com/a/google.com/p/roboto/issues/detail?id=8 https://code.google.com/a/google.com/p/roboto/issues/detail?id=29 """ loaded_fonts = FONTS - mark_heavier_as_bold = True + mark_heavier_as_bold = False test_us_weight = None #expected_version = '2.' + roboto_data.get_build_number() diff --git a/scripts/touchup_for_android.py b/scripts/touchup_for_android.py index 187a432..4426bd0 100755 --- a/scripts/touchup_for_android.py +++ b/scripts/touchup_for_android.py @@ -68,6 +68,17 @@ def apply_android_specific_fixes(font): if table in font: del font[table] + # Set bold bits for Black (macStyle bit 0, fsSelection bit 5, subfamily) + name_records = font_data.get_name_records(font) + family_name = name_records[1] + subfam_name = name_records[2] + if family_name.endswith('Black'): + font['head'].macStyle |= (1 << 0) + font['OS/2'].fsSelection |= (1 << 5) + new_subfam_name = ( + ('Bold ' + subfam_name) if subfam_name != 'Regular' else 'Bold') + font_data.set_name_record(font, 2, new_subfam_name) + def correct_font(source_font_name, target_font_name): """Corrects metrics and other meta information.""" diff --git a/scripts/touchup_for_web.py b/scripts/touchup_for_web.py index 3e29e58..7879d9e 100755 --- a/scripts/touchup_for_web.py +++ b/scripts/touchup_for_web.py @@ -47,12 +47,6 @@ def apply_web_specific_fixes(font, family_name): family_name += ' Condensed' full_name = family_name + ' ' + subfamily_name - # macStyle - bold = subfamily_name.startswith(('Bold', 'Black')) - italic = subfamily_name.endswith('Italic') - macStyle = (italic << 1) | bold - font['head'].macStyle = macStyle - # Family, subfamily names font_data.set_name_record(font, 16, family_name) style_map = ['Regular', 'Bold', 'Italic', 'Bold Italic'] -- cgit v1.2.3 From 2b60cf910ae45ddc036d3e0ea5e2621b5f88f6e4 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 11 Dec 2015 12:13:45 -0800 Subject: Add name, metadata tests for general and android There's no reason not to have these, and they're quite helpful for catching little issues. --- scripts/run_android_tests.py | 20 ++++++++++++++++++++ scripts/run_general_tests.py | 14 ++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/scripts/run_android_tests.py b/scripts/run_android_tests.py index 3751670..f6a1f46 100755 --- a/scripts/run_android_tests.py +++ b/scripts/run_android_tests.py @@ -19,12 +19,32 @@ import unittest from nototools.unittests import font_tests +import run_general_tests + FONTS = font_tests.load_fonts( ['out/android/*.ttf'], expected_count=18) +class TestMetaInfo(run_general_tests.TestMetaInfo): + """Bugs: + https://github.com/google/roboto/issues/142 + """ + + loaded_fonts = FONTS + mark_heavier_as_bold = True + + +class TestNames(run_general_tests.TestNames): + """Bugs: + https://github.com/google/roboto/issues/37 + """ + + loaded_fonts = FONTS + mark_heavier_as_bold = True + + class TestVerticalMetrics(font_tests.TestVerticalMetrics): loaded_fonts = FONTS test_glyphs_ymin_ymax = None diff --git a/scripts/run_general_tests.py b/scripts/run_general_tests.py index 3321fa3..78ba44c 100755 --- a/scripts/run_general_tests.py +++ b/scripts/run_general_tests.py @@ -61,6 +61,20 @@ class TestMetaInfo(font_tests.TestMetaInfo): expected_os2_achVendID = 'GOOG' +class TestNames(font_tests.TestNames): + """Bugs: + https://github.com/google/roboto/issues/37 + """ + + loaded_fonts = FONTS + family_name = 'Roboto' + mark_heavier_as_bold = False + expected_copyright = 'Font data copyright Google 2015' + + def expected_unique_id(self, family, style): + return 'Google:%s:2015' % family + + class TestDigitWidths(font_tests.TestDigitWidths): loaded_fonts = FONTS -- cgit v1.2.3 From c57250d04a3645583776a7f3c8d997780bcb0ea6 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 11 Dec 2015 12:16:35 -0800 Subject: Update an outdated context link for keycaps test --- scripts/touchup_for_android.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/touchup_for_android.py b/scripts/touchup_for_android.py index 4426bd0..9c896bc 100755 --- a/scripts/touchup_for_android.py +++ b/scripts/touchup_for_android.py @@ -56,7 +56,7 @@ def apply_android_specific_fixes(font): hhea.lineGap = 0 # Remove combining keycap and the arrows from the cmap table: - # https://code.google.com/a/google.com/p/roboto/issues/detail?id=52 + # https://github.com/google/roboto/issues/99 font_data.delete_from_cmap(font, [ 0x20E3, # COMBINING ENCLOSING KEYCAP 0x2191, # UPWARDS ARROW -- cgit v1.2.3 From 27233df647b182263c7176f3956574cd3a2947cc Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 11 Dec 2015 12:19:02 -0800 Subject: Switch general tests to test unhinted fonts This was changed to test hinted fonts in a4ef18e29638cd1a0c814adb762177a342898eeb, but I think we want to go back to unhinted fonts by default. It can always be changed locally. --- scripts/run_general_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_general_tests.py b/scripts/run_general_tests.py index 78ba44c..a1a9a43 100755 --- a/scripts/run_general_tests.py +++ b/scripts/run_general_tests.py @@ -24,7 +24,7 @@ from nototools.unittests import font_tests import roboto_data FONTS = font_tests.load_fonts( - ['hinted/*.ttf'], + ['out/RobotoTTF/*.ttf', 'out/RobotoCondensedTTF/*.ttf'], expected_count=18) UFOS = font_tests.load_fonts( -- cgit v1.2.3 From db517c3b0639140ebd1516d2669a29d612da1c0b Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 11 Dec 2015 14:59:36 -0800 Subject: Update calls to ufo2ft Due to changes in https://github.com/jamesgk/ufo2ft/commit/6c46f7050bfdd346c33c1312f07830c48fcc07f2 --- scripts/lib/fontbuild/Build.py | 5 +++-- scripts/lib/fontbuild/markFeature.py | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py index cf07d55..a4e5bac 100644 --- a/scripts/lib/fontbuild/Build.py +++ b/scripts/lib/fontbuild/Build.py @@ -28,7 +28,7 @@ from fontbuild.features import readFeatureFile, writeFeatureFile from fontbuild.generateGlyph import generateGlyph from fontbuild.instanceNames import setNamesRF from fontbuild.italics import italicizeGlyph -from fontbuild.markFeature import RobotoFeatureCompiler +from fontbuild.markFeature import RobotoFeatureCompiler, RobotoKernWriter from fontbuild.mitreGlyph import mitreGlyph from fontbuild.mix import Mix,Master,narrowFLGlyph @@ -288,5 +288,6 @@ def saveOTF(font, destFile, truetype=False): compiler = compileTTF else: compiler = compileOTF - otf = compiler(font, featureCompilerClass=RobotoFeatureCompiler) + otf = compiler(font, featureCompilerClass=RobotoFeatureCompiler, + kernWriter=RobotoKernWriter) otf.save(destFile) diff --git a/scripts/lib/fontbuild/markFeature.py b/scripts/lib/fontbuild/markFeature.py index 945cef0..395e537 100755 --- a/scripts/lib/fontbuild/markFeature.py +++ b/scripts/lib/fontbuild/markFeature.py @@ -43,3 +43,8 @@ class RobotoFeatureCompiler(FeatureOTFCompiler): ["a", "uni0430"], ["e", "uni0435"], ["p", "uni0440"], ["c", "uni0441"], ["x", "uni0445"], ["s", "uni0455"], ["i", "uni0456"], ["psi", "uni0471"]] + + +class RobotoKernWriter(KernFeatureWriter): + leftFeaClassRe = r"@_(.+)_L$" + rightFeaClassRe = r"@_(.+)_R$" -- cgit v1.2.3 From 2a2800a5a6a80fa677cae12ca5272a1bb8c2f2b7 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Mon, 14 Dec 2015 17:10:38 -0800 Subject: Change copyright in sources to 2011 date --- src/v2/Roboto_Bold.ufo/fontinfo.plist | 2 +- src/v2/Roboto_Bold.vfb | Bin 415462 -> 415462 bytes src/v2/Roboto_Regular.ufo/fontinfo.plist | 2 +- src/v2/Roboto_Regular.vfb | Bin 471237 -> 471237 bytes src/v2/Roboto_Thin.ufo/fontinfo.plist | 2 +- src/v2/Roboto_Thin.vfb | Bin 405061 -> 405061 bytes 6 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/v2/Roboto_Bold.ufo/fontinfo.plist b/src/v2/Roboto_Bold.ufo/fontinfo.plist index cad5eeb..c0a6068 100644 --- a/src/v2/Roboto_Bold.ufo/fontinfo.plist +++ b/src/v2/Roboto_Bold.ufo/fontinfo.plist @@ -7,7 +7,7 @@ capHeight 1456 copyright - Copyright 2014 Google Inc. All Rights Reserved. + Copyright 2011 Google Inc. All Rights Reserved. descender -555 familyName diff --git a/src/v2/Roboto_Bold.vfb b/src/v2/Roboto_Bold.vfb index 7c54196..5f81153 100644 Binary files a/src/v2/Roboto_Bold.vfb and b/src/v2/Roboto_Bold.vfb differ diff --git a/src/v2/Roboto_Regular.ufo/fontinfo.plist b/src/v2/Roboto_Regular.ufo/fontinfo.plist index 58edbfa..8f7dd23 100644 --- a/src/v2/Roboto_Regular.ufo/fontinfo.plist +++ b/src/v2/Roboto_Regular.ufo/fontinfo.plist @@ -7,7 +7,7 @@ capHeight 1456 copyright - Copyright 2014 Google Inc. All Rights Reserved. + Copyright 2011 Google Inc. All Rights Reserved. descender -555 familyName diff --git a/src/v2/Roboto_Regular.vfb b/src/v2/Roboto_Regular.vfb index 252e7cb..8c9d12b 100644 Binary files a/src/v2/Roboto_Regular.vfb and b/src/v2/Roboto_Regular.vfb differ diff --git a/src/v2/Roboto_Thin.ufo/fontinfo.plist b/src/v2/Roboto_Thin.ufo/fontinfo.plist index 1aac006..e1ba393 100644 --- a/src/v2/Roboto_Thin.ufo/fontinfo.plist +++ b/src/v2/Roboto_Thin.ufo/fontinfo.plist @@ -7,7 +7,7 @@ capHeight 1456 copyright - Copyright 2014 Google Inc. All Rights Reserved. + Copyright 2011 Google Inc. All Rights Reserved. descender -555 familyName diff --git a/src/v2/Roboto_Thin.vfb b/src/v2/Roboto_Thin.vfb index d3cf141..ee8c2f6 100644 Binary files a/src/v2/Roboto_Thin.vfb and b/src/v2/Roboto_Thin.vfb differ -- cgit v1.2.3 From bd17627cd66730ba06b9a305d02266c886eb870a Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Mon, 14 Dec 2015 17:14:43 -0800 Subject: Don't change copyright in build scripts The copyright message now found in the source files is correct. --- scripts/lib/fontbuild/instanceNames.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/lib/fontbuild/instanceNames.py b/scripts/lib/fontbuild/instanceNames.py index 281eb03..890b5a6 100644 --- a/scripts/lib/fontbuild/instanceNames.py +++ b/scripts/lib/fontbuild/instanceNames.py @@ -57,7 +57,7 @@ class InstanceNames: f.info.versionMajor = version f.info.versionMinor = versionMinor f.info.year = self.year - f.info.copyright = "Font data copyright %s %s" %(self.foundry, self.year) + #f.info.copyright = "Font data copyright %s %s" %(self.foundry, self.year) f.info.trademark = "%s is a trademark of %s." %(self.longfamily, self.foundry) f.info.openTypeNameDesigner = "Christian Robertson" -- cgit v1.2.3 From 6cc897c80ab9c515769dd9f7ea8c7341bafea567 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Mon, 14 Dec 2015 17:26:26 -0800 Subject: Test for correct copyright message --- scripts/run_general_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_general_tests.py b/scripts/run_general_tests.py index a1a9a43..494651d 100755 --- a/scripts/run_general_tests.py +++ b/scripts/run_general_tests.py @@ -69,7 +69,7 @@ class TestNames(font_tests.TestNames): loaded_fonts = FONTS family_name = 'Roboto' mark_heavier_as_bold = False - expected_copyright = 'Font data copyright Google 2015' + expected_copyright = 'Copyright 2011 Google Inc. All Rights Reserved.' def expected_unique_id(self, family, style): return 'Google:%s:2015' % family -- cgit v1.2.3 From 69467fac76d48280050a3e1770f6595dc6967b6a Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Thu, 17 Dec 2015 16:00:45 -0800 Subject: Clear fsSelection regular bit for Black --- scripts/touchup_for_android.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/touchup_for_android.py b/scripts/touchup_for_android.py index 9c896bc..e13adea 100755 --- a/scripts/touchup_for_android.py +++ b/scripts/touchup_for_android.py @@ -75,6 +75,7 @@ def apply_android_specific_fixes(font): if family_name.endswith('Black'): font['head'].macStyle |= (1 << 0) font['OS/2'].fsSelection |= (1 << 5) + font['OS/2'].fsSelection &= ~(1 << 6) new_subfam_name = ( ('Bold ' + subfam_name) if subfam_name != 'Regular' else 'Bold') font_data.set_name_record(font, 2, new_subfam_name) -- cgit v1.2.3