fonttools/Tests/designspaceLib/designspace_test.py
Erik van Blokland 086de222fa WIP axes are compulsory, so don't accomodate missing axes. Raise error if document is missing axes.
We know what the default location is before we start reading the masters and there is no guessing.
findDefault() method: default master is at all default values of all axes, or if that fails, the master with the copyInfo flag.
Warn if a location contains an axis that is not defined in the axes element. Otherwise ignore this value, no guessing.
Remove rulesToFeature() function.
2018-05-23 13:19:23 +02:00

801 lines
28 KiB
Python

# coding=utf-8
from __future__ import (print_function, division, absolute_import,
unicode_literals)
import os
import pytest
from fontTools.misc.py23 import open
from fontTools.designspaceLib import (
DesignSpaceDocument, SourceDescriptor, AxisDescriptor, RuleDescriptor,
InstanceDescriptor, evaluateRule, processRules, posix, DesignSpaceDocumentError)
def assert_equals_test_file(path, test_filename):
with open(path) as fp:
actual = fp.read()
test_path = os.path.join(os.path.dirname(__file__), test_filename)
with open(test_path) as fp:
expected = fp.read()
assert actual == expected
def test_fill_document(tmpdir):
tmpdir = str(tmpdir)
testDocPath = os.path.join(tmpdir, "test.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")
doc = DesignSpaceDocument()
# write some axes
a1 = AxisDescriptor()
a1.minimum = 0
a1.maximum = 1000
a1.default = 0
a1.name = "weight"
a1.tag = "wght"
# note: just to test the element language, not an actual label name recommendations.
a1.labelNames[u'fa-IR'] = u"قطر"
a1.labelNames[u'en'] = u"Wéíght"
doc.addAxis(a1)
a2 = AxisDescriptor()
a2.minimum = 0
a2.maximum = 1000
a2.default = 20
a2.name = "width"
a2.tag = "wdth"
a2.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)]
a2.hidden = True
a2.labelNames[u'fr'] = u"Chasse"
doc.addAxis(a2)
# add master 1
s1 = SourceDescriptor()
s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath))
assert s1.font is None
s1.name = "master.ufo1"
s1.copyLib = True
s1.copyInfo = True
s1.copyFeatures = True
s1.location = dict(weight=0)
s1.familyName = "MasterFamilyName"
s1.styleName = "MasterStyleNameOne"
s1.mutedGlyphNames.append("A")
s1.mutedGlyphNames.append("Z")
doc.addSource(s1)
# add master 2
s2 = SourceDescriptor()
s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath))
s2.name = "master.ufo2"
s2.copyLib = False
s2.copyInfo = False
s2.copyFeatures = False
s2.muteKerning = True
s2.location = dict(weight=1000)
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))
i1.familyName = "InstanceFamilyName"
i1.styleName = "InstanceStyleName"
i1.name = "instance.ufo1"
i1.location = dict(weight=500, spooky=666) # this adds a dimension that is not defined.
i1.postScriptFontName = "InstancePostscriptName"
i1.styleMapFamilyName = "InstanceStyleMapFamilyName"
i1.styleMapStyleName = "InstanceStyleMapStyleName"
glyphData = dict(name="arrow", mute=True, unicodes=[0x123, 0x124, 0x125])
i1.glyphs['arrow'] = glyphData
i1.lib['com.coolDesignspaceApp.specimenText'] = "Hamburgerwhatever"
doc.addInstance(i1)
# add instance 2
i2 = InstanceDescriptor()
i2.filename = os.path.relpath(instancePath2, os.path.dirname(testDocPath))
i2.familyName = "InstanceFamilyName"
i2.styleName = "InstanceStyleName"
i2.name = "instance.ufo2"
# anisotropic location
i2.location = dict(weight=500, width=(400,300))
i2.postScriptFontName = "InstancePostscriptName"
i2.styleMapFamilyName = "InstanceStyleMapFamilyName"
i2.styleMapStyleName = "InstanceStyleMapStyleName"
glyphMasters = [dict(font="master.ufo1", glyphName="BB", location=dict(width=20,weight=20)), dict(font="master.ufo2", glyphName="CC", location=dict(width=900,weight=900))]
glyphData = dict(name="arrow", unicodes=[101, 201, 301])
glyphData['masters'] = glyphMasters
glyphData['note'] = "A note about this glyph"
glyphData['instanceLocation'] = dict(width=100, weight=120)
i2.glyphs['arrow'] = glyphData
i2.glyphs['arrow2'] = dict(mute=False)
doc.addInstance(i2)
doc.filename = "suggestedFileName.designspace"
doc.lib['com.coolDesignspaceApp.previewSize'] = 30
# write some rules
r1 = RuleDescriptor()
r1.name = "named.rule.1"
r1.conditionSets.append([
dict(name='axisName_a', minimum=0, maximum=1),
dict(name='axisName_b', minimum=2, maximum=3)
])
r1.subs.append(("a", "a.alt"))
doc.addRule(r1)
# write the document
doc.write(testDocPath)
assert os.path.exists(testDocPath)
assert_equals_test_file(testDocPath, 'data/test.designspace')
# import it again
new = DesignSpaceDocument()
new.read(testDocPath)
assert new.default.location == {'width': 20.0, 'weight': 0.0}
assert new.filename == 'test.designspace'
assert new.lib == doc.lib
assert new.instances[0].lib == doc.instances[0].lib
# test roundtrip for the axis attributes and data
axes = {}
for axis in doc.axes:
if axis.tag not in axes:
axes[axis.tag] = []
axes[axis.tag].append(axis.serialize())
for axis in new.axes:
if axis.tag[0] == "_":
continue
if axis.tag not in axes:
axes[axis.tag] = []
axes[axis.tag].append(axis.serialize())
for v in axes.values():
a, b = v
assert a == b
def test_unicodes(tmpdir):
tmpdir = str(tmpdir)
testDocPath = os.path.join(tmpdir, "testUnicodes.designspace")
testDocPath2 = os.path.join(tmpdir, "testUnicodes_roundtrip.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")
doc = DesignSpaceDocument()
# add master 1
s1 = SourceDescriptor()
s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath))
s1.name = "master.ufo1"
s1.copyInfo = True
s1.location = dict(weight=0)
doc.addSource(s1)
# add master 2
s2 = SourceDescriptor()
s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath))
s2.name = "master.ufo2"
s2.location = dict(weight=1000)
doc.addSource(s2)
# add instance 1
i1 = InstanceDescriptor()
i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath))
i1.name = "instance.ufo1"
i1.location = dict(weight=500)
glyphData = dict(name="arrow", mute=True, unicodes=[100, 200, 300])
i1.glyphs['arrow'] = glyphData
doc.addInstance(i1)
# now we have sources and instances, but no axes yet.
doc.axes = [] # clear the axes
# write some axes
a1 = AxisDescriptor()
a1.minimum = 0
a1.maximum = 1000
a1.default = 0
a1.name = "weight"
a1.tag = "wght"
doc.addAxis(a1)
# write the document
doc.write(testDocPath)
assert os.path.exists(testDocPath)
# import it again
new = DesignSpaceDocument()
new.read(testDocPath)
new.write(testDocPath2)
# compare the file contents
f1 = open(testDocPath, 'r', encoding='utf-8')
t1 = f1.read()
f1.close()
f2 = open(testDocPath2, 'r', encoding='utf-8')
t2 = f2.read()
f2.close()
assert t1 == t2
# check the unicode values read from the document
assert new.instances[0].glyphs['arrow']['unicodes'] == [100,200,300]
def test_localisedNames(tmpdir):
tmpdir = str(tmpdir)
testDocPath = os.path.join(tmpdir, "testLocalisedNames.designspace")
testDocPath2 = os.path.join(tmpdir, "testLocalisedNames_roundtrip.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")
doc = DesignSpaceDocument()
# add master 1
s1 = SourceDescriptor()
s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath))
s1.name = "master.ufo1"
s1.copyInfo = True
s1.location = dict(weight=0)
doc.addSource(s1)
# add master 2
s2 = SourceDescriptor()
s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath))
s2.name = "master.ufo2"
s2.location = dict(weight=1000)
doc.addSource(s2)
# add instance 1
i1 = InstanceDescriptor()
i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath))
i1.familyName = "Montserrat"
i1.styleName = "SemiBold"
i1.styleMapFamilyName = "Montserrat SemiBold"
i1.styleMapStyleName = "Regular"
i1.setFamilyName("Montserrat", "fr")
i1.setFamilyName(u"モンセラート", "ja")
i1.setStyleName("Demigras", "fr")
i1.setStyleName(u"半ば", "ja")
i1.setStyleMapStyleName(u"Standard", "de")
i1.setStyleMapFamilyName("Montserrat Halbfett", "de")
i1.setStyleMapFamilyName(u"モンセラート SemiBold", "ja")
i1.name = "instance.ufo1"
i1.location = dict(weight=500, spooky=666) # this adds a dimension that is not defined.
i1.postScriptFontName = "InstancePostscriptName"
glyphData = dict(name="arrow", mute=True, unicodes=[0x123])
i1.glyphs['arrow'] = glyphData
doc.addInstance(i1)
# now we have sources and instances, but no axes yet.
doc.axes = [] # clear the axes
# write some axes
a1 = AxisDescriptor()
a1.minimum = 0
a1.maximum = 1000
a1.default = 0
a1.name = "weight"
a1.tag = "wght"
# note: just to test the element language, not an actual label name recommendations.
a1.labelNames[u'fa-IR'] = u"قطر"
a1.labelNames[u'en'] = u"Wéíght"
doc.addAxis(a1)
a2 = AxisDescriptor()
a2.minimum = 0
a2.maximum = 1000
a2.default = 0
a2.name = "width"
a2.tag = "wdth"
a2.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)]
a2.labelNames[u'fr'] = u"Poids"
doc.addAxis(a2)
# add an axis that is not part of any location to see if that works
a3 = AxisDescriptor()
a3.minimum = 333
a3.maximum = 666
a3.default = 444
a3.name = "spooky"
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
r1 = RuleDescriptor()
r1.name = "named.rule.1"
r1.conditionSets.append([
dict(name='weight', minimum=200, maximum=500),
dict(name='width', minimum=0, maximum=150)
])
r1.subs.append(("a", "a.alt"))
doc.addRule(r1)
# write the document
doc.write(testDocPath)
assert os.path.exists(testDocPath)
# import it again
new = DesignSpaceDocument()
new.read(testDocPath)
new.write(testDocPath2)
f1 = open(testDocPath, 'r', encoding='utf-8')
t1 = f1.read()
f1.close()
f2 = open(testDocPath2, 'r', encoding='utf-8')
t2 = f2.read()
f2.close()
assert t1 == t2
def test_handleNoAxes(tmpdir):
tmpdir = str(tmpdir)
# test what happens if the designspacedocument has no axes element.
testDocPath = os.path.join(tmpdir, "testNoAxes_source.designspace")
testDocPath2 = os.path.join(tmpdir, "testNoAxes_recontructed.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")
# Case 1: No axes element in the document, but there are sources and instances
doc = DesignSpaceDocument()
for name, value in [('One', 1),('Two', 2),('Three', 3)]:
a = AxisDescriptor()
a.minimum = 0
a.maximum = 1000
a.default = 0
a.name = "axisName%s" % (name)
a.tag = "ax_%d" % (value)
doc.addAxis(a)
# add master 1
s1 = SourceDescriptor()
s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath))
s1.name = "master.ufo1"
s1.copyLib = True
s1.copyInfo = True
s1.copyFeatures = True
s1.location = dict(axisNameOne=-1000, axisNameTwo=0, axisNameThree=1000)
s1.familyName = "MasterFamilyName"
s1.styleName = "MasterStyleNameOne"
doc.addSource(s1)
# add master 2
s2 = SourceDescriptor()
s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath))
s2.name = "master.ufo1"
s2.copyLib = False
s2.copyInfo = False
s2.copyFeatures = False
s2.location = dict(axisNameOne=1000, axisNameTwo=1000, axisNameThree=0)
s2.familyName = "MasterFamilyName"
s2.styleName = "MasterStyleNameTwo"
doc.addSource(s2)
# add instance 1
i1 = InstanceDescriptor()
i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath))
i1.familyName = "InstanceFamilyName"
i1.styleName = "InstanceStyleName"
i1.name = "instance.ufo1"
i1.location = dict(axisNameOne=(-1000,500), axisNameTwo=100)
i1.postScriptFontName = "InstancePostscriptName"
i1.styleMapFamilyName = "InstanceStyleMapFamilyName"
i1.styleMapStyleName = "InstanceStyleMapStyleName"
doc.addInstance(i1)
doc.write(testDocPath)
#__removeAxesFromDesignSpace(testDocPath)
verify = DesignSpaceDocument()
verify.read(testDocPath)
verify.write(testDocPath2)
def test_pathNameResolve(tmpdir):
tmpdir = str(tmpdir)
# test how descriptor.path and descriptor.filename are resolved
testDocPath1 = os.path.join(tmpdir, "testPathName_case1.designspace")
testDocPath2 = os.path.join(tmpdir, "testPathName_case2.designspace")
testDocPath3 = os.path.join(tmpdir, "testPathName_case3.designspace")
testDocPath4 = os.path.join(tmpdir, "testPathName_case4.designspace")
testDocPath5 = os.path.join(tmpdir, "testPathName_case5.designspace")
testDocPath6 = os.path.join(tmpdir, "testPathName_case6.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")
a1 = AxisDescriptor()
a1.tag = "TAGA"
a1.name = "axisName_a"
a1.minimum = 0
a1.maximum = 1000
a1.default = 0
# Case 1: filename and path are both empty. Nothing to calculate, nothing to put in the file.
doc = DesignSpaceDocument()
doc.addAxis(a1)
s = SourceDescriptor()
s.filename = None
s.path = None
s.copyInfo = True
s.location = dict(weight=0)
s.familyName = "MasterFamilyName"
s.styleName = "MasterStyleNameOne"
doc.addSource(s)
doc.write(testDocPath1)
verify = DesignSpaceDocument()
verify.read(testDocPath1)
assert verify.sources[0].filename == None
assert verify.sources[0].path == None
# Case 2: filename is empty, path points somewhere: calculate a new filename.
doc = DesignSpaceDocument()
doc.addAxis(a1)
s = SourceDescriptor()
s.filename = None
s.path = masterPath1
s.copyInfo = True
s.location = dict(weight=0)
s.familyName = "MasterFamilyName"
s.styleName = "MasterStyleNameOne"
doc.addSource(s)
doc.write(testDocPath2)
verify = DesignSpaceDocument()
verify.read(testDocPath2)
assert verify.sources[0].filename == "masters/masterTest1.ufo"
assert verify.sources[0].path == posix(masterPath1)
# Case 3: the filename is set, the path is None.
doc = DesignSpaceDocument()
doc.addAxis(a1)
s = SourceDescriptor()
s.filename = "../somewhere/over/the/rainbow.ufo"
s.path = None
s.copyInfo = True
s.location = dict(weight=0)
s.familyName = "MasterFamilyName"
s.styleName = "MasterStyleNameOne"
doc.addSource(s)
doc.write(testDocPath3)
verify = DesignSpaceDocument()
verify.read(testDocPath3)
assert verify.sources[0].filename == "../somewhere/over/the/rainbow.ufo"
# make the absolute path for filename so we can see if it matches the path
p = os.path.abspath(os.path.join(os.path.dirname(testDocPath3), verify.sources[0].filename))
assert verify.sources[0].path == posix(p)
# Case 4: the filename points to one file, the path points to another. The path takes precedence.
doc = DesignSpaceDocument()
doc.addAxis(a1)
s = SourceDescriptor()
s.filename = "../somewhere/over/the/rainbow.ufo"
s.path = masterPath1
s.copyInfo = True
s.location = dict(weight=0)
s.familyName = "MasterFamilyName"
s.styleName = "MasterStyleNameOne"
doc.addSource(s)
doc.write(testDocPath4)
verify = DesignSpaceDocument()
verify.read(testDocPath4)
assert verify.sources[0].filename == "masters/masterTest1.ufo"
# Case 5: the filename is None, path has a value, update the filename
doc = DesignSpaceDocument()
doc.addAxis(a1)
s = SourceDescriptor()
s.filename = None
s.path = masterPath1
s.copyInfo = True
s.location = dict(weight=0)
s.familyName = "MasterFamilyName"
s.styleName = "MasterStyleNameOne"
doc.addSource(s)
doc.write(testDocPath5) # so that the document has a path
doc.updateFilenameFromPath()
assert doc.sources[0].filename == "masters/masterTest1.ufo"
# Case 6: the filename has a value, path has a value, update the filenames with force
doc = DesignSpaceDocument()
doc.addAxis(a1)
s = SourceDescriptor()
s.filename = "../somewhere/over/the/rainbow.ufo"
s.path = masterPath1
s.copyInfo = True
s.location = dict(weight=0)
s.familyName = "MasterFamilyName"
s.styleName = "MasterStyleNameOne"
doc.write(testDocPath5) # so that the document has a path
doc.addSource(s)
assert doc.sources[0].filename == "../somewhere/over/the/rainbow.ufo"
doc.updateFilenameFromPath(force=True)
assert doc.sources[0].filename == "masters/masterTest1.ufo"
def test_normalise():
doc = DesignSpaceDocument()
# write some axes
a1 = AxisDescriptor()
a1.minimum = -1000
a1.maximum = 1000
a1.default = 0
a1.name = "axisName_a"
a1.tag = "TAGA"
doc.addAxis(a1)
assert doc.normalizeLocation(dict(axisName_a=0)) == {'axisName_a': 0.0}
assert doc.normalizeLocation(dict(axisName_a=1000)) == {'axisName_a': 1.0}
# clipping beyond max values:
assert doc.normalizeLocation(dict(axisName_a=1001)) == {'axisName_a': 1.0}
assert doc.normalizeLocation(dict(axisName_a=500)) == {'axisName_a': 0.5}
assert doc.normalizeLocation(dict(axisName_a=-1000)) == {'axisName_a': -1.0}
assert doc.normalizeLocation(dict(axisName_a=-1001)) == {'axisName_a': -1.0}
# anisotropic coordinates normalise to isotropic
assert doc.normalizeLocation(dict(axisName_a=(1000, -1000))) == {'axisName_a': 1.0}
doc.normalize()
r = []
for axis in doc.axes:
r.append((axis.name, axis.minimum, axis.default, axis.maximum))
r.sort()
assert r == [('axisName_a', -1.0, 0.0, 1.0)]
doc = DesignSpaceDocument()
# write some axes
a2 = AxisDescriptor()
a2.minimum = 100
a2.maximum = 1000
a2.default = 100
a2.name = "axisName_b"
doc.addAxis(a2)
assert doc.normalizeLocation(dict(axisName_b=0)) == {'axisName_b': 0.0}
assert doc.normalizeLocation(dict(axisName_b=1000)) == {'axisName_b': 1.0}
# clipping beyond max values:
assert doc.normalizeLocation(dict(axisName_b=1001)) == {'axisName_b': 1.0}
assert doc.normalizeLocation(dict(axisName_b=500)) == {'axisName_b': 0.4444444444444444}
assert doc.normalizeLocation(dict(axisName_b=-1000)) == {'axisName_b': 0.0}
assert doc.normalizeLocation(dict(axisName_b=-1001)) == {'axisName_b': 0.0}
# anisotropic coordinates normalise to isotropic
assert doc.normalizeLocation(dict(axisName_b=(1000,-1000))) == {'axisName_b': 1.0}
assert doc.normalizeLocation(dict(axisName_b=1001)) == {'axisName_b': 1.0}
doc.normalize()
r = []
for axis in doc.axes:
r.append((axis.name, axis.minimum, axis.default, axis.maximum))
r.sort()
assert r == [('axisName_b', 0.0, 0.0, 1.0)]
doc = DesignSpaceDocument()
# write some axes
a3 = AxisDescriptor()
a3.minimum = -1000
a3.maximum = 0
a3.default = 0
a3.name = "ccc"
doc.addAxis(a3)
assert doc.normalizeLocation(dict(ccc=0)) == {'ccc': 0.0}
assert doc.normalizeLocation(dict(ccc=1)) == {'ccc': 0.0}
assert doc.normalizeLocation(dict(ccc=-1000)) == {'ccc': -1.0}
assert doc.normalizeLocation(dict(ccc=-1001)) == {'ccc': -1.0}
doc.normalize()
r = []
for axis in doc.axes:
r.append((axis.name, axis.minimum, axis.default, axis.maximum))
r.sort()
assert r == [('ccc', -1.0, 0.0, 0.0)]
doc = DesignSpaceDocument()
# write some axes
a3 = AxisDescriptor()
a3.minimum = 2000
a3.maximum = 3000
a3.default = 2000
a3.name = "ccc"
doc.addAxis(a3)
assert doc.normalizeLocation(dict(ccc=0)) == {'ccc': 0.0}
assert doc.normalizeLocation(dict(ccc=1)) == {'ccc': 0.0}
assert doc.normalizeLocation(dict(ccc=-1000)) == {'ccc': 0.0}
assert doc.normalizeLocation(dict(ccc=-1001)) == {'ccc': 0.0}
doc.normalize()
r = []
for axis in doc.axes:
r.append((axis.name, axis.minimum, axis.default, axis.maximum))
r.sort()
assert r == [('ccc', 0.0, 0.0, 1.0)]
doc = DesignSpaceDocument()
# write some axes
a4 = AxisDescriptor()
a4.minimum = 0
a4.maximum = 1000
a4.default = 0
a4.name = "ddd"
a4.map = [(0,100), (300, 500), (600, 500), (1000,900)]
doc.addAxis(a4)
doc.normalize()
r = []
for axis in doc.axes:
r.append((axis.name, axis.map))
r.sort()
assert r == [('ddd', [(0, 0.1), (300, 0.5), (600, 0.5), (1000, 0.9)])]
def test_rules(tmpdir):
tmpdir = str(tmpdir)
testDocPath = os.path.join(tmpdir, "testRules.designspace")
testDocPath2 = os.path.join(tmpdir, "testRules_roundtrip.designspace")
doc = DesignSpaceDocument()
# write some axes
a1 = AxisDescriptor()
a1.tag = "TAGA"
a1.name = "axisName_a"
a1.minimum = 0
a1.maximum = 1000
a1.default = 0
doc.addAxis(a1)
a2 = AxisDescriptor()
a2.tag = "TAGB"
a2.name = "axisName_b"
a2.minimum = 0
a2.maximum = 3000
a2.default = 0
doc.addAxis(a2)
r1 = RuleDescriptor()
r1.name = "named.rule.1"
r1.conditionSets.append([
dict(name='axisName_a', minimum=0, maximum=1000),
dict(name='axisName_b', minimum=0, maximum=3000)
])
r1.subs.append(("a", "a.alt"))
# rule with minium and maximum
doc.addRule(r1)
assert len(doc.rules) == 1
assert len(doc.rules[0].conditionSets) == 1
assert len(doc.rules[0].conditionSets[0]) == 2
assert evaluateRule(r1, dict(axisName_a = 500, axisName_b = 0)) == True
assert evaluateRule(r1, dict(axisName_a = 0, axisName_b = 0)) == True
assert evaluateRule(r1, dict(axisName_a = 1000, axisName_b = 0)) == True
assert evaluateRule(r1, dict(axisName_a = 1000, axisName_b = -100)) == False
assert evaluateRule(r1, dict(axisName_a = 1000.0001, axisName_b = 0)) == False
assert evaluateRule(r1, dict(axisName_a = -0.0001, axisName_b = 0)) == False
assert evaluateRule(r1, dict(axisName_a = -100, axisName_b = 0)) == False
assert processRules([r1], dict(axisName_a = 500, axisName_b = 0), ["a", "b", "c"]) == ['a.alt', 'b', 'c']
assert processRules([r1], dict(axisName_a = 500, axisName_b = 0), ["a.alt", "b", "c"]) == ['a.alt', 'b', 'c']
assert processRules([r1], dict(axisName_a = 2000, axisName_b = 0), ["a", "b", "c"]) == ['a', 'b', 'c']
# rule with only a maximum
r2 = RuleDescriptor()
r2.name = "named.rule.2"
r2.conditionSets.append([dict(name='axisName_a', maximum=500)])
r2.subs.append(("b", "b.alt"))
assert evaluateRule(r2, dict(axisName_a = 0)) == True
assert evaluateRule(r2, dict(axisName_a = -500)) == True
assert evaluateRule(r2, dict(axisName_a = 1000)) == False
# rule with only a minimum
r3 = RuleDescriptor()
r3.name = "named.rule.3"
r3.conditionSets.append([dict(name='axisName_a', minimum=500)])
r3.subs.append(("c", "c.alt"))
assert evaluateRule(r3, dict(axisName_a = 0)) == False
assert evaluateRule(r3, dict(axisName_a = 1000)) == True
assert evaluateRule(r3, dict(axisName_a = 1000)) == True
# rule with only a minimum, maximum in separate conditions
r4 = RuleDescriptor()
r4.name = "named.rule.4"
r4.conditionSets.append([
dict(name='axisName_a', minimum=500),
dict(name='axisName_b', maximum=500)
])
r4.subs.append(("c", "c.alt"))
assert evaluateRule(r4, dict(axisName_a = 1000, axisName_b = 0)) == True
assert evaluateRule(r4, dict(axisName_a = 0, axisName_b = 0)) == False
assert evaluateRule(r4, dict(axisName_a = 1000, axisName_b = 1000)) == False
a1 = AxisDescriptor()
a1.minimum = 0
a1.maximum = 1000
a1.default = 0
a1.name = "axisName_a"
a1.tag = "TAGA"
b1 = AxisDescriptor()
b1.minimum = 2000
b1.maximum = 3000
b1.default = 2000
b1.name = "axisName_b"
b1.tag = "TAGB"
doc.addAxis(a1)
doc.addAxis(b1)
assert doc._prepAxesForBender() == {'axisName_a': {'map': [], 'name': 'axisName_a', 'default': 0, 'minimum': 0, 'maximum': 1000, 'tag': 'TAGA'}, 'axisName_b': {'map': [], 'name': 'axisName_b', 'default': 2000, 'minimum': 2000, 'maximum': 3000, 'tag': 'TAGB'}}
assert doc.rules[0].conditionSets == [[
{'minimum': 0, 'maximum': 1000, 'name': 'axisName_a'},
{'minimum': 0, 'maximum': 3000, 'name': 'axisName_b'}]]
assert doc.rules[0].subs == [('a', 'a.alt')]
doc.normalize()
assert doc.rules[0].name == 'named.rule.1'
assert doc.rules[0].conditionSets == [[
{'minimum': 0.0, 'maximum': 1.0, 'name': 'axisName_a'},
{'minimum': 0.0, 'maximum': 1.0, 'name': 'axisName_b'}]]
doc.write(testDocPath)
new = DesignSpaceDocument()
new.read(testDocPath)
assert len(new.axes) == 4
assert len(new.rules) == 1
new.write(testDocPath2)
def test_incompleteRule(tmpdir):
tmpdir = str(tmpdir)
testDocPath1 = os.path.join(tmpdir, "testIncompleteRule.designspace")
doc = DesignSpaceDocument()
# write some axes
a1 = AxisDescriptor()
a1.tag = "TAGA"
a1.name = "axisName_a"
a1.minimum = 0
a1.maximum = 1000
a1.default = 0
doc.addAxis(a1)
a2 = AxisDescriptor()
a2.tag = "TAGB"
a2.name = "axisName_b"
a2.minimum = 0
a2.maximum = 3000
a2.default = 0
doc.addAxis(a2)
r1 = RuleDescriptor()
r1.name = "incomplete.rule.1"
r1.conditionSets.append([
dict(name='axisName_a', minimum=100),
dict(name='axisName_b', maximum=200)
])
r1.subs.append(("c", "c.alt"))
doc.addRule(r1)
doc.write(testDocPath1)
__removeConditionMinimumMaximumDesignSpace(testDocPath1)
new = DesignSpaceDocument()
with pytest.raises(DesignSpaceDocumentError) as excinfo:
new.read(testDocPath1)
assert "No minimum or maximum defined in rule" in str(excinfo.value)
def __removeConditionMinimumMaximumDesignSpace(path):
# only for testing, so we can make an invalid designspace file
# without making the designSpaceDocument also support it.
f = open(path, 'r', encoding='utf-8')
d = f.read()
f.close()
d = d.replace(' minimum="100"', '')
f = open(path, 'w', encoding='utf-8')
f.write(d)
f.close()
def __removeAxesFromDesignSpace(path):
# only for testing, so we can make an invalid designspace file
# without making the designSpaceDocument also support it.
f = open(path, 'r', encoding='utf-8')
d = f.read()
f.close()
start = d.find("<axes>")
end = d.find("</axes>")+len("</axes>")
n = d[0:start] + d[end:]
f = open(path, 'w', encoding='utf-8')
f.write(n)
f.close()