Esempio n. 1
0
    def parse_glyphclass_(self, accept_glyphname):
        if (accept_glyphname and
                self.next_token_type_ in (Lexer.NAME, Lexer.CID)):
            glyph = self.expect_glyph_()
            return ast.GlyphName(self.cur_token_location_, glyph)
        if self.next_token_type_ is Lexer.GLYPHCLASS:
            self.advance_lexer_()
            gc = self.glyphclasses_.resolve(self.cur_token_)
            if gc is None:
                raise FeatureLibError(
                    "Unknown glyph class @%s" % self.cur_token_,
                    self.cur_token_location_)
            if isinstance(gc, ast.MarkClass):
                return ast.MarkClassName(self.cur_token_location_, gc)
            else:
                return ast.GlyphClassName(self.cur_token_location_, gc)

        self.expect_symbol_("[")
        glyphs = set()
        location = self.cur_token_location_
        while self.next_token_ != "]":
            if self.next_token_type_ is Lexer.NAME:
                glyph = self.expect_glyph_()
                if self.next_token_ == "-":
                    range_location = self.cur_token_location_
                    range_start = glyph
                    self.expect_symbol_("-")
                    range_end = self.expect_glyph_()
                    glyphs.update(self.make_glyph_range_(range_location,
                                                         range_start,
                                                         range_end))
                else:
                    glyphs.add(glyph)
            elif self.next_token_type_ is Lexer.CID:
                glyph = self.expect_glyph_()
                if self.next_token_ == "-":
                    range_location = self.cur_token_location_
                    range_start = self.cur_token_
                    self.expect_symbol_("-")
                    range_end = self.expect_cid_()
                    glyphs.update(self.make_cid_range_(range_location,
                                                       range_start, range_end))
                else:
                    glyphs.add("cid%05d" % self.cur_token_)
            elif self.next_token_type_ is Lexer.GLYPHCLASS:
                self.advance_lexer_()
                gc = self.glyphclasses_.resolve(self.cur_token_)
                if gc is None:
                    raise FeatureLibError(
                        "Unknown glyph class @%s" % self.cur_token_,
                        self.cur_token_location_)
                glyphs.update(gc.glyphSet())
            else:
                raise FeatureLibError(
                    "Expected glyph name, glyph range, "
                    "or glyph class reference",
                    self.next_token_location_)
        self.expect_symbol_("]")
        return ast.GlyphClass(location, glyphs)
Esempio n. 2
0
def asFeaAST(self):
    lut = lookup_type(self)
    if not lut:
        return feaast.Comment("")

    if lut == 1:  # GSUB 1 Single Substitution
        return feaast.SingleSubstStatement(
            [glyphref(x) for x in self.input],
            [glyphref(x) for x in self.replacement],
            [glyphref(x) for x in self.precontext],
            [glyphref(x) for x in self.postcontext],
            False,
        )
    elif lut == 2:  # GSUB 2 Multiple Substitution
        # Paired rules need to become a set of statements
        if is_paired(self):
            return paired_mult(self)

        return feaast.MultipleSubstStatement(
            [glyphref(x) for x in self.precontext],
            glyphref(self.input[0]),
            [glyphref(x) for x in self.postcontext],
            [glyphref(x) for x in self.replacement],
        )
    elif lut == 3:  # GSUB 3 Alternate Substitution
        return feaast.AlternateSubstStatement(
            [glyphref(x) for x in self.precontext],
            glyphref(self.input[0]),
            [glyphref(x) for x in self.postcontext],
            feaast.GlyphClass(
                [feaast.GlyphName(x) for x in self.replacement[0]]),
        )
    elif lut == 4:  # GSUB 4 Ligature Substitution
        # Paired rules need to become a set of statements
        if is_paired(self):
            return paired_ligature(self)

        return feaast.LigatureSubstStatement(
            [glyphref(x) for x in self.precontext],
            [glyphref(x) for x in self.input],
            [glyphref(x) for x in self.postcontext],
            glyphref(self.replacement[0]),
            False,
        )
    elif lut in [5, 6, 7
                 ]:  # GSUB 5, 6, 7 Different types of contextual substitutions
        raise NotImplementedError("Use the Chain verb for this")
    elif lut == 8:  # GSUB 8 Reverse Chaining Single Substitution
        return feaast.ReverseChainSingleSubstStatement(
            [glyphref(x) for x in self.precontext],
            [glyphref(x) for x in self.postcontext],
            [glyphref(x) for x in self.input],
            [glyphref(self.replacement[0])],
        )
    elif lut >= 9:
        raise NotImplementedError(
            "Invalid GSUB lookup type requested: {}".format(lut))

    raise ValueError("LookupType must be a single positive integer")
Esempio n. 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)
Esempio n. 4
0
def makeGlyphClassDefinition(className, members):
    glyphNames = [ast.GlyphName(g) for g in members]
    glyphClass = ast.GlyphClass(glyphNames)
    classDef = ast.GlyphClassDefinition(className, glyphClass)
    return classDef
Esempio n. 5
0
def _to_inline_class(glyphs):
    return feaast.GlyphClass([feaast.GlyphName(x) for x in glyphs])
Esempio n. 6
0
def glyphref(g):
    if len(g) == 1:
        return feaast.GlyphName(g[0])
    return feaast.GlyphClass([feaast.GlyphName(x) for x in g])
Esempio n. 7
0
 def _glyphName(self, glyph):
     try:
         name = glyph.glyph
     except AttributeError:
         name = glyph
     return ast.GlyphName(self._glyph_map.get(name, name))
Esempio n. 8
0
 def test_glyphname_escape(self):
     statement = ast.GlyphClass()
     for name in ("BASE", "NULL", "foo", "a"):
         statement.append(ast.GlyphName(name))
     self.assertEqual(statement.asFea(), r"[\BASE \NULL foo a]")