def test_namedclass_in_slot(): font = Babelfont.load("tests/data/LibertinusSans-Regular.otf") buf = Buffer(font, glyphs=["A", "B", "C"]) r = Routine() r.addRule(Substitution([["G", "@AB"]], [["X"]])) r.apply_to_buffer(buf, namedclasses={"AB": ["A", "B"]}) assert buf.serialize(position=False) == "X|X|C"
def test_double_application(): font = Babelfont.load( "tests/data/acbe26ce904463c690fb67f70679447059d13ee4.otf") buf = Buffer(font, glyphs=["dvKA", "dvVirama", "dvKA", "dvVirama", "dvKA"]) rule = Substitution([["dvKA"], ["dvVirama"]], [["dvK"]]) rule.apply_to_buffer(buf) assert buf.serialize(position=False) == "dvK|dvK|dvKA"
def test_multiple_applications(): font = Babelfont.load("tests/data/LibertinusSans-Regular.otf") buf = Buffer(font, glyphs=["A", "B", "C"]) r = Routine() r.addRule(Substitution([["A"]], [["X"]])) r.addRule(Substitution([["B"]], [["Y"]])) r.apply_to_buffer(buf) assert buf.serialize(position=False) == "X|Y|C"
def test_chain(parser): parser.parseString(""" Routine a_to_b { Substitute A -> B; }; Feature liga { Chain X ( A ^a_to_b) Y; }; """) buf = Buffer(parser.font, unicodes="XAYXAX") shaper = Shaper(parser.fontfeatures, parser.font) shaper.execute(buf) assert buf.serialize(position=False) == "X|B|Y|X|A|X"
def test_swap2(parser): parser.parseString(""" LoadPlugin Swap; Feature liga { Swap C ( A B ) E; }; """) buf = Buffer(parser.font, unicodes="XABDCABDCABE") shaper = Shaper(parser.fontfeatures, parser.font) shaper.execute(buf) assert buf.serialize(position=False) == "X|A|B|D|C|A|B|D|C|B|A|E"
def test_swap(parser): parser.parseString(""" LoadPlugin Swap; Feature liga { Swap A B; }; """) buf = Buffer(parser.font, unicodes="CABD") shaper = Shaper(parser.fontfeatures, parser.font) shaper.execute(buf) assert buf.serialize(position=False) == "C|B|A|D"
def test_shaping(request, fontname, hb_args, input_string, expectation): font = TTFont(fontname) ff = unparse(font) bbf = Babelfont.load(fontname) if not bbf: return pytest.skip("Font too busted to use") buf = Buffer(bbf, unicodes=tounicode(input_string)) shaper = Shaper(ff, bbf) feature_string = "" if "[" in hb_args: return pytest.skip("Partial application not supported yet") if "variations" in hb_args: return pytest.skip("Not planning to support variations") if "ttb" in hb_args: return pytest.skip("Not planning to support top-to-bottom") if "rand" in request.node.name: return pytest.skip("Not planning to support rand feature") if "aat" in request.node.name: return pytest.skip("Not planning to support Apple Advanced Typography") if "kern-format2" in request.node.name: return pytest.skip("Not planning to support kern table") if "fraction" in request.node.name: return pytest.skip("Not planning to support automatic fractions") m = re.search(r'--features="([^"]+)"', hb_args) if m: feature_string = m[1] if "--script" in hb_args: m = re.search(r"--script=(\w+)", hb_args) buf.script = script_map[m[1]] shaper.execute(buf, features=feature_string) serialize_options = {} if "--no-glyph-names" in hb_args: serialize_options["names"] = False if "--ned" in hb_args: serialize_options["ned"] = True if "--no-position" in hb_args: serialize_options["position"] = False if "post" not in font or font["post"].formatType != 2.0: serialize_options["names"] = False expectation = re.sub("gid", "", expectation) serialized = buf.serialize(**serialize_options) # Finesse cluster information serialized = re.sub(r"=\d+", "", serialized) expectation = re.sub(r"=\d+", "", expectation) assert "[" + serialized + "]" == expectation
def makeRepresentativeString(self): inputglyphs = [] if not self.rule: return inputglyphs # "x and x[0]" thing because slots may be empty if newly added if hasattr(self.rule, "precontext"): inputglyphs.extend([x and x[0] for x in self.rule.precontext]) inputglyphs.extend([x and x[0] for x in self.rule.shaper_inputs()]) if hasattr(self.rule, "postcontext"): inputglyphs.extend([x and x[0] for x in self.rule.postcontext]) representative_string = [x for x in inputglyphs if x] for ix, g in enumerate(representative_string): if (g.startswith("@") and g[1:] in self.project.fontfeatures.namedClasses.keys()): representative_string[ ix] = self.project.fontfeatures.namedClasses[g[1:]][0] # We use this representative string to guess information about # how the *real* shaping process will take place; buffer direction # and script, and hence choice of complex shaper, and hence from # that choice of features to be processed. unicodes = [ self.project.font.codepointForGlyph(x) for x in representative_string ] unicodes = [x for x in unicodes if x] tounicodes = "".join(map(chr, unicodes)) bufferForGuessing = Buffer(self.project.font, unicodes=tounicodes) self.buffer_direction = bufferForGuessing.direction self.buffer_script = bufferForGuessing.script # print("Guessed buffer direction ", self.buffer_direction) # print("Guessed buffer script ", self.buffer_script) shaper = Shaper(self.project.fontfeatures, self.project.font) bufferForGuessing = Buffer(self.project.font, glyphs=representative_string) shaper.execute(bufferForGuessing) self.availableFeatures = [] for stage in shaper.stages: if not isinstance(stage, list): continue for f in stage: if (f not in self.availableFeatures and f in self.project.fontfeatures.features): self.availableFeatures.append(f) self.makeFeatureButtons() return representative_string
def would_substitute(self, feature, subbuffer_items): """Test whether the feature would apply to a list of BufferItem objects.""" if not feature in self.plan.fontfeatures.features: return False subbuffer = Buffer( self.buffer.font, direction=self.buffer.direction, script=self.buffer.script, language=self.buffer.language, ) subbuffer.clear_mask() subbuffer.items = [copy(x) for x in subbuffer_items] subbuffer.clear_mask() routines = self.plan.fontfeatures.features[feature] for r in routines: if isinstance(r, RoutineReference): r = r.routine for rule in r.rules: if rule.stage == "pos": continue if any( [ rule.would_apply_at_position(subbuffer, i, namedclasses=self.plan.fontfeatures.namedClasses) for i in range(len(subbuffer)) ] ): return True return False
def __init__(self, parent, glyph): super().__init__() wlayout = QVBoxLayout() self.setLayout(wlayout) self.parent = parent self.glyph = glyph buf = Buffer(self.parent.project.font, glyphs=[glyph]) renderer = QBufferRenderer(self.parent.project, buf) wlayout.addWidget(renderer) label = QTextEdit(glyph) label.setReadOnly(True) label.setFrameStyle(QFrame.NoFrame) label.setAlignment(Qt.AlignCenter) label.setLineWrapMode(QTextEdit.WidgetWidth) label.setWordWrapMode(QTextOption.WrapAtWordBoundaryOrAnywhere) wlayout.addWidget(label) renderer.resizeEvent(None) # self.setContentsMargins(QMargins(25, 25, 25, 25)) self.setMaximumSize(100, 100)
def makeRepresentativeString(self): inputglyphs = [] if not self.rule or not self.rule.bases or not self.rule.marks: return inputglyphs inputglyphs = [ list(self.rule.bases.keys())[0], list(self.rule.marks.keys())[0] ] # We use this representative string to guess information about # how the *real* shaping process will take place; buffer direction # and script, and hence choice of complex shaper, and hence from # that choice of features to be processed. unicodes = [ self.project.font.codepointForGlyph(x) for x in inputglyphs ] unicodes = [x for x in unicodes if x] tounicodes = "".join(map(chr, unicodes)) bufferForGuessing = Buffer(self.project.font, unicodes=tounicodes) self.buffer_direction = bufferForGuessing.direction self.buffer_script = bufferForGuessing.script # print("Guessed buffer direction ", self.buffer_direction) # print("Guessed buffer script ", self.buffer_script) shaper = Shaper(self.project.fontfeatures, self.project.font) shaper.execute(bufferForGuessing) self.availableFeatures = [] for stage in shaper.stages: if not isinstance(stage, list): continue for f in stage: if f not in self.availableFeatures and f in self.project.fontfeatures.features: self.availableFeatures.append(f) self.makeFeatureButtons() return inputglyphs
def resizeEvent(self, e): self.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) if __name__ == "__main__": from Flux.project import FluxProject from PyQt5.QtWidgets import QApplication, QVBoxLayout from fontFeatures.shaperLib.Buffer import Buffer import sys app = 0 if QApplication.instance(): app = QApplication.instance() else: app = QApplication(sys.argv) w = QWidget() w.resize(510, 210) v_box_1 = QVBoxLayout() proj = FluxProject.new("Rajdhani.glyphs") buf = Buffer(proj.font, unicodes="ABC") buf.map_to_glyphs() v_box_1.addWidget(QBufferRenderer(proj, buf)) w.setLayout(v_box_1) w.show() sys.exit(app.exec_())