def test_custom_stylemap_style_name(): ufo = defcon.Font() ufo.info.styleMapStyleName = 'bold' # Not "regular" font = to_glyphs([ufo], minimize_ufo_diffs=True) ufo, = to_ufos(font) assert ufo.info.styleMapStyleName == 'bold'
def test_custom_stylemap_style_name(ufo_module): ufo = ufo_module.Font() ufo.info.styleMapStyleName = "bold" # Not "regular" font = to_glyphs([ufo], minimize_ufo_diffs=True) (ufo,) = to_ufos(font) assert ufo.info.styleMapStyleName == "bold"
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_guidelines(): ufo = defcon.Font() a = ufo.newGlyph('a') for obj in [ufo, a]: # Complete guideline obj.appendGuideline( dict(x=10, y=20, angle=30, name="lc", color="1,0,0,1", identifier="lc1")) # Don't crash if a guideline misses information obj.appendGuideline({'x': 10}) obj.appendGuideline({'y': 20}) obj.appendGuideline({}) font = to_glyphs([ufo]) for gobj in [font.masters[0], font.glyphs['a'].layers[0]]: assert len(gobj.guides) == 4 angled, vertical, horizontal, empty = gobj.guides assert angled.position.x == 10 assert angled.position.y == 20 assert angled.angle == 330 assert angled.name == "lc [1,0,0,1] [#lc1]" assert vertical.position.x == 10 assert vertical.angle == 90 assert horizontal.position.y == 20 assert horizontal.angle == 0 ufo, = to_ufos(font) for obj in [ufo, ufo['a']]: angled, vertical, horizontal, empty = obj.guidelines assert angled.x == 10 assert angled.y == 20 assert angled.angle == 30 assert angled.name == 'lc' assert angled.color == '1,0,0,1' assert angled.identifier == 'lc1' assert vertical.x == 10 assert vertical.y is None assert vertical.angle is None assert horizontal.x is None assert horizontal.y == 20 assert horizontal.angle is None
def test_guidelines(ufo_module): ufo = ufo_module.Font() a = ufo.newGlyph("a") for obj in [ufo, a]: # Complete guideline obj.appendGuideline( dict(x=10, y=20, angle=30, name="lc", color="1,0,0,1", identifier="lc1")) # Don't crash if a guideline misses information obj.appendGuideline({"x": 10}) obj.appendGuideline({"y": 20}) font = to_glyphs([ufo]) for gobj in [font.masters[0], font.glyphs["a"].layers[0]]: assert len(gobj.guides) == 3 angled, vertical, horizontal = gobj.guides assert angled.position.x == 10 assert angled.position.y == 20 assert angled.angle == 30 assert angled.name == "lc [1,0,0,1] [#lc1]" assert vertical.position.x == 10 assert vertical.angle == 90 assert horizontal.position.y == 20 assert horizontal.angle == 0 (ufo, ) = to_ufos(font) for obj in [ufo, ufo["a"]]: angled, vertical, horizontal = obj.guidelines assert angled.x == 10 assert angled.y == 20 assert angled.angle == 30 assert angled.name == "lc" assert angled.color == "1,0,0,1" assert angled.identifier == "lc1" assert vertical.x == 10 assert vertical.y is None assert vertical.angle is None assert horizontal.x is None assert horizontal.y == 20 assert horizontal.angle is None
def doit(args): logger = args.logger logger.log("Creating UFO objects from GlyphsApp file", "I") with open(args.glyphsfont, 'r', encoding='utf-8') as gfile: gfont = glyphsLib.parser.load(gfile) ufos = glyphsLib.to_ufos(gfont, include_instances=False, family_name=None, propagate_anchors=False, generate_GDEF=False) # Extract directory name for use with restores (glyphsdir, filen) = os.path.split(args.glyphsfont) keylists = { "librestorekeys": [ "org.sil.pysilfontparams", "org.sil.altLineMetrics", "org.sil.lcg.toneLetters", "org.sil.lcg.transforms", "public.glyphOrder", "public.postscriptNames", "com.schriftgestaltung.disablesLastChange", "com.schriftgestaltung.disablesAutomaticAlignment" ], "libdeletekeys": ("UFOFormat", "com.schriftgestaltung.blueFuzz", "com.schriftgestaltung.blueScale", "com.schriftgestaltung.blueShift", "com.schriftgestaltung.customParameter.GSFont.note", "com.schriftgestaltung.customParameter.GSFont.Axes", "com.schriftgestaltung.customParameter.GSFontMaster.Master Name", "org.sil.glyphsappversion"), "libdeleteempty": ("com.schriftgestaltung.DisplayStrings", ), "inforestorekeys": [ "openTypeHeadCreated", "openTypeNamePreferredFamilyName", "openTypeNamePreferredSubfamilyName", "openTypeNameUniqueID", "openTypeOS2WeightClass", "openTypeOS2WidthClass", "postscriptFontName", "postscriptFullName", "styleMapFamilyName", "styleMapStyleName", "note" ], "integerkeys": ("openTypeOS2WeightClass", "openTypeOS2WidthClass"), "infodeleteempty": ("openTypeOS2Selection", ) } if args.restore: # Extra kesys to restore. Add to both lists, since should never be duplicated names keylist = args.restore.split(",") keylists["librestorekeys"] += keylist keylists["inforestorekeys"].append(keylist) loglists = [] for ufo in ufos: loglists.append(process_ufo(ufo, keylists, glyphsdir, args)) for loglist in loglists: for logitem in loglist: logger.log(logitem[0], logitem[1])
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_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_roundtrip_disabled_feature(): font = to_glyphs([defcon.Font()]) feature = classes.GSFeature(name="ccmp") feature.code = dedent( """\ sub a by a.ss03; sub b by b.ss03; sub c by c.ss03; """ ) feature.disabled = True font.features.append(feature) ufo, = to_ufos(font) assert ufo.features.text == dedent( """\ feature ccmp { # disabled #sub a by a.ss03; #sub b by b.ss03; #sub c by c.ss03; } ccmp; """ ) font_r = to_glyphs([ufo]) assert len(font_r.features) == 1 feature_r = font_r.features[0] assert feature_r.name == "ccmp" assert feature_r.code == feature.code assert feature_r.disabled is True font_rr = to_glyphs(to_ufos(font_r)) assert len(font_rr.features) == 1 feature_rr = font_rr.features[0] assert feature_rr.name == "ccmp" assert feature_rr.code == feature.code assert feature_rr.disabled is True
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_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_custom_default_layer_name(ufo_module): ufo1 = ufo_module.Font() ufo2 = ufo_module.Font() if hasattr(ufo1, "renameLayer") and callable(ufo1.renameLayer): ufo1.renameLayer("public.default", "custom default") ufo2.renameLayer("public.default", "other default") else: ufo1.layers.defaultLayer.name = "custom default" ufo2.layers.defaultLayer.name = "other default" font = to_glyphs([ufo1, ufo2]) (ufo1, ufo2) = to_ufos(font) assert ufo1.layers.defaultLayer.name == "custom default" assert ufo2.layers.defaultLayer.name == "other default"
def test_ufo2ft_filter_roundtrip(self): ufo_filters = [ {"name": "propagateAnchors", "pre": True, "include": ["a", "b", "c"]} ] glyphs_filter = "propagateAnchors;include:a,b,c" self.master.customParameters["PreFilter"] = glyphs_filter self.set_custom_params() self.assertEqual(self.ufo.lib[UFO2FT_FILTERS_KEY], ufo_filters) font_rt = glyphsLib.to_glyphs([self.ufo]) self.assertEqual( font_rt.masters[0].customParameters["PreFilter"], glyphs_filter ) ufo_rt = glyphsLib.to_ufos(font_rt)[0] self.assertEqual(ufo_rt.lib[UFO2FT_FILTERS_KEY], ufo_filters)
def test_glyph_color(): ufo = defcon.Font() a = ufo.newGlyph('a') a.markColor = GLYPHS_COLORS[3] b = ufo.newGlyph('b') b.markColor = '{:.3f},{:.3f},0,1'.format(4.0 / 255, 128.0 / 255) font = to_glyphs([ufo]) assert font.glyphs['a'].color == 3 assert font.glyphs['b'].color == [4, 128, 0, 255] ufo, = to_ufos(font) assert ufo['a'].markColor == GLYPHS_COLORS[3] assert ufo['b'].markColor == b.markColor
def test_master_user_location_goes_into_os2_classes(): font = to_glyphs([defcon.Font(), defcon.Font()]) font.customParameters['Axes'] = [ { 'Tag': 'wght', 'Name': 'Weight', }, { 'Tag': 'wdth', 'Name': 'Width', }, ] font.masters[0].weightValue = 0 font.masters[0].widthValue = 1000 # This master will be Light Expanded # as per https://docs.microsoft.com/en-gb/typography/opentype/spec/os2#uswidthclass font.masters[0].customParameters['Axis Location'] = [ { 'Axis': 'Weight', 'Location': 300, }, { 'Axis': 'Width', 'Location': 125, }, ] font.masters[1].weightValue = 1000 font.masters[1].widthValue = 0 # This master is Black Ultra-condensed but not quite font.masters[1].customParameters['Axis Location'] = [ { 'Axis': 'Weight', 'Location': 920, # instead of 900 }, { 'Axis': 'Width', 'Location': 55, # instead of 50 }, ] light, black = to_ufos(font) assert light.info.openTypeOS2WeightClass == 300 assert light.info.openTypeOS2WidthClass == 7 assert black.info.openTypeOS2WeightClass == 920 assert black.info.openTypeOS2WidthClass == 1
def test_master_user_location_goes_into_os2_classes(ufo_module): font = to_glyphs([ufo_module.Font(), ufo_module.Font()]) font.customParameters["Axes"] = [ { "Tag": "wght", "Name": "Weight" }, { "Tag": "wdth", "Name": "Width" }, ] font.masters[0].weightValue = 0 font.masters[0].widthValue = 1000 # This master will be Light Expanded # as per https://docs.microsoft.com/en-gb/typography/opentype/spec/os2#uswidthclass font.masters[0].customParameters["Axis Location"] = [ { "Axis": "Weight", "Location": 300 }, { "Axis": "Width", "Location": 125 }, ] font.masters[1].weightValue = 1000 font.masters[1].widthValue = 0 # This master is Black Ultra-condensed but not quite font.masters[1].customParameters["Axis Location"] = [ { "Axis": "Weight", "Location": 920 }, # instead of 900 { "Axis": "Width", "Location": 55 }, # instead of 50 ] light, black = to_ufos(font) assert light.info.openTypeOS2WeightClass == 300 assert light.info.openTypeOS2WidthClass == 7 assert black.info.openTypeOS2WeightClass == 920 assert black.info.openTypeOS2WidthClass == 1
def test_ufo_lib_equivalent_to_font_master_user_data(): ufo1 = defcon.Font() ufo1.lib['ufoLibKey1'] = 'ufoLibValue1' ufo2 = defcon.Font() ufo2.lib['ufoLibKey2'] = 'ufoLibValue2' font = to_glyphs([ufo1, ufo2]) assert font.masters[0].userData['ufoLibKey1'] == 'ufoLibValue1' assert font.masters[1].userData['ufoLibKey2'] == 'ufoLibValue2' ufo1, ufo2 = to_ufos(font) assert ufo1.lib['ufoLibKey1'] == 'ufoLibValue1' assert ufo2.lib['ufoLibKey2'] == 'ufoLibValue2' assert 'ufoLibKey2' not in ufo1.lib assert 'ufoLibKey1' not in ufo2.lib
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_ufo_lib_equivalent_to_font_master_user_data(ufo_module): ufo1 = ufo_module.Font() ufo1.lib["ufoLibKey1"] = "ufoLibValue1" ufo2 = ufo_module.Font() ufo2.lib["ufoLibKey2"] = "ufoLibValue2" font = to_glyphs([ufo1, ufo2]) assert font.masters[0].userData["ufoLibKey1"] == "ufoLibValue1" assert font.masters[1].userData["ufoLibKey2"] == "ufoLibValue2" ufo1, ufo2 = to_ufos(font) assert ufo1.lib["ufoLibKey1"] == "ufoLibValue1" assert ufo2.lib["ufoLibKey2"] == "ufoLibValue2" assert "ufoLibKey2" not in ufo1.lib assert "ufoLibKey1" not in ufo2.lib
def test_glyph_color(ufo_module): ufo = ufo_module.Font() a = ufo.newGlyph("a") a.markColor = GLYPHS_COLORS[3] b = ufo.newGlyph("b") b.markColor = "{:.3f},{:.3f},0,1".format(4.0 / 255, 128.0 / 255) font = to_glyphs([ufo]) rt_a_color = font.glyphs["a"].color assert rt_a_color == 3 and type(rt_a_color) is int rt_b_color = font.glyphs["b"].color assert rt_b_color == [4, 128, 0, 255] and all(type(x) is int for x in rt_b_color) (ufo,) = to_ufos(font) assert ufo["a"].markColor == GLYPHS_COLORS[3] assert ufo["b"].markColor == b.markColor
def test_layer_lib_into_master_user_data(ufo_module): ufo1 = ufo_module.Font() ufo1.layers["public.default"].lib["layerLibKey1"] = "ufo1 layerLibValue1" layer = ufo1.newLayer("sketches") layer.lib["layerLibKey2"] = "ufo1 layerLibValue2" # layers won't roundtrip if they contain no glyph, except for the default layer.newGlyph("bob") ufo2 = ufo_module.Font() ufo2.layers["public.default"].lib["layerLibKey1"] = "ufo2 layerLibValue1" layer = ufo2.newLayer("sketches") layer.lib["layerLibKey2"] = "ufo2 layerLibValue2" layer.newGlyph("bob") font = to_glyphs([ufo1, ufo2]) default_layer_key = GLYPHLIB_PREFIX + "layerLib.public.default" sketches_layer_key = GLYPHLIB_PREFIX + "layerLib.sketches" assert default_layer_key not in font.userData assert sketches_layer_key not in font.userData assert font.masters[0].userData[default_layer_key] == { "layerLibKey1": "ufo1 layerLibValue1" } assert font.masters[0].userData[sketches_layer_key] == { "layerLibKey2": "ufo1 layerLibValue2" } assert font.masters[1].userData[default_layer_key] == { "layerLibKey1": "ufo2 layerLibValue1" } assert font.masters[1].userData[sketches_layer_key] == { "layerLibKey2": "ufo2 layerLibValue2" } (ufo1, ufo2) = to_ufos(font) assert ufo1.layers["public.default"].lib[ "layerLibKey1"] == "ufo1 layerLibValue1" assert "layerLibKey1" not in ufo1.layers["sketches"].lib assert ufo1.layers["sketches"].lib["layerLibKey2"] == "ufo1 layerLibValue2" assert "layerLibKey2" not in ufo1.layers["public.default"].lib assert ufo2.layers["public.default"].lib[ "layerLibKey1"] == "ufo2 layerLibValue1" assert "layerLibKey1" not in ufo2.layers["sketches"].lib assert ufo2.layers["sketches"].lib["layerLibKey2"] == "ufo2 layerLibValue2" assert "layerLibKey2" not in ufo2.layers["public.default"].lib
def test_roundtrip_feature_prefix_with_only_a_comment(ufo_module): font = to_glyphs([ufo_module.Font()]) prefix = classes.GSFeaturePrefix(name="include") # Contents: just a comment prefix.code = "#include(../family.fea)" font.featurePrefixes.append(prefix) (ufo, ) = to_ufos(font, ufo_module=ufo_module) assert ufo.features.text == dedent("""\ # Prefix: include #include(../family.fea) """) font_r = to_glyphs([ufo]) assert len(font_r.featurePrefixes) == 1 prefix_r = font_r.featurePrefixes[0] assert prefix_r.name == "include" assert prefix_r.code == "#include(../family.fea)"
def test_roundtrip_empty_feature(ufo_module): # https://github.com/googlefonts/glyphsLib/issues/562 font = to_glyphs([ufo_module.Font()]) feature = classes.GSFeature(name="dlig") feature.code = "" font.features.append(feature) (ufo, ) = to_ufos(font, ufo_module=ufo_module) assert ufo.features.text == dedent("""\ feature dlig { } dlig; """) font_r = to_glyphs([ufo]) assert len(font_r.features) == 1 feature_r = font_r.features[0] assert feature_r.name == "dlig" assert feature_r.code == ""
def test_ufo2ft_filter_roundtrip(self): ufo_filters = [ {"name": "propagateAnchors", "pre": True, "include": ["a", "b", "c"]} ] glyphs_filter = "propagateAnchors;include:a,b,c" # Test the one-way conversion of (Pre)Filters into ufo2ft filters. See the # docstring for FilterParamHandler. self.master.customParameters["PreFilter"] = glyphs_filter self.set_custom_params() self.assertEqual(self.ufo.lib[UFO2FT_FILTERS_KEY], ufo_filters) # Test the round-tripping of ufo2ft filters from UFO -> Glyphs master -> UFO. # See the docstring for FilterParamHandler. font_rt = glyphsLib.to_glyphs([self.ufo]) self.assertNotIn("PreFilter", font_rt.masters[0].customParameters) self.assertEqual(font_rt.masters[0].userData[UFO2FT_FILTERS_KEY], ufo_filters) ufo_rt = glyphsLib.to_ufos(font_rt)[0] self.assertEqual(ufo_rt.lib[UFO2FT_FILTERS_KEY], ufo_filters)
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_roundtrip_feature_prefix_with_only_a_comment(): font = to_glyphs([defcon.Font()]) prefix = classes.GSFeaturePrefix(name="include") # Contents: just a comment prefix.code = "#include(../family.fea)" font.featurePrefixes.append(prefix) ufo, = to_ufos(font) assert ufo.features.text == dedent('''\ # Prefix: include #include(../family.fea) ''') font_r = to_glyphs([ufo]) assert len(font_r.featurePrefixes) == 1 prefix_r = font_r.featurePrefixes[0] assert prefix_r.name == 'include' assert prefix_r.code == "#include(../family.fea)"
def test_open_contour(ufo_module): ufo = ufo_module.Font() a = ufo.newGlyph("a") pen = a.getPen() pen.moveTo((10, 20)) pen.lineTo((30, 40)) pen.endPath() font = to_glyphs([ufo]) path = font.glyphs["a"].layers[0].paths[0] assert not path.closed assert len(path.nodes) == 2 assert path.nodes[0].type == classes.LINE (ufo_rt, ) = to_ufos(font) assert [(p.segmentType, p.x, p.y) for p in a[0] ] == [(p.segmentType, p.x, p.y) for p in ufo_rt["a"][0]]
def test_lib_data_types(tmpdir, ufo_module): # Test the roundtrip of a few basic types both at the top level and in a # nested object. data = OrderedDict({ "boolean": True, "smooth": False, "integer": 1, "float": 0.5, "array": [], "dict": {}, }) ufo = ufo_module.Font() a = ufo.newGlyph("a") for key, value in data.items(): a.lib[key] = value a.lib["nestedDict"] = dict(data) a.lib["nestedArray"] = list(data.values()) a.lib["crazyNesting"] = [{"a": [{"b": [dict(data)]}]}] font = to_glyphs([ufo]) # FIXME: This test will stop working if the font is written and read back, # because the file format of Glyphs does not make a difference between # `True` (bool) and `1` (int). # filename = os.path.join(str(tmpdir), 'font.glyphs') # font.save(filename) # font = classes.GSFont(filename) (ufo, ) = to_ufos(font) for index, (key, value) in enumerate(data.items()): assert value == ufo["a"].lib[key] assert value == ufo["a"].lib["nestedDict"][key] assert value == ufo["a"].lib["nestedArray"][index] assert value == ufo["a"].lib["crazyNesting"][0]["a"][0]["b"][0][key] assert type(value) is type(ufo["a"].lib[key]) # noqa: E721 assert type(value) is type( ufo["a"].lib["nestedDict"][key]) # noqa: E721 assert type(value) is type( ufo["a"].lib["nestedArray"][index]) # noqa: E721 assert type(value) is type( # noqa: E721 ufo["a"].lib["crazyNesting"][0]["a"][0]["b"][0][key])
def doit(args): logger = args.logger logger.log("Creating UFO objects from GlyphsApp file", "I") with open(args.glyphsfont, 'r', encoding='utf-8') as gfile: gfont = glyphsLib.parser.load(gfile) ufos = glyphsLib.to_ufos(gfont, include_instances=False, family_name=None, propagate_anchors=False) # Extract directory name for use with backups (glyphsdir, filen) = os.path.split(args.glyphsfont) librestorekeys = ("org.sil.pysilfontparams", "org.sil.altLineMetrics", "org.sil.lcg.toneLetters", "org.sil.lcg.transforms", "public.glyphOrder", "public.postscriptNames") libdeletekeys = ("UFOFormat", "com.schriftgestaltung.blueFuzz", "com.schriftgestaltung.blueScale", "com.schriftgestaltung.blueShift") libdeleteempty = ("com.schriftgestaltung.DisplayStrings",) inforestorekeys = ("openTypeHeadCreated", "openTypeNamePreferredFamilyName", "openTypeNamePreferredSubfamilyName", "openTypeNameUniqueID", "openTypeOS2WeightClass", "openTypeOS2WidthClass", "postscriptFontName", "postscriptFullName", "styleMapFamilyName", "styleMapStyleName") integerkeys = ("openTypeOS2WeightClass", "openTypeOS2WidthClass") infodeleteempty = ("openTypeOS2Selection",) infodeletekeys = ("openTypeOS2Type",) for ufo in ufos: sn = ufo.info.styleName # ) sn = sn.replace("Italic Italic", "Italic") # ) Temp fixes due to glyphLib incorrectly sn = sn.replace("Italic Bold Italic", "Bold Italic") # ) forming styleName ufo.info.styleName = sn # ) fontname = ufo.info.familyName.replace(" ", "") + "-" + ufo.info.styleName.replace(" ", "") # Fixes to the data if not args.nofixes: logger.log("Fixing data in " + fontname, "P") # lib.plist processing logger.log("Checking lib.plist", "P") # Process UFO.lib if present if "UFO.lib" in ufo.lib: logger.log("UFOlib found in lib.plist for " + fontname + ". Values will be copied to root", "P") ul = ufo.lib["UFO.lib"] # Copy fields from UFO.lib to root for key in ul: if key in librestorekeys: continue # They will be restored later if key in libdeleteempty: if ul[key] == "" or ul[key] == []: logger.log("Emtpy field ignored: " + key, "I") continue if key in libdeletekeys: logger.log(key + " ignored", "I") continue if key in ufo.lib: current = ufo.lib[key] logmess = " updated from UFO.lib. " else: current = None logmess = " copied from UFO.lib. " new = ul[key] if current == new: continue else: ufo.lib[key] = new logchange(logger, logmess, key, current, new) del ufo.lib["UFO.lib"] logger.log("UFO.lib field deleted", "I") # Restore values from original UFOs, assuming nameed as <fontname>.ufo in same directory as input .gylphs file ufodir = os.path.join(glyphsdir,fontname+".ufo") try: origlibplist = silfont.ufo.Uplist(font=None, dirn=ufodir, filen="lib.plist") except Exception as e: logger.log("Unable to open lib.plist in " + ufodir + "; values will not be restored", "E") origlibplist = None if origlibplist is not None: for key in librestorekeys: if key in origlibplist: new = origlibplist.getval(key) current = None if key not in ufo.lib else ufo.lib[key] if current == new: continue else: ufo.lib[key] = new logchange(logger, " restored from backup ufo. ", key, current, new) # Delete unneeded keys for key in libdeletekeys: if key in ufo.lib: current = ufo.lib[key] del ufo.lib[key] logchange(logger, " deleted. ", key, current, None) for key in libdeleteempty: if key in ufo.lib and (ufo.lib[key] == "" or ufo.lib[key] == []): del ufo.lib[key] logchange(logger, " empty field deleted. ", key, current, None) ufo.lib["org.sil.glyphsappversion"] = gfont.appVersion # fontinfo.plist processing logger.log("Checking fontinfo.plist", "P") try: origfontinfo = silfont.ufo.Uplist(font=None, dirn=ufodir, filen="fontinfo.plist") except Exception as e: logger.log("Unable to open fontinfo.plist in " + ufodir + "; values will not be restored", "E") origfontinfo = None if origfontinfo is not None: for key in inforestorekeys: if key in origfontinfo: new = origfontinfo.getval(key) if key in integerkeys: new = int(new) current = None if not hasattr(ufo.info, key) else getattr(ufo.info, key) if current == new: continue else: setattr(ufo.info, key, new) logchange(logger, " restored from backup ufo. ", key, current, new) # Delete unneeded keys for key in infodeletekeys: if hasattr(ufo.info, key): current = getattr(ufo.info, key) setattr(ufo.info, key, None) logchange(logger, " deleted. ", key, current, None) for key in infodeleteempty: if hasattr(ufo.info, key) and getattr(ufo.info, key) == "": setattr(ufo.info, key, None) logchange(logger, " empty field deleted. ", key, current, None) # Write ufo out logger.log("Writing out " + fontname, "P") glyphsLib.write_ufo(ufo, args.masterdir)