From a9399ccfa784268eece38fa946bf9f531f2db306 Mon Sep 17 00:00:00 2001 From: James Godfrey-Kittle Date: Fri, 20 Feb 2015 13:30:53 -0800 Subject: 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. --- scripts/lib/fontbuild/Build.py | 3 +- scripts/lib/fontbuild/features.py | 55 +++-------------------- scripts/lib/fontbuild/saveOTF.py | 94 +++++++-------------------------------- 3 files changed, 23 insertions(+), 129 deletions(-) (limited to 'scripts') 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[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() -- cgit v1.2.3