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_MergeMultipleSingleSubstitutions_1(self): r1 = Routine(rules=[ Substitution([["a", "y"]], [["b", "z"]]), Substitution([["b", "d", "a"]], [["c", "e", "f"]]), ]) Optimizer(FontFeatures()).optimize_routine(r1, level=1) self.assertEqual(r1.asFea(), " sub [a y b d] by [c z c e];\n")
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_single(self): s = Substitution(["a"], ["b"]) self.assertEqual(s.asFea(), "sub a by b;") self.assertEqual(s.involved_glyphs, set(["a", "b"])) self.assertEqual( etree.tostring(s.toXML()), "<substitution><from><slot><glyph>a</glyph></slot></from><to><slot><glyph>b</glyph></slot></to></substitution>" .encode("utf-8")) self.roundTrip(s)
def test_MergeMultipleSingleSubstitutions_2(self): r1 = Routine(rules=[ Substitution([["a"]], [["b"]]), Substitution([["b"]], [["c"]]), Substitution([["d"]], [["e"]]), Substitution([["y"]], [["z"]]), ]) Optimizer(FontFeatures()).optimize_routine(r1, level=1) self.assertEqual(r1.asFea(), " sub [a b d y] by [c c e z];\n")
def test_complex(self): pos1 = Substitution(["a"], ["b"]) pos2 = Substitution(["b"], ["c"]) r1 = Routine(rules=[pos1], name="dummy1") r2 = Routine(rules=[pos2], name="dummy2") rr1 = RoutineReference(routine=r1) rr2 = RoutineReference(routine=r2) c = Chaining([["a"], ["b"]], lookups=[[rr1, rr2], None]) self.assertEqual(c.asFea(), "sub a' lookup dummy1 lookup dummy2 b';")
def test_routine_partition_not_needed(): f = FontFeatures() s1 = Substitution([["A"]], [["A.grk"]], languages=["grek/*"]) s2 = Substitution([["A"]], [["A.esp"]], languages=["grek/*"]) r = Routine(rules=[s1, s2], flags=0x2) f.routines.append(r) f.partitionRoutine(r, lambda rule: tuple(rule.languages or [])) assert len(f.routines) == 1 r.rules = [] f.partitionRoutine(r, lambda rule: tuple(rule.languages or [])) assert len(f.routines) == 1
def test_script_language_split(): f = FontFeatures() s1 = Substitution([["question"]], [["questiongreek"]], languages=[("grek", "*")]) s2 = Substitution([["A"]], [["alpha"]], languages=[("grek", "ELL ")]) s3 = Substitution([["question"]], [["questiondown"]], languages=[("latn", "ESP ")]) s4 = Substitution([["question"]], [["B"]], languages=[("latn", "POL ")]) s5 = Substitution([["X"]], [["Y"]]) r = Routine(rules=[s1, s2, s3, s4, s5]) f.addFeature("locl", [r]) f.buildBinaryFeatures(font) gsub = "\n".join(getXML(font["GSUB"].toXML)) assert gsub == """<Version value="0x00010000"/>
def merge_two(self, first, second): assert len(first.input) == 1 and len(second.input) == 1 assert len(first.replacement) == 1 and len(second.replacement) == 1 assert first.precontext == second.precontext assert first.postcontext == second.postcontext firstmapping = { l: r for l, r in zip(first.input[0], first.replacement[0]) } secondmapping = { l: r for l, r in zip(second.input[0], second.replacement[0]) } for l, r in firstmapping.items(): if r in secondmapping: firstmapping[l] = secondmapping[r] for l, r in secondmapping.items(): if not (l in firstmapping): firstmapping[l] = r logger = logging.getLogger("fontFeatures") logger.info("Merging two adjacent single subs") if logger.isEnabledFor(logging.DEBUG): logger.debug(first.asFea()) logger.debug(second.asFea()) address = first.address or second.address return Substitution( [list(firstmapping.keys())], [list(firstmapping.values())], address=address, precontext=first.precontext, postcontext=first.postcontext, )
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_multiple_languages_routine(): f = FontFeatures() s1 = Substitution([["a"]], ["b"]) expected = """languagesystem arab URD; languagesystem arab FAR; lookup Routine_1 { lookupflag 0; ; sub a by b; } Routine_1; feature locl { script arab; language URD; lookup Routine_1; } locl; feature locl { script arab; language FAR; lookup Routine_1; } locl; """ f = FontFeatures() r1 = Routine(rules=[s1], languages=[("arab", "URD "), ("arab", "FAR ")]) f.addFeature("locl", [r1]) assert f.asFea(do_gdef=False) == expected
def test_simple_sub(self): pos = Substitution(["a"], ["b"]) r = Routine(rules=[pos], name="dummy") rr = RoutineReference(routine=r) c = Chaining([["a"], ["b"]], lookups=[[rr], None]) self.assertEqual(c.asFea(), "sub a' lookup dummy b';")
def test_GlyphClasses(self): r1 = Routine(rules=[ Substitution([["a", "b", "c", "d", "e", "f", "g", "h"]], [["z"]]), ]) ff = FontFeatures() Optimizer(ff).optimize_routine(r1, level=1) self.assertEqual(r1.asFea(), " sub @class1 by z;\n") self.assertEqual(ff.namedClasses["class1"], ("a", "b", "c", "d", "e", "f", "g", "h"))
def test_routine_partition(): f = FontFeatures() s1 = Substitution([["A"]], [["A.grk"]], languages=["grek/*"]) s2 = Substitution([["A"]], [["A.esp"]], languages=["latn/ESP "]) r = Routine(rules=[s1, s2], flags=0x2) f.routines.append(r) dummy = Routine(rules=[Substitution([["G"]], [["G"]])]) f.routines.append(dummy) c = Chaining( [["A"], ["V"]], lookups=[ [RoutineReference(routine=dummy), RoutineReference(routine=r)], [RoutineReference(routine=r), RoutineReference(routine=dummy)], ]) r2 = Routine(rules=[c]) f.routines.append(r2) f.addFeature("locl", [r]) f.partitionRoutine(r, lambda rule: tuple(rule.languages or [])) assert len(f.routines) == 4 assert f.routines[0].flags == f.routines[1].flags assert len(f.routines[0].rules) == 1 assert len(f.routines[1].rules) == 1 assert f.routines[0].rules[0].replacement[0][0] == "A.grk" assert f.routines[1].rules[0].replacement[0][0] == "A.esp" assert len(c.lookups[0]) == 3 assert len(f.features["locl"]) == 2
def _gsubLookup(self, lookup, prefix, suffix, ignore, chain, routine): sub = lookup.sub for key, val in sub.mapping.items(): if not key or not val: path, line, column = sub.location log.warning( f"{path}:{line}:{column}: Ignoring empty substitution") continue statement = None glyphs = self._coverage(key) replacements = self._coverage(val) if ignore: chain_context = (prefix, glyphs, suffix) statement = ast.IgnoreSubstStatement([chain_context]) else: statement = Substitution(glyphs, replacements, precontext=prefix, postcontext=suffix, lookups=chain) if isinstance( sub, VAst.SubstitutionReverseChainingSingleDefinition): statement.reversed = True routine.rules.append(statement)
def test_multiple(self): s = Substitution(["a"], ["b", "c"]) self.assertEqual(s.asFea(), "sub a by b c;") self.assertEqual(s.involved_glyphs, set(["a", "b", "c"])) self.roundTrip(s)
def addSubRule(self): self.model().addRule(self.selectedIndexes()[0], Substitution([[]], [[]])) self.parent.editor.setWindowModified(True)
def test_language_ordering(): f = FontFeatures() s1 = Substitution([["a"]], ["b"], languages=[("arab", "URD ")]) s2 = Substitution([["a"]], ["c"], languages=[("arab", "FAR ")]) s3 = Substitution([["x"], ["y"]], ["z"], languages=[("arab", "URD ")]) f.addFeature("locl", [Routine(rules=[s1, s2, s3])]) # When going to fea, we put everything in its own feature block to # avoid stupid problems with language systems expected = """languagesystem arab URD; languagesystem arab FAR; lookup Routine_1 { lookupflag 0; ; sub a by b; } Routine_1; lookup Routine_2 { lookupflag 0; ; sub a by c; } Routine_2; lookup Routine_3 { lookupflag 0; ; sub x y by z; } Routine_3; feature locl { script arab; language URD; lookup Routine_1; } locl; feature locl { script arab; language FAR; lookup Routine_2; } locl; feature locl { script arab; language URD; lookup Routine_3; } locl; """ assert f.asFea(do_gdef=False) == expected # But the same is true of multiple lookups in the same feature f = FontFeatures() r1 = Routine(rules=[s1]) r2 = Routine(rules=[s2]) r3 = Routine(rules=[s3]) f.addFeature("locl", [r1, r2, r3]) assert f.asFea(do_gdef=False) == expected
def test_single_classes(self): s = Substitution([["a", "b"]], [["c", "d"]]) self.assertEqual(s.asFea(), "sub [a b] by [c d];") self.assertEqual(s.involved_glyphs, set(["a", "b", "c", "d"])) self.roundTrip(s)
def test_ligature(self): s = Substitution(["a", "b"], ["c"]) self.assertEqual(s.asFea(), "sub a b by c;") self.assertEqual(s.involved_glyphs, set(["a", "b", "c"])) self.roundTrip(s)
def test_ligature_expansion(self): s = Substitution([["f", "f.ss01"], ["i", "i.ss01"]], [["f_i", "f_i.ss01"]]) self.assertEqual(s.asFea(), " sub f i by f_i;\n sub f.ss01 i.ss01 by f_i.ss01;\n") self.assertEqual(s.involved_glyphs, set(["f", "i", "f_i", "f.ss01", "i.ss01", "f_i.ss01"])) self.roundTrip(s)
def test_multiple_expansion_middle(self): s = Substitution([["aa", "bb"]], ["d", ["aa", "bb"], "c"]) self.assertEqual(s.asFea(), " sub aa by d aa c;\n sub bb by d bb c;\n") self.assertEqual(s.involved_glyphs, set(["aa", "bb", "c", "d"])) self.roundTrip(s)
def test_alternate(self): s = Substitution(["a"], [["b", "c"]]) self.assertEqual(s.asFea(), "sub a from [b c];") self.assertEqual(s.involved_glyphs, set(["a", "b", "c"])) self.roundTrip(s)
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("qalam.fluxml") proj.fontfeatures.features["mark"] = [proj.fontfeatures.routines[2]] proj.fontfeatures.features["curs"] = [proj.fontfeatures.routines[1]] # v = ValueRecord(yPlacement=0) # rule = Positioning( # [["dda", "tda"]], # [v], # precontext=[["BEm2", "BEi3"]], # postcontext=[["GAFm1", "GAFf1"]], # ) rule = Substitution(input_=[["space"]], replacement=[["space"]]) v_box_1.addWidget(QRuleEditor(proj, None)) w.setLayout(v_box_1) w.show() sys.exit(app.exec_())
def test_multiple_expansion(self): s = Substitution([["a", "b"]], [["a", "b"], "c"]) self.assertEqual(s.asFea(), " sub a by a c;\n sub b by b c;\n") self.assertEqual(s.involved_glyphs, set(["a", "b", "c"])) self.roundTrip(s)