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.
801 lines
28 KiB
Python
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()
|
|
|