def __init__(self, path): self.doc_ = ast.FeatureFile() self.anchors_ = SymbolTable() self.glyphclasses_ = SymbolTable() self.lookups_ = SymbolTable() self.valuerecords_ = SymbolTable() self.symbol_tables_ = { self.anchors_, self.valuerecords_ } self.next_token_type_, self.next_token_ = (None, None) self.next_token_location_ = None self.lexer_ = IncludingLexer(path) self.advance_lexer_()
def asFeaAST(self): """Returns this font's features as a feaLib AST object, for later translation to AFDKO code.""" from fontFeatures import Routine, Chaining ff = feaast.FeatureFile() add_language_system_statements(self, ff) add_gdef(self, ff) newRoutines = [self.routines[i] for i in reorderAndResolve(self)] # Preamble for k in newRoutines: assert isinstance(k, Routine) if not k.name: k.name = self.gensym("Routine_") pre = k.feaPreamble(self) if k.rules: ff.statements.extend(pre) for k, v in self.namedClasses.items(): asclass = _to_inline_class(v) ff.statements.append(feaast.GlyphClassDefinition(k, asclass)) ff.statements.append(feaast.Comment("")) for k in newRoutines: if k.rules and k.usecount != 1: ff.statements.append(k.asFeaAST()) expandedLanguages = [] for s, ls in self.scripts_and_languages.items(): for l in ls: expandedLanguages.append((s, l)) for k, v in self.features.items(): f = feaast.FeatureBlock(k) for n in v: f.statements.append(n.asFeaAST(allLanguages=expandedLanguages)) ff.statements.append(f) return ff
def _buildFeatureFile(self, tables): doc = ast.FeatureFile() statements = doc.statements if self._glyphclasses: statements.append(ast.Comment("# Glyph classes")) statements.extend(self._glyphclasses.values()) if self._markclasses: statements.append(ast.Comment("\n# Mark classes")) statements.extend(c[1] for c in sorted(self._markclasses.items())) if self._lookups: statements.append(ast.Comment("\n# Lookups")) for lookup in self._lookups.values(): statements.extend(getattr(lookup, "targets", [])) statements.append(lookup) # Prune features features = self._features.copy() for ftag in features: scripts = features[ftag] for stag in scripts: langs = scripts[stag] for ltag in langs: langs[ltag] = [ l for l in langs[ltag] if l.lower() in self._lookups ] scripts[stag] = {t: l for t, l in langs.items() if l} features[ftag] = {t: s for t, s in scripts.items() if s} features = {t: f for t, f in features.items() if f} if features: statements.append(ast.Comment("# Features")) for ftag, scripts in features.items(): feature = ast.FeatureBlock(ftag) stags = sorted(scripts, key=lambda k: 0 if k == "DFLT" else 1) for stag in stags: feature.statements.append(ast.ScriptStatement(stag)) ltags = sorted(scripts[stag], key=lambda k: 0 if k == "dflt" else 1) for ltag in ltags: include_default = True if ltag == "dflt" else False feature.statements.append( ast.LanguageStatement( ltag, include_default=include_default)) for name in scripts[stag][ltag]: lookup = self._lookups[name.lower()] lookupref = ast.LookupReferenceStatement(lookup) feature.statements.append(lookupref) statements.append(feature) if self._gdef and "GDEF" in tables: classes = [] for name in ("BASE", "MARK", "LIGATURE", "COMPONENT"): if name in self._gdef: classname = "GDEF_" + name.lower() glyphclass = ast.GlyphClassDefinition( classname, self._gdef[name]) statements.append(glyphclass) classes.append(ast.GlyphClassName(glyphclass)) else: classes.append(None) gdef = ast.TableBlock("GDEF") gdef.statements.append(ast.GlyphClassDefStatement(*classes)) statements.append(gdef) return doc
def asFeaAST(self, do_gdef=True): """Returns this font's features as a feaLib AST object, for later translation to AFDKO code.""" from fontFeatures import Routine, Chaining ff = feaast.FeatureFile() add_language_system_statements(self, ff) if do_gdef: add_gdef(self, ff) # In OpenType, we need to rearrange the routines such that all rules for # a given languagesystem, lookup type, and lookup flags, appear in the # same lookup. Also, lookups with the same languagesystem need to appear next # to one another, because FEA syntax is stupid. # Now arrange them by type/etc. for k, v in self.features.items(): for reference in v: routine = reference.routine # If a rule has >1 language it must first be split newrules = [] for r in routine.rules: if len(r.languages or []) > 1: for language in r.languages: newrule = copy.copy(r) newrule.languages = [language] newrules.append(newrule) else: newrules.append(r) routine.rules = newrules partitioned = self.partitionRoutine( routine, lambda rule: tuple([ tuple(rule.languages or []), type(rule), lookup_type(rule) ])) if routine.name and partitioned and len(partitioned) > 1: for p in partitioned: rule = p.rules[0] language = (rule.languages or [("DFLT", "dflt")])[0] p.name = p.name + "%s_%s_%s_%i" % ( language[0].strip(), language[1].strip(), type(rule).__name__, lookup_type(rule)) for r in self.routines: r.usecount = 0 # Bubble up flags and languages if r.rules and not r.flags: r.flags = r.rules[0].flags if r.rules and not r.languages: r.languages = r.rules[0].languages for k, v in self.features.items(): # Similarly split routines with multiple languages references = [] for reference in v: routine = reference.routine if len(routine.languages or []) > 1: splitroutines = [] for language in routine.languages: # This is wrong. It should really be a new reference to the # same routine. But this will do for now. newreference = copy.copy(reference) newreference.languages = [language] references.append(newreference) else: references.append(reference) reference.languages = routine.languages self.features[k] = references # Order the arranged routines by language # new_references = list(sorted(v, key=lambda x: tuple(x.routine.languages or []))) # Next, we'll ensure that all chaining lookups are resolved and in the right order newRoutines = [self.routines[i] for i in reorderAndResolve(self)] # Preamble for k in newRoutines: assert isinstance(k, Routine) if not k.name and k.usecount != 1: k.name = self.gensym("Routine_") pre = k.feaPreamble(self) if k.rules: ff.statements.extend(pre) for k, v in self.namedClasses.items(): asclass = _to_inline_class(v) ff.statements.append(feaast.GlyphClassDefinition(k, asclass)) ff.statements.append(feaast.Comment("")) for k in newRoutines: if k.rules: ff.statements.append(k.asFeaAST()) for k, v in self.features.items(): for routine in v: # Putting each routine in its own feature saves problems... lang = routine.languages f = feaast.FeatureBlock(k) if lang: f.statements.append(feaast.ScriptStatement(lang[0][0])) f.statements.append( feaast.LanguageStatement("%4s" % lang[0][1])) f.statements.append(routine.asFeaAST(expand=k == "aalt")) ff.statements.append(f) return ff