summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/build-v2.py61
-rw-r--r--scripts/lib/fontbuild/Build.py109
-rw-r--r--scripts/lib/fontbuild/generateGlyph.py116
-rw-r--r--scripts/lib/fontbuild/instanceNames.py4
-rwxr-xr-xscripts/lib/fontbuild/markFeature.py39
-rw-r--r--scripts/lib/fontbuild/mix.py47
-rwxr-xr-xscripts/render.sh2
-rw-r--r--scripts/roboto_data.py18
-rwxr-xr-xscripts/run_android_tests.py31
-rwxr-xr-xscripts/run_exhaustive_tests.py2
-rwxr-xr-xscripts/run_general_tests.py48
-rwxr-xr-xscripts/run_web_tests.py36
-rw-r--r--scripts/temporary_touchups.py30
-rwxr-xr-xscripts/touchup_for_android.py51
-rwxr-xr-xscripts/touchup_for_cros.py60
-rwxr-xr-xscripts/touchup_for_web.py73
16 files changed, 412 insertions, 315 deletions
diff --git a/scripts/build-v2.py b/scripts/build-v2.py
index 6026939..181c0ec 100644
--- a/scripts/build-v2.py
+++ b/scripts/build-v2.py
@@ -31,14 +31,9 @@ BASEDIR = os.path.abspath(
# Masters
-rg = Master("%s/src/v2/Roboto_Regular.ufo" % BASEDIR,
- anchorPath="%s/res/anchors_regular.json" % BASEDIR)
-bd = Master("%s/src/v2/Roboto_Bold.ufo" % BASEDIR,
- anchorPath="%s/res/anchors_bold.json" % BASEDIR)
-th = Master("%s/src/v2/Roboto_Thin.ufo" % BASEDIR,
- anchorPath="%s/res/anchors_thin.json" % BASEDIR)
-
-# build condensed masters
+rg = Master("%s/src/v2/Roboto-Regular.ufo" % BASEDIR)
+bd = Master("%s/src/v2/Roboto-Bold.ufo" % BASEDIR)
+th = Master("%s/src/v2/Roboto-Thin.ufo" % BASEDIR)
lessCondensed = (
"plusminus bracketleft bracketright dieresis macron "
@@ -65,7 +60,6 @@ def condenseFont(font, scale=.8, stemWidth=185):
# for g in [f[name] for name in LC]:
for g in f:
if len(g) > 0:
- # print g.name
if g.name in lessCondensed:
scale = xscale * 1.1
if g.name in uncondensed:
@@ -83,39 +77,38 @@ def condenseFont(font, scale=.8, stemWidth=185):
return f
-proj = FontProject(rg.font, BASEDIR, "res/roboto.cfg", th.ffont)
-#proj.incrementBuildNumber()
+proj = FontProject(rg.font, BASEDIR, "res/roboto.cfg")
-# FAMILYNAME = "Roboto 2 DRAFT"
-# FAMILYNAME = "Roboto2"
FAMILYNAME = "Roboto"
proj.buildOTF = 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)
+proj.generateFont(th.font, "%s/Thin/Regular/Th" % FAMILYNAME)
+proj.generateFont(Mix([th, rg], 0.45), "%s/Light/Regular/Lt" % FAMILYNAME)
proj.generateFont(Mix([th, rg], RPoint(0.90, 0.92)),
- "%s/Regular/Regular/Rg"%FAMILYNAME)
-proj.generateFont(Mix([rg, bd], 0.35), "%s/Medium/Regular/Lt"%FAMILYNAME)
+ "%s/Regular/Regular/Rg" % FAMILYNAME)
+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)
+ "%s/Bold/Bold/Rg" % FAMILYNAME)
proj.generateFont(Mix([rg, bd], RPoint(1.125, 1.0)),
- "%s/Black/Regular/Bk"%FAMILYNAME)
+ "%s/Black/Regular/Bk" % FAMILYNAME)
-proj.generateFont(th.font, "%s/Thin Italic/Italic/Th"%FAMILYNAME,
+proj.generateFont(th.font, "%s/Thin Italic/Italic/Th" % FAMILYNAME,
italic=True, stemWidth=80)
-proj.generateFont(Mix([th, rg], 0.45), "%s/Light Italic/Italic/Lt"%FAMILYNAME,
+proj.generateFont(Mix([th, rg], 0.45), "%s/Light Italic/Italic/Lt" % FAMILYNAME,
italic=True, stemWidth=120)
proj.generateFont(Mix([th, rg], RPoint(0.90, 0.92)),
- "%s/Italic/Italic/Rg"%FAMILYNAME, italic=True, stemWidth=185)
-proj.generateFont(Mix([rg, bd], 0.35), "%s/Medium Italic/Italic/Lt"%FAMILYNAME,
+ "%s/Italic/Italic/Rg" % FAMILYNAME,
+ italic=True, stemWidth=185)
+proj.generateFont(Mix([rg, bd], 0.35),
+ "%s/Medium Italic/Italic/Lt" % FAMILYNAME,
italic=True, stemWidth=230)
proj.generateFont(Mix([rg, bd], RPoint(0.73, 0.73)),
- "%s/Bold Italic/Bold Italic/Rg"%FAMILYNAME,
+ "%s/Bold Italic/Bold Italic/Rg" % FAMILYNAME,
italic=True, stemWidth=290)
proj.generateFont(Mix([rg, bd], RPoint(1.125, 1.0)),
- "%s/Black Italic/Italic/Bk"%FAMILYNAME,
+ "%s/Black Italic/Italic/Bk" % FAMILYNAME,
italic=True, stemWidth=290)
# unfortunately some condensed forms (*.cn) of glyphs are not compatible with
@@ -127,23 +120,29 @@ cn1 = Master(rg.ffont.addDiff(thcn1.ffont, th.ffont))
bdcn1 = Master(bd.ffont.addDiff(thcn1.ffont, th.ffont))
proj.generateFont(Mix([thcn1, cn1], RPoint(0.45, 0.47)),
- "%s Condensed/Light/Regular/Lt"%FAMILYNAME,
+ "%s Condensed/Light/Regular/Lt" % FAMILYNAME,
swapSuffixes=[".cn"])
proj.generateFont(Mix([thcn1, cn1], RPoint(0.9, 0.92)),
- "%s Condensed/Regular/Regular/Rg"%FAMILYNAME,
+ "%s Condensed/Regular/Regular/Rg" % FAMILYNAME,
+ swapSuffixes=[".cn"])
+proj.generateFont(Mix([cn1, bdcn1], 0.4),
+ "%s Condensed/Medium/Regular/Lt"%FAMILYNAME,
swapSuffixes=[".cn"])
proj.generateFont(Mix([cn1, bdcn1], RPoint(0.75, 0.75)),
- "%s Condensed/Bold/Bold/Rg"%FAMILYNAME,
+ "%s Condensed/Bold/Bold/Rg" % FAMILYNAME,
swapSuffixes=[".cn"])
proj.generateFont(Mix([thcn1, cn1], RPoint(0.45, 0.47)),
- "%s Condensed/Light Italic/Italic/Lt"%FAMILYNAME,
+ "%s Condensed/Light Italic/Italic/Lt" % FAMILYNAME,
italic=True, swapSuffixes=[".cn"], stemWidth=120)
proj.generateFont(Mix([thcn1, cn1], RPoint(0.9, 0.92)),
- "%s Condensed/Italic/Italic/Rg"%FAMILYNAME,
+ "%s Condensed/Italic/Italic/Rg" % FAMILYNAME,
italic=True, swapSuffixes=[".cn"], stemWidth=185)
+proj.generateFont(Mix([cn1, bdcn1], 0.4),
+ "%s Condensed/Medium Italic/Italic/Lt"%FAMILYNAME,
+ italic=True, swapSuffixes=[".cn"], stemWidth=230)
proj.generateFont(Mix([cn1, bdcn1], RPoint(0.75, 0.75)),
- "%s Condensed/Bold Italic/Bold Italic/Rg"%FAMILYNAME,
+ "%s Condensed/Bold Italic/Bold Italic/Rg" % FAMILYNAME,
italic=True, swapSuffixes=[".cn"], stemWidth=240)
proj.generateTTFs()
diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py
index a4e5bac..03defa6 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 cu2qu.rf import fonts_to_quadratic
+from cu2qu.ufo import fonts_to_quadratic
from fontTools.misc.transform import Transform
from robofab.world import OpenFont
from ufo2ft import compileOTF, compileTTF
@@ -34,23 +34,23 @@ from fontbuild.mix import Mix,Master,narrowFLGlyph
class FontProject:
-
- def __init__(self, basefont, basedir, configfile, thinfont = None):
+
+ def __init__(self, basefont, basedir, configfile):
self.basefont = basefont
- self.thinfont = thinfont
self.basedir = basedir
self.config = ConfigParser.RawConfigParser()
- self.configfile = self.basedir+"/"+configfile
+ self.configfile = os.path.join(self.basedir, configfile)
self.config.read(self.configfile)
-
- diacriticList = open(self.basedir + "/" + self.config.get("res","diacriticfile")).readlines()
- self.diacriticList = [line.strip() for line in diacriticList if not line.startswith("#")]
- self.ot_classes = open(self.basedir + "/" + self.config.get("res","ot_classesfile")).read()
- self.ot_kerningclasses = open(self.basedir + "/" + self.config.get("res","ot_kerningclassesfile")).read()
- #self.ot_features = open(self.basedir + "/" + self.config.get("res","ot_featuresfile")).read()
- adobeGlyphList = open(self.basedir + "/" + self.config.get("res", "agl_glyphlistfile")).readlines()
- self.adobeGlyphList = dict([line.split(";") for line in adobeGlyphList if not line.startswith("#")])
-
+
+ self.diacriticList = [
+ line.strip() for line in self.openResource("diacriticfile")
+ if not line.startswith("#")]
+ self.adobeGlyphList = dict(
+ line.split(";") for line in self.openResource("agl_glyphlistfile")
+ if not line.startswith("#"))
+ self.glyphOrder = self.openResource("glyphorder")
+ self.thinGlyphOrder = self.openResource("glyphorder_thin")
+
# map exceptional glyph names in Roboto to names in the AGL
roboNames = (
('Obar', 'Ocenteredtilde'), ('obar', 'obarred'),
@@ -64,30 +64,16 @@ class FontProject:
self.lessItalic = self.config.get("glyphs","lessitalic").split()
self.deleteList = self.config.get("glyphs","delete").split()
self.noItalic = self.config.get("glyphs","noitalic").split()
- self.buildnumber = self.loadBuildNumber()
-
+
self.buildOTF = False
self.compatible = False
self.generatedFonts = []
-
-
- def loadBuildNumber(self):
- versionFile = open(self.basedir + "/" + self.config.get("main","buildnumberfile"), "r+")
- buildnumber = int(versionFile.read().strip())
- buildnumber = "%05d" %(int(buildnumber) + 1)
- print "BuildNumber: %s" %(buildnumber)
- versionFile.close()
- return buildnumber
-
- def incrementBuildNumber(self):
- if len(self.buildnumber) > 0:
- versionFile = open(self.basedir + "/" + self.config.get("main","buildnumberfile"), "r+")
- versionFile.seek(0)
- versionFile.write(self.buildnumber)
- versionFile.truncate()
- versionFile.close()
- else:
- raise Exception("Empty build number")
+
+ def openResource(self, name):
+ with open(os.path.join(
+ self.basedir, self.config.get("res", name))) as resourceFile:
+ resource = resourceFile.read()
+ return resource.splitlines()
def generateOutputPath(self, font, ext):
family = font.info.familyName.replace(" ", "")
@@ -96,9 +82,9 @@ class FontProject:
if not os.path.exists(path):
os.makedirs(path)
return os.path.join(path, "%s-%s.%s" % (family, style, ext))
-
- def generateFont(self, mix, names, italic=False, swapSuffixes=None, stemWidth=185, kern=True):
-
+
+ def generateFont(self, mix, names, italic=False, swapSuffixes=None, stemWidth=185):
+
n = names.split("/")
log("---------------------\n%s %s\n----------------------" %(n[0],n[1]))
log(">> Mixing masters")
@@ -118,7 +104,7 @@ class FontProject:
for g in f:
i += 1
if i % 10 == 0: print g.name
-
+
if g.name == "uniFFFD":
continue
@@ -128,11 +114,12 @@ class FontProject:
italicizeGlyph(f, g, 9, stemWidth=stemWidth)
elif False == (g.name in self.noItalic):
italicizeGlyph(f, g, 10, stemWidth=stemWidth)
- #elif g.name != ".notdef":
- # italicizeGlyph(g, 10, stemWidth=stemWidth)
if g.width != 0:
g.width += 10
+ # set the oblique flag in fsSelection
+ f.info.openTypeOS2Selection.append(9)
+
if swapSuffixes != None:
for swap in swapSuffixes:
swapList = [g.name for g in f if g.name.endswith(swap)]
@@ -146,7 +133,7 @@ class FontProject:
log(">> Generating glyphs")
generateGlyphs(f, self.diacriticList, self.adobeGlyphList)
log(">> Copying features")
- readFeatureFile(f, self.ot_classes + self.basefont.features.text)
+ readFeatureFile(f, self.basefont.features.text)
log(">> Decomposing")
for gname in self.decompose:
if f.has_key(gname):
@@ -158,10 +145,6 @@ class FontProject:
cleanCurves(f)
deleteGlyphs(f, self.deleteList)
- if kern:
- log(">> Generating kern classes")
- readFeatureFile(f, self.ot_kerningclasses)
-
log(">> Generating font files")
ufoName = self.generateOutputPath(f, "ufo")
f.save(ufoName)
@@ -171,7 +154,9 @@ class FontProject:
log(">> Generating OTF file")
newFont = OpenFont(ufoName)
otfName = self.generateOutputPath(f, "otf")
- saveOTF(newFont, otfName)
+ saveOTF(
+ newFont, otfName,
+ self.thinGlyphOrder if "Thin" in otfName else self.glyphOrder)
def generateTTFs(self):
"""Build TTF for each font generated since last call to generateTTFs."""
@@ -184,19 +169,19 @@ 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_stats=True)
+ fonts_to_quadratic(fonts, max_err_em=max_err, dump_stats=True, reverse_direction=True)
else:
for font in fonts:
- fonts_to_quadratic([font], max_err_em=max_err, dump_stats=True)
+ fonts_to_quadratic([font], max_err_em=max_err, dump_stats=True, reverse_direction=True)
log(">> Generating TTF files")
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)
+ saveOTF(
+ font, ttfName,
+ self.thinGlyphOrder if "Thin" in ttfName else self.glyphOrder,
+ truetype=True)
def transformGlyphMembers(g, m):
@@ -219,6 +204,7 @@ def transformGlyphMembers(g, m):
s.Transform(m)
#c.scale = s
+
def swapContours(f,gName1,gName2):
try:
g1 = f[gName1]
@@ -248,7 +234,7 @@ def log(msg):
def generateGlyphs(f, glyphNames, glyphList={}):
log(">> Generating diacritics")
glyphnames = [gname for gname in glyphNames if not gname.startswith("#") and gname != ""]
-
+
for glyphName in glyphNames:
generateGlyph(f, glyphName, glyphList)
@@ -260,7 +246,7 @@ def cleanCurves(f):
# log(">> Mitring sharp corners")
# for g in f:
# mitreGlyph(g, 3., .7)
-
+
# log(">> Converting curves to quadratic")
# for g in f:
# glyphCurvesToQuadratic(g)
@@ -281,13 +267,16 @@ def removeGlyphOverlap(glyph):
manager.union(contours, glyph.getPointPen())
-def saveOTF(font, destFile, truetype=False):
+def saveOTF(font, destFile, glyphOrder, truetype=False):
"""Save a RoboFab font as an OTF binary using ufo2fdk."""
if truetype:
- compiler = compileTTF
+ otf = compileTTF(font, featureCompilerClass=RobotoFeatureCompiler,
+ kernWriter=RobotoKernWriter, glyphOrder=glyphOrder,
+ convertCubics=False,
+ useProductionNames=False)
else:
- compiler = compileOTF
- otf = compiler(font, featureCompilerClass=RobotoFeatureCompiler,
- kernWriter=RobotoKernWriter)
+ otf = compileOTF(font, featureCompilerClass=RobotoFeatureCompiler,
+ kernWriter=RobotoKernWriter, glyphOrder=glyphOrder,
+ useProductionNames=False)
otf.save(destFile)
diff --git a/scripts/lib/fontbuild/generateGlyph.py b/scripts/lib/fontbuild/generateGlyph.py
index 5787a37..465f940 100644
--- a/scripts/lib/fontbuild/generateGlyph.py
+++ b/scripts/lib/fontbuild/generateGlyph.py
@@ -14,91 +14,53 @@
import re
-from anchors import alignComponentsToAnchors
from string import find
+from anchors import alignComponentsToAnchors, getAnchorByName
+
+
def parseComposite(composite):
c = composite.split("=")
d = c[1].split("/")
glyphName = d[0]
if len(d) == 1:
- offset = [0,0]
+ offset = [0, 0]
else:
offset = [int(i) for i in d[1].split(",")]
accentString = c[0]
accents = accentString.split("+")
baseName = accents.pop(0)
- accentNames = [i.split(":") for i in accents ]
+ accentNames = [i.split(":") for i in accents]
return (glyphName, baseName, accentNames, offset)
def copyMarkAnchors(f, g, srcname, width):
- unicode_range = range(0x0030, 0x02B0) + range(0x1E00, 0x1EFF)
- anchors = f[srcname].anchors
- for anchor in anchors:
- if "top_dd" == anchor.name:
- g.appendAnchor(anchor.name, (anchor.x + width, anchor.y))
- if "bottom_dd" == anchor.name:
+ for anchor in f[srcname].anchors:
+ if anchor.name in ("top_dd", "bottom_dd", "top0315"):
g.appendAnchor(anchor.name, (anchor.x + width, anchor.y))
- if "top0315" == anchor.name:
- g.appendAnchor(anchor.name, (anchor.x + width, anchor.y))
- if "top" == anchor.name:
- if g.unicode == None:
- if not g.name.endswith(('.ccmp', '.smcp', '.NAV')):
- continue
- if False == (g.unicode in unicode_range):
- if not g.name.endswith(('.ccmp', '.smcp', '.NAV')):
- continue
- #if g.unicode > 0x02B0:
- # continue
- parenttop_present = 0
- for anc in g.anchors:
- if anc.name == "parent_top":
- parenttop_present = 1
- if 0 == parenttop_present:
- g.appendAnchor("parent_top", anchor.position)
-
- if "bottom" == anchor.name:
- if g.unicode == None:
- if -1 == find(g.name, ".smcp"):
- continue
- if False == (g.unicode in unicode_range):
- if -1 == find(g.name, ".smcp"):
- continue
- #if g.unicode > 0x02B0:
- # continue
- bottom_present = 0
- for anc in g.anchors:
- if anc.name == "bottom":
- bottom_present = 1
- if 0 == bottom_present:
- g.appendAnchor("bottom", anchor.position)
-
-
-# g.appendAnchor("top", anchor.position)
-
- # if "rhotichook" == anchor.name:
- # g.appendAnchor(anchor.name, (anchor.x + width, anchor.y))
-
- #print g.anchors
- for anchor in g.anchors:
- if "top" == anchor.name:
- #print g.name, g.anchors
- return
-
- anchor_parent_top = None
- for anchor in g.anchors:
- if "parent_top" == anchor.name:
- anchor_parent_top = anchor
- break
+ if ("top" == anchor.name and
+ not any(a.name == "parent_top" for a in g.anchors)):
+ g.appendAnchor("parent_top", anchor.position)
+ if ("bottom" == anchor.name and
+ not any(a.name == "bottom" for a in g.anchors)):
+ g.appendAnchor("bottom", anchor.position)
+
+ if any(a.name == "top" for a in g.anchors):
+ return
+
+ anchor_parent_top = getAnchorByName(g, "parent_top")
if anchor_parent_top is not None:
g.appendAnchor("top", anchor_parent_top.position)
def generateGlyph(f,gname,glyphList={}):
glyphName, baseName, accentNames, offset = parseComposite(gname)
+ if f.has_key(glyphName):
+ print('Existing glyph "%s" found in font, ignoring composition rule '
+ '"%s"' % (glyphName, gname))
+ return
if baseName.find("_") != -1:
g = f.newGlyph(glyphName)
@@ -107,25 +69,21 @@ def generateGlyph(f,gname,glyphList={}):
g.width += f[componentName].width
setUnicodeValue(g, glyphList)
- else:
- if not f.has_key(glyphName):
- try:
- f.compileGlyph(glyphName, baseName, accentNames)
- except KeyError as e:
- print ("KeyError raised for composition rule '%s', likely %s "
- "anchor not found in glyph '%s'" % (gname, e, baseName))
- return
- g = f[glyphName]
- setUnicodeValue(g, glyphList)
- copyMarkAnchors(f, g, baseName, offset[1] + offset[0])
- if offset[0] != 0 or offset[1] != 0:
- g.width += offset[1] + offset[0]
- g.move((offset[0], 0), anchors=False)
- if len(accentNames) > 0:
- alignComponentsToAnchors(f, glyphName, baseName, accentNames)
- else:
- print ("Existing glyph '%s' found in font, ignoring composition "
- "rule '%s'" % (glyphName, gname))
+ else:
+ try:
+ f.compileGlyph(glyphName, baseName, accentNames)
+ except KeyError as e:
+ print('KeyError raised for composition rule "%s", likely "%s" '
+ 'anchor not found in glyph "%s"' % (gname, e, baseName))
+ return
+ g = f[glyphName]
+ setUnicodeValue(g, glyphList)
+ copyMarkAnchors(f, g, baseName, offset[1] + offset[0])
+ if len(accentNames) > 0:
+ alignComponentsToAnchors(f, glyphName, baseName, accentNames)
+ if offset[0] != 0 or offset[1] != 0:
+ g.width += offset[1] + offset[0]
+ g.move((offset[0], 0), anchors=False)
def setUnicodeValue(glyph, glyphList):
diff --git a/scripts/lib/fontbuild/instanceNames.py b/scripts/lib/fontbuild/instanceNames.py
index 890b5a6..dd7cba1 100644
--- a/scripts/lib/fontbuild/instanceNames.py
+++ b/scripts/lib/fontbuild/instanceNames.py
@@ -66,8 +66,8 @@ class InstanceNames:
f.info.openTypeNameManufacturerURL = self.foundry + ".com"
f.info.openTypeNameLicense = self.license
f.info.openTypeNameLicenseURL = self.licenseURL
- f.info.openTypeNameVersion = "%i.%i" %(version,versionMinor)
- f.info.openTypeNameUniqueID = "%s:%s:%s" %(self.foundry, self.longfamily, self.year)
+ f.info.openTypeNameVersion = "Version %i.%i" %(version,versionMinor)
+ f.info.openTypeNameUniqueID = "%s:%s:%s" %(self.foundry, self.fullname, self.year)
# f.info.openTypeNameDescription = ""
# f.info.openTypeNameCompatibleFullName = ""
# f.info.openTypeNameSampleText = ""
diff --git a/scripts/lib/fontbuild/markFeature.py b/scripts/lib/fontbuild/markFeature.py
index 395e537..9d091bb 100755
--- a/scripts/lib/fontbuild/markFeature.py
+++ b/scripts/lib/fontbuild/markFeature.py
@@ -23,26 +23,31 @@ class RobotoFeatureCompiler(FeatureOTFCompiler):
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]]
+ ["top", "_marktop"],
+ ["bottom", "_markbottom"],
+ ["top_dd", "_marktop_dd"],
+ ["bottom_dd", "_markbottom_dd"],
+ ["rhotichook", "_markrhotichook"],
+ ["top0315", "_marktop0315"],
+ ["parent_top", "_markparent_top"],
+ ["parenthesses.w1", "_markparenthesses.w1"],
+ ["parenthesses.w2", "_markparenthesses.w2"],
+ ["parenthesses.w3", "_markparenthesses.w3"]]
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"]]
+ ["mkmkbottom_acc", "_markbottom"],
+
+ # By providing a pair with accent anchor _bottom and no base anchor,
+ # we designate all glyphs with _bottom as accents (so that they will
+ # be used as base glyphs for mkmk features) without generating any
+ # positioning rules actually using this anchor (which is instead
+ # used to generate composite glyphs). This is all for consistency
+ # with older roboto versions.
+ ["", "_bottom"],
+ ]
+
+ self.ligaAnchorPairs = []
class RobotoKernWriter(KernFeatureWriter):
diff --git a/scripts/lib/fontbuild/mix.py b/scripts/lib/fontbuild/mix.py
index 519b50d..c958701 100644
--- a/scripts/lib/fontbuild/mix.py
+++ b/scripts/lib/fontbuild/mix.py
@@ -218,13 +218,12 @@ class FGlyph:
class Master:
- def __init__(self, font=None, v=0, kernlist=None, overlay=None,
- anchorPath=None):
+ def __init__(self, font=None, v=0, kernlist=None, overlay=None):
if isinstance(font, FFont):
self.font = None
self.ffont = font
elif isinstance(font,str):
- self.openFont(font,overlay, anchorPath)
+ self.openFont(font,overlay)
elif isinstance(font,Mix):
self.font = font
else:
@@ -243,7 +242,7 @@ class Master:
and not k[0] == ""]
#TODO implement class based kerning / external kerning file
- def openFont(self, path, overlayPath=None, anchorPath=None):
+ def openFont(self, path, overlayPath=None):
self.font = OpenFont(path)
for g in self.font:
size = len(g)
@@ -257,16 +256,6 @@ class Master:
for overlayGlyph in overlayFont:
font.insertGlyph(overlayGlyph)
- # work around a bug with vfb2ufo in which anchors are dropped from
- # glyphs containing components and no contours. "anchorPath" should
- # point to the output of src/v2/get_dropped_anchors.py
- if anchorPath:
- anchorData = json.load(open(anchorPath))
- for glyphName, anchors in anchorData.items():
- glyph = self.font[glyphName]
- for name, (x, y) in anchors.items():
- glyph.appendAnchor(str(name), (x, y))
-
self.ffont = FFont(self.font)
@@ -345,14 +334,24 @@ def interpolate(a,b,v,e=0):
qe = (b-a)*v*v*v + a #cubic easing
le = a+(b-a)*v # linear easing
return le + (qe-le) * e
-
+
def interpolateKerns(kA, kB, v):
- kerns = {}
- for pair in kA.keys():
- matchedKern = kB.get(pair)
- # if matchedkern == None:
- # matchedkern = Kern(kA)
- # matchedkern.value = 0
- if matchedKern != None:
- kerns[pair] = interpolate(kA[pair], matchedKern, v.x)
- return kerns
+ # to yield correct kerning for Roboto output, we must emulate the behavior
+ # of old versions of this code; namely, take the kerning values of the first
+ # master instead of actually interpolating.
+ # old code:
+ # https://github.com/google/roboto/blob/7f083ac31241cc86d019ea6227fa508b9fcf39a6/scripts/lib/fontbuild/mix.py
+ # bug:
+ # https://github.com/google/roboto/issues/213
+
+ #kerns = {}
+ #for pair, val in kA.items():
+ # kerns[pair] = interpolate(val, kB.get(pair, 0), v.x)
+ #for pair, val in kB.items():
+ # lerped_val = interpolate(val, kA.get(pair, 0), 1 - v.x)
+ # if pair in kerns:
+ # assert abs(kerns[pair] - lerped_val) < 1e-6
+ # else:
+ # kerns[pair] = lerped_val
+ #return kerns
+ return dict(kA)
diff --git a/scripts/render.sh b/scripts/render.sh
index 2266b2a..9e2862b 100755
--- a/scripts/render.sh
+++ b/scripts/render.sh
@@ -16,7 +16,7 @@
HARFBUZZ=$HOME/harfbuzz
-FONTDIR=$(dirname $0)/../hinted
+FONTDIR=$(dirname $0)/../src/hinted
input_file=$1
diff --git a/scripts/roboto_data.py b/scripts/roboto_data.py
index a9a947a..eaeaaea 100644
--- a/scripts/roboto_data.py
+++ b/scripts/roboto_data.py
@@ -14,16 +14,18 @@
"""General module for Roboto-specific data and methods."""
+import ConfigParser
import os
-from os import path
import re
-def get_build_number():
- """Returns the build number as a five-digit string."""
- build_number_txt = path.join(
- path.dirname(__file__), os.pardir, 'res', 'buildnumber.txt')
- build_number = open(build_number_txt).read().strip()
- assert re.match('[0-9]{5}', build_number)
- return build_number
+def get_version_number():
+ """Returns the version number as a string."""
+ config_parser = ConfigParser.RawConfigParser()
+ config_file = os.path.join(
+ os.path.dirname(__file__), os.pardir, 'res', 'roboto.cfg')
+ config_parser.read(config_file)
+ version_number = config_parser.get('main', 'version')
+ assert re.match(r'[0-9]+\.[0-9]{3}', version_number)
+ return version_number
diff --git a/scripts/run_android_tests.py b/scripts/run_android_tests.py
index f6a1f46..9bf52e7 100755
--- a/scripts/run_android_tests.py
+++ b/scripts/run_android_tests.py
@@ -24,7 +24,11 @@ import run_general_tests
FONTS = font_tests.load_fonts(
['out/android/*.ttf'],
- expected_count=18)
+ expected_count=20)
+
+
+class TestItalicAngle(run_general_tests.TestItalicAngle):
+ loaded_fonts = FONTS
class TestMetaInfo(run_general_tests.TestMetaInfo):
@@ -35,6 +39,18 @@ class TestMetaInfo(run_general_tests.TestMetaInfo):
loaded_fonts = FONTS
mark_heavier_as_bold = True
+ def test_glyphs_dont_round_to_grid(self):
+ """Bug: https://github.com/google/roboto/issues/153"""
+
+ for font in self.fonts:
+ glyph_set = font.getGlyphSet()
+
+ # only concerned with this glyph for now, but maybe more later
+ for name in ['ellipsis']:
+ glyph = glyph_set[name]._glyph
+ for component in glyph.components:
+ self.assertFalse(component.flags & (1 << 2))
+
class TestNames(run_general_tests.TestNames):
"""Bugs:
@@ -42,22 +58,25 @@ class TestNames(run_general_tests.TestNames):
"""
loaded_fonts = FONTS
- mark_heavier_as_bold = True
class TestVerticalMetrics(font_tests.TestVerticalMetrics):
loaded_fonts = FONTS
- test_glyphs_ymin_ymax = None
- test_hhea_table_metrics = None
test_os2_metrics = None
+ # tests yMin and yMax to be equal to Roboto v1 values
+ # android requires this, and web fonts expect this
expected_head_yMin = -555
expected_head_yMax = 2163
+ # test ascent, descent, and lineGap to be equal to Roboto v1 values
+ expected_hhea_descent = -500
+ expected_hhea_ascent = 1900
+ expected_hhea_lineGap = 0
+
class TestDigitWidths(font_tests.TestDigitWidths):
loaded_fonts = FONTS
- test_superscript_digits = None
class TestCharacterCoverage(font_tests.TestCharacterCoverage):
@@ -76,7 +95,7 @@ class TestCharacterCoverage(font_tests.TestCharacterCoverage):
) - include # don't exclude legacy PUA
-class TestSpacingMarks(font_tests.TestSpacingMarks):
+class TestLigatures(run_general_tests.TestLigatures):
loaded_fonts = FONTS
diff --git a/scripts/run_exhaustive_tests.py b/scripts/run_exhaustive_tests.py
index 10dd2d7..5c94dca 100755
--- a/scripts/run_exhaustive_tests.py
+++ b/scripts/run_exhaustive_tests.py
@@ -21,7 +21,7 @@ from nototools.unittests import font_tests
FONTS = font_tests.load_fonts(
['out/RobotoTTF/*.ttf', 'out/RobotoCondensedTTF/*.ttf'],
- expected_count=18)
+ expected_count=20)
class TestSpacingMarks(font_tests.TestSpacingMarks):
diff --git a/scripts/run_general_tests.py b/scripts/run_general_tests.py
index 494651d..fbae2cf 100755
--- a/scripts/run_general_tests.py
+++ b/scripts/run_general_tests.py
@@ -1,4 +1,5 @@
#!/usr/bin/python
+# coding=UTF-8
#
# Copyright 2015 Google Inc. All Rights Reserved.
#
@@ -25,18 +26,14 @@ import roboto_data
FONTS = font_tests.load_fonts(
['out/RobotoTTF/*.ttf', 'out/RobotoCondensedTTF/*.ttf'],
- expected_count=18)
-
-UFOS = font_tests.load_fonts(
- ['out/RobotoUFO/*.ufo', 'out/RobotoCondensedUFO/*.ufo'],
- expected_count=18,
- font_class=OpenFont)
+ expected_count=20)
UFO_MASTERS = font_tests.load_fonts(
['src/v2/*.ufo'],
expected_count=3,
font_class=OpenFont)
+
class TestItalicAngle(font_tests.TestItalicAngle):
loaded_fonts = FONTS
expected_italic_angle = -12.0
@@ -51,10 +48,10 @@ class TestMetaInfo(font_tests.TestMetaInfo):
loaded_fonts = FONTS
mark_heavier_as_bold = False
+ mark_italic_as_oblique = True
test_us_weight = None
- #expected_version = '2.' + roboto_data.get_build_number()
- test_version_numbers = None
+ expected_version = roboto_data.get_version_number()
# fsType of 0 marks the font free for installation, embedding, etc.
expected_os2_fsType = 0
@@ -72,7 +69,7 @@ class TestNames(font_tests.TestNames):
expected_copyright = 'Copyright 2011 Google Inc. All Rights Reserved.'
def expected_unique_id(self, family, style):
- return 'Google:%s:2015' % family
+ return 'Google:%s %s:2016' % (family, style)
class TestDigitWidths(font_tests.TestDigitWidths):
@@ -94,10 +91,24 @@ class TestCharacterCoverage(font_tests.TestCharacterCoverage):
class TestLigatures(font_tests.TestLigatures):
loaded_fonts = FONTS
+ active = (
+ (None, ('fi', 'fl', 'ffi', 'ffl')),
+ ('--language=FRA', ('fi', 'fl', 'ffi', 'ffl')),
+ ('--language=TRK', ('fl', 'ffl')),
+ ('--features=dlig', ('ff', 'st', u'ſt')),
+ )
+ inactive = (
+ (None, ('ff', 'st', u'ſt')),
+ ('--language=TRK', ('fi', 'ffi')),
+ ('--script=arab', ('fi', 'fl', 'ffi', 'ffl')),
+ )
class TestFeatures(font_tests.TestFeatures):
loaded_fonts = FONTS
+ smcp_reqs_path = 'res/smcp_requirements.txt'
+ c2sc_reqs_path = 'res/c2sc_requirements.txt'
+ unic_reqs_path = 'res/unic_requirements.txt'
class TestVerticalMetrics(font_tests.TestVerticalMetrics):
@@ -106,10 +117,29 @@ class TestVerticalMetrics(font_tests.TestVerticalMetrics):
test_hhea_table_metrics = None
test_os2_metrics = None
+ # tests yMin and yMax to be equal to Roboto v1 values
+ # android requires this, and web fonts expect this
expected_head_yMin = -555
expected_head_yMax = 2163
+class TestGlyphBounds(font_tests.TestGlyphBounds):
+ loaded_fonts = FONTS
+
+ should_exceed = (
+ (('chi',), (None, 0, None, None), 0, 'is it an alias of x?'),
+ )
+
+ should_not_exceed = (
+ (('Epsilontonos', 'Etatonos', 'Iotatonos', 'Upsilontonos'),
+ (-150, None, None, None), 0.4,
+ 'may be susceptible to aggressive clipping'),
+ (('uni1F1B', 'uni1F2B', 'uni1F3B', 'uni1F5B', 'uni1F9B'),
+ (-550, None, None, None), 0.1,
+ 'may be susceptible to aggressive clipping'),
+ )
+
+
class TestGlyphAreas(font_tests.TestGlyphAreas):
master_weights_to_test = ['Thin', 'Bold']
instance_weights_to_test = ['Thin', 'Regular', 'Bold']
diff --git a/scripts/run_web_tests.py b/scripts/run_web_tests.py
index 5d3fd34..160510e 100755
--- a/scripts/run_web_tests.py
+++ b/scripts/run_web_tests.py
@@ -17,22 +17,23 @@
"""Test assumptions that web fonts rely on."""
import unittest
-
-from nototools import font_data
from nototools.unittests import font_tests
+import run_general_tests
+
+
FONTS = font_tests.load_fonts(
['out/web/*.ttf'],
expected_count=18)
-class TestItalicAngle(font_tests.TestItalicAngle):
+
+class TestItalicAngle(run_general_tests.TestItalicAngle):
loaded_fonts = FONTS
- expected_italic_angle = -12.0
class TestMetaInfo(font_tests.TestMetaInfo):
loaded_fonts = FONTS
- mark_heavier_as_bold = True
+ mark_heavier_as_bold = False
# Since different font files are hinted at different times, the actual
# outlines differ slightly. So we are keeping the version numbers as a hint.
@@ -43,18 +44,18 @@ class TestMetaInfo(font_tests.TestMetaInfo):
expected_os2_achVendID = 'GOOG'
-class TestNames(font_tests.TestNames):
+class TestNames(run_general_tests.TestNames):
"""Bugs:
https://github.com/google/roboto/issues/37
"""
loaded_fonts = FONTS
- family_name = 'Roboto'
- mark_heavier_as_bold = True
- expected_copyright = 'Copyright 2011 Google Inc. All Rights Reserved.'
def expected_unique_id(self, family, style):
- return family + ' ' + style
+ expected = family
+ if style != 'Regular':
+ expected += ' ' + style
+ return expected
class TestDigitWidths(font_tests.TestDigitWidths):
@@ -79,13 +80,17 @@ class TestCharacterCoverage(font_tests.TestCharacterCoverage):
class TestVerticalMetrics(font_tests.TestVerticalMetrics):
loaded_fonts = FONTS
+ # tests yMin and yMax to be equal to Roboto v1 values
+ # android requires this, and web fonts expect this
expected_head_yMin = -555
expected_head_yMax = 2163
+ # test ascent, descent, and lineGap to be equal to Roboto v1 values
expected_hhea_descent = -500
expected_hhea_ascent = 1900
expected_hhea_lineGap = 0
+ # test OS/2 vertical metrics to be equal to the old values
expected_os2_sTypoDescender = -512
expected_os2_sTypoAscender = 1536
expected_os2_sTypoLineGap = 102
@@ -93,10 +98,19 @@ class TestVerticalMetrics(font_tests.TestVerticalMetrics):
expected_os2_usWinAscent = 1946
-class TestLigatures(font_tests.TestLigatures):
+class TestLigatures(run_general_tests.TestLigatures):
loaded_fonts = FONTS
+class TestGlyphBounds(run_general_tests.TestGlyphBounds):
+ loaded_fonts = FONTS
+
+ # a bug in which monotonic and polytonic glyphs extend too far left is
+ # fixed in the unhinted output, but still present in the hinted binaries and
+ # not fixed by the web target
+ should_not_exceed = ()
+
+
class TestHints(font_tests.TestHints):
loaded_fonts = FONTS
diff --git a/scripts/temporary_touchups.py b/scripts/temporary_touchups.py
index 8953b64..a20a938 100644
--- a/scripts/temporary_touchups.py
+++ b/scripts/temporary_touchups.py
@@ -20,30 +20,30 @@ from nototools import noto_fonts
import roboto_data
-def apply_temporary_fixes(font):
+def apply_temporary_fixes(font, is_for_cros=False, is_for_web=False):
"""Apply some temporary fixes."""
# Fix usWeight:
font_name = font_data.font_name(font)
weight = noto_fonts.parse_weight(font_name)
weight_number = noto_fonts.WEIGHTS[weight]
+ # Chrome OS wants Thin to have usWeightClass=100
+ if is_for_cros and weight == 'Thin':
+ weight_number = 100
font['OS/2'].usWeightClass = weight_number
- # Set ascent, descent, and lineGap values to Android K values
- hhea = font['hhea']
- hhea.ascent = 1900
- hhea.descent = -500
- hhea.lineGap = 0
-
- # Copyright message
- font_data.set_name_record(
- font, 0, 'Copyright 2011 Google Inc. All Rights Reserved.')
-
+ # Set bold bits for Black (macStyle bit 0, fsSelection bit 5)
+ if is_for_web is False:
+ name_records = font_data.get_name_records(font)
+ family_name = name_records[1]
+ if family_name.endswith('Black'):
+ font['head'].macStyle |= (1 << 0)
+ font['OS/2'].fsSelection |= (1 << 5)
+ font['OS/2'].fsSelection &= ~(1 << 6)
def update_version_and_revision(font):
- """Update version and revision numbers from buildnumber.txt."""
- build_number = roboto_data.get_build_number()
- version_number = '2.' + build_number
+ """Update version and revision numbers."""
+
+ version_number = roboto_data.get_version_number()
version_record = 'Version %s; %d' % (version_number, date.today().year)
font_data.set_name_record(font, 5, version_record)
font['head'].fontRevision = float(version_number)
-
diff --git a/scripts/touchup_for_android.py b/scripts/touchup_for_android.py
index e13adea..9855717 100755
--- a/scripts/touchup_for_android.py
+++ b/scripts/touchup_for_android.py
@@ -16,44 +16,16 @@
"""Post-build changes for Roboto for Android."""
-import os
-from os import path
import sys
from fontTools import ttLib
from nototools import font_data
-
-def apply_temporary_fixes(font):
- """Apply some temporary fixes.
- """
- # Fix version number from buildnumber.txt
- from datetime import date
-
- build_number_txt = path.join(
- path.dirname(__file__), os.pardir, 'res', 'buildnumber.txt')
- build_number = open(build_number_txt).read().strip()
-
- version_record = 'Version 2.%s; %d' % (build_number, date.today().year)
-
- for record in font['name'].names:
- if record.nameID == 5:
- if record.platformID == 1 and record.platEncID == 0: # MacRoman
- record.string = version_record
- elif record.platformID == 3 and record.platEncID == 1:
- # Windows UCS-2
- record.string = version_record.encode('UTF-16BE')
- else:
- assert False
+import temporary_touchups
def apply_android_specific_fixes(font):
"""Apply fixes needed for Android."""
- # Set ascent, descent, and lineGap values to Android K values
- hhea = font['hhea']
- hhea.ascent = 1900
- hhea.descent = -500
- hhea.lineGap = 0
# Remove combining keycap and the arrows from the cmap table:
# https://github.com/google/roboto/issues/99
@@ -68,23 +40,20 @@ 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)
- 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)
+ # turn off round-to-grid flags in certain problem components
+ # https://github.com/google/roboto/issues/153
+ glyph_set = font.getGlyphSet()
+ ellipsis = glyph_set['ellipsis']._glyph
+ for component in ellipsis.components:
+ component.flags &= ~(1 << 2)
def correct_font(source_font_name, target_font_name):
"""Corrects metrics and other meta information."""
+
font = ttLib.TTFont(source_font_name)
- apply_temporary_fixes(font)
+ temporary_touchups.apply_temporary_fixes(font)
+ temporary_touchups.update_version_and_revision(font)
apply_android_specific_fixes(font)
font.save(target_font_name)
diff --git a/scripts/touchup_for_cros.py b/scripts/touchup_for_cros.py
new file mode 100755
index 0000000..0219f03
--- /dev/null
+++ b/scripts/touchup_for_cros.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+#
+# Copyright 2016 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.
+
+"""Post-build changes for Roboto to deploy on Google Chrome/Chromium OS"""
+
+import sys
+
+from fontTools import ttLib
+from nototools import font_data
+from touchup_for_web import apply_web_cros_common_fixes
+
+import temporary_touchups
+
+def drop_non_windows_name_records(font):
+ """Drop name records whose (PID,EID,Lang) != (3,1,0x409)"""
+ names = font['name'].names
+ records_to_drop = set()
+ for record_number, record in enumerate(names):
+ name_ids = (record.platformID, record.platEncID, record.langID)
+ if name_ids != (3, 1, 0x409):
+ records_to_drop.add(record_number)
+
+ # Taken from nototools/font_data.py
+ if records_to_drop:
+ font['name'].names = [
+ record for record_number, record in enumerate(names)
+ if record_number not in records_to_drop]
+
+def correct_font(source_name, unhinted_name, target_font_name, family_name):
+ """Corrects metrics and other meta information."""
+
+ font = ttLib.TTFont(source_name)
+ unhinted = ttLib.TTFont(unhinted_name)
+
+ apply_web_cros_common_fixes(font, unhinted, family_name)
+ temporary_touchups.apply_temporary_fixes(font, is_for_cros=True)
+ temporary_touchups.update_version_and_revision(font)
+ drop_non_windows_name_records(font)
+ font.save(target_font_name)
+
+def main(argv):
+ """Correct the font specified in the command line."""
+ correct_font(*argv[1:])
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/scripts/touchup_for_web.py b/scripts/touchup_for_web.py
index 7879d9e..108807a 100755
--- a/scripts/touchup_for_web.py
+++ b/scripts/touchup_for_web.py
@@ -24,9 +24,14 @@ from nototools import font_data
import temporary_touchups
-def apply_web_specific_fixes(font, family_name):
+def apply_web_specific_fixes(font, unhinted, family_name):
"""Apply fixes needed for web fonts."""
- # Set OS/2 table values to old values
+
+ # set vertical metrics to old values
+ hhea = font['hhea']
+ hhea.ascent = 1900
+ hhea.descent = -500
+
os2 = font['OS/2']
os2.sTypoAscender = 1536
os2.sTypoDescender = -512
@@ -34,6 +39,12 @@ def apply_web_specific_fixes(font, family_name):
os2.usWinAscent = 1946
os2.usWinDescent = 512
+ # correct anything else needed for both web and Chrome OS
+ apply_web_cros_common_fixes(font, unhinted, family_name)
+
+
+def apply_web_cros_common_fixes(font, unhinted, family_name):
+ """Apply fixes needed for web and CrOS targets"""
subfamily_name = font_data.get_name_records(font)[2].encode('ASCII')
assert(subfamily_name in
['Thin', 'Thin Italic',
@@ -45,7 +56,9 @@ def apply_web_specific_fixes(font, family_name):
if 'Condensed' in font_data.get_name_records(font)[1]:
family_name += ' Condensed'
- full_name = family_name + ' ' + subfamily_name
+ full_name = family_name
+ if subfamily_name != 'Regular':
+ full_name += ' ' + subfamily_name
# Family, subfamily names
font_data.set_name_record(font, 16, family_name)
@@ -54,8 +67,15 @@ def apply_web_specific_fixes(font, family_name):
font_data.set_name_record(font, 1, family_name)
else:
weight = subfamily_name.split()[0]
- font_data.set_name_record(font, 1, '%s %s' % (family_name, weight))
- font_data.set_name_record(font, 2, style_map[macStyle])
+ new_family_name = family_name
+ if weight != 'Regular':
+ new_family_name += ' ' + weight
+ font_data.set_name_record(font, 1, new_family_name)
+
+ # all weights outside regular and bold should only have subfamily
+ # "Regular" or "Italic"
+ italic = subfamily_name.endswith('Italic')
+ font_data.set_name_record(font, 2, style_map[italic << 1])
# Unique identifier and full name
font_data.set_name_record(font, 3, full_name)
@@ -66,18 +86,51 @@ def apply_web_specific_fixes(font, family_name):
font_data.set_name_record(
font, 6, (family_name+'-'+subfamily_name).replace(' ', ''))
+ # Copyright message
+ font_data.set_name_record(
+ font, 0, 'Copyright 2011 Google Inc. All Rights Reserved.')
+
+ # hotpatch glyphs by swapping
+ # https://github.com/google/roboto/issues/18
+ glyf = font['glyf']
+ glyf['chi'], glyf['chi.alt'] = glyf['chi.alt'], glyf['chi']
-def correct_font(source_font_name, target_font_name, family_name):
+ # make glyph orders consistent for feature copying
+ # https://github.com/google/roboto/issues/71
+ glyph_order = font.getGlyphOrder()
+ for i, glyph_name in enumerate(glyph_order):
+ if glyph_name.endswith('.lnum'):
+ new_name = glyph_name.replace('.lnum', '.pnum')
+ glyph_order[i] = new_name
+ font['glyf'][new_name] = font['glyf'][glyph_name]
+
+ # append old name to glyph order so del succeeds
+ glyph_order.append(glyph_name)
+ del font['glyf'][glyph_name]
+
+ # copy features from unhinted
+ # https://github.com/google/roboto/pull/163
+ for table in ['GDEF', 'GPOS', 'GSUB']:
+ font[table] = unhinted[table]
+
+
+def correct_font(source_name, unhinted_name, target_font_name, family_name):
"""Corrects metrics and other meta information."""
- font = ttLib.TTFont(source_font_name)
- temporary_touchups.apply_temporary_fixes(font)
- apply_web_specific_fixes(font, family_name)
+
+ font = ttLib.TTFont(source_name)
+ unhinted = ttLib.TTFont(unhinted_name)
+
+ # apply web-specific fixes before shared, so that sub/family names are
+ # correct for black weights and their bold bits will be set
+ apply_web_specific_fixes(font, unhinted, family_name)
+ temporary_touchups.apply_temporary_fixes(font, is_for_web=True)
+ temporary_touchups.update_version_and_revision(font)
font.save(target_font_name)
def main(argv):
"""Correct the font specified in the command line."""
- correct_font(argv[1], argv[2], argv[3])
+ correct_font(*argv[1:])
if __name__ == "__main__":