diff --git a/src/ufoLib2/objects/glyph.py b/src/ufoLib2/objects/glyph.py index d5eecfa7..00185cc3 100644 --- a/src/ufoLib2/objects/glyph.py +++ b/src/ufoLib2/objects/glyph.py @@ -1,4 +1,5 @@ import attr +from copy import deepcopy from typing import Union, List, Any, Dict, Optional from ufoLib2.objects.anchor import Anchor from ufoLib2.objects.guideline import Guideline @@ -145,6 +146,36 @@ def appendGuideline(self, guideline): guideline = Guideline(**guideline) self._guidelines.append(guideline) + def copy(self, name=None): + """Return a new Glyph (deep) copy, optionally override the new glyph name. + """ + other = deepcopy(self) + if name is not None: + other._name = name + return other + + def copyDataFromGlyph(self, glyph): + """Deep-copy everything from the other glyph, except for the name. + Existing glyph data is overwritten. + + This method was added for compatibility with the defcon API, and + it may be removed in the future. + """ + self.width = glyph.width + self.height = glyph.height + self.unicodes = list(glyph.unicodes) + self.image = deepcopy(glyph.image) + self.note = glyph.note + self.lib = deepcopy(glyph.lib) + self.anchors = deepcopy(glyph.anchors) + self.guidelines = deepcopy(glyph.guidelines) + # NOTE: defcon's copyDataFromGlyph appends instead of overwrites here, + # but we do the right thing, for consistency with the rest. + self.clearContours() + self.clearComponents() + pointPen = self.getPointPen() + glyph.drawPoints(pointPen) + # ----------- # Pen methods # ----------- diff --git a/tests/conftest.py b/tests/conftest.py index 815d1ea4..19cb586e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,15 +1,16 @@ -import py +import pathlib +import shutil import pytest import ufoLib2 @pytest.fixture def datadir(request): - return py.path.local(request.fspath.dirname).join("data") + return pathlib.Path(__file__).parent / "data" @pytest.fixture -def ufo_UbuTestData(tmpdir, datadir): - ufo_path = tmpdir.join("UbuTestData.ufo") - datadir.join("UbuTestData.ufo").copy(ufo_path) - return ufoLib2.Font.open(str(ufo_path)) +def ufo_UbuTestData(tmp_path, datadir): + ufo_path = tmp_path / "UbuTestData.ufo" + shutil.copytree(datadir / "UbuTestData.ufo", ufo_path) + return ufoLib2.Font.open(ufo_path) diff --git a/tests/objects/test_glyph.py b/tests/objects/test_glyph.py new file mode 100644 index 00000000..44cee46c --- /dev/null +++ b/tests/objects/test_glyph.py @@ -0,0 +1,72 @@ +from ufoLib2.objects import Anchor, Component, Glyph, Guideline, Image + + +def test_copyDataFromGlyph(ufo_UbuTestData): + font = ufo_UbuTestData + + a = font["a"] + a.height = 500 + a.image = Image("a.png") + a.note = "a note" + a.lib = {"bar": [3, 2, 1]} + a.anchors = [Anchor(250, 0, "bottom")] + a.guidelines = [Guideline(y=500)] + a.components = [Component("A")] + + b = Glyph("b") + b.width = 350 + b.height = 1000 + b.image = Image("b.png") + b.note = "b note" + b.lib = {"foo": [1, 2, 3]} + b.anchors = [Anchor(350, 800, "top")] + b.guidelines = [Guideline(x=50)] + + assert b.name != a.name + assert b.width != a.width + assert b.height != a.height + assert b.unicodes != a.unicodes + assert b.image != a.image + assert b.note != a.note + assert b.lib != a.lib + assert b.anchors != a.anchors + assert b.guidelines != a.guidelines + assert b.contours != a.contours + assert b.components != a.components + + def _assert_equal_but_distinct_objects(glyph1, glyph2): + assert glyph1.width == glyph2.width + assert glyph1.height == glyph2.height + assert glyph1.unicodes == glyph2.unicodes + assert glyph1.unicodes is not glyph2.unicodes + assert glyph1.image == glyph2.image + assert glyph1.image is not glyph2.image + assert glyph1.note == glyph2.note + assert glyph1.lib == glyph2.lib + assert glyph1.lib is not glyph2.lib + assert glyph1.lib["bar"] == glyph2.lib["bar"] + assert glyph1.lib["bar"] is not glyph2.lib["bar"] + assert glyph1.anchors == glyph2.anchors + assert glyph1.anchors is not glyph2.anchors + assert glyph1.anchors[0] is not glyph2.anchors[0] + assert glyph1.guidelines == glyph2.guidelines + assert glyph1.guidelines is not glyph2.guidelines + assert glyph1.guidelines[0] is not glyph2.guidelines[0] + assert glyph1.contours == glyph2.contours + assert glyph1.contours is not glyph2.contours + assert glyph1.contours[0] is not glyph2.contours[0] + assert glyph1.components == glyph2.components + assert glyph1.components is not glyph2.components + assert glyph1.components[0] is not glyph2.components[0] + + b.copyDataFromGlyph(a) + assert b.name != a.name + _assert_equal_but_distinct_objects(b, a) + + c = a.copy() + assert c.name == a.name + _assert_equal_but_distinct_objects(c, a) + + d = a.copy(name="d") + assert d.name == "d" + _assert_equal_but_distinct_objects(d, a) diff --git a/tests/test_ufoLib2.py b/tests/test_ufoLib2.py index 42357958..d72cbaf5 100644 --- a/tests/test_ufoLib2.py +++ b/tests/test_ufoLib2.py @@ -7,19 +7,19 @@ def test_import_version(): assert isinstance(ufoLib2.__version__, str) -def test_LayerSet_load_layers_on_iteration(tmpdir): +def test_LayerSet_load_layers_on_iteration(tmp_path): ufo = ufoLib2.Font() ufo.layers.newLayer("test") - ufo_save_path = str(tmpdir.join("test.ufo")) + ufo_save_path = tmp_path / "test.ufo" ufo.save(ufo_save_path) ufo = ufoLib2.Font.open(ufo_save_path) for layer in ufo.layers: assert layer is not _NOT_LOADED -def test_lazy_data_loading_saveas(ufo_UbuTestData, tmpdir): +def test_lazy_data_loading_saveas(ufo_UbuTestData, tmp_path): ufo = ufo_UbuTestData - ufo_path = str(tmpdir.join("UbuTestData2.ufo")) + ufo_path = tmp_path / "UbuTestData2.ufo" ufo.save(ufo_path) assert all(v is not _NOT_LOADED for v in ufo.data._data.values())