diff --git a/.gitignore b/.gitignore
index afeef5372..388cdac76 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@ htmlcov/
# OSX Finder
.DS_Store
+.pytest_cache
diff --git a/Doc/source/designspaceLib/readme.rst b/Doc/source/designspaceLib/readme.rst
index 139122273..614705ca0 100644
--- a/Doc/source/designspaceLib/readme.rst
+++ b/Doc/source/designspaceLib/readme.rst
@@ -129,6 +129,8 @@ Attributes
- ``path``: string. Absolute path to the source file, calculated from
the document path and the string in the filename attr. MutatorMath +
Varlib.
+ ``layer``: string. The name of the layer in the source to look for
+ outline data. Defaults to ``None`` which means the foreground layer.
- ``font``: Any Python object. Optional. Points to a representation of
this source font that is loaded in memory, as a Python object
(e.g. a ``defcon.Font`` or a ``fontTools.ttFont.TTFont``). The default
@@ -538,6 +540,9 @@ Attributes
- ``filename``: required, string. A path to the source file, relative
to the root path of this document. The path can be at the same level
as the document or lower.
+- ``layer``: optional, string. The name of the layer in the source file.
+ If no layer is given assume it is the foreground layer. The layer names
+ should follow the conventions layed out in the .. _http://unifiedfontobject.org/versions/ufo3/layercontents.plist/
.. 31-lib-element:
diff --git a/Lib/fontTools/designspaceLib/__init__.py b/Lib/fontTools/designspaceLib/__init__.py
index 0e1b5e1a7..3be15e33b 100644
--- a/Lib/fontTools/designspaceLib/__init__.py
+++ b/Lib/fontTools/designspaceLib/__init__.py
@@ -114,7 +114,7 @@ class SimpleDescriptor(object):
class SourceDescriptor(SimpleDescriptor):
"""Simple container for data related to the source"""
flavor = "source"
- _attrs = ['filename', 'path', 'name',
+ _attrs = ['filename', 'path', 'name', 'layerName',
'location', 'copyLib',
'copyGroups', 'copyFeatures',
'muteKerning', 'muteInfo',
@@ -142,6 +142,7 @@ class SourceDescriptor(SimpleDescriptor):
self.name = None
self.location = None
+ self.layerName = None
self.copyLib = False
self.copyInfo = False
self.copyGroups = False
@@ -567,6 +568,8 @@ class BaseDocWriter(object):
sourceElement.attrib['familyname'] = sourceObject.familyName
if sourceObject.styleName is not None:
sourceElement.attrib['stylename'] = sourceObject.styleName
+ if sourceObject.layerName is not None:
+ sourceElement.attrib['layer'] = sourceObject.layerName
if sourceObject.copyLib:
libElement = ET.Element('lib')
libElement.attrib['copy'] = "1"
@@ -769,6 +772,7 @@ class BaseDocReader(object):
# Look at all locations and collect the axis names and values
# assumptions:
# look for the default value on an axis from a master location
+ # Needs deprecation warning
allLocations = []
minima = {}
maxima = {}
@@ -822,6 +826,9 @@ class BaseDocReader(object):
if styleName is not None:
sourceObject.styleName = styleName
sourceObject.location = self.locationFromElement(sourceElement)
+ layerName = sourceElement.attrib.get('layer')
+ if layerName is not None:
+ sourceObject.layerName = layerName
for libElement in sourceElement.findall('.lib'):
if libElement.attrib.get('copy') == '1':
sourceObject.copyLib = True
diff --git a/Tests/designspaceLib/data/test.designspace b/Tests/designspaceLib/data/test.designspace
index e91aad1e4..43116deb2 100644
--- a/Tests/designspaceLib/data/test.designspace
+++ b/Tests/designspaceLib/data/test.designspace
@@ -38,6 +38,12 @@
+
+
+
+
+
+
diff --git a/Tests/designspaceLib/designspace_test.py b/Tests/designspaceLib/designspace_test.py
index 210be8123..f352d410b 100644
--- a/Tests/designspaceLib/designspace_test.py
+++ b/Tests/designspaceLib/designspace_test.py
@@ -57,6 +57,19 @@ def test_fill_document(tmpdir):
s2.familyName = "MasterFamilyName"
s2.styleName = "MasterStyleNameTwo"
doc.addSource(s2)
+ # add master 3 from a different layer
+ s3 = SourceDescriptor()
+ s3.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath))
+ s3.name = "master.ufo2"
+ s3.copyLib = False
+ s3.copyInfo = False
+ s3.copyFeatures = False
+ s3.muteKerning = False
+ s3.layerName = "supports"
+ s3.location = dict(weight=1000)
+ s3.familyName = "MasterFamilyName"
+ s3.styleName = "Supports"
+ doc.addSource(s3)
# add instance 1
i1 = InstanceDescriptor()
i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath))
@@ -788,98 +801,100 @@ def __removeAxesFromDesignSpace(path):
f.close()
-@pytest.fixture
-def invalid_designspace():
- p = "testCheck.designspace"
- __removeAxesFromDesignSpace(p)
- yield p
+# @pytest.fixture
+# def invalid_designspace():
+# p = "testCheck.designspace"
+# __removeAxesFromDesignSpace(p)
+# yield p
-@pytest.mark.xfail(reason="The check method requires MutatorMath")
-def test_check(invalid_designspace, tmpdir):
- tmpdir = str(tmpdir)
- # check if the checks are checking
- testDocPath = os.path.join(tmpdir, invalid_designspace)
- masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo")
- masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo")
- instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo")
- instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo")
+# #@pytest.mark.xfail(reason="The check method requires MutatorMath")
+# def test_check(invalid_designspace, tmpdir):
+# tmpdir = str(tmpdir)
+# # check if the checks are checking
+# testDocPath = os.path.join(tmpdir, invalid_designspace)
+# masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo")
+# masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo")
+# instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo")
+# instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo")
- # no default selected
- doc = DesignSpaceDocument()
- # add master 1
- s1 = SourceDescriptor()
- s1.path = masterPath1
- s1.name = "master.ufo1"
- s1.location = dict(snap=0, pop=10)
- s1.familyName = "MasterFamilyName"
- s1.styleName = "MasterStyleNameOne"
- doc.addSource(s1)
- # add master 2
- s2 = SourceDescriptor()
- s2.path = masterPath2
- s2.name = "master.ufo2"
- s2.location = dict(snap=1000, pop=20)
- s2.familyName = "MasterFamilyName"
- s2.styleName = "MasterStyleNameTwo"
- doc.addSource(s2)
- doc.checkAxes()
- doc.getAxisOrder() == ['snap', 'pop']
- assert doc.default == None
- doc.checkDefault()
- assert doc.default.name == 'master.ufo1'
+# # no default selected
+# doc = DesignSpaceDocument()
+# # add master 1
+# s1 = SourceDescriptor()
+# s1.path = masterPath1
+# s1.name = "master.ufo1"
+# s1.location = dict(snap=0, pop=10)
+# s1.familyName = "MasterFamilyName"
+# s1.styleName = "MasterStyleNameOne"
+# doc.addSource(s1)
+# # add master 2
+# s2 = SourceDescriptor()
+# s2.path = masterPath2
+# s2.name = "master.ufo2"
+# s2.location = dict(snap=1000, pop=20)
+# s2.familyName = "MasterFamilyName"
+# s2.styleName = "MasterStyleNameTwo"
+# doc.addSource(s2)
+# doc.checkAxes()
+# doc.getAxisOrder() == ['snap', 'pop']
+# assert doc.default == None
+# doc.checkDefault()
+# assert doc.default.name == 'master.ufo1'
- # default selected
- doc = DesignSpaceDocument()
- # add master 1
- s1 = SourceDescriptor()
- s1.path = masterPath1
- s1.name = "master.ufo1"
- s1.location = dict(snap=0, pop=10)
- s1.familyName = "MasterFamilyName"
- s1.styleName = "MasterStyleNameOne"
- doc.addSource(s1)
- # add master 2
- s2 = SourceDescriptor()
- s2.path = masterPath2
- s2.name = "master.ufo2"
- s2.copyInfo = True
- s2.location = dict(snap=1000, pop=20)
- s2.familyName = "MasterFamilyName"
- s2.styleName = "MasterStyleNameTwo"
- doc.addSource(s2)
- doc.checkAxes()
- assert doc.getAxisOrder() == ['snap', 'pop']
- assert doc.default == None
- doc.checkDefault()
- assert doc.default.name == 'master.ufo2'
+# # default selected
+# doc = DesignSpaceDocument()
+# # add master 1
+# s1 = SourceDescriptor()
+# s1.path = masterPath1
+# s1.name = "master.ufo1"
+# s1.location = dict(snap=0, pop=10)
+# s1.familyName = "MasterFamilyName"
+# s1.styleName = "MasterStyleNameOne"
+# doc.addSource(s1)
+# # add master 2
+# s2 = SourceDescriptor()
+# s2.path = masterPath2
+# s2.name = "master.ufo2"
+# s2.copyInfo = True
+# s2.location = dict(snap=1000, pop=20)
+# s2.familyName = "MasterFamilyName"
+# s2.styleName = "MasterStyleNameTwo"
+# doc.addSource(s2)
+# doc.checkAxes()
+# assert doc.getAxisOrder() == ['snap', 'pop']
+# assert doc.default == None
+# doc.checkDefault()
+# assert doc.default.name == 'master.ufo2'
+
+# # generate a doc without axes, save and read again
+# doc = DesignSpaceDocument()
+# # add master 1
+# s1 = SourceDescriptor()
+# s1.path = masterPath1
+# s1.name = "master.ufo1"
+# s1.location = dict(snap=0, pop=10)
+# s1.familyName = "MasterFamilyName"
+# s1.styleName = "MasterStyleNameOne"
+# doc.addSource(s1)
+# # add master 2
+# s2 = SourceDescriptor()
+# s2.path = masterPath2
+# s2.name = "master.ufo2"
+# s2.location = dict(snap=1000, pop=20)
+# s2.familyName = "MasterFamilyName"
+# s2.styleName = "MasterStyleNameTwo"
+# doc.addSource(s2)
+# doc.checkAxes()
+# doc.write(testDocPath)
+# __removeAxesFromDesignSpace(testDocPath)
+
+# new = DesignSpaceDocument()
+# new.read(testDocPath)
+# assert len(new.axes) == 2
+# new.checkAxes()
+# assert len(new.axes) == 2
+# assert print([a.name for a in new.axes]) == ['snap', 'pop']
+# new.write(testDocPath)
- # generate a doc without axes, save and read again
- doc = DesignSpaceDocument()
- # add master 1
- s1 = SourceDescriptor()
- s1.path = masterPath1
- s1.name = "master.ufo1"
- s1.location = dict(snap=0, pop=10)
- s1.familyName = "MasterFamilyName"
- s1.styleName = "MasterStyleNameOne"
- doc.addSource(s1)
- # add master 2
- s2 = SourceDescriptor()
- s2.path = masterPath2
- s2.name = "master.ufo2"
- s2.location = dict(snap=1000, pop=20)
- s2.familyName = "MasterFamilyName"
- s2.styleName = "MasterStyleNameTwo"
- doc.addSource(s2)
- doc.checkAxes()
- doc.write(testDocPath)
- __removeAxesFromDesignSpace(testDocPath)
- new = DesignSpaceDocument()
- new.read(testDocPath)
- assert len(new.axes) == 2
- new.checkAxes()
- assert len(new.axes) == 2
- assert print([a.name for a in new.axes]) == ['snap', 'pop']
- new.write(testDocPath)