summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Godfrey-Kittle <jamesgk@google.com>2015-02-06 17:39:33 -0800
committerJames Godfrey-Kittle <jamesgk@google.com>2015-04-16 12:16:29 -0700
commit6eccbae108d93018a34faa1982327b8cb3964fda (patch)
treeab7986ad3c76beba7603c5840e4bc776f955c95e
parent7c64f9abc0992d7e56b8acf4c3460e73a308b847 (diff)
Detect undefined references in feature files.
-rw-r--r--scripts/lib/fontbuild/Build.py3
-rwxr-xr-xscripts/lib/fontbuild/features.py62
2 files changed, 63 insertions, 2 deletions
diff --git a/scripts/lib/fontbuild/Build.py b/scripts/lib/fontbuild/Build.py
index 1646a8b..630d84e 100644
--- a/scripts/lib/fontbuild/Build.py
+++ b/scripts/lib/fontbuild/Build.py
@@ -7,7 +7,7 @@ from fontbuild.mitreGlyph import mitreGlyph
from fontbuild.generateGlyph import generateGlyph
from fontTools.misc.transform import Transform
from fontbuild.kerning import generateFLKernClassesFromOTString
-from fontbuild.features import CreateFeaFile
+from fontbuild.features import CreateFeaFile, validateFeatureFile
from fontbuild.markFeature import GenerateFeature_mark
from fontbuild.mkmkFeature import GenerateFeature_mkmk
from fontbuild.decomposeGlyph import decomposeGlyph
@@ -133,6 +133,7 @@ class FontProject:
log(">> Copying features")
f.ot_classes = self.ot_classes
copyFeatures(self.basefont, f)
+ validateFeatureFile(f)
log(">> Decomposing")
for gname in self.decompose:
if f.has_key(gname):
diff --git a/scripts/lib/fontbuild/features.py b/scripts/lib/fontbuild/features.py
index 1f210ea..6be3bad 100755
--- a/scripts/lib/fontbuild/features.py
+++ b/scripts/lib/fontbuild/features.py
@@ -1,4 +1,64 @@
-import string
+import re
+
+
+className = re.compile(r"@[\w\.]+")
+classVal = re.compile(r"\[(\s*(?:[\w\.-]+\s*)+)\]")
+classDef = re.compile(
+ r"(%s)\s*?=\s*?%s;" % (className.pattern, classVal.pattern))
+featureDef = re.compile(
+ r"(feature (?P<tag>[A-Za-z]{4}) \{.*?\} (?P=tag);\n)", re.DOTALL)
+comment = re.compile(r"\s*#.*")
+
+
+def validateFeatureFile(font):
+ """Remove invalid features and glyph/class references from an RFont."""
+
+ classes, text = validateGlyphClasses(font, font.features.text)
+ for feature, name in featureDef.findall(text):
+ remove = False
+ for reference in className.findall(feature):
+ if reference not in classes:
+ print ("Undefined glyph class %s referenced in feature "
+ "definition %s (removed)." % (reference, name))
+ remove = True
+ for references in classVal.findall(feature):
+ for reference in references.split():
+ if "-" not in reference and not font.has_key(reference):
+ print ("Undefined glyph %s referenced in feature "
+ "definition %s (removed)." % (reference, name))
+ remove = True
+ if remove:
+ text = text.replace(feature, "")
+ font.features.text = text
+
+
+def validateGlyphClasses(font, text):
+ """Parse glyph classes from feature text, removing invalid references."""
+
+ classes = set()
+ validLines = []
+ for line in [l for l in re.split(r"[\r\n]+", text) if not comment.match(l)]:
+ match = classDef.match(line)
+ if match:
+ name, references = match.groups()
+ classes.add(name)
+
+ validRefs = []
+ for reference in references.split():
+ if reference.startswith("@") and reference not in classes:
+ print ("Undefined glyph class %s referenced in glyph class "
+ "definition %s (removed)." % (reference, name))
+ elif "-" not in reference and not font.has_key(reference):
+ print ("Undefined glyph %s referenced in glyph class "
+ "definition %s (removed)." % (reference, name))
+ else:
+ validRefs.append(reference)
+
+ line = "%s = [%s];" % (name, " ".join(validRefs))
+ validLines.append(line)
+
+ return classes, "\n".join(validLines)
+
def CreateFeaFile(font, path):
fea_text = font.ot_classes