def roundtrip(ufo, tmpdir): font = to_glyphs([ufo], minimize_ufo_diffs=True) filename = os.path.join(str(tmpdir), 'font.glyphs') font.save(filename) font = classes.GSFont(filename) ufo, = to_ufos(font) return font, ufo
def test_custom_featureWriters_in_designpace_lib(tmpdir, ufo_module): """Test that we can roundtrip custom user-defined ufo2ft featureWriters settings that are stored in the designspace lib or GSFont.userData. """ font = classes.GSFont() font.masters.append(classes.GSFontMaster()) kern = classes.GSFeature(name="kern", code="pos a b 100;") font.features.append(kern) customFeatureWriters = list(DEFAULT_FEATURE_WRITERS) + [{ "class": "MyCustomWriter", "module": "myCustomWriter" }] font.userData[UFO2FT_FEATURE_WRITERS_KEY] = customFeatureWriters designspace = to_designspace(font, ufo_module=ufo_module) path = str(tmpdir / "test.designspace") designspace.write(path) for source in designspace.sources: source.font.save(str(tmpdir / source.filename)) designspace2 = DesignSpaceDocument.fromfile(path) assert UFO2FT_FEATURE_WRITERS_KEY in designspace2.lib assert designspace2.lib[UFO2FT_FEATURE_WRITERS_KEY] == customFeatureWriters font2 = to_glyphs(designspace2, ufo_module=ufo_module) assert len(font2.userData) == 1 assert font2.userData[UFO2FT_FEATURE_WRITERS_KEY] == customFeatureWriters
def test_pen_roundtrip(datadir, ufo_module): font = classes.GSFont(str(datadir.join("PenTest.glyphs"))) font_temp = ufo_module.Font() glyph_temp = font_temp.newGlyph("test") for glyph in font.glyphs: for layer in glyph.layers: glyph_temp.clear() ufo_pen = glyph_temp.getPointPen() layer.drawPoints(ufo_pen) layer_temp = classes.GSLayer() glyphs_pen = layer_temp.getPointPen() glyph_temp.drawPoints(glyphs_pen) assert len(layer.paths) == len(layer_temp.paths) for path_orig, path_temp in zip(layer.paths, layer_temp.paths): assert len(path_orig.nodes) == len(path_temp.nodes) assert path_orig.closed == path_temp.closed for node_orig, node_temp in zip(path_orig.nodes, path_temp.nodes): assert node_orig.position.x == node_temp.position.x assert node_orig.position.y == node_temp.position.y assert node_orig.smooth == node_temp.smooth assert node_orig.type == node_temp.type assert len(layer.components) == len(layer_temp.components) for comp_orig, comp_temp in zip(layer.components, layer_temp.components): assert comp_orig.name == comp_temp.name assert comp_orig.transform == comp_temp.transform
def assertDesignspaceRoundtrip(self, designspace): directory = tempfile.mkdtemp() font = to_glyphs(designspace, minimize_ufo_diffs=True) # Check that round-tripping in memory is the same as writing on disk roundtrip_in_mem = to_designspace(font, propagate_anchors=False) tmpfont_path = os.path.join(directory, "font.glyphs") font.save(tmpfont_path) font_rt = classes.GSFont(tmpfont_path) roundtrip = to_designspace(font_rt, propagate_anchors=False) font.save("intermediary.glyphs") write_designspace_and_UFOs(designspace, "expected/test.designspace") for source in designspace.sources: normalize_ufo_lib(source.path) normalizeUFO(source.path, floatPrecision=3, writeModTimes=False) write_designspace_and_UFOs(roundtrip, "actual/test.designspace") for source in roundtrip.sources: normalize_ufo_lib(source.path) normalizeUFO(source.path, floatPrecision=3, writeModTimes=False) self.assertDesignspacesEqual( roundtrip_in_mem, roundtrip, "The round-trip in memory or written to disk should be equivalent", ) self.assertDesignspacesEqual( designspace, roundtrip, "The font should not be modified by the roundtrip" )
def test_dont_copy_advance_to_the_background_unless_it_was_there(tmpdir): ufo = defcon.Font() bg = ufo.newLayer('public.background') fg_a = ufo.newGlyph('a') fg_a.width = 100 bg_a = bg.newGlyph('a') fg_b = ufo.newGlyph('b') fg_b.width = 200 bg_b = bg.newGlyph('b') bg_b.width = 300 fg_c = ufo.newGlyph('c') fg_c.width = 400 bg_c = bg.newGlyph('c') bg_c.width = 400 font = to_glyphs([ufo]) path = os.path.join(str(tmpdir), 'test.glyphs') font.save(path) saved_font = classes.GSFont(path) for font in [font, saved_font]: ufo, = to_ufos(font) assert ufo['a'].width == 100 assert ufo.layers['public.background']['a'].width == 0 assert ufo['b'].width == 200 assert ufo.layers['public.background']['b'].width == 300 assert ufo['c'].width == 400 assert ufo.layers['public.background']['c'].width == 400
def test_pen_recording(datadir): font = classes.GSFont(str(datadir.join("PenTest.glyphs"))) pen = fontTools.pens.recordingPen.RecordingPen() layer = font.glyphs["recording1"].layers[0] layer.draw(pen) assert pen.value == [ ("moveTo", ((0, 141),)), ("lineTo", ((0, 0),)), ("lineTo", ((162, 0),)), ("lineTo", ((162, 141),)), ("closePath", ()), ("moveTo", ((366, 0),)), ("curveTo", ((413, 0), (452, 32), (452, 71))), ("curveTo", ((452, 109), (413, 141), (366, 141))), ("curveTo", ((319, 141), (280, 109), (280, 71))), ("curveTo", ((280, 32), (319, 0), (366, 0))), ("closePath", ()), ("moveTo", ((64, 255),)), ("lineTo", ((56, 419),)), ("lineTo", ((186, 415),)), ("curveTo", ((189, 387), (118, 295), (196, 331))), ("endPath", ()), ("moveTo", ((266, 285),)), ("lineTo", ((412, 421),)), ("endPath", ()), ("moveTo", ((462, 387),)), ("curveTo", ((458, 358), (514, 295), (450, 301))), ("endPath", ()), ("addComponent", ("dieresis", Transform(1, 0, 0, 1, 108, -126))), ( "addComponent", ("adieresis", Transform(0.84572, 0.30782, -0.27362, 0.75175, 517, 308)), ), ]
def test_dump(self): obj = classes.GSFont() fp = UnicodeIO() dump(obj, fp) self.assertTrue(fp.getvalue())
def test_default_featureWriters_in_designspace_lib(tmpdir, ufo_module): """Test that the glyphsLib custom featureWriters settings (with mode="append") are exported to the designspace lib whenever a GSFont contains a manual 'kern' feature. And that they are not imported back to GSFont.userData if they are the same as the default value. """ font = classes.GSFont() font.masters.append(classes.GSFontMaster()) kern = classes.GSFeature(name="kern", code="pos a b 100;") font.features.append(kern) designspace = to_designspace(font, ufo_module=ufo_module) path = str(tmpdir / "test.designspace") designspace.write(path) for source in designspace.sources: source.font.save(str(tmpdir / source.filename)) designspace2 = DesignSpaceDocument.fromfile(path) assert UFO2FT_FEATURE_WRITERS_KEY in designspace2.lib assert designspace2.lib[ UFO2FT_FEATURE_WRITERS_KEY] == DEFAULT_FEATURE_WRITERS font2 = to_glyphs(designspace2, ufo_module=ufo_module) assert not len(font2.userData) assert len([f for f in font2.features if f.name == "kern"]) == 1
def test_groups_remain_at_top(tmpdir, ufo_module): ufo = ufo_module.Font() ufo.newGlyph("zero") ufo.newGlyph("zero.alt") fea_example = dedent("""\ @FIG_DFLT = [zero]; @FIG_ALT = [zero.alt]; lookup pnum_text { sub @FIG_DFLT by @FIG_ALT; } pnum_text; feature pnum { lookup pnum_text; } pnum; """) ufo.features.text = fea_example font = to_glyphs([ufo], minimize_ufo_diffs=True) filename = os.path.join(str(tmpdir), "font.glyphs") font.save(filename) font = classes.GSFont(filename) (rtufo, ) = to_ufos(font, ufo_module=ufo_module) fea_rt = rtufo.features.text assert fea_rt.index("@FIG_DFLT") < fea_rt.index("lookup") < fea_rt.index( "feature")
def roundtrip(ufo, tmpdir, ufo_module): font = to_glyphs([ufo], minimize_ufo_diffs=True) filename = os.path.join(str(tmpdir), "font.glyphs") font.save(filename) font = classes.GSFont(filename) (ufo, ) = to_ufos(font, ufo_module=ufo_module) return font, ufo
def test_info_name_records(tmpdir, ufo_module): ufo = ufo_module.Font() name_records = [ { "nameID": 19, "platformID": 1, "encodingID": 0, "languageID": 1, "string": "Les cornichons sont nos amis", }, { "nameID": 1, "platformID": 3, "encodingID": 1, "languageID": 0x0410, "string": "Illustrativo Sans", }, ] ufo.info.openTypeNameRecords = name_records font = to_glyphs([ufo], minimize_ufo_diffs=True) filename = os.path.join(str(tmpdir), "font.glyphs") font.save(filename) font = classes.GSFont(filename) (ufo, ) = to_ufos(font, ufo_module=ufo_module) print(ufo.info.openTypeNameRecords) assert [dict(r) for r in ufo.info.openTypeNameRecords] == name_records
def test_dont_copy_advance_to_the_background_unless_it_was_there( tmpdir, ufo_module): ufo = ufo_module.Font() bg = ufo.newLayer("public.background") fg_a = ufo.newGlyph("a") fg_a.width = 100 bg.newGlyph("a") fg_b = ufo.newGlyph("b") fg_b.width = 200 bg_b = bg.newGlyph("b") bg_b.width = 300 fg_c = ufo.newGlyph("c") fg_c.width = 400 bg_c = bg.newGlyph("c") bg_c.width = 400 font = to_glyphs([ufo]) path = os.path.join(str(tmpdir), "test.glyphs") font.save(path) saved_font = classes.GSFont(path) for font in [font, saved_font]: (ufo, ) = to_ufos(font) assert ufo["a"].width == 100 assert ufo.layers["public.background"]["a"].width == 0 assert ufo["b"].width == 200 assert ufo.layers["public.background"]["b"].width == 300 assert ufo["c"].width == 400 assert ufo.layers["public.background"]["c"].width == 400
def test_generate_GDEF_already_exists(tmpdir, ufo_with_GDEF): ufo = ufo_with_GDEF[0] font = to_glyphs([ufo]) filename = os.path.join(str(tmpdir), "font.glyphs") font.save(filename) font = classes.GSFont(filename) with pytest.raises(ValueError, match="features already contain a `table GDEF"): to_ufos(font, generate_GDEF=True)
def test_pen_recording_equivalent(datadir): font = classes.GSFont(str(datadir.join("PenTest.glyphs"))) for glyph in font.glyphs: for layer in glyph.layers: rpen1 = fontTools.pens.recordingPen.RecordingPen() rpen2 = fontTools.pens.recordingPen.RecordingPen() layer.draw(rpen1) layer.drawPoints(PointToSegmentPen(rpen2)) assert rpen1.value == rpen2.value
def test_comments_in_classes(ufo_module): filename = os.path.join(os.path.dirname(__file__), "../data/CommentedClass.glyphs") font = classes.GSFont(filename) (ufo, ) = to_ufos(font) assert ufo.features.text == dedent("""\ @Test = [ A # B ]; """)
def test_roundtrip_existing_GDEF(tmpdir, ufo_with_GDEF): """Test that an existing GDEF table in UFO is preserved unchanged and no extra GDEF table is generated upon roundtripping to UFO when `generate_GDEF` is False. """ ufo, gdef, ufo_module = ufo_with_GDEF font = to_glyphs([ufo]) filename = os.path.join(str(tmpdir), "font.glyphs") font.save(filename) font = classes.GSFont(filename) (rtufo, ) = to_ufos(font, generate_GDEF=False, ufo_module=ufo_module) assert rtufo.features.text == gdef
def test_info(fields, tmpdir): ufo = defcon.Font() for field in fields: setattr(ufo.info, field.name, field.test_value) font = to_glyphs([ufo], minimize_ufo_diffs=True) filename = os.path.join(str(tmpdir), 'font.glyphs') font.save(filename) font = classes.GSFont(filename) ufo, = to_ufos(font) for field in fields: assert getattr(ufo.info, field.name) == field.test_value
def test_info(fields, tmpdir, ufo_module): ufo = ufo_module.Font() for field in fields: setattr(ufo.info, field.name, field.test_value) font = to_glyphs([ufo], minimize_ufo_diffs=True) filename = os.path.join(str(tmpdir), "font.glyphs") font.save(filename) font = classes.GSFont(filename) (ufo, ) = to_ufos(font, ufo_module=ufo_module) for field in fields: assert getattr(ufo.info, field.name) == field.test_value
def test_different_features_in_different_UFOS(tmpdir): # If the input UFOs have different features, Glyphs cannot model the # differences easily. # # TODO: (jany) A complex solution would be to put all the features that we # find across all the UFOS into the GSFont's features, and then add custom # parameters "Replace Features" and "Remove features" to the GSFontMasters # of the UFOs that didn't have the feature originally. # # What is done now: if feature files differ between the input UFOs, the # original text of each UFO's feature is stored in userData, and a single # GSFeaturePrefix is created just to warn the user that features were not # imported because of differences. ufo1 = defcon.Font() ufo1.features.text = dedent( """\ include('../family.fea'); """ ) ufo2 = defcon.Font() ufo2.features.text = dedent( """\ include('../family.fea'); feature ss03 { sub c by c.ss03; } ss03; """ ) font = to_glyphs([ufo1, ufo2], minimize_ufo_diffs=True) filename = os.path.join(str(tmpdir), "font.glyphs") font.save(filename) font = classes.GSFont(filename) ufo1rt, ufo2rt = to_ufos(font) assert len(font.features) == 0 assert len(font.featurePrefixes) == 1 assert font.featurePrefixes[0].code == dedent( """\ # Do not use Glyphs to edit features. # # This Glyphs file was made from several UFOs that had different # features. As a result, the features are not editable in Glyphs and # the original features will be restored when you go back to UFOs. """ ) assert ufo1rt.features.text == ufo1.features.text assert ufo2rt.features.text == ufo2.features.text
def test_double_unicodes(tmpdir): ufo = defcon.Font() z = ufo.newGlyph('z') z.unicodes = [0x005A, 0x007A] font = to_glyphs([ufo]) path = os.path.join(str(tmpdir), 'test.glyphs') font.save(path) saved_font = classes.GSFont(path) for font in [font, saved_font]: assert font.glyphs['z'].unicode == "005A" assert font.glyphs['z'].unicodes == ["005A", "007A"] ufo, = to_ufos(font) assert ufo['z'].unicodes == [0x005A, 0x007A]
def test_double_unicodes(tmpdir, ufo_module): ufo = ufo_module.Font() z = ufo.newGlyph("z") z.unicodes = [0x005A, 0x007A] font = to_glyphs([ufo]) path = os.path.join(str(tmpdir), "test.glyphs") font.save(path) saved_font = classes.GSFont(path) for font in [font, saved_font]: assert font.glyphs["z"].unicode == "005A" assert font.glyphs["z"].unicodes == ["005A", "007A"] (ufo, ) = to_ufos(font) assert ufo["z"].unicodes == [0x005A, 0x007A]
def test_node_user_data_into_glif_lib(): font = classes.GSFont() master = classes.GSFontMaster() master.id = "M1" font.masters.append(master) glyph = classes.GSGlyph("a") layer = classes.GSLayer() layer.layerId = "M1" layer.associatedMasterId = "M1" glyph.layers.append(layer) font.glyphs.append(glyph) path = classes.GSPath() layer.paths.append(path) node1 = classes.GSNode() node1.userData["nodeUserDataKey1"] = "nodeUserDataValue1" node1.name = "node1" node2 = classes.GSNode() node2.userData["nodeUserDataKey2"] = "nodeUserDataValue2" node2.name = "node2" path.nodes.append(classes.GSNode()) path.nodes.append(node1) path.nodes.append(classes.GSNode()) path.nodes.append(classes.GSNode()) path.nodes.append(node2) (ufo, ) = to_ufos(font, minimize_glyphs_diffs=True) assert ufo["a"].lib[GLYPHLIB_PREFIX + "nodeUserData.0.1"] == { "nodeUserDataKey1": "nodeUserDataValue1" } assert ufo["a"][0][2].name == "node1" assert ufo["a"].lib[GLYPHLIB_PREFIX + "nodeUserData.0.4"] == { "nodeUserDataKey2": "nodeUserDataValue2" } assert ufo["a"][0][0].name == "node2" font = to_glyphs([ufo]) path = font.glyphs["a"].layers["M1"].paths[0] assert path.nodes[1].userData["nodeUserDataKey1"] == "nodeUserDataValue1" assert path.nodes[1].name == "node1" assert path.nodes[4].userData["nodeUserDataKey2"] == "nodeUserDataValue2" assert path.nodes[4].name == "node2"
def test_glyph_user_data_into_ufo_lib(): font = classes.GSFont() font.masters.append(classes.GSFontMaster()) glyph = classes.GSGlyph('a') glyph.userData['glyphUserDataKey'] = 'glyphUserDataValue' font.glyphs.append(glyph) layer = classes.GSLayer() layer.layerId = font.masters[0].id glyph.layers.append(layer) ufo, = to_ufos(font) assert ufo.lib[GLYPHLIB_PREFIX + 'glyphUserData.a'] == { 'glyphUserDataKey': 'glyphUserDataValue' } font = to_glyphs([ufo]) assert font.glyphs['a'].userData[ 'glyphUserDataKey'] == 'glyphUserDataValue'
def test_glyph_user_data_into_ufo_lib(): font = classes.GSFont() font.masters.append(classes.GSFontMaster()) glyph = classes.GSGlyph("a") glyph.userData["glyphUserDataKey"] = "glyphUserDataValue" font.glyphs.append(glyph) layer = classes.GSLayer() layer.layerId = font.masters[0].id glyph.layers.append(layer) (ufo, ) = to_ufos(font) assert ufo.lib[GLYPHLIB_PREFIX + "glyphUserData.a"] == { "glyphUserDataKey": "glyphUserDataValue" } font = to_glyphs([ufo]) assert font.glyphs["a"].userData[ "glyphUserDataKey"] == "glyphUserDataValue"
def test_ufo_data_into_font_master_user_data(tmpdir): filename = os.path.join('org.customTool', 'ufoData.bin') data = b'\x00\x01\xFF' ufo = defcon.Font() ufo.data[filename] = data font = to_glyphs([ufo]) # Round-trip to disk for this one because I'm not sure there are other # tests that read-write binary data path = os.path.join(str(tmpdir), 'font.glyphs') font.save(path) font = classes.GSFont(path) # The path in the glyphs file should be os-agnostic (forward slashes) assert font.masters[0].userData[GLYPHLIB_PREFIX + 'ufoData'] == { 'org.customTool/ufoData.bin': BinaryData(data) } ufo, = to_ufos(font) assert ufo.data[filename] == data
def test_font_user_data_to_ufo_lib(): # This happens only when not building a designspace # Since there is no designspace.lib to store the font userData, # the latter is duplicated in each output ufo font = classes.GSFont() font.masters.append(classes.GSFontMaster()) font.masters.append(classes.GSFontMaster()) font.userData['fontUserDataKey'] = 'fontUserDataValue' ufo1, ufo2 = to_ufos(font) assert ufo1.lib[GLYPHLIB_PREFIX + 'fontUserData'] == { 'fontUserDataKey': 'fontUserDataValue' } assert ufo2.lib[GLYPHLIB_PREFIX + 'fontUserData'] == { 'fontUserDataKey': 'fontUserDataValue' } font = to_glyphs([ufo1, ufo2]) assert font.userData['fontUserDataKey'] == 'fontUserDataValue'
def test_ufo_data_into_font_master_user_data(tmpdir, ufo_module): filename = "org.customTool/ufoData.bin" data = b"\x00\x01\xFF" ufo = ufo_module.Font() ufo.data[filename] = data font = to_glyphs([ufo]) # Round-trip to disk for this one because I'm not sure there are other # tests that read-write binary data path = os.path.join(str(tmpdir), "font.glyphs") font.save(path) font = classes.GSFont(path) # The path in the glyphs file should be os-agnostic (forward slashes) assert font.masters[0].userData[GLYPHLIB_PREFIX + "ufoData"] == { "org.customTool/ufoData.bin": BinaryData(data) } (ufo, ) = to_ufos(font) assert ufo.data[filename] == data
def test_info_gasp_ranges(tmpdir, ufo_module): ufo = ufo_module.Font() gasp_ranges = [ { "rangeMaxPPEM": 16, "rangeGaspBehavior": [0] }, { "rangeMaxPPEM": 65535, "rangeGaspBehavior": [0, 1] }, ] ufo.info.openTypeGaspRangeRecords = gasp_ranges font = to_glyphs([ufo], minimize_ufo_diffs=True) filename = os.path.join(str(tmpdir), "font.glyphs") font.save(filename) font = classes.GSFont(filename) (ufo, ) = to_ufos(font, ufo_module=ufo_module) assert [dict(r) for r in ufo.info.openTypeGaspRangeRecords] == gasp_ranges
def test_DisplayStrings_ufo_lib(): font = classes.GSFont() font.masters.append(classes.GSFontMaster()) font.masters.append(classes.GSFontMaster()) font.DisplayStrings = "" ufo1, ufo2 = to_ufos(font) assert FONT_CUSTOM_PARAM_PREFIX + "DisplayStrings" not in ufo1.lib assert FONT_CUSTOM_PARAM_PREFIX + "DisplayStrings" not in ufo2.lib font = to_glyphs([ufo1, ufo2]) assert font.DisplayStrings == "" # --- font.DisplayStrings = "a" ufo1, ufo2 = to_ufos(font) assert ufo1.lib[FONT_CUSTOM_PARAM_PREFIX + "DisplayStrings"] == "a" assert ufo2.lib[FONT_CUSTOM_PARAM_PREFIX + "DisplayStrings"] == "a" font = to_glyphs([ufo1, ufo2]) assert font.DisplayStrings == "a"
def test_node_user_data_into_glif_lib(): font = classes.GSFont() master = classes.GSFontMaster() master.id = "M1" font.masters.append(master) glyph = classes.GSGlyph('a') layer = classes.GSLayer() layer.layerId = "M1" layer.associatedMasterId = "M1" glyph.layers.append(layer) font.glyphs.append(glyph) path = classes.GSPath() layer.paths.append(path) node1 = classes.GSNode() node1.userData['nodeUserDataKey1'] = 'nodeUserDataValue1' node2 = classes.GSNode() node2.userData['nodeUserDataKey2'] = 'nodeUserDataValue2' path.nodes.append(classes.GSNode()) path.nodes.append(node1) path.nodes.append(classes.GSNode()) path.nodes.append(classes.GSNode()) path.nodes.append(node2) ufo, = to_ufos(font, minimize_glyphs_diffs=True) assert ufo['a'].lib[GLYPHLIB_PREFIX + 'nodeUserData.0.1'] == { 'nodeUserDataKey1': 'nodeUserDataValue1' } assert ufo['a'].lib[GLYPHLIB_PREFIX + 'nodeUserData.0.4'] == { 'nodeUserDataKey2': 'nodeUserDataValue2' } font = to_glyphs([ufo]) path = font.glyphs['a'].layers['M1'].paths[0] assert path.nodes[1].userData['nodeUserDataKey1'] == 'nodeUserDataValue1' assert path.nodes[4].userData['nodeUserDataKey2'] == 'nodeUserDataValue2'