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_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_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_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_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_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_empty_font(self):
     empty_font = classes.GSFont()
     empty_font.masters.append(classes.GSFontMaster())
     self.assertUFORoundtrip(empty_font)
Beispiel #11
0
    def test_write_font_attributes(self):
        """Test the writer on all GSFont attributes"""
        font = classes.GSFont()
        # List of properties from https://docu.glyphsapp.com/#gsfont
        # parent: not handled because it's internal and read-only
        # masters
        m1 = classes.GSFontMaster()
        m1.id = "M1"
        font.masters.insert(0, m1)
        m2 = classes.GSFontMaster()
        m2.id = "M2"
        font.masters.insert(1, m2)
        # instances
        i1 = classes.GSInstance()
        i1.name = "MuchBold"
        font.instances.append(i1)
        # glyphs
        g1 = classes.GSGlyph()
        g1.name = 'G1'
        font.glyphs.append(g1)
        # classes
        c1 = classes.GSClass()
        c1.name = "C1"
        font.classes.append(c1)
        # features
        f1 = classes.GSFeature()
        f1.name = "F1"
        font.features.append(f1)
        # featurePrefixes
        fp1 = classes.GSFeaturePrefix()
        fp1 = "FP1"
        font.featurePrefixes.append(fp1)
        # copyright
        font.copyright = "Copyright Bob"
        # designer
        font.designer = "Bob"
        # designerURL
        font.designerURL = "bob.me"
        # manufacturer
        font.manufacturer = "Manu"
        # manufacturerURL
        font.manufacturerURL = "manu.com"
        # versionMajor
        font.versionMajor = 2
        # versionMinor
        font.versionMinor = 104
        # date
        font.date = glyphs_datetime('2017-10-03 07:35:46 +0000')
        # familyName
        font.familyName = "Sans Rien"
        # upm
        font.upm = 2000
        # note
        font.note = "Was bored, made this"
        # kerning
        font.kerning = OrderedDict([
            ('M1', OrderedDict([
                ('@MMK_L_G1', OrderedDict([
                    ('@MMK_R_G1', 0.1)
                ]))
            ]))
        ])
        # userData
        font.userData = {
            'a': 'test',
            'b': [1, {'c': 2}],
            'd': [1, "1"],
        }
        # grid -> gridLength
        font.grid = 35
        # gridSubDivisions
        font.gridSubDivisions = 5
        # keyboardIncrement
        font.keyboardIncrement = 1.2
        # disablesNiceNames
        font.disablesNiceNames = True
        # customParameters
        font.customParameters['ascender'] = 300
        # selection: not written
        # selectedLayers: not written
        # selectedFontMaster: not written
        # masterIndex: not written
        # currentText: not written
        # tabs: not written
        # currentTab: not written
        # filepath: not written
        # tool: not written
        # tools: not handled because it is a read-only list of GUI features
        # .appVersion (extra property that is not in the docs!)
        font.appVersion = "895"
        self.assertWrites(font, dedent("""\
            {
            .appVersion = "895";
            classes = (
            {
            code = "";
            name = C1;
            }
            );
            copyright = "Copyright Bob";
            customParameters = (
            {
            name = note;
            value = "Was bored, made this";
            },
            {
            name = ascender;
            value = 300;
            }
            );
            date = "2017-10-03 07:35:46 +0000";
            designer = Bob;
            designerURL = bob.me;
            disablesNiceNames = 1;
            familyName = "Sans Rien";
            featurePrefixes = (
            FP1
            );
            features = (
            {
            code = "";
            name = F1;
            }
            );
            fontMaster = (
            {
            ascender = 800;
            capHeight = 700;
            id = M1;
            xHeight = 500;
            },
            {
            ascender = 800;
            capHeight = 700;
            id = M2;
            xHeight = 500;
            }
            );
            glyphs = (
            {
            glyphname = G1;
            }
            );
            gridLength = 35;
            gridSubDivision = 5;
            instances = (
            {
            name = MuchBold;
            }
            );
            kerning = {
            M1 = {
            "@MMK_L_G1" = {
            "@MMK_R_G1" = 0.1;
            };
            };
            };
            keyboardIncrement = 1.2;
            manufacturer = Manu;
            manufacturerURL = manu.com;
            unitsPerEm = 2000;
            userData = {
            a = test;
            b = (
            1,
            {
            c = 2;
            }
            );
            d = (
            1,
            "1"
            );
            };
            versionMajor = 2;
            versionMinor = 104;
            }
        """))

        # Don't write the keyboardIncrement if it's 1 (default)
        font.keyboardIncrement = 1
        written = test_helpers.write_to_lines(font)
        self.assertFalse(any("keyboardIncrement" in line for line in written))
Beispiel #12
0
    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)
Beispiel #13
0
    def test_write_font_master_attributes(self):
        """Test the writer on all GSFontMaster attributes"""
        master = classes.GSFontMaster()
        # List of properties from https://docu.glyphsapp.com/#gsfontmaster
        # id
        master.id = "MASTER-ID"
        # name
        # Cannot set the `name` attribute directly
        # master.name = "Hairline Megawide"
        master.customParameters['Master Name'] = "Hairline Megawide"
        # weight
        master.weight = "Thin"
        # width
        master.width = "Wide"
        # weightValue
        master.weightValue = 0.01
        # widthValue
        master.widthValue = 0.99
        # customValue
        # customName
        master.customName = "cuteness"
        # A value of 0.0 is not written to the file.
        master.customValue = 0.001
        master.customName1 = "color"
        master.customValue1 = 0.1
        master.customName2 = "depth"
        master.customValue2 = 0.2
        master.customName3 = "surealism"
        master.customValue3 = 0.3
        # ascender
        master.ascender = 234.5
        # capHeight
        master.capHeight = 200.6
        # xHeight
        master.xHeight = 59.1
        # descender
        master.descender = -89.2
        # italicAngle
        master.italicAngle = 12.2
        # verticalStems
        master.verticalStems = [1, 2, 3]
        # horizontalStems
        master.horizontalStems = [4, 5, 6]
        # alignmentZones
        zone = classes.GSAlignmentZone(0, -30)
        master.alignmentZones = [
            zone
        ]
        # blueValues: not handled because it is read-only
        # otherBlues: not handled because it is read-only
        # guides
        guide = classes.GSGuideLine()
        guide.name = "middle"
        master.guides.append(guide)
        # userData
        master.userData['rememberToMakeTea'] = True
        # customParameters
        master.customParameters['underlinePosition'] = -135
        self.assertWrites(master, dedent("""\
            {
            alignmentZones = (
            "{0, -30}"
            );
            ascender = 234.5;
            capHeight = 200.6;
            custom = cuteness;
            customValue = 0.001;
            custom1 = color;
            customValue1 = 0.1;
            custom2 = depth;
            customValue2 = 0.2;
            custom3 = surealism;
            customValue3 = 0.3;
            customParameters = (
            {
            name = "Master Name";
            value = "Hairline Megawide";
            },
            {
            name = underlinePosition;
            value = -135;
            }
            );
            descender = -89.2;
            guideLines = (
            {
            name = middle;
            }
            );
            horizontalStems = (
            4,
            5,
            6
            );
            id = "MASTER-ID";
            italicAngle = 12.2;
            name = "Hairline Megawide";
            userData = {
            rememberToMakeTea = 1;
            };
            verticalStems = (
            1,
            2,
            3
            );
            weight = Thin;
            weightValue = 0.01;
            width = Wide;
            widthValue = 0.99;
            xHeight = 59.1;
            }
        """))

        # Write the capHeight and xHeight even if they are "0"
        master.xHeight = 0
        master.capHeight = 0
        written = test_helpers.write_to_lines(master)
        self.assertIn("xHeight = 0;", written)
        self.assertIn("capHeight = 0;", written)