Merge pull request #1253 from fonttools/designspacelib-layer-spec

Designspacelib layer spec
This commit is contained in:
Erik van Blokland 2018-05-03 10:32:57 +02:00 committed by GitHub
commit 6958481eb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 35 additions and 99 deletions

1
.gitignore vendored
View File

@ -24,3 +24,4 @@ htmlcov/
# OSX Finder
.DS_Store
.pytest_cache

View File

@ -13,7 +13,7 @@ A couple of differences between things that use designspaces:
- Varlib does not support anisotropic interpolations.
- MutatorMath and Superpolator will extrapolate over the boundaries of
the axes. Varlib can not.
the axes. Varlib can not (at the moment).
- Varlib requires much less data to define an instance than
MutatorMath.
- The goals of Varlib and MutatorMath are different, so not all
@ -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.
``layerName``: string. The name of the layer in the source to look for
outline data. Default ``None`` which means ``foreground``.
- ``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,8 @@ 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 attribute is given assume it is the foreground layer.
.. 31-lib-element:

View File

@ -111,6 +111,7 @@ outline geometry, kerning and font.info that we want to work with.
- The **path** attribute is the absolute path to an existing UFO.
- The **name** attribute is a unique name for this source used to keep
track it.
- The **layerName** attribute is the name of the UFO3 layer. Default None for ``foreground``.
So go ahead and add another master:

View File

@ -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

View File

@ -38,6 +38,12 @@
<dimension name="width" xvalue="20" />
</location>
</source>
<source familyname="MasterFamilyName" filename="masters/masterTest2.ufo" layer="supports" name="master.ufo2" stylename="Supports">
<location>
<dimension name="weight" xvalue="1000" />
<dimension name="width" xvalue="20" />
</location>
</source>
</sources>
<instances>
<instance familyname="InstanceFamilyName" filename="instances/instanceTest1.ufo" name="instance.ufo1" postscriptfontname="InstancePostscriptName" stylemapfamilyname="InstanceStyleMapFamilyName" stylemapstylename="InstanceStyleMapStyleName" stylename="InstanceStyleName">

View File

@ -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))
@ -130,7 +143,7 @@ def test_fill_document(tmpdir):
a3.maximum = 666
a3.default = 444
a3.name = "spooky"
a3.tag = "spok"
a3.tag = "SPOK"
a3.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)]
#doc.addAxis(a3) # uncomment this line to test the effects of default axes values
# write some rules
@ -787,99 +800,3 @@ def __removeAxesFromDesignSpace(path):
f.write(n)
f.close()
@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")
# 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'
# 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)