def addLookupReferences( feature, lookups, script=None, languages=None, exclude_dflt=False ): """Add references to named lookups to the feature's statements. If `script` (str) and `languages` (sequence of str) are provided, only register the lookup for the given script and languages, optionally with `exclude_dflt` directive. Otherwise add a global reference which will be registered for all the scripts and languages in the feature file's `languagesystems` statements. """ assert lookups if not script: for lookup in lookups: feature.statements.append(ast.LookupReferenceStatement(lookup)) return feature.statements.append(ast.ScriptStatement(script)) if exclude_dflt: for language in languages or ("dflt",): feature.statements.append( ast.LanguageStatement(language, include_default=False) ) for lookup in lookups: feature.statements.append(ast.LookupReferenceStatement(lookup)) else: feature.statements.append(ast.LanguageStatement("dflt", include_default=True)) for lookup in lookups: feature.statements.append(ast.LookupReferenceStatement(lookup)) for language in languages or (): if language == "dflt": continue feature.statements.append( ast.LanguageStatement(language, include_default=True) )
def asFeaAST(self, allLanguages=[("DFLT", "dflt")]): if set(allLanguages) == set(self.routine.languages): if self.routine.usecount == 1: return self.routine.asFeaAST(inFeature=True) return feaast.LookupReferenceStatement(self.routine.asFeaAST()) f = feaast.Block() lastLang = 'dflt' for s,l in self.routine.languages: f.statements.append(feaast.ScriptStatement(s)) if l != lastLang: f.statements.append(feaast.LanguageStatement("%4s" % l)) lastLang = l f.statements.append(feaast.LookupReferenceStatement(self.routine.asFeaAST())) return f
def asFeaAST(self, expand=False): if expand or not self.routine.languages: if self.routine.usecount == 1: return self.routine.asFeaAST(inFeature=True) return feaast.LookupReferenceStatement(self.routine.asFeaAST()) f = feaast.Block() lastLang = 'dflt' for s, l in self.routine.languages: f.statements.append(feaast.ScriptStatement(s)) if l != lastLang: f.statements.append(feaast.LanguageStatement("%4s" % l)) lastLang = l f.statements.append( feaast.LookupReferenceStatement(self.routine.asFeaAST())) return f
def parse_script_(self): assert self.is_cur_keyword_("script") location, script = self.cur_token_location_, self.expect_script_tag_() self.expect_symbol_(";") return ast.ScriptStatement(location, script)
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