示例#1
0
    def parse_feature_block_(self):
        assert self.cur_token_ == "feature"
        location = self.cur_token_location_
        tag = self.expect_tag_()
        vertical = (tag in {"vkrn", "vpal", "vhal", "valt"})

        use_extension = False
        if self.next_token_ == "useExtension":
            self.expect_keyword_("useExtension")
            use_extension = True

        block = ast.FeatureBlock(location, tag, use_extension)
        self.parse_block_(block, vertical)
        return block
示例#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 makeQuranSajdaLine(font):
    pos = font["uni06D7"].getBounds(font).yMax
    thickness = font.info.postscriptUnderlineThickness
    minwidth = 100

    _, gdefclasses = findGDEF(font)
    # collect glyphs grouped by their widths rounded by 100 units, we will use
    # them to decide the widths of over/underline glyphs we will draw
    widths = {}
    for glyph in font:
        u = glyph.unicode
        if ((u is None ) or (0x0600 <= u <= 0x06FF) or u == ord(" ")) \
        and glyph.width > 0:
            width = round(glyph.width / minwidth) * minwidth
            width = width > minwidth and width or minwidth
            if not width in widths:
                widths[width] = []
            widths[width].append(glyph.name)

    base = 'uni0305'
    drawOverline(font, base, 0x0305, pos, thickness, 500)

    mark = ast.FeatureBlock("mark")
    overset = ast.GlyphClassDefinition("OverSet", ast.GlyphClass([base]))
    lookupflag = ast.LookupFlagStatement(
        markFilteringSet=ast.GlyphClassName(overset))
    mark.statements.extend([overset, lookupflag])

    for width in sorted(widths.keys()):
        # for each width group we create an over/underline glyph with the same
        # width, and add a contextual substitution lookup to use it when an
        # over/underline follows any glyph in this group
        replace = f"uni0305.{width}"
        drawOverline(font, replace, None, pos, thickness, width)
        sub = ast.SingleSubstStatement([ast.GlyphName(base)],
                                       [ast.GlyphName(replace)],
                                       [ast.GlyphClass(widths[width])], [],
                                       False)
        gdefclasses.markGlyphs.append(replace)
        mark.statements.append(sub)

    font.features.text.statements.append(mark)
示例#4
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
示例#5
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