def paired_ligature(self) -> feaast.LigatureSubstStatement: b = feaast.Block() inputs = [] for i in self.input: if len(i) == 1: inputs.append(cycle(i)) else: inputs.append(i) lhs = zip(*inputs) replacements = [] for j in self.replacement: if len(j) == 1: replacements.append(cycle(j)) else: replacements.append(j) rhs = zip(*replacements) for l, r in zip(lhs, rhs): stmt = feaast.LigatureSubstStatement( [glyphref(x) for x in self.precontext], [glyphref([x]) for x in l], [glyphref(x) for x in self.postcontext], glyphref([r[0]]), False, ) b.statements.append(stmt) return b
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 asFeaAST(self): import fontTools.feaLib.ast as feaast f = feaast.Block() for r in self.routines: f.statements.append(r.asFeaAST()) return f
def asFeaAST(self, inFeature=False): if self.name and not inFeature: f = feaast.LookupBlock(name=self.name) elif self.name: f = feaast.LookupBlock(name=self.name) else: f = feaast.Block() arranged = arrange(self) if arranged and inFeature: f = feaast.Block() for a in arranged: f.statements.append(asFeaAST(a, inFeature)) return f if hasattr(self, "flags"): flags = feaast.LookupFlagStatement(self.flags) if self.flags & 0x10 and hasattr(self, "markFilteringSetAsClass"): # XXX # We only need the name, not the contents mfs = feaast.GlyphClassDefinition(self.markFilteringSetAsClass, feaast.GlyphClass([])) flags.markFilteringSet = feaast.GlyphClassName(mfs) if self.flags & 0xFF00 and hasattr(self, "markAttachmentSetAsClass"): # XXX mfs = feaast.GlyphClassDefinition(self.markAttachmentSetAsClass, feaast.GlyphClass([])) flags.markAttachment = feaast.GlyphClassName(mfs) f.statements.append(flags) for x in self.comments: f.statements.append(feaast.Comment(x)) f.statements.append(feaast.Comment(";")) lastaddress = self.address if lastaddress: f.statements.append( feaast.Comment("# Original source: %s " % (" ".join([str(x) for x in lastaddress])))) for x in self.rules: if x.address and x.address != lastaddress: f.statements.append( feaast.Comment("# Original source: %s " % x.address)) lastaddress = x.address f.statements.append(x.asFeaAST()) return f
def asFeaAST(self): """Returns this extension routine as ``fontTools.feaLib.ast`` objects.""" import fontTools.feaLib.ast as feaast f = feaast.Block() for r in self.routines: f.statements.append(r.asFeaAST()) return f
def asFeaAST(self, allLanguages=[("DFLT", "dflt")]): if set(allLanguages) == set(self.routine.languages): if self.routine.usecount == 1: return self.routine.asFeaAST(inFeature=True) return feaast.LookupReferenceStatement(self.routine.asFeaAST()) f = feaast.Block() lastLang = 'dflt' for s,l in self.routine.languages: f.statements.append(feaast.ScriptStatement(s)) if l != lastLang: f.statements.append(feaast.LanguageStatement("%4s" % l)) lastLang = l f.statements.append(feaast.LookupReferenceStatement(self.routine.asFeaAST())) return f
def asFeaAST(self, expand=False): if expand or not self.routine.languages: if self.routine.usecount == 1: return self.routine.asFeaAST(inFeature=True) return feaast.LookupReferenceStatement(self.routine.asFeaAST()) f = feaast.Block() lastLang = 'dflt' for s, l in self.routine.languages: f.statements.append(feaast.ScriptStatement(s)) if l != lastLang: f.statements.append(feaast.LanguageStatement("%4s" % l)) lastLang = l f.statements.append( feaast.LookupReferenceStatement(self.routine.asFeaAST())) return f
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): if self.name: f = feaast.LookupBlock(name=self.name) else: f = feaast.Block() if hasattr(self, "flags"): flags = feaast.LookupFlagStatement(self.flags) if self.flags & 0x10 and hasattr(self, "markFilteringSetAsClass"): # XXX # We only need the name, not the contents mfs = feaast.GlyphClassDefinition(self.markFilteringSetAsClass, feaast.GlyphClass([])) flags.markFilteringSet = feaast.GlyphClassName(mfs) if self.flags & 0xFF00 and hasattr(self, "markAttachmentSetAsClass"): # XXX mfs = feaast.GlyphClassDefinition(self.markAttachmentSetAsClass, feaast.GlyphClass([])) flags.markAttachment = feaast.GlyphClassName(mfs) f.statements.append(flags) for x in self.comments: f.statements.append(feaast.Comment(x)) f.statements.append(feaast.Comment(";")) lastaddress = self.address if lastaddress: f.statements.append( feaast.Comment("# Original source: %s " % (" ".join([str(x) for x in lastaddress])))) for x in self.rules: if x.address and x.address != lastaddress: f.statements.append( feaast.Comment("# Original source: %s " % x.address)) lastaddress = x.address if hasattr(x, "note"): f.statements.append( feaast.Comment("\n".join( [f"# {n}" for n in x.note.split("\n")]))) f.statements.append(x.asFeaAST()) return f
def paired_mult(self) -> feaast.MultipleSubstStatement: b = feaast.Block() input_lengths = [len(x) for x in self.input] replacement_lengths = [len(x) for x in self.replacement] if len(input_lengths) != 1: raise ValueError( "Multiple substitution only valid on input of length one, use a Chain instead" ) input_length = input_lengths[0] if not sum([l for l in replacement_lengths if l == 1 ]) in [len(replacement_lengths), len(replacement_lengths) - 1]: raise ValueError( "Cannot expand multiple glyph classes in a multiple substitution — creates ambiguity" ) # Look for the glyph class in the replacement, or default to first glyph in replacement glyphcls = next((i for i, v in enumerate(self.replacement) if len(v) > 1), 0) if input_length != len(self.replacement[glyphcls]): raise ValueError( "Glyph class in input must be same length as that in replacement. {} != {}" .format(input_length, len(self.replacement[glyphcls]))) zipped = zip(self.input[0], self.replacement[glyphcls]) prior_reps = self.replacement[:glyphcls] after_reps = self.replacement[glyphcls + 1:] for f, t in zipped: stmt = feaast.MultipleSubstStatement( [glyphref(x) for x in self.precontext], glyphref([f]), [glyphref(x) for x in self.postcontext], [glyphref(g) for g in prior_reps + [[t]] + after_reps]) b.statements.append(stmt) 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 asFeaAST(self, expand=False): # if self.routine.usecount == 1: # return self.routine.asFeaAST() f = feaast.Block() f.statements.append(feaast.LookupReferenceStatement(self.routine.asFeaAST())) return f