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 test_layer_lib_in_font_user_data(ufo_module): font = classes.GSFont() font.masters.append(classes.GSFontMaster()) font.masters.append(classes.GSFontMaster()) glyph = classes.GSGlyph(name="a") font.glyphs.append(glyph) layer = classes.GSLayer() layer.layerId = font.masters[0].id layer.width = 0 glyph.layers.append(layer) layer = classes.GSLayer() layer.layerId = font.masters[1].id layer.width = 0 glyph.layers.append(layer) font.userData[GLYPHLIB_PREFIX + "layerLib.public.default"] = { "layerLibKey": "layerLibValue" } ufo1, ufo2 = to_ufos(font) assert "layerLibKey" in ufo1.layers["public.default"].lib assert ufo1.layers["public.default"].lib["layerLibKey"] == "layerLibValue" assert "layerLibKey" in ufo2.layers["public.default"].lib assert ufo2.layers["public.default"].lib["layerLibKey"] == "layerLibValue"
def test_write_hint(self): hint = classes.GSHint() # http://docu.glyphsapp.com/#gshint layer = classes.GSLayer() path1 = classes.GSPath() layer.paths.append(path1) node1 = classes.GSNode(Point(100, 100)) path1.nodes.append(node1) hint.originNode = node1 node2 = classes.GSNode(Point(200, 200)) path1.nodes.append(node2) hint.targetNode = node2 node3 = classes.GSNode(Point(300, 300)) path1.nodes.append(node3) hint.otherNode1 = node3 path2 = classes.GSPath() layer.paths.append(path2) node4 = classes.GSNode(Point(400, 400)) path2.nodes.append(node4) hint.otherNode2 = node4 hint.type = classes.CORNER hint.options = classes.TTROUND | classes.TRIPLE hint.horizontal = True # selected: not written hint.name = "My favourite hint" self.assertWrites( hint, dedent( """\ { horizontal = 1; origin = "{0, 0}"; target = "{0, 1}"; other1 = "{0, 2}"; other2 = "{1, 0}"; type = 16; name = "My favourite hint"; options = 128; } """ ), )
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_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'
def test_write_layer(self): layer = classes.GSLayer() # http://docu.glyphsapp.com/#gslayer # parent: not written # name layer.name = '{125, 100}' # associatedMasterId layer.associatedMasterId = 'M1' # layerId layer.layerId = 'L1' # color layer.color = 2 # brown # colorObject: read-only, computed # components component = classes.GSComponent(glyph='glyphName') layer.components.append(component) # guides guide = classes.GSGuideLine() guide.name = 'xheight' layer.guides.append(guide) # annotations annotation = classes.GSAnnotation() annotation.type = classes.TEXT annotation.text = 'F**k, this curve is ugly!' layer.annotations.append(annotation) # hints hint = classes.GSHint() hint.name = 'hintName' layer.hints.append(hint) # anchors anchor = classes.GSAnchor() anchor.name = 'top' layer.anchors['top'] = anchor # paths path = classes.GSPath() layer.paths.append(path) # selection: read-only # LSB, RSB, TSB, BSB: not written # width layer.width = 890.4 # leftMetricsKey layer.leftMetricsKey = "A" # rightMetricsKey layer.rightMetricsKey = "A" # widthMetricsKey layer.widthMetricsKey = "A" # bounds: read-only, computed # selectionBounds: read-only, computed # background # FIXME: (jany) why not use a GSLayer like the official doc suggests? background_layer = classes.GSBackgroundLayer() layer.background = background_layer # backgroundImage image = classes.GSBackgroundImage('/path/to/file.jpg') layer.backgroundImage = image # bezierPath: read-only, objective-c # openBezierPath: read-only, objective-c # completeOpenBezierPath: read-only, objective-c # isAligned # FIXME: (jany) is this read-only? # is this computed from each component's alignment? # layer.isAligned = False # userData layer.userData['rememberToMakeCoffe'] = True # smartComponentPoleMapping layer.smartComponentPoleMapping['crotchDepth'] = 2 # Top pole layer.smartComponentPoleMapping['shoulderWidth'] = 1 # Bottom pole self.assertWrites(layer, dedent("""\ { anchors = ( { name = top; } ); annotations = ( { position = ; text = "F**k, this curve is ugly!"; type = 1; } ); associatedMasterId = M1; background = { }; backgroundImage = { crop = "{{0, 0}, {0, 0}}"; imagePath = "/path/to/file.jpg"; }; color = 2; components = ( { name = glyphName; } ); guideLines = ( { name = xheight; } ); hints = ( { name = hintName; } ); layerId = L1; leftMetricsKey = A; widthMetricsKey = A; rightMetricsKey = A; name = "{125, 100}"; paths = ( { } ); userData = { PartSelection = { crotchDepth = 2; shoulderWidth = 1; }; rememberToMakeCoffe = 1; }; width = 890.4; } """)) # Don't write a blank layer name layer.name = "" written = test_helpers.write_to_lines(layer) self.assertNotIn('name = "";', written)
def test_write_glyph(self): glyph = classes.GSGlyph() # https://docu.glyphsapp.com/#gsglyph # parent: not written # layers # Put the glyph in a font with at least one master for the magic in # `glyph.layers.append()` to work. font = classes.GSFont() master = classes.GSFontMaster() master.id = "MASTER-ID" font.masters.insert(0, master) font.glyphs.append(glyph) layer = classes.GSLayer() layer.layerId = "LAYER-ID" layer.name = "L1" glyph.layers.insert(0, layer) # name glyph.name = "Aacute" # unicode glyph.unicode = "00C1" # string: not written # id: not written # category glyph.category = "Letter" # subCategory glyph.subCategory = "Uppercase" # script glyph.script = "latin" # productionName glyph.productionName = "Aacute.prod" # glyphInfo: not written # leftKerningGroup glyph.leftKerningGroup = "A" # rightKerningGroup glyph.rightKerningGroup = "A" # leftKerningKey: not written # rightKerningKey: not written # leftMetricsKey glyph.leftMetricsKey = "A" # rightMetricsKey glyph.rightMetricsKey = "A" # widthMetricsKey glyph.widthMetricsKey = "A" # export glyph.export = False # color glyph.color = 11 # colorObject: not written # note glyph.note = "Stunning one-bedroom A with renovated acute accent" # selected: not written # mastersCompatible: not stored # userData glyph.userData['rememberToMakeCoffe'] = True # Check that empty collections are written glyph.userData['com.someoneelse.coolsoftware.customdata'] = [ OrderedDict([ ('zero', 0), ('emptyList', []), ('emptyDict', {}), ('emptyString', ""), ]), [], {}, "", "hey", 0, 1, ] # smartComponentAxes axis = classes.GSSmartComponentAxis() axis.name = "crotchDepth" glyph.smartComponentAxes.append(axis) # lastChange glyph.lastChange = glyphs_datetime('2017-10-03 07:35:46 +0000') self.assertWrites(glyph, dedent("""\ { color = 11; export = 0; glyphname = Aacute; lastChange = "2017-10-03 07:35:46 +0000"; layers = ( { associatedMasterId = "MASTER-ID"; layerId = "LAYER-ID"; name = L1; width = 0; } ); leftKerningGroup = A; leftMetricsKey = A; widthMetricsKey = A; note = "Stunning one-bedroom A with renovated acute accent"; rightKerningGroup = A; rightMetricsKey = A; unicode = 00C1; script = latin; category = Letter; subCategory = Uppercase; userData = { com.someoneelse.coolsoftware.customdata = ( { zero = 0; emptyList = ( ); emptyDict = { }; emptyString = ""; }, ( ), { }, "", hey, 0, 1 ); rememberToMakeCoffe = 1; }; partsSettings = ( { name = crotchDepth; bottomValue = 0; topValue = 0; } ); } """)) # Write the script even when it's an empty string # Same for category and subCategory glyph.script = "" glyph.category = "" glyph.subCategory = "" written = test_helpers.write_to_lines(glyph) self.assertIn('script = "";', written) self.assertIn('category = "";', written) self.assertIn('subCategory = "";', written)
def test_write_layer(self): layer = classes.GSLayer() # http://docu.glyphsapp.com/#gslayer # parent: not written # name layer.name = "{125, 100}" # associatedMasterId layer.associatedMasterId = "M1" # layerId layer.layerId = "L1" # color layer.color = (1, 2, 3, 4) # colorObject: read-only, computed # components component = classes.GSComponent(glyph="glyphName") layer.components.append(component) # guides guide = classes.GSGuideLine() guide.name = "xheight" layer.guides.append(guide) # annotations annotation = classes.GSAnnotation() annotation.type = classes.TEXT annotation.text = "F**k, this curve is ugly!" layer.annotations.append(annotation) # hints hint = classes.GSHint() hint.name = "hintName" layer.hints.append(hint) # anchors anchor = classes.GSAnchor() anchor.name = "top" layer.anchors["top"] = anchor # paths path = classes.GSPath() layer.paths.append(path) # selection: read-only # LSB, RSB, TSB, BSB: not written # width layer.width = 890.4 # leftMetricsKey layer.leftMetricsKey = "A" # rightMetricsKey layer.rightMetricsKey = "A" # widthMetricsKey layer.widthMetricsKey = "A" # bounds: read-only, computed # selectionBounds: read-only, computed # background # XXX bg is unused? bg = layer.background # noqa: F841 # backgroundImage image = classes.GSBackgroundImage("/path/to/file.jpg") layer.backgroundImage = image # bezierPath: read-only, objective-c # openBezierPath: read-only, objective-c # completeOpenBezierPath: read-only, objective-c # isAligned # FIXME: (jany) is this read-only? # is this computed from each component's alignment? # layer.isAligned = False # userData layer.userData["rememberToMakeCoffe"] = True # smartComponentPoleMapping layer.smartComponentPoleMapping["crotchDepth"] = 2 # Top pole layer.smartComponentPoleMapping["shoulderWidth"] = 1 # Bottom pole self.assertWrites( layer, dedent("""\ { anchors = ( { name = top; position = "{0, 0}"; } ); annotations = ( { text = "F**k, this curve is ugly!"; type = 1; } ); associatedMasterId = M1; background = { }; backgroundImage = { crop = "{{0, 0}, {0, 0}}"; imagePath = "/path/to/file.jpg"; }; color = (1, 2, 3, 4); components = ( { name = glyphName; } ); guideLines = ( { name = xheight; } ); hints = ( { name = hintName; } ); layerId = L1; leftMetricsKey = A; widthMetricsKey = A; rightMetricsKey = A; name = "{125, 100}"; paths = ( { closed = 1; } ); userData = { PartSelection = { crotchDepth = 2; shoulderWidth = 1; }; rememberToMakeCoffe = 1; }; width = 890.4; } """), ) # Don't write a blank layer name layer.name = "" written = test_helpers.write_to_lines(layer) self.assertNotIn('name = "";', written) # Write the width even if 0 layer.width = 0 written = test_helpers.write_to_lines(layer) self.assertIn("width = 0;", written)