def asFeaAST(self): b = feaast.Block() if self.is_cursive: allglyphs = set(self.bases.keys()) | set(self.marks.keys()) for g in allglyphs: b.statements.append( feaast.CursivePosStatement( glyphref([g]), g in self.bases and feaast.Anchor(*self.bases[g]), g in self.marks and feaast.Anchor(*self.marks[g]), )) else: if not hasattr(self, "baseslist"): sortByAnchor(self) # e.g. when testing for base in self.baseslist: statementtype = feaast.MarkBasePosStatement if self.font: if categorize_glyph(self.font, base[0][0])[0] == "mark": statementtype = feaast.MarkMarkPosStatement b.statements.append( statementtype( glyphref(base[0]), [[ feaast.Anchor(*base[1]), feaast.MarkClass(self.base_name) ]], )) return b
def parse_markClass_(self): assert self.is_cur_keyword_("markClass") location = self.cur_token_location_ glyphs = self.parse_glyphclass_(accept_glyphname=True) anchor = self.parse_anchor_() name = self.expect_class_name_() self.expect_symbol_(";") markClass = self.doc_.markClasses.get(name) if markClass is None: markClass = ast.MarkClass(name) self.doc_.markClasses[name] = markClass self.glyphclasses_.define(name, markClass) mcdef = ast.MarkClassDefinition(location, markClass, anchor, glyphs) markClass.addDefinition(mcdef) return mcdef
def _anchorDefinition(self, anchordef): anchorname = anchordef.name glyphname = anchordef.glyph_name anchor = self._anchor(anchordef.pos) if anchorname.startswith("MARK_"): name = "_".join(anchorname.split("_")[1:]) markclass = ast.MarkClass(self._className(name)) glyph = self._glyphName(glyphname) markdef = MarkClassDefinition(markclass, anchor, glyph) self._markclasses[(glyphname, anchorname)] = markdef else: if glyphname not in self._anchors: self._anchors[glyphname] = {} if anchorname not in self._anchors[glyphname]: self._anchors[glyphname][anchorname] = {} self._anchors[glyphname][anchorname][anchordef.component] = anchor
def feaPreamble(self, ff): if self.is_cursive: return [] sortByAnchor(self) if not "mark_classes_done" in ff.scratch: ff.scratch["mark_classes_done"] = {} b = feaast.Block() for mark in self.markslist: if not (self.base_name, tuple( mark[0])) in ff.scratch["mark_classes_done"]: b.statements.append( feaast.MarkClassDefinition( feaast.MarkClass(self.base_name), feaast.Anchor(*mark[1]), _glyphref(mark[0]), )) ff.scratch["mark_classes_done"][(self.base_name, tuple(mark[0]))] = True return [b]
def asFeaAST(self): b = feaast.Block() # if any( # isinstance(x[0], VariableScalar) or isinstance(x[1], VariableScalar) # for x in list(self.bases.values()) + list(self.marks.values()) # ): # raise ValueError("Can't directly express a variable anchor in FEA") if self.is_cursive: allglyphs = set(self.bases.keys()) | set(self.marks.keys()) for g in allglyphs: b.statements.append( feaast.CursivePosStatement( _glyphref([g]), g in self.bases and feaast.Anchor(*self.bases[g]), g in self.marks and feaast.Anchor(*self.marks[g]), )) else: if not hasattr(self, "baseslist"): sortByAnchor(self) # e.g. when testing for base in self.baseslist: statementtype = feaast.MarkBasePosStatement if self.font: if categorize_glyph(self.font, base[0][0])[0] == "mark": statementtype = feaast.MarkMarkPosStatement if self.force_markmark: statementtype = feaast.MarkMarkPosStatement b.statements.append( statementtype( _glyphref(base[0]), [[ feaast.Anchor(*base[1]), feaast.MarkClass(self.base_name) ]], )) return b
def _gposLookup(self, lookup, fealookup): statements = fealookup.statements pos = lookup.pos if isinstance(pos, VAst.PositionAdjustPairDefinition): for (idx1, idx2), (pos1, pos2) in pos.adjust_pair.items(): coverage_1 = pos.coverages_1[idx1 - 1] coverage_2 = pos.coverages_2[idx2 - 1] # If not both are groups, use “enum pos” otherwise makeotf will # fail. enumerated = False for item in coverage_1 + coverage_2: if not isinstance(item, VAst.GroupName): enumerated = True glyphs1 = self._coverage(coverage_1) glyphs2 = self._coverage(coverage_2) record1 = self._adjustment(pos1) record2 = self._adjustment(pos2) assert len(glyphs1) == 1 assert len(glyphs2) == 1 statements.append( ast.PairPosStatement(glyphs1[0], record1, glyphs2[0], record2, enumerated=enumerated)) elif isinstance(pos, VAst.PositionAdjustSingleDefinition): for a, b in pos.adjust_single: glyphs = self._coverage(a) record = self._adjustment(b) assert len(glyphs) == 1 statements.append( ast.SinglePosStatement([(glyphs[0], record)], [], [], False)) elif isinstance(pos, VAst.PositionAttachDefinition): anchors = {} for marks, classname in pos.coverage_to: for mark in marks: # Set actually used mark classes. Basically a hack to get # around the feature file syntax limitation of making mark # classes global and not allowing mark positioning to # specify mark coverage. for name in mark.glyphSet(): key = (name, "MARK_" + classname) self._markclasses[key].used = True markclass = ast.MarkClass(self._className(classname)) for base in pos.coverage: for name in base.glyphSet(): if name not in anchors: anchors[name] = [] if classname not in anchors[name]: anchors[name].append(classname) for name in anchors: components = 1 if name in self._ligatures: components = self._ligatures[name] marks = [] for mark in anchors[name]: markclass = ast.MarkClass(self._className(mark)) for component in range(1, components + 1): if len(marks) < component: marks.append([]) anchor = None if component in self._anchors[name][mark]: anchor = self._anchors[name][mark][component] marks[component - 1].append((anchor, markclass)) base = self._glyphName(name) if name in self._marks: mark = ast.MarkMarkPosStatement(base, marks[0]) elif name in self._ligatures: mark = ast.MarkLigPosStatement(base, marks) else: mark = ast.MarkBasePosStatement(base, marks[0]) statements.append(mark) elif isinstance(pos, VAst.PositionAttachCursiveDefinition): # Collect enter and exit glyphs enter_coverage = [] for coverage in pos.coverages_enter: for base in coverage: for name in base.glyphSet(): enter_coverage.append(name) exit_coverage = [] for coverage in pos.coverages_exit: for base in coverage: for name in base.glyphSet(): exit_coverage.append(name) # Write enter anchors, also check if the glyph has exit anchor and # write it, too. for name in enter_coverage: glyph = self._glyphName(name) entry = self._anchors[name]["entry"][1] exit = None if name in exit_coverage: exit = self._anchors[name]["exit"][1] exit_coverage.pop(exit_coverage.index(name)) statements.append(ast.CursivePosStatement(glyph, entry, exit)) # Write any remaining exit anchors. for name in exit_coverage: glyph = self._glyphName(name) exit = self._anchors[name]["exit"][1] statements.append(ast.CursivePosStatement(glyph, None, exit)) else: raise NotImplementedError(pos)