def makeLookupFlag(name=None, markAttachment=None, markFilteringSet=None): value = 0 if name is None else LOOKUP_FLAGS[name] if markAttachment is not None: assert isinstance(markAttachment, ast.GlyphClassDefinition) markAttachment = ast.GlyphClassName(markAttachment) if markFilteringSet is not None: assert isinstance(markFilteringSet, ast.GlyphClassDefinition) markFilteringSet = ast.GlyphClassName(markFilteringSet) return ast.LookupFlagStatement(value, markAttachment=markAttachment, markFilteringSet=markFilteringSet)
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 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)
def parse_class_name_(self): name = self.expect_class_name_() gc = self.glyphclasses_.resolve(name) if gc is None: raise FeatureLibError("Unknown glyph class @%s" % name, 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)
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 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)
def _groupName(self, group): try: name = group.group except AttributeError: name = group return ast.GlyphClassName(self._glyphclasses[name.lower()])
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