예제 #1
0
 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_()
예제 #2
0
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
예제 #3
0
    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
예제 #4
0
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