798 lines
28 KiB
Python
798 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)
|
|
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_normalise1():
|
|
# normalisation of anisotropic locations, clipping
|
|
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)]
|
|
|
|
def test_normalise2():
|
|
# normalisation with minimum > 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)]
|
|
|
|
def test_normalise3():
|
|
# normalisation of negative values, with default == maximum
|
|
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)]
|
|
|
|
def test_normalise4():
|
|
# normalisation with a map
|
|
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_axisMapping():
|
|
# note: because designspance lib does not do any actual
|
|
# processing of the mapping data, we can only check if there data is there.
|
|
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_rulesConditions(tmpdir):
|
|
# tests of rules, conditionsets and conditions
|
|
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"))
|
|
|
|
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
|
|
|
|
def test_rulesDocument(tmpdir):
|
|
# tests of rules in a document, roundtripping.
|
|
tmpdir = str(tmpdir)
|
|
testDocPath = os.path.join(tmpdir, "testRules.designspace")
|
|
testDocPath2 = os.path.join(tmpdir, "testRules_roundtrip.designspace")
|
|
doc = DesignSpaceDocument()
|
|
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)
|
|
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 doc._axesAsDict() == {'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) == 2
|
|
assert len(new.rules) == 1
|
|
new.write(testDocPath2)
|
|
# verify these results?
|
|
|
|
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 test_documentLib(tmpdir):
|
|
# roundtrip test of the document lib with some nested data
|
|
tmpdir = str(tmpdir)
|
|
testDocPath1 = os.path.join(tmpdir, "testDocumentLibTest.designspace")
|
|
doc = DesignSpaceDocument()
|
|
a1 = AxisDescriptor()
|
|
a1.tag = "TAGA"
|
|
a1.name = "axisName_a"
|
|
a1.minimum = 0
|
|
a1.maximum = 1000
|
|
a1.default = 0
|
|
doc.addAxis(a1)
|
|
dummyData = dict(a=123, b=u"äbc"+chr(127921), c=[1,2,3], d={'a':123})
|
|
dummyKey = "org.fontTools.designspaceLib"
|
|
doc.lib = {dummyKey: dummyData}
|
|
doc.write(testDocPath1)
|
|
new = DesignSpaceDocument()
|
|
new.read(testDocPath1)
|
|
assert dummyKey in new.lib
|
|
assert new.lib[dummyKey] == dummyData
|
|
|
|
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()
|
|
|
|
|