summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Godfrey-Kittle <jamesgk@google.com>2015-02-20 13:30:53 -0800
committerJames Godfrey-Kittle <jamesgk@google.com>2015-04-16 12:16:30 -0700
commita9399ccfa784268eece38fa946bf9f531f2db306 (patch)
tree4e80034efc99252f046280deadcaa6d5e2e11df6
parente8775eb72e7db4c0c13150334cdadba4aa1153ef (diff)
Simplify conversion to OTF via ufo2fdk.
The issues with dropped glyphs during OTF conversion can be fixed with a custom glyph file, which addresses an issue with ufo2fdk.
-rw-r--r--scripts/lib/fontbuild/Build.py3
-rwxr-xr-xscripts/lib/fontbuild/features.py55
-rw-r--r--scripts/lib/fontbuild/saveOTF.py94
3 files changed, 23 insertions, 129 deletions
diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py
index af21f59..f96e487 100644
--- a/scripts/lib/fontbuild/Build.py
+++ b/scripts/lib/fontbuild/Build.py
@@ -11,7 +11,7 @@ from fontbuild.markFeature import GenerateFeature_mark
from fontbuild.mkmkFeature import GenerateFeature_mkmk
from fontbuild.decomposeGlyph import decomposeGlyph
from fontbuild.removeGlyphOverlap import removeGlyphOverlap
-from fontbuild.saveOTF import saveOTF, conformToAGL
+from fontbuild.saveOTF import saveOTF
import ConfigParser
import os
@@ -160,7 +160,6 @@ class FontProject:
if self.buildOTF:
log(">> Generating OTF file")
newFont = OpenFont(ufoName)
- conformToAGL(newFont, self.adobeGlyphList)
otfName = self.generateOutputPath(f, "otf")
saveOTF(newFont, otfName, autohint=self.autohintOTF)
diff --git a/scripts/lib/fontbuild/features.py b/scripts/lib/fontbuild/features.py
index 60f5155..fb1f1c9 100755
--- a/scripts/lib/fontbuild/features.py
+++ b/scripts/lib/fontbuild/features.py
@@ -8,17 +8,15 @@ import re
# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
_glyphNameChars = r"[A-Za-z_][\w.]"
_glyphName = r"%s{,30}" % _glyphNameChars
-_className = re.compile(r"@%s{,29}" % _glyphNameChars)
+_className = r"@%s{,29}" % _glyphNameChars
_classValToken = (
- r"(?:%s|%s(?:\s*-\s*%s)?)" % (_className.pattern, _glyphName, _glyphName))
-_classVal = re.compile(
- r"\[\s*(%s(?:\s+%s)*)\s*\]" % (_classValToken, _classValToken))
-_classDef = re.compile(
- r"(%s)\s*=\s*%s\s*;" % (_className.pattern, _classVal.pattern))
+ r"(?:%s|%s(?:\s*-\s*%s)?)" % (_className, _glyphName, _glyphName))
+_classVal = r"\[\s*(%s(?:\s+%s)*)\s*\]" % (_classValToken, _classValToken)
+_classDef = re.compile(r"(%s)\s*=\s*%s\s*;" % (_className, _classVal))
_featureDef = re.compile(
r"(feature\s+(?P<tag>[A-Za-z]{4})\s+\{.*?\}\s+(?P=tag)\s*;)",
re.DOTALL)
-_subRuleToken = r"(?:%s|%s)'?" % (_glyphName, _className.pattern)
+_subRuleToken = r"(?:%s|%s)'?" % (_glyphName, _className)
_subRuleTokenList = (
r"\[?\s*(%s(?:\s+%s)*)\s*\]?" % (_subRuleToken, _subRuleToken))
_subRule = re.compile(
@@ -102,49 +100,6 @@ def _isValidRef(referencer, ref, font):
return True
-def _matchLine(line):
- """Try to match a line against some feature file language patterns."""
-
- match = _classDef.match(line)
- if match:
- return match, "classDef"
- match = _subRule.match(line)
- if match:
- return match, "subRule"
- return None, ""
-
-
-def replaceFeatureFileReferences(font, replace):
- """Replace references according to a given mapping of old names to new."""
-
- lines = font.features.text.splitlines()
- for i, line in enumerate(lines):
- match, flag = _matchLine(line)
- if not match:
- continue
-
- # check for reference in class definitions
- if flag == "classDef":
- name, value = match.groups()
- value = " ".join([replace.get(n, n) for n in value.split()])
- lines[i]= "%s = [%s];" % (name, value)
-
- # check in substitution rules
- elif flag == "subRule":
- indentation, subbed, sub = match.groups()
- subbed = " ".join([replace.get(n, n) for n in subbed.split()])
- sub = " ".join([replace.get(n, n) for n in sub.split()])
-
- # put brackets around tokens if they were there before
- if re.match(r"\s*sub(stitute)?\s+\[.+\]\s+by", line):
- subbed = "[%s]" % subbed
- if re.match(r"\s*sub(stitute)?.+by\s+\[.+\]\s*;", line):
- sub = "[%s]" % sub
- lines[i] = "%ssub %s by %s;" % (indentation, subbed, sub)
-
- font.features.text = "\n".join(lines)
-
-
def generateFeatureFile(font):
"""Populate a font's feature file text from its classes and features."""
diff --git a/scripts/lib/fontbuild/saveOTF.py b/scripts/lib/fontbuild/saveOTF.py
index 3241cbf..bb54622 100644
--- a/scripts/lib/fontbuild/saveOTF.py
+++ b/scripts/lib/fontbuild/saveOTF.py
@@ -1,94 +1,34 @@
-import re
-
-from fontTools.ttLib import newTable
-from fontTools.ttLib.tables._c_m_a_p import cmap_format_12
from ufo2fdk import OTFCompiler
from ufo2fdk.makeotfParts import MakeOTFPartsCompiler
-from ufo2fdk.outlineOTF import OutlineOTFCompiler
-
-from fontbuild.features import replaceFeatureFileReferences
def saveOTF(font, destFile, autohint=False):
"""Save a RoboFab font as an OTF binary using ufo2fdk."""
- compiler = OTFCompiler(partsCompilerClass=_PartsCompilerCustomGlyphOrder,
- outlineCompilerClass=_OutlineCompilerFormat12)
+ compiler = OTFCompiler(partsCompilerClass=_PartsCompilerCustomGlyphOrder)
reports = compiler.compile(font, destFile, autohint=autohint)
if autohint:
print reports["autohint"]
print reports["makeotf"]
-def conformToAGL(font, glyphList):
- """Ensure a font's glyph names conform to the AGL specification.
-
- The spec is described at http://sourceforge.net/adobe/aglfn/aglspec.
- This function only checks for some Roboto-specific problems.
- """
-
- nameChanges = {}
- for glyph in font:
- name_components = glyph.name.split(".", 1)
- name = name_components.pop(0)
- ext = ("." + name_components[0]) if name_components else ""
- if name in glyphList:
- continue
-
- # if a ligature name (without underscores) is in the AGL, remove the
- # underscores so AFDKO will keep the glyph in the font during conversion
- #TODO(jamesgk) figure out why AFDKO throws out names with underscores
- if re.match("([a-z]_)+[a-z]", name):
- ligaName = name.replace("_", "") + ext
- if ligaName in glyphList:
- nameChanges[glyph.name] = ligaName
- continue
-
- # use the glyph's unicode value as its name, if possible
- if not re.match(r"u(ni)?[\dA-F]+", glyph.name) and glyph.unicode:
- uvName = ("uni%04X" % glyph.unicode) + ext
- nameChanges[glyph.name] = uvName
-
- # names of glyphs outside the BMP must have prefix "u" and not "uni"
- if re.match(r"uni[\dA-F]{5,}", glyph.name):
- nameChanges[glyph.name] = re.sub("^uni", "u", glyph.name)
-
- for oldName, newName in nameChanges.items():
- font[oldName].name = newName
- replaceFeatureFileReferences(font, nameChanges)
-
-
class _PartsCompilerCustomGlyphOrder(MakeOTFPartsCompiler):
"""OTF parts compiler that produces a custom glyph order file."""
def setupFile_glyphOrder(self, path):
- # just create a blank file -- this seems necessary for now because
- # AFDKO's makeotf function produces Roboto OTFs without ASCII mappings
- # when called with ufo2fdk's default glyph order file
- #TODO(jamesgk) figure out why this is necessary
- f = open(path, "w")
- f.close()
-
-
-class _OutlineCompilerFormat12(OutlineOTFCompiler):
- """OTF outline compiler to work with format 12 cmaps."""
-
- def setupTable_cmap(self):
- """Set up cmap exactly like ufo2fdk, switching format 4 for 12."""
-
- # set up a mac-compatible table
- cmap12_0_3 = cmap_format_12(12)
- cmap12_0_3.platformID = 0
- cmap12_0_3.platEncID = 3
- cmap12_0_3.language = 0
- cmap12_0_3.cmap = dict(self.unicodeToGlyphNameMapping)
- # set up a windows-compatible table
- cmap12_3_1 = cmap_format_12(12)
- cmap12_3_1.platformID = 3
- cmap12_3_1.platEncID = 1
- cmap12_3_1.language = 0
- cmap12_3_1.cmap = dict(self.unicodeToGlyphNameMapping)
- # store the tables in the cmap
- self.otf["cmap"] = cmap = newTable("cmap")
- cmap.tableVersion = 0
- cmap.tables = [cmap12_0_3, cmap12_3_1]
+ # fixes: https://github.com/typesupply/ufo2fdk/pull/3
+ lines = []
+ for name in self.glyphOrder:
+ if name in self.font and self.font[name].unicode is not None:
+ code = "%04X" % self.font[name].unicode
+ if len(code) <= 4:
+ code = "uni%s" % code
+ else:
+ code = "u%s" % code
+ line = "%s %s %s" % (name, name, code)
+ else:
+ line = "%s %s" % (name, name)
+ lines.append(line)
+ ofile = open(path, "wb")
+ ofile.write("\n".join(lines) + "\n")
+ ofile.close()