Ejemplo n.º 1
0
 def parse_def_anchor_(self):
     assert self.is_cur_keyword_("DEF_ANCHOR")
     location = self.cur_token_location_
     name = self.expect_string_()
     self.expect_keyword_("ON")
     gid = self.expect_number_()
     self.expect_keyword_("GLYPH")
     glyph_name = self.expect_name_()
     # check for duplicate anchor names on this glyph
     if (glyph_name in self.anchors_
             and self.anchors_[glyph_name].resolve(name) is not None):
         raise VoltLibError(
             'Anchor "%s" already defined, '
             'anchor names are case insensitive' % name,
             location
         )
     self.expect_keyword_("COMPONENT")
     component = self.expect_number_()
     if self.next_token_ == "LOCKED":
         locked = True
         self.advance_lexer_()
     else:
         locked = False
     self.expect_keyword_("AT")
     pos = self.parse_pos_()
     self.expect_keyword_("END_ANCHOR")
     anchor = ast.AnchorDefinition(location, name, gid, glyph_name,
                                   component, locked, pos)
     if glyph_name not in self.anchors_:
         self.anchors_[glyph_name] = SymbolTable()
     self.anchors_[glyph_name].define(name, anchor)
     return anchor
Ejemplo n.º 2
0
def exportVoltAnchors(font):
    glyphOrder = [g.name for g in font]

    # Save groups and lookups files for each master.
    for master in font.masters:
        # Collect anchors that need mkmk lookups (base glyph is a mark).
        mkmk = set()
        for glyph in font:
            layer = glyph.layers[master.name]
            for anchor in layer.anchors:
                name = anchor.name
                if not name.startswith('_') and glyph.openTypeGlyphClass == 3:
                    mkmk.add(name.split('_')[0])

        # Collect anchors that need ligature lookups (anchor with index).
        ligs = set()
        for glyph in font:
            layer = glyph.layers[master.name]
            for anchor in layer.anchors:
                name = anchor.name
                if name.startswith('_'):
                    name = name[1:]
                if '_' in name:
                    ligs.add(name.split('_')[0])

        lookups = {}
        groups = {}
        anchors = []
        for glyph in font:
            layer = glyph.layers[master.name]
            for anchor in layer.anchors:
                x = otRound(anchor.x)
                y = otRound(anchor.y)
                if anchor.name.startswith('_'):
                    # Mark anchor.
                    if glyph.openTypeGlyphClass != 3:
                        # Not a mark glyph? Ignore the anchor or VOLT will error.
                        continue

                    name = anchor.name[1:]

                    # Add to groups. We build groups for mark glyphs by anchor.
                    group = f'MARK_{name}'
                    if group not in groups:
                        groups[group] = set()
                    groups[group].add(glyph.name)

                    # mkmk anchors are added to both mark and mkmk lookups
                    # because they might be used with non-mark bases after
                    # anchor propagation.
                    lookupnames = [f'mark_{name}']
                    if name in mkmk:
                        lookupnames.append(f'mkmk_{name}')
                    if name in ligs:
                        lookupnames.append(f'mark_{name}_ligs')

                    # Add the glyph to respective lookup(s).
                    for lookupname in lookupnames:
                        if lookupname not in lookups:
                            lookups[lookupname] = _attachment_lookup(
                                lookupname)

                        # For mkmk lookups we use individual glyphs, for mark
                        # lookups we use groups. There is no technical reason
                        # for this, just how JH likes it.
                        if lookupname.startswith('mkmk'):
                            to = ([ast.GlyphName(glyph.name)], name)
                            lookups[lookupname].pos.coverage_to.append(to)
                        elif not lookups[lookupname].pos.coverage_to:
                            to = ([ast.GroupName(group, None)], name)
                            lookups[lookupname].pos.coverage_to.append(to)

                    # Add the anchor.
                    name, comp = f'MARK_{name}', 1
                    pos = ast.Pos(None, x, y, {}, {}, {})
                    gid = glyphOrder.index(glyph.name)
                    anchors.append(
                        ast.AnchorDefinition(name, gid, glyph.name, comp,
                                             False, pos))
                else:
                    # Base anchor.
                    name, comp = anchor.name, 1
                    lookupname = f'mark_{name}'
                    if '_' in name:
                        # Split ligature anchor (e.g. “top_1” and use the
                        # number for ligature component.
                        name, comp = name.split('_')
                        lookupname = f'mark_{name}_ligs'

                    if glyph.openTypeGlyphClass == 3:
                        # If this is a mark glyph, then add to mkmk lookup.
                        lookupname = f'mkmk_{name}'

                    # Add the glyph to respective lookup.
                    if lookupname not in lookups:
                        lookups[lookupname] = _attachment_lookup(lookupname)
                    lookups[lookupname].pos.coverage.add(glyph.name)

                    # Add the anchor.
                    pos = ast.Pos(None, x, y, {}, {}, {})
                    gid = glyphOrder.index(glyph.name)
                    anchors.append(
                        ast.AnchorDefinition(name, gid, glyph.name, comp,
                                             False, pos))

        # Save groups file.
        with open(master.psn + '-anchors.vtg', 'w') as fp:
            doc = ast.VoltFile()
            for group in groups:
                glyphs = tuple(ast.GlyphName(g) for g in sorted(groups[group]))
                enum = ast.Enum(glyphs)
                doc.statements.append(ast.GroupDefinition(group, enum))
            fp.write(str(doc))

        # Save lookups file.
        with open(master.psn + '-anchors.vtl', 'w') as fp:
            doc = ast.VoltFile()
            for lookup in lookups.values():
                # Sort coverage by glyph ID to be stable.
                lookup.pos.coverage = sorted(
                    [ast.GlyphName(g) for g in lookup.pos.coverage],
                    key=lambda g: glyphOrder.index(g.glyph),
                )
                doc.statements.append(lookup)
            doc.statements += anchors
            fp.write(str(doc))