2017-11-29 17:08:23 +00:00
|
|
|
# coding=utf-8
|
|
|
|
|
|
|
|
import os
|
2022-11-14 10:56:04 +00:00
|
|
|
from pathlib import Path
|
2022-04-14 15:04:38 +01:00
|
|
|
import re
|
2023-06-01 12:12:34 -06:00
|
|
|
import shutil
|
2017-11-29 17:08:23 +00:00
|
|
|
|
2022-04-14 15:04:38 +01:00
|
|
|
import pytest
|
2019-05-10 16:32:11 +01:00
|
|
|
from fontTools import ttLib
|
2022-04-14 15:04:38 +01:00
|
|
|
from fontTools.designspaceLib import (
|
|
|
|
AxisDescriptor,
|
2023-05-28 17:00:41 -06:00
|
|
|
AxisMappingDescriptor,
|
2022-04-14 15:04:38 +01:00
|
|
|
AxisLabelDescriptor,
|
|
|
|
DesignSpaceDocument,
|
|
|
|
DesignSpaceDocumentError,
|
|
|
|
DiscreteAxisDescriptor,
|
|
|
|
InstanceDescriptor,
|
|
|
|
RuleDescriptor,
|
|
|
|
SourceDescriptor,
|
|
|
|
evaluateRule,
|
|
|
|
posix,
|
|
|
|
processRules,
|
|
|
|
)
|
2022-04-26 10:50:36 +01:00
|
|
|
from fontTools.designspaceLib.types import Range
|
2022-04-14 15:04:38 +01:00
|
|
|
from fontTools.misc import plistlib
|
|
|
|
|
2022-11-14 10:56:04 +00:00
|
|
|
from .fixtures import datadir
|
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
|
2018-05-30 22:26:59 +02:00
|
|
|
def _axesAsDict(axes):
|
|
|
|
"""
|
|
|
|
Make the axis data we have available in
|
|
|
|
"""
|
|
|
|
axesDict = {}
|
|
|
|
for axisDescriptor in axes:
|
|
|
|
d = {
|
|
|
|
"name": axisDescriptor.name,
|
|
|
|
"tag": axisDescriptor.tag,
|
|
|
|
"minimum": axisDescriptor.minimum,
|
|
|
|
"maximum": axisDescriptor.maximum,
|
|
|
|
"default": axisDescriptor.default,
|
|
|
|
"map": axisDescriptor.map,
|
|
|
|
}
|
|
|
|
axesDict[axisDescriptor.name] = d
|
|
|
|
return axesDict
|
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
|
2018-02-14 12:45:32 +00:00
|
|
|
def assert_equals_test_file(path, test_filename):
|
2022-04-14 15:04:38 +01:00
|
|
|
with open(path, encoding="utf-8") as fp:
|
2018-02-14 12:45:32 +00:00
|
|
|
actual = fp.read()
|
|
|
|
|
|
|
|
test_path = os.path.join(os.path.dirname(__file__), test_filename)
|
2022-04-14 15:04:38 +01:00
|
|
|
with open(test_path, encoding="utf-8") as fp:
|
2018-02-14 12:45:32 +00:00
|
|
|
expected = fp.read()
|
2022-04-14 15:04:38 +01:00
|
|
|
expected = re.sub(r"<!--(.|\n)*?-->", "", expected)
|
|
|
|
expected = re.sub(r"\s*\n+", "\n", expected)
|
2018-02-14 12:45:32 +00:00
|
|
|
|
|
|
|
assert actual == expected
|
|
|
|
|
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
def test_fill_document(tmpdir):
|
|
|
|
tmpdir = str(tmpdir)
|
2022-04-14 15:04:38 +01:00
|
|
|
testDocPath = os.path.join(tmpdir, "test_v4.designspace")
|
|
|
|
testDocPath5 = os.path.join(tmpdir, "test_v5.designspace")
|
2017-11-29 17:08:23 +00:00
|
|
|
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()
|
2019-10-18 13:33:29 +02:00
|
|
|
doc.rulesProcessingLast = True
|
2018-05-23 13:19:23 +02:00
|
|
|
|
|
|
|
# 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["fa-IR"] = "قطر"
|
|
|
|
a1.labelNames["en"] = "Wéíght"
|
|
|
|
doc.addAxis(a1)
|
|
|
|
a2 = AxisDescriptor()
|
|
|
|
a2.minimum = 0
|
|
|
|
a2.maximum = 1000
|
2019-03-10 22:20:00 +00:00
|
|
|
a2.default = 15
|
2018-05-23 13:19:23 +02:00
|
|
|
a2.name = "width"
|
|
|
|
a2.tag = "wdth"
|
2019-03-10 22:20:00 +00:00
|
|
|
a2.map = [(0.0, 10.0), (15.0, 20.0), (401.0, 66.0), (1000.0, 990.0)]
|
2018-05-23 13:19:23 +02:00
|
|
|
a2.hidden = True
|
|
|
|
a2.labelNames["fr"] = "Chasse"
|
|
|
|
doc.addAxis(a2)
|
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
# add master 1
|
|
|
|
s1 = SourceDescriptor()
|
|
|
|
s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath))
|
2018-02-14 12:45:32 +00:00
|
|
|
assert s1.font is None
|
2017-11-29 17:08:23 +00:00
|
|
|
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)
|
2018-04-27 13:24:11 +02:00
|
|
|
# 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)
|
2017-11-29 17:08:23 +00:00
|
|
|
# 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"
|
2022-04-14 15:04:38 +01:00
|
|
|
i1.localisedStyleName = dict(fr="Demigras", ja="半ば")
|
|
|
|
i1.localisedFamilyName = dict(fr="Montserrat", ja="モンセラート")
|
|
|
|
i1.localisedStyleMapStyleName = dict(de="Standard")
|
|
|
|
i1.localisedStyleMapFamilyName = dict(
|
|
|
|
de="Montserrat Halbfett", ja="モンセラート SemiBold"
|
|
|
|
)
|
2017-11-29 17:08:23 +00:00
|
|
|
glyphData = dict(name="arrow", mute=True, unicodes=[0x123, 0x124, 0x125])
|
|
|
|
i1.glyphs["arrow"] = glyphData
|
2018-10-18 20:10:59 +01:00
|
|
|
i1.lib["com.coolDesignspaceApp.binaryData"] = plistlib.Data(b"<binary gunk>")
|
2018-02-14 12:45:32 +00:00
|
|
|
i1.lib["com.coolDesignspaceApp.specimenText"] = "Hamburgerwhatever"
|
2017-11-29 17:08:23 +00:00
|
|
|
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)
|
|
|
|
|
2018-02-14 12:45:32 +00:00
|
|
|
doc.filename = "suggestedFileName.designspace"
|
|
|
|
doc.lib["com.coolDesignspaceApp.previewSize"] = 30
|
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
# write some rules
|
|
|
|
r1 = RuleDescriptor()
|
|
|
|
r1.name = "named.rule.1"
|
2018-05-14 10:29:48 +01:00
|
|
|
r1.conditionSets.append(
|
|
|
|
[
|
|
|
|
dict(name="axisName_a", minimum=0, maximum=1),
|
|
|
|
dict(name="axisName_b", minimum=2, maximum=3),
|
|
|
|
]
|
|
|
|
)
|
2017-11-29 17:08:23 +00:00
|
|
|
r1.subs.append(("a", "a.alt"))
|
|
|
|
doc.addRule(r1)
|
2022-04-14 15:04:38 +01:00
|
|
|
# write the document; without an explicit format it will be 5.0 by default
|
|
|
|
doc.write(testDocPath5)
|
|
|
|
assert os.path.exists(testDocPath5)
|
|
|
|
assert_equals_test_file(testDocPath5, "data/test_v5_original.designspace")
|
|
|
|
# write again with an explicit format = 4.1
|
|
|
|
doc.formatVersion = "4.1"
|
2017-11-29 17:08:23 +00:00
|
|
|
doc.write(testDocPath)
|
|
|
|
assert os.path.exists(testDocPath)
|
2022-04-14 15:04:38 +01:00
|
|
|
assert_equals_test_file(testDocPath, "data/test_v4_original.designspace")
|
2017-11-29 17:08:23 +00:00
|
|
|
# import it again
|
|
|
|
new = DesignSpaceDocument()
|
|
|
|
new.read(testDocPath)
|
|
|
|
|
|
|
|
assert new.default.location == {"width": 20.0, "weight": 0.0}
|
2022-04-14 15:04:38 +01:00
|
|
|
assert new.filename == "test_v4.designspace"
|
2018-02-14 12:45:32 +00:00
|
|
|
assert new.lib == doc.lib
|
|
|
|
assert new.instances[0].lib == doc.instances[0].lib
|
2017-11-29 17:08:23 +00:00
|
|
|
|
|
|
|
# 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()
|
2022-04-14 15:04:38 +01:00
|
|
|
doc.formatVersion = "4.1" # This test about instance glyphs is deprecated in v5
|
2017-11-29 17:08:23 +00:00
|
|
|
# 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
|
2018-09-25 23:27:51 +02:00
|
|
|
with open(testDocPath, "r", encoding="utf-8") as f1:
|
|
|
|
t1 = f1.read()
|
|
|
|
with open(testDocPath2, "r", encoding="utf-8") as f2:
|
|
|
|
t2 = f2.read()
|
2017-11-29 17:08:23 +00:00
|
|
|
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("モンセラート", "ja")
|
|
|
|
i1.setStyleName("Demigras", "fr")
|
|
|
|
i1.setStyleName("半ば", "ja")
|
|
|
|
i1.setStyleMapStyleName("Standard", "de")
|
|
|
|
i1.setStyleMapFamilyName("Montserrat Halbfett", "de")
|
|
|
|
i1.setStyleMapFamilyName("モンセラート 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["fa-IR"] = "قطر"
|
|
|
|
a1.labelNames["en"] = "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["fr"] = "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"
|
2018-05-14 10:29:48 +01:00
|
|
|
r1.conditionSets.append(
|
|
|
|
[
|
|
|
|
dict(name="weight", minimum=200, maximum=500),
|
|
|
|
dict(name="width", minimum=0, maximum=150),
|
|
|
|
]
|
|
|
|
)
|
2017-11-29 17:08:23 +00:00
|
|
|
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)
|
2018-09-25 23:27:51 +02:00
|
|
|
with open(testDocPath, "r", encoding="utf-8") as f1:
|
|
|
|
t1 = f1.read()
|
|
|
|
with open(testDocPath2, "r", encoding="utf-8") as f2:
|
|
|
|
t2 = f2.read()
|
2017-11-29 17:08:23 +00:00
|
|
|
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
|
2018-05-14 10:29:48 +01:00
|
|
|
a.name = "axisName%s" % (name)
|
|
|
|
a.tag = "ax_%d" % (value)
|
2017-11-29 17:08:23 +00:00
|
|
|
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)
|
|
|
|
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
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")
|
|
|
|
|
2018-05-23 13:19:23 +02:00
|
|
|
a1 = AxisDescriptor()
|
|
|
|
a1.tag = "TAGA"
|
|
|
|
a1.name = "axisName_a"
|
|
|
|
a1.minimum = 0
|
|
|
|
a1.maximum = 1000
|
|
|
|
a1.default = 0
|
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
# Case 1: filename and path are both empty. Nothing to calculate, nothing to put in the file.
|
|
|
|
doc = DesignSpaceDocument()
|
2018-05-23 13:19:23 +02:00
|
|
|
doc.addAxis(a1)
|
2017-11-29 17:08:23 +00:00
|
|
|
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()
|
2018-05-23 13:19:23 +02:00
|
|
|
doc.addAxis(a1)
|
2017-11-29 17:08:23 +00:00
|
|
|
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()
|
2018-05-23 13:19:23 +02:00
|
|
|
doc.addAxis(a1)
|
2017-11-29 17:08:23 +00:00
|
|
|
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)
|
2022-12-13 11:26:36 +00:00
|
|
|
)
|
2017-11-29 17:08:23 +00:00
|
|
|
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()
|
2018-05-23 13:19:23 +02:00
|
|
|
doc.addAxis(a1)
|
2017-11-29 17:08:23 +00:00
|
|
|
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()
|
2018-05-23 13:19:23 +02:00
|
|
|
doc.addAxis(a1)
|
2017-11-29 17:08:23 +00:00
|
|
|
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()
|
2018-05-23 13:19:23 +02:00
|
|
|
doc.addAxis(a1)
|
2017-11-29 17:08:23 +00:00
|
|
|
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"
|
|
|
|
|
|
|
|
|
2018-05-25 11:43:58 +02:00
|
|
|
def test_normalise1():
|
|
|
|
# normalisation of anisotropic locations, clipping
|
2017-11-29 17:08:23 +00:00
|
|
|
doc = DesignSpaceDocument()
|
|
|
|
# write some axes
|
|
|
|
a1 = AxisDescriptor()
|
|
|
|
a1.minimum = -1000
|
|
|
|
a1.maximum = 1000
|
|
|
|
a1.default = 0
|
2018-05-14 10:29:48 +01:00
|
|
|
a1.name = "axisName_a"
|
|
|
|
a1.tag = "TAGA"
|
2017-11-29 17:08:23 +00:00
|
|
|
doc.addAxis(a1)
|
2018-05-14 10:29:48 +01:00
|
|
|
assert doc.normalizeLocation(dict(axisName_a=0)) == {"axisName_a": 0.0}
|
|
|
|
assert doc.normalizeLocation(dict(axisName_a=1000)) == {"axisName_a": 1.0}
|
2017-11-29 17:08:23 +00:00
|
|
|
# clipping beyond max values:
|
2018-05-14 10:29:48 +01:00
|
|
|
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}
|
2017-11-29 17:08:23 +00:00
|
|
|
# anisotropic coordinates normalise to isotropic
|
2018-05-14 10:29:48 +01:00
|
|
|
assert doc.normalizeLocation(dict(axisName_a=(1000, -1000))) == {"axisName_a": 1.0}
|
2017-11-29 17:08:23 +00:00
|
|
|
doc.normalize()
|
|
|
|
r = []
|
|
|
|
for axis in doc.axes:
|
|
|
|
r.append((axis.name, axis.minimum, axis.default, axis.maximum))
|
|
|
|
r.sort()
|
2018-05-14 10:29:48 +01:00
|
|
|
assert r == [("axisName_a", -1.0, 0.0, 1.0)]
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
|
2018-05-25 11:43:58 +02:00
|
|
|
def test_normalise2():
|
|
|
|
# normalisation with minimum > 0
|
2017-11-29 17:08:23 +00:00
|
|
|
doc = DesignSpaceDocument()
|
|
|
|
# write some axes
|
|
|
|
a2 = AxisDescriptor()
|
|
|
|
a2.minimum = 100
|
|
|
|
a2.maximum = 1000
|
|
|
|
a2.default = 100
|
2018-05-14 10:29:48 +01:00
|
|
|
a2.name = "axisName_b"
|
2017-11-29 17:08:23 +00:00
|
|
|
doc.addAxis(a2)
|
2018-05-14 10:29:48 +01:00
|
|
|
assert doc.normalizeLocation(dict(axisName_b=0)) == {"axisName_b": 0.0}
|
|
|
|
assert doc.normalizeLocation(dict(axisName_b=1000)) == {"axisName_b": 1.0}
|
2017-11-29 17:08:23 +00:00
|
|
|
# clipping beyond max values:
|
2018-05-14 10:29:48 +01:00
|
|
|
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}
|
2017-11-29 17:08:23 +00:00
|
|
|
# anisotropic coordinates normalise to isotropic
|
2018-05-14 10:29:48 +01:00
|
|
|
assert doc.normalizeLocation(dict(axisName_b=(1000, -1000))) == {"axisName_b": 1.0}
|
|
|
|
assert doc.normalizeLocation(dict(axisName_b=1001)) == {"axisName_b": 1.0}
|
2017-11-29 17:08:23 +00:00
|
|
|
doc.normalize()
|
|
|
|
r = []
|
|
|
|
for axis in doc.axes:
|
|
|
|
r.append((axis.name, axis.minimum, axis.default, axis.maximum))
|
|
|
|
r.sort()
|
2018-05-14 10:29:48 +01:00
|
|
|
assert r == [("axisName_b", 0.0, 0.0, 1.0)]
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
|
2018-05-25 11:43:58 +02:00
|
|
|
def test_normalise3():
|
|
|
|
# normalisation of negative values, with default == maximum
|
2017-11-29 17:08:23 +00:00
|
|
|
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)]
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
|
2018-05-25 11:43:58 +02:00
|
|
|
def test_normalise4():
|
|
|
|
# normalisation with a map
|
2017-11-29 17:08:23 +00:00
|
|
|
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()
|
2019-02-27 14:22:35 -08:00
|
|
|
assert r == [("ddd", [(0, 0.0), (300, 0.5), (600, 0.5), (1000, 1.0)])]
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
|
2018-05-27 09:50:00 +02:00
|
|
|
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()
|
2019-02-27 14:22:35 -08:00
|
|
|
assert r == [("ddd", [(0, 0.0), (300, 0.5), (600, 0.5), (1000, 1.0)])]
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2018-05-27 09:50:00 +02:00
|
|
|
|
2023-05-30 12:21:42 -06:00
|
|
|
def test_axisMappingsRoundtrip(tmpdir):
|
|
|
|
# tests of axisMappings in a document, roundtripping.
|
|
|
|
|
|
|
|
tmpdir = str(tmpdir)
|
2023-06-01 12:12:34 -06:00
|
|
|
srcDocPath = (Path(__file__) / "../data/test_avar2.designspace").resolve()
|
|
|
|
testDocPath = os.path.join(tmpdir, "test_avar2.designspace")
|
|
|
|
shutil.copy(srcDocPath, testDocPath)
|
2023-05-30 12:21:42 -06:00
|
|
|
testDocPath2 = os.path.join(tmpdir, "test_avar2_roundtrip.designspace")
|
|
|
|
doc = DesignSpaceDocument()
|
|
|
|
doc.read(testDocPath)
|
|
|
|
assert doc.axisMappings
|
2024-02-05 16:10:58 -07:00
|
|
|
assert len(doc.axisMappings) == 2
|
2023-06-01 11:02:28 -06:00
|
|
|
assert doc.axisMappings[0].inputLocation == {"Justify": -100.0, "Width": 100.0}
|
2023-05-30 13:51:43 -06:00
|
|
|
|
|
|
|
# This is a bit of a hack, but it's the only way to make sure
|
|
|
|
# that the save works on Windows if the tempdir and the data
|
|
|
|
# dir are on different drives.
|
|
|
|
for descriptor in doc.sources + doc.instances:
|
|
|
|
descriptor.path = None
|
|
|
|
|
2023-05-30 12:21:42 -06:00
|
|
|
doc.write(testDocPath2)
|
|
|
|
# verify these results
|
|
|
|
doc2 = DesignSpaceDocument()
|
|
|
|
doc2.read(testDocPath2)
|
|
|
|
assert [mapping.inputLocation for mapping in doc.axisMappings] == [
|
|
|
|
mapping.inputLocation for mapping in doc2.axisMappings
|
|
|
|
]
|
|
|
|
assert [mapping.outputLocation for mapping in doc.axisMappings] == [
|
|
|
|
mapping.outputLocation for mapping in doc2.axisMappings
|
|
|
|
]
|
2024-02-02 13:11:28 -07:00
|
|
|
assert [mapping.description for mapping in doc.axisMappings] == [
|
|
|
|
mapping.description for mapping in doc2.axisMappings
|
|
|
|
]
|
|
|
|
assert [mapping.groupDescription for mapping in doc.axisMappings] == [
|
|
|
|
mapping.groupDescription for mapping in doc2.axisMappings
|
|
|
|
]
|
2023-05-30 12:21:42 -06:00
|
|
|
|
|
|
|
|
2018-05-25 11:43:58 +02:00
|
|
|
def test_rulesConditions(tmpdir):
|
|
|
|
# tests of rules, conditionsets and conditions
|
2017-11-29 17:08:23 +00:00
|
|
|
r1 = RuleDescriptor()
|
|
|
|
r1.name = "named.rule.1"
|
2018-05-14 10:29:48 +01:00
|
|
|
r1.conditionSets.append(
|
|
|
|
[
|
|
|
|
dict(name="axisName_a", minimum=0, maximum=1000),
|
|
|
|
dict(name="axisName_b", minimum=0, maximum=3000),
|
|
|
|
]
|
|
|
|
)
|
2017-11-29 17:08:23 +00:00
|
|
|
r1.subs.append(("a", "a.alt"))
|
|
|
|
|
2018-05-14 10:29:48 +01:00
|
|
|
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",
|
2022-12-13 11:26:36 +00:00
|
|
|
"b",
|
2018-05-14 10:29:48 +01:00
|
|
|
"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",
|
2022-12-13 11:26:36 +00:00
|
|
|
"b",
|
2018-05-14 10:29:48 +01:00
|
|
|
"c",
|
|
|
|
]
|
2017-11-29 17:08:23 +00:00
|
|
|
|
|
|
|
# rule with only a maximum
|
|
|
|
r2 = RuleDescriptor()
|
|
|
|
r2.name = "named.rule.2"
|
2018-05-14 10:29:48 +01:00
|
|
|
r2.conditionSets.append([dict(name="axisName_a", maximum=500)])
|
2017-11-29 17:08:23 +00:00
|
|
|
r2.subs.append(("b", "b.alt"))
|
|
|
|
|
2018-05-14 10:29:48 +01:00
|
|
|
assert evaluateRule(r2, dict(axisName_a=0)) == True
|
|
|
|
assert evaluateRule(r2, dict(axisName_a=-500)) == True
|
|
|
|
assert evaluateRule(r2, dict(axisName_a=1000)) == False
|
2017-11-29 17:08:23 +00:00
|
|
|
|
|
|
|
# rule with only a minimum
|
|
|
|
r3 = RuleDescriptor()
|
|
|
|
r3.name = "named.rule.3"
|
2018-05-14 10:29:48 +01:00
|
|
|
r3.conditionSets.append([dict(name="axisName_a", minimum=500)])
|
2017-11-29 17:08:23 +00:00
|
|
|
r3.subs.append(("c", "c.alt"))
|
|
|
|
|
2018-05-14 10:29:48 +01:00
|
|
|
assert evaluateRule(r3, dict(axisName_a=0)) == False
|
|
|
|
assert evaluateRule(r3, dict(axisName_a=1000)) == True
|
|
|
|
assert evaluateRule(r3, dict(axisName_a=1000)) == True
|
2017-11-29 17:08:23 +00:00
|
|
|
|
|
|
|
# rule with only a minimum, maximum in separate conditions
|
|
|
|
r4 = RuleDescriptor()
|
|
|
|
r4.name = "named.rule.4"
|
2018-05-14 10:29:48 +01:00
|
|
|
r4.conditionSets.append(
|
|
|
|
[dict(name="axisName_a", minimum=500), dict(name="axisName_b", maximum=500)]
|
|
|
|
)
|
2017-11-29 17:08:23 +00:00
|
|
|
r4.subs.append(("c", "c.alt"))
|
|
|
|
|
2018-05-14 10:29:48 +01:00
|
|
|
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
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2017-11-29 17:08:23 +00:00
|
|
|
|
2018-05-25 11:43:58 +02:00
|
|
|
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()
|
2019-10-18 13:33:29 +02:00
|
|
|
doc.rulesProcessingLast = True
|
2017-11-29 17:08:23 +00:00
|
|
|
a1 = AxisDescriptor()
|
|
|
|
a1.minimum = 0
|
|
|
|
a1.maximum = 1000
|
|
|
|
a1.default = 0
|
2018-05-14 10:29:48 +01:00
|
|
|
a1.name = "axisName_a"
|
|
|
|
a1.tag = "TAGA"
|
2017-11-29 17:08:23 +00:00
|
|
|
b1 = AxisDescriptor()
|
|
|
|
b1.minimum = 2000
|
|
|
|
b1.maximum = 3000
|
|
|
|
b1.default = 2000
|
2018-05-14 10:29:48 +01:00
|
|
|
b1.name = "axisName_b"
|
|
|
|
b1.tag = "TAGB"
|
2017-11-29 17:08:23 +00:00
|
|
|
doc.addAxis(a1)
|
|
|
|
doc.addAxis(b1)
|
2018-05-25 11:43:58 +02:00
|
|
|
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
|
2018-05-30 22:26:59 +02:00
|
|
|
assert _axesAsDict(doc.axes) == {
|
|
|
|
"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",
|
2022-12-13 11:26:36 +00:00
|
|
|
},
|
2018-05-30 22:26:59 +02:00
|
|
|
}
|
2018-05-14 10:29:48 +01:00
|
|
|
assert doc.rules[0].conditionSets == [
|
2022-12-13 11:26:36 +00:00
|
|
|
[
|
2018-05-14 10:29:48 +01:00
|
|
|
{"minimum": 0, "maximum": 1000, "name": "axisName_a"},
|
|
|
|
{"minimum": 0, "maximum": 3000, "name": "axisName_b"},
|
|
|
|
]
|
2022-12-13 11:26:36 +00:00
|
|
|
]
|
2017-11-29 17:08:23 +00:00
|
|
|
assert doc.rules[0].subs == [("a", "a.alt")]
|
|
|
|
doc.normalize()
|
|
|
|
assert doc.rules[0].name == "named.rule.1"
|
2018-05-14 10:29:48 +01:00
|
|
|
assert doc.rules[0].conditionSets == [
|
2022-12-13 11:26:36 +00:00
|
|
|
[
|
2018-05-14 10:29:48 +01:00
|
|
|
{"minimum": 0.0, "maximum": 1.0, "name": "axisName_a"},
|
|
|
|
{"minimum": 0.0, "maximum": 1.0, "name": "axisName_b"},
|
|
|
|
]
|
2022-12-13 11:26:36 +00:00
|
|
|
]
|
2018-05-27 19:18:13 +02:00
|
|
|
# still one conditionset
|
|
|
|
assert len(doc.rules[0].conditionSets) == 1
|
2017-11-29 17:08:23 +00:00
|
|
|
doc.write(testDocPath)
|
2018-05-27 19:18:13 +02:00
|
|
|
# add a stray conditionset
|
|
|
|
_addUnwrappedCondition(testDocPath)
|
|
|
|
doc2 = DesignSpaceDocument()
|
|
|
|
doc2.read(testDocPath)
|
2019-10-18 13:33:29 +02:00
|
|
|
assert doc2.rulesProcessingLast
|
2018-05-27 19:18:13 +02:00
|
|
|
assert len(doc2.axes) == 2
|
|
|
|
assert len(doc2.rules) == 1
|
|
|
|
assert len(doc2.rules[0].conditionSets) == 2
|
|
|
|
doc2.write(testDocPath2)
|
|
|
|
# verify these results
|
|
|
|
# make sure the stray condition is now neatly wrapped in a conditionset.
|
|
|
|
doc3 = DesignSpaceDocument()
|
|
|
|
doc3.read(testDocPath2)
|
|
|
|
assert len(doc3.rules) == 1
|
|
|
|
assert len(doc3.rules[0].conditionSets) == 2
|
|
|
|
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2018-05-27 19:18:13 +02:00
|
|
|
def _addUnwrappedCondition(path):
|
|
|
|
# only for testing, so we can make an invalid designspace file
|
|
|
|
# older designspace files may have conditions that are not wrapped in a conditionset
|
|
|
|
# These can be read into a new conditionset.
|
2018-09-25 23:27:51 +02:00
|
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
|
|
d = f.read()
|
2018-05-27 19:18:13 +02:00
|
|
|
print(d)
|
|
|
|
d = d.replace(
|
|
|
|
'<rule name="named.rule.1">',
|
|
|
|
'<rule name="named.rule.1">\n\t<condition maximum="22" minimum="33" name="axisName_a" />',
|
|
|
|
)
|
2018-09-25 23:27:51 +02:00
|
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
|
|
f.write(d)
|
2018-05-14 10:29:48 +01:00
|
|
|
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2018-05-24 21:49:33 +02:00
|
|
|
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)
|
2018-05-27 19:18:13 +02:00
|
|
|
dummyData = dict(a=123, b="äbc", c=[1, 2, 3], d={"a": 123})
|
2018-05-24 21:49:33 +02:00
|
|
|
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
|
|
|
|
|
2019-01-02 14:06:55 +00:00
|
|
|
|
2019-01-03 14:21:09 +00:00
|
|
|
def test_updatePaths(tmpdir):
|
2019-01-02 14:06:55 +00:00
|
|
|
doc = DesignSpaceDocument()
|
2019-01-03 14:21:09 +00:00
|
|
|
doc.path = str(tmpdir / "foo" / "bar" / "MyDesignspace.designspace")
|
|
|
|
|
2019-01-02 14:06:55 +00:00
|
|
|
s1 = SourceDescriptor()
|
2019-01-03 14:21:09 +00:00
|
|
|
doc.addSource(s1)
|
2019-01-02 14:10:22 +00:00
|
|
|
|
2019-01-03 14:21:09 +00:00
|
|
|
doc.updatePaths()
|
2019-01-02 14:10:22 +00:00
|
|
|
|
2019-01-03 14:21:09 +00:00
|
|
|
# expect no changes
|
|
|
|
assert s1.path is None
|
|
|
|
assert s1.filename is None
|
2019-01-02 14:06:55 +00:00
|
|
|
|
2019-01-03 14:21:09 +00:00
|
|
|
name1 = "../masters/Source1.ufo"
|
|
|
|
path1 = posix(str(tmpdir / "foo" / "masters" / "Source1.ufo"))
|
2019-01-02 14:06:55 +00:00
|
|
|
|
2019-01-03 14:21:09 +00:00
|
|
|
s1.path = path1
|
|
|
|
s1.filename = None
|
|
|
|
|
|
|
|
doc.updatePaths()
|
2019-01-02 14:06:55 +00:00
|
|
|
|
2019-01-03 14:21:09 +00:00
|
|
|
assert s1.path == path1
|
|
|
|
assert s1.filename == name1 # empty filename updated
|
|
|
|
|
|
|
|
name2 = "../masters/Source2.ufo"
|
|
|
|
s1.filename = name2
|
|
|
|
|
|
|
|
doc.updatePaths()
|
|
|
|
|
|
|
|
# conflicting filename discarded, path always gets precedence
|
|
|
|
assert s1.path == path1
|
|
|
|
assert s1.filename == "../masters/Source1.ufo"
|
|
|
|
|
|
|
|
s1.path = None
|
|
|
|
s1.filename = name2
|
2019-01-02 14:06:55 +00:00
|
|
|
|
2019-01-03 14:21:09 +00:00
|
|
|
doc.updatePaths()
|
2019-01-02 14:06:55 +00:00
|
|
|
|
2019-01-03 14:21:09 +00:00
|
|
|
# expect no changes
|
|
|
|
assert s1.path is None
|
|
|
|
assert s1.filename == name2
|
2019-01-02 14:12:56 +00:00
|
|
|
|
|
|
|
|
2018-12-30 12:07:05 +01:00
|
|
|
def test_read_with_path_object():
|
2023-06-01 11:02:28 -06:00
|
|
|
source = (Path(__file__) / "../data/test_v4_original.designspace").resolve()
|
2018-12-30 12:07:05 +01:00
|
|
|
assert source.exists()
|
|
|
|
doc = DesignSpaceDocument()
|
|
|
|
doc.read(source)
|
|
|
|
|
2019-01-02 14:12:56 +00:00
|
|
|
|
2018-12-30 12:07:05 +01:00
|
|
|
def test_with_with_path_object(tmpdir):
|
|
|
|
tmpdir = str(tmpdir)
|
2023-06-01 11:02:28 -06:00
|
|
|
dest = Path(tmpdir) / "test_v4_original.designspace"
|
2018-12-30 12:07:05 +01:00
|
|
|
doc = DesignSpaceDocument()
|
|
|
|
doc.write(dest)
|
|
|
|
assert dest.exists()
|
2019-03-10 22:12:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_findDefault_axis_mapping():
|
|
|
|
designspace_string = """\
|
|
|
|
<?xml version='1.0' encoding='UTF-8'?>
|
|
|
|
<designspace format="4.0">
|
|
|
|
<axes>
|
|
|
|
<axis tag="wght" name="Weight" minimum="100" maximum="800" default="400">
|
|
|
|
<map input="100" output="20"/>
|
|
|
|
<map input="300" output="40"/>
|
|
|
|
<map input="400" output="80"/>
|
|
|
|
<map input="700" output="126"/>
|
|
|
|
<map input="800" output="170"/>
|
|
|
|
</axis>
|
|
|
|
<axis tag="ital" name="Italic" minimum="0" maximum="1" default="1"/>
|
|
|
|
</axes>
|
|
|
|
<sources>
|
|
|
|
<source filename="Font-Light.ufo">
|
|
|
|
<location>
|
|
|
|
<dimension name="Weight" xvalue="20"/>
|
|
|
|
<dimension name="Italic" xvalue="0"/>
|
|
|
|
</location>
|
|
|
|
</source>
|
|
|
|
<source filename="Font-Regular.ufo">
|
|
|
|
<location>
|
|
|
|
<dimension name="Weight" xvalue="80"/>
|
|
|
|
<dimension name="Italic" xvalue="0"/>
|
|
|
|
</location>
|
|
|
|
</source>
|
|
|
|
<source filename="Font-Bold.ufo">
|
|
|
|
<location>
|
|
|
|
<dimension name="Weight" xvalue="170"/>
|
|
|
|
<dimension name="Italic" xvalue="0"/>
|
|
|
|
</location>
|
|
|
|
</source>
|
|
|
|
<source filename="Font-LightItalic.ufo">
|
|
|
|
<location>
|
|
|
|
<dimension name="Weight" xvalue="20"/>
|
|
|
|
<dimension name="Italic" xvalue="1"/>
|
|
|
|
</location>
|
|
|
|
</source>
|
|
|
|
<source filename="Font-Italic.ufo">
|
|
|
|
<location>
|
|
|
|
<dimension name="Weight" xvalue="80"/>
|
|
|
|
<dimension name="Italic" xvalue="1"/>
|
|
|
|
</location>
|
|
|
|
</source>
|
|
|
|
<source filename="Font-BoldItalic.ufo">
|
|
|
|
<location>
|
|
|
|
<dimension name="Weight" xvalue="170"/>
|
|
|
|
<dimension name="Italic" xvalue="1"/>
|
|
|
|
</location>
|
|
|
|
</source>
|
|
|
|
</sources>
|
|
|
|
</designspace>
|
|
|
|
"""
|
|
|
|
designspace = DesignSpaceDocument.fromstring(designspace_string)
|
|
|
|
assert designspace.findDefault().filename == "Font-Italic.ufo"
|
2019-04-08 16:58:55 +01:00
|
|
|
|
|
|
|
designspace.axes[1].default = 0
|
|
|
|
|
|
|
|
assert designspace.findDefault().filename == "Font-Regular.ufo"
|
2019-05-10 16:32:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_loadSourceFonts():
|
|
|
|
def opener(path):
|
|
|
|
font = ttLib.TTFont()
|
|
|
|
font.importXML(path)
|
|
|
|
return font
|
|
|
|
|
|
|
|
# this designspace file contains .TTX source paths
|
|
|
|
path = os.path.join(
|
|
|
|
os.path.dirname(os.path.dirname(__file__)),
|
|
|
|
"varLib",
|
|
|
|
"data",
|
|
|
|
"SparseMasters.designspace",
|
|
|
|
)
|
|
|
|
designspace = DesignSpaceDocument.fromfile(path)
|
|
|
|
|
|
|
|
# force two source descriptors to have the same path
|
|
|
|
designspace.sources[1].path = designspace.sources[0].path
|
|
|
|
|
|
|
|
fonts = designspace.loadSourceFonts(opener)
|
|
|
|
|
|
|
|
assert len(fonts) == 3
|
|
|
|
assert all(isinstance(font, ttLib.TTFont) for font in fonts)
|
|
|
|
assert fonts[0] is fonts[1] # same path, identical font object
|
|
|
|
|
|
|
|
fonts2 = designspace.loadSourceFonts(opener)
|
|
|
|
|
|
|
|
for font1, font2 in zip(fonts, fonts2):
|
|
|
|
assert font1 is font2
|
|
|
|
|
|
|
|
|
|
|
|
def test_loadSourceFonts_no_required_path():
|
|
|
|
designspace = DesignSpaceDocument()
|
|
|
|
designspace.sources.append(SourceDescriptor())
|
|
|
|
|
|
|
|
with pytest.raises(DesignSpaceDocumentError, match="no 'path' attribute"):
|
|
|
|
designspace.loadSourceFonts(lambda p: p)
|
2020-03-20 16:07:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_addAxisDescriptor():
|
|
|
|
ds = DesignSpaceDocument()
|
|
|
|
|
|
|
|
axis = ds.addAxisDescriptor(
|
|
|
|
name="Weight", tag="wght", minimum=100, default=400, maximum=900
|
|
|
|
)
|
|
|
|
|
|
|
|
assert ds.axes[0] is axis
|
|
|
|
assert isinstance(axis, AxisDescriptor)
|
|
|
|
assert axis.name == "Weight"
|
|
|
|
assert axis.tag == "wght"
|
|
|
|
assert axis.minimum == 100
|
|
|
|
assert axis.default == 400
|
|
|
|
assert axis.maximum == 900
|
|
|
|
|
|
|
|
|
2023-05-28 17:00:41 -06:00
|
|
|
def test_addAxisDescriptor():
|
|
|
|
ds = DesignSpaceDocument()
|
|
|
|
|
|
|
|
mapping = ds.addAxisMappingDescriptor(
|
|
|
|
inputLocation={"weight": 900, "width": 150}, outputLocation={"weight": 870}
|
|
|
|
)
|
|
|
|
|
|
|
|
assert ds.axisMappings[0] is mapping
|
|
|
|
assert isinstance(mapping, AxisMappingDescriptor)
|
|
|
|
assert mapping.inputLocation == {"weight": 900, "width": 150}
|
|
|
|
assert mapping.outputLocation == {"weight": 870}
|
|
|
|
|
|
|
|
|
2020-03-20 16:07:30 +00:00
|
|
|
def test_addSourceDescriptor():
|
|
|
|
ds = DesignSpaceDocument()
|
|
|
|
|
|
|
|
source = ds.addSourceDescriptor(name="TestSource", location={"Weight": 400})
|
|
|
|
|
|
|
|
assert ds.sources[0] is source
|
|
|
|
assert isinstance(source, SourceDescriptor)
|
|
|
|
assert source.name == "TestSource"
|
|
|
|
assert source.location == {"Weight": 400}
|
|
|
|
|
|
|
|
|
|
|
|
def test_addInstanceDescriptor():
|
|
|
|
ds = DesignSpaceDocument()
|
|
|
|
|
|
|
|
instance = ds.addInstanceDescriptor(
|
|
|
|
name="TestInstance",
|
|
|
|
location={"Weight": 400},
|
|
|
|
styleName="Regular",
|
|
|
|
styleMapStyleName="regular",
|
|
|
|
)
|
|
|
|
|
|
|
|
assert ds.instances[0] is instance
|
|
|
|
assert isinstance(instance, InstanceDescriptor)
|
|
|
|
assert instance.name == "TestInstance"
|
|
|
|
assert instance.location == {"Weight": 400}
|
|
|
|
assert instance.styleName == "Regular"
|
|
|
|
assert instance.styleMapStyleName == "regular"
|
|
|
|
|
|
|
|
|
2020-05-13 15:45:27 +01:00
|
|
|
def test_addRuleDescriptor(tmp_path):
|
2020-03-20 16:07:30 +00:00
|
|
|
ds = DesignSpaceDocument()
|
|
|
|
|
|
|
|
rule = ds.addRuleDescriptor(
|
2020-05-13 15:45:27 +01:00
|
|
|
name="TestRule",
|
|
|
|
conditionSets=[
|
|
|
|
[
|
|
|
|
dict(name="Weight", minimum=100, maximum=200),
|
|
|
|
dict(name="Weight", minimum=700, maximum=900),
|
|
|
|
]
|
|
|
|
],
|
|
|
|
subs=[("a", "a.alt")],
|
2020-03-20 16:07:30 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
assert ds.rules[0] is rule
|
|
|
|
assert isinstance(rule, RuleDescriptor)
|
|
|
|
assert rule.name == "TestRule"
|
|
|
|
assert rule.conditionSets == [
|
2020-05-13 15:45:27 +01:00
|
|
|
[
|
|
|
|
dict(name="Weight", minimum=100, maximum=200),
|
|
|
|
dict(name="Weight", minimum=700, maximum=900),
|
|
|
|
]
|
2020-03-20 16:07:30 +00:00
|
|
|
]
|
|
|
|
assert rule.subs == [("a", "a.alt")]
|
2020-05-13 15:45:27 +01:00
|
|
|
|
|
|
|
# Test it doesn't crash.
|
|
|
|
ds.write(tmp_path / "test.designspace")
|
2022-04-26 10:15:37 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_deepcopyExceptFonts():
|
|
|
|
ds = DesignSpaceDocument()
|
|
|
|
ds.addSourceDescriptor(font=object())
|
|
|
|
ds.addSourceDescriptor(font=object())
|
|
|
|
|
|
|
|
ds_copy = ds.deepcopyExceptFonts()
|
|
|
|
|
|
|
|
assert ds.tostring() == ds_copy.tostring()
|
|
|
|
assert ds.sources[0].font is ds_copy.sources[0].font
|
|
|
|
assert ds.sources[1].font is ds_copy.sources[1].font
|
2022-04-26 10:50:36 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_Range_post_init():
|
|
|
|
# test min and max are sorted and default is clamped to either min/max
|
|
|
|
r = Range(minimum=2, maximum=-1, default=-2)
|
|
|
|
assert r.minimum == -1
|
|
|
|
assert r.maximum == 2
|
|
|
|
assert r.default == -1
|
2022-11-14 10:56:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_get_axes(datadir: Path) -> None:
|
|
|
|
ds = DesignSpaceDocument.fromfile(datadir / "test_v5.designspace")
|
|
|
|
|
|
|
|
assert ds.getAxis("Width") is ds.getAxisByTag("wdth")
|
|
|
|
assert ds.getAxis("Italic") is ds.getAxisByTag("ital")
|