def _load_fontfile(self): try: if self.fontfile.endswith(".ufo") or self.fontfile.endswith("tf"): # Single master workflow self.font = Babelfont.open(self.fontfile) self.variations = None else: self.variations = VariableFont(self.fontfile) # We need a "scratch copy" because we will be trashing the # glyph data with our interpolations if len(self.variations.masters.keys()) == 1: self.font = list(self.variations.masters.values())[0] self.variations = None else: firstmaster = self.variations.designspace.sources[0].path if firstmaster: self.font = Babelfont.open(firstmaster) else: # Glyphs, fontlab? self.font = Babelfont.open(self.fontfile) except Exception as e: if self.editor: self.editor.showError("Couldn't open %s: %s" % (self.fontfile, e)) else: raise e return False return True
def load_masters(self): if self.masters: return for src in self.config["sources"]: if src.endswith("glyphs"): gsfont = glyphsLib.GSFont(src) self.masters[src] = [] for master in gsfont.masters: self.masters[src].append( Babelfont.open(src, master=master.name)) elif src.endswith("designspace"): continue else: self.masters[src] = [Babelfont.open(src)]
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_parse_to_ff(self): p = FeeParser(Babelfont.load("fonts/Roboto-Regular.ttf")) p.parseString(""" Anchors A { top <679 1600> bottom <691 0> }; Anchors B { top <611 1612> }; Anchors acutecomb { _top <-570 1290> }; Anchors tildecomb { _top <-542 1256> }; Feature mark { Attach &top &_top bases; }; """) self.assertEqual(p.fontfeatures.anchors["A"]["top"], (679, 1600)) self.assertSufficientlyEqual( p.fontfeatures.asFea(), """ markClass acutecomb <anchor -570 1290> @top; markClass tildecomb <anchor -542 1256> @top; feature mark { lookup Routine_1 { ; pos base A <anchor 679 1600> mark @top; pos base B <anchor 611 1612> mark @top; } Routine_1; } mark; """, )
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_roundtrip(): if sys.platform != "darwin": pytest.skip("Needs to be run on OS X") font = Babelfont.open("tests/data/Test1.glyphs") Babelfont.save(font, "tests/data/Test1.roundtrip.glyphs") pl1_json = subprocess.run( ["plutil", "-convert", "xml1", "-o", "-", "tests/data/Test1.glyphs"], capture_output=True, ) pl2_json = subprocess.run( [ "plutil", "-convert", "xml1", "-o", "-", "tests/data/Test1.roundtrip.glyphs" ], capture_output=True, ) pl1 = plistlib.load(BytesIO(pl1_json.stdout)) pl2 = plistlib.load(BytesIO(pl2_json.stdout)) assert pl1 == pl2
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 test_load(): font = Babelfont.load("tests/data/NewFont-Regular.ttf") a_layer = font["A"] assert isinstance(a_layer, Glyph) assert len(a_layer.contours) == 2 assert len(a_layer.contours[0].points) == 8 assert len(a_layer.contours[1].points) == 4 assert a_layer.contours[0].points[0].x == 345 assert a_layer.contours[0].points[0].y == 700 assert a_layer.leftMargin == 25 assert a_layer.rightMargin == 25 assert a_layer.bounds == (25, 0, 505, 700) assert a_layer.width == 530 dot = font["uni0307"] assert dot.category == "mark" assert font.glyphForCodepoint(ord("A")) == "A"
def main(): parser = argparse.ArgumentParser( prog="babelfont", description='Convert between font formats') parser.add_argument('input', metavar='IN', help="Input file") parser.add_argument('output', metavar='OUT', help="Output file") args = parser.parse_args() try: font = Babelfont.load(args.input) except Exception as e: print("Couldn't read %s: %s" % (args.input, e)) sys.exit(1) try: font.save(args.output) except Exception as e: print("Couldn't write %s: %s" % (args.output, e)) raise e sys.exit(1) sys.exit(0)
class TestFeeDefinitions(unittest.TestCase): roboto = FeeParser(Babelfont.load("fonts/Roboto-Regular.ttf")) def assertSufficientlyEqual(self, s1, s2): def alltrim(a): a = re.sub("#.*", "", a) a = re.sub("\\s+", " ", a) return a.strip() self.assertEqual(alltrim(s1), alltrim(s2)) def test_parse_to_ff(self): self.roboto.parseString(""" DefineClass @vowels = /^[aeiou]$/; DefineClass @consonants = /^[bcdfghjklmnpqrstvwxyz]$/; DefineClass @letters = [@vowels @consonants]; """) self.assertEqual(len(self.roboto.fontfeatures.namedClasses["letters"]), 26) def test_unicode_range_selector(self): self.roboto.parseString("DefineClass @digits = U+0030=>U+0039;") self.assertEqual(len(self.roboto.fontfeatures.namedClasses["digits"]), 10)
def test_ufo_roundtrip(): font = Babelfont.open("tests/data/Test1.ufo") Babelfont.save(font, "tests/data/Test1.roundtrip.ufo") dc1 = defcon.Font("tests/data/Test1.ufo") dc2 = defcon.Font("tests/data/Test1.roundtrip.ufo") assert dc1.getDataForSerialization() == dc2.getDataForSerialization()
def test_ufo_load(): font = Babelfont.open("tests/data/Test1.ufo") assert font.glyphForCodepoint(ord("A")) == "A"
def test_loader(): filename = "tests/data/Castoro Roman.vfj" font = Babelfont.open(filename)
def parser(): path, _ = os.path.split(__file__) fontpath = os.path.join(path, "data", "LibertinusSans-Regular.otf") return FeeParser(Babelfont.load(fontpath))
def _save(self, path=None, **kwargs): if not path: path = self._path if not path: raise ValueError Babelfont.save(self, path)
def test_round_trip(): font = Babelfont.load("tests/data/Nunito-Regular.ttf") TTFont("tests/data/Nunito-Regular.ttf").saveXML("tests/data/Nunito-1.ttx") font.save("tests/data/Nunito-from-ttf.ttf") TTFont("tests/data/Nunito-from-ttf.ttf").saveXML("tests/data/Nunito-2.ttx")
import pytest import os import re def alltrim(a): a = re.sub("lookupflag 0;", "", a) a = re.sub("#.*", "", a) a = re.sub("\\s+", " ", a) a = re.sub("table GDEF.*GDEF;", "", a) return a.strip() path, _ = os.path.split(__file__) fontpath = os.path.join(path, "data", "LibertinusSans-Regular.otf") font = Babelfont.load(fontpath) @pytest.fixture def parser(): return FeeParser(font) ############ # BareName # ############ class BareNameModule: GRAMMAR = """ ?start: action
def test_ufo_to_glyphs(): font = Babelfont.open("tests/data/Test1.ufo") Babelfont.save(font, "tests/data/Test1-from-ufo.glyphs")
def test_otfloader(fontname): font = Babelfont.load(fontname)
def test_load_glyph(): filename = "tests/data/Castoro Roman.vfj" font = Babelfont.open(filename) assert len(font["A"].contours[0]) == 20 assert len(font["A"].contours[1]) == 4
def test_glyphs_to_ufo(): font = Babelfont.open("tests/data/Test1.glyphs") Babelfont.save(font, "tests/data/Test1-from-glyphs.ufo")