def parse_lookupflag_(self): assert self.is_cur_keyword_("lookupflag") location = self.cur_token_location_ # format B: "lookupflag 6;" if self.next_token_type_ == Lexer.NUMBER: value = self.expect_number_() self.expect_symbol_(";") return ast.LookupFlagStatement(location, value, None, None) # format A: "lookupflag RightToLeft MarkAttachmentType @M;" value, markAttachment, markFilteringSet = 0, None, None flags = { "RightToLeft": 1, "IgnoreBaseGlyphs": 2, "IgnoreLigatures": 4, "IgnoreMarks": 8 } seen = set() while self.next_token_ != ";": if self.next_token_ in seen: raise FeatureLibError( "%s can be specified only once" % self.next_token_, self.next_token_location_) seen.add(self.next_token_) if self.next_token_ == "MarkAttachmentType": self.expect_keyword_("MarkAttachmentType") markAttachment = self.parse_class_name_() elif self.next_token_ == "UseMarkFilteringSet": self.expect_keyword_("UseMarkFilteringSet") markFilteringSet = self.parse_class_name_() elif self.next_token_ in flags: value = value | flags[self.expect_name_()] else: raise FeatureLibError( '"%s" is not a recognized lookupflag' % self.next_token_, self.next_token_location_) self.expect_symbol_(";") return ast.LookupFlagStatement(location, value, markAttachment, markFilteringSet)
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 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 _lookupDefinition(self, lookup): mark_attachement = None mark_filtering = None flags = 0 if lookup.direction == "RTL": flags |= 1 if not lookup.process_base: flags |= 2 # FIXME: Does VOLT support this? # if not lookup.process_ligatures: # flags |= 4 if not lookup.process_marks: flags |= 8 elif isinstance(lookup.process_marks, str): mark_attachement = self._groupName(lookup.process_marks) elif lookup.mark_glyph_set is not None: mark_filtering = self._groupName(lookup.mark_glyph_set) lookupflags = None if flags or mark_attachement is not None or mark_filtering is not None: lookupflags = ast.LookupFlagStatement(flags, mark_attachement, mark_filtering) if "\\" in lookup.name: # Merge sub lookups as subtables (lookups named “base\sub”), # makeotf/feaLib will issue a warning and ignore the subtable # statement if it is not a pairpos lookup, though. name = lookup.name.split("\\")[0] if name.lower() not in self._lookups: fealookup = ast.LookupBlock(self._lookupName(name)) if lookupflags is not None: fealookup.statements.append(lookupflags) fealookup.statements.append(ast.Comment("# " + lookup.name)) else: fealookup = self._lookups[name.lower()] fealookup.statements.append(ast.SubtableStatement()) fealookup.statements.append(ast.Comment("# " + lookup.name)) self._lookups[name.lower()] = fealookup else: fealookup = ast.LookupBlock(self._lookupName(lookup.name)) if lookupflags is not None: fealookup.statements.append(lookupflags) self._lookups[lookup.name.lower()] = fealookup if lookup.comments is not None: fealookup.statements.append(ast.Comment("# " + lookup.comments)) contexts = [] if lookup.context: for context in lookup.context: prefix = self._context(context.left) suffix = self._context(context.right) ignore = context.ex_or_in == "EXCEPT_CONTEXT" contexts.append([prefix, suffix, ignore, False]) # It seems that VOLT will create contextual substitution using # only the input if there is no other contexts in this lookup. if ignore and len(lookup.context) == 1: contexts.append([[], [], False, True]) else: contexts.append([[], [], False, False]) targetlookup = None for prefix, suffix, ignore, chain in contexts: if lookup.sub is not None: self._gsubLookup(lookup, prefix, suffix, ignore, chain, fealookup) if lookup.pos is not None: if self._settings.get("COMPILER_USEEXTENSIONLOOKUPS"): fealookup.use_extension = True if prefix or suffix or chain or ignore: if not ignore and targetlookup is None: targetname = self._lookupName(lookup.name + " target") targetlookup = ast.LookupBlock(targetname) fealookup.targets = getattr(fealookup, "targets", []) fealookup.targets.append(targetlookup) self._gposLookup(lookup, targetlookup) self._gposContextLookup(lookup, prefix, suffix, ignore, fealookup, targetlookup) else: self._gposLookup(lookup, fealookup)