1*e1fe3e4aSElliott Hughes# coding=utf-8 2*e1fe3e4aSElliott Hughes 3*e1fe3e4aSElliott Hughesimport os 4*e1fe3e4aSElliott Hughesfrom pathlib import Path 5*e1fe3e4aSElliott Hughesimport re 6*e1fe3e4aSElliott Hughesimport shutil 7*e1fe3e4aSElliott Hughes 8*e1fe3e4aSElliott Hughesimport pytest 9*e1fe3e4aSElliott Hughesfrom fontTools import ttLib 10*e1fe3e4aSElliott Hughesfrom fontTools.designspaceLib import ( 11*e1fe3e4aSElliott Hughes AxisDescriptor, 12*e1fe3e4aSElliott Hughes AxisMappingDescriptor, 13*e1fe3e4aSElliott Hughes AxisLabelDescriptor, 14*e1fe3e4aSElliott Hughes DesignSpaceDocument, 15*e1fe3e4aSElliott Hughes DesignSpaceDocumentError, 16*e1fe3e4aSElliott Hughes DiscreteAxisDescriptor, 17*e1fe3e4aSElliott Hughes InstanceDescriptor, 18*e1fe3e4aSElliott Hughes RuleDescriptor, 19*e1fe3e4aSElliott Hughes SourceDescriptor, 20*e1fe3e4aSElliott Hughes evaluateRule, 21*e1fe3e4aSElliott Hughes posix, 22*e1fe3e4aSElliott Hughes processRules, 23*e1fe3e4aSElliott Hughes) 24*e1fe3e4aSElliott Hughesfrom fontTools.designspaceLib.types import Range 25*e1fe3e4aSElliott Hughesfrom fontTools.misc import plistlib 26*e1fe3e4aSElliott Hughes 27*e1fe3e4aSElliott Hughesfrom .fixtures import datadir 28*e1fe3e4aSElliott Hughes 29*e1fe3e4aSElliott Hughes 30*e1fe3e4aSElliott Hughesdef _axesAsDict(axes): 31*e1fe3e4aSElliott Hughes """ 32*e1fe3e4aSElliott Hughes Make the axis data we have available in 33*e1fe3e4aSElliott Hughes """ 34*e1fe3e4aSElliott Hughes axesDict = {} 35*e1fe3e4aSElliott Hughes for axisDescriptor in axes: 36*e1fe3e4aSElliott Hughes d = { 37*e1fe3e4aSElliott Hughes "name": axisDescriptor.name, 38*e1fe3e4aSElliott Hughes "tag": axisDescriptor.tag, 39*e1fe3e4aSElliott Hughes "minimum": axisDescriptor.minimum, 40*e1fe3e4aSElliott Hughes "maximum": axisDescriptor.maximum, 41*e1fe3e4aSElliott Hughes "default": axisDescriptor.default, 42*e1fe3e4aSElliott Hughes "map": axisDescriptor.map, 43*e1fe3e4aSElliott Hughes } 44*e1fe3e4aSElliott Hughes axesDict[axisDescriptor.name] = d 45*e1fe3e4aSElliott Hughes return axesDict 46*e1fe3e4aSElliott Hughes 47*e1fe3e4aSElliott Hughes 48*e1fe3e4aSElliott Hughesdef assert_equals_test_file(path, test_filename): 49*e1fe3e4aSElliott Hughes with open(path, encoding="utf-8") as fp: 50*e1fe3e4aSElliott Hughes actual = fp.read() 51*e1fe3e4aSElliott Hughes 52*e1fe3e4aSElliott Hughes test_path = os.path.join(os.path.dirname(__file__), test_filename) 53*e1fe3e4aSElliott Hughes with open(test_path, encoding="utf-8") as fp: 54*e1fe3e4aSElliott Hughes expected = fp.read() 55*e1fe3e4aSElliott Hughes expected = re.sub(r"<!--(.|\n)*?-->", "", expected) 56*e1fe3e4aSElliott Hughes expected = re.sub(r"\s*\n+", "\n", expected) 57*e1fe3e4aSElliott Hughes 58*e1fe3e4aSElliott Hughes assert actual == expected 59*e1fe3e4aSElliott Hughes 60*e1fe3e4aSElliott Hughes 61*e1fe3e4aSElliott Hughesdef test_fill_document(tmpdir): 62*e1fe3e4aSElliott Hughes tmpdir = str(tmpdir) 63*e1fe3e4aSElliott Hughes testDocPath = os.path.join(tmpdir, "test_v4.designspace") 64*e1fe3e4aSElliott Hughes testDocPath5 = os.path.join(tmpdir, "test_v5.designspace") 65*e1fe3e4aSElliott Hughes masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo") 66*e1fe3e4aSElliott Hughes masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo") 67*e1fe3e4aSElliott Hughes instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo") 68*e1fe3e4aSElliott Hughes instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo") 69*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 70*e1fe3e4aSElliott Hughes doc.rulesProcessingLast = True 71*e1fe3e4aSElliott Hughes 72*e1fe3e4aSElliott Hughes # write some axes 73*e1fe3e4aSElliott Hughes a1 = AxisDescriptor() 74*e1fe3e4aSElliott Hughes a1.minimum = 0 75*e1fe3e4aSElliott Hughes a1.maximum = 1000 76*e1fe3e4aSElliott Hughes a1.default = 0 77*e1fe3e4aSElliott Hughes a1.name = "weight" 78*e1fe3e4aSElliott Hughes a1.tag = "wght" 79*e1fe3e4aSElliott Hughes # note: just to test the element language, not an actual label name recommendations. 80*e1fe3e4aSElliott Hughes a1.labelNames["fa-IR"] = "قطر" 81*e1fe3e4aSElliott Hughes a1.labelNames["en"] = "Wéíght" 82*e1fe3e4aSElliott Hughes doc.addAxis(a1) 83*e1fe3e4aSElliott Hughes a2 = AxisDescriptor() 84*e1fe3e4aSElliott Hughes a2.minimum = 0 85*e1fe3e4aSElliott Hughes a2.maximum = 1000 86*e1fe3e4aSElliott Hughes a2.default = 15 87*e1fe3e4aSElliott Hughes a2.name = "width" 88*e1fe3e4aSElliott Hughes a2.tag = "wdth" 89*e1fe3e4aSElliott Hughes a2.map = [(0.0, 10.0), (15.0, 20.0), (401.0, 66.0), (1000.0, 990.0)] 90*e1fe3e4aSElliott Hughes a2.hidden = True 91*e1fe3e4aSElliott Hughes a2.labelNames["fr"] = "Chasse" 92*e1fe3e4aSElliott Hughes doc.addAxis(a2) 93*e1fe3e4aSElliott Hughes 94*e1fe3e4aSElliott Hughes # add master 1 95*e1fe3e4aSElliott Hughes s1 = SourceDescriptor() 96*e1fe3e4aSElliott Hughes s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath)) 97*e1fe3e4aSElliott Hughes assert s1.font is None 98*e1fe3e4aSElliott Hughes s1.name = "master.ufo1" 99*e1fe3e4aSElliott Hughes s1.copyLib = True 100*e1fe3e4aSElliott Hughes s1.copyInfo = True 101*e1fe3e4aSElliott Hughes s1.copyFeatures = True 102*e1fe3e4aSElliott Hughes s1.location = dict(weight=0) 103*e1fe3e4aSElliott Hughes s1.familyName = "MasterFamilyName" 104*e1fe3e4aSElliott Hughes s1.styleName = "MasterStyleNameOne" 105*e1fe3e4aSElliott Hughes s1.mutedGlyphNames.append("A") 106*e1fe3e4aSElliott Hughes s1.mutedGlyphNames.append("Z") 107*e1fe3e4aSElliott Hughes doc.addSource(s1) 108*e1fe3e4aSElliott Hughes # add master 2 109*e1fe3e4aSElliott Hughes s2 = SourceDescriptor() 110*e1fe3e4aSElliott Hughes s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath)) 111*e1fe3e4aSElliott Hughes s2.name = "master.ufo2" 112*e1fe3e4aSElliott Hughes s2.copyLib = False 113*e1fe3e4aSElliott Hughes s2.copyInfo = False 114*e1fe3e4aSElliott Hughes s2.copyFeatures = False 115*e1fe3e4aSElliott Hughes s2.muteKerning = True 116*e1fe3e4aSElliott Hughes s2.location = dict(weight=1000) 117*e1fe3e4aSElliott Hughes s2.familyName = "MasterFamilyName" 118*e1fe3e4aSElliott Hughes s2.styleName = "MasterStyleNameTwo" 119*e1fe3e4aSElliott Hughes doc.addSource(s2) 120*e1fe3e4aSElliott Hughes # add master 3 from a different layer 121*e1fe3e4aSElliott Hughes s3 = SourceDescriptor() 122*e1fe3e4aSElliott Hughes s3.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath)) 123*e1fe3e4aSElliott Hughes s3.name = "master.ufo2" 124*e1fe3e4aSElliott Hughes s3.copyLib = False 125*e1fe3e4aSElliott Hughes s3.copyInfo = False 126*e1fe3e4aSElliott Hughes s3.copyFeatures = False 127*e1fe3e4aSElliott Hughes s3.muteKerning = False 128*e1fe3e4aSElliott Hughes s3.layerName = "supports" 129*e1fe3e4aSElliott Hughes s3.location = dict(weight=1000) 130*e1fe3e4aSElliott Hughes s3.familyName = "MasterFamilyName" 131*e1fe3e4aSElliott Hughes s3.styleName = "Supports" 132*e1fe3e4aSElliott Hughes doc.addSource(s3) 133*e1fe3e4aSElliott Hughes # add instance 1 134*e1fe3e4aSElliott Hughes i1 = InstanceDescriptor() 135*e1fe3e4aSElliott Hughes i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath)) 136*e1fe3e4aSElliott Hughes i1.familyName = "InstanceFamilyName" 137*e1fe3e4aSElliott Hughes i1.styleName = "InstanceStyleName" 138*e1fe3e4aSElliott Hughes i1.name = "instance.ufo1" 139*e1fe3e4aSElliott Hughes i1.location = dict( 140*e1fe3e4aSElliott Hughes weight=500, spooky=666 141*e1fe3e4aSElliott Hughes ) # this adds a dimension that is not defined. 142*e1fe3e4aSElliott Hughes i1.postScriptFontName = "InstancePostscriptName" 143*e1fe3e4aSElliott Hughes i1.styleMapFamilyName = "InstanceStyleMapFamilyName" 144*e1fe3e4aSElliott Hughes i1.styleMapStyleName = "InstanceStyleMapStyleName" 145*e1fe3e4aSElliott Hughes i1.localisedStyleName = dict(fr="Demigras", ja="半ば") 146*e1fe3e4aSElliott Hughes i1.localisedFamilyName = dict(fr="Montserrat", ja="モンセラート") 147*e1fe3e4aSElliott Hughes i1.localisedStyleMapStyleName = dict(de="Standard") 148*e1fe3e4aSElliott Hughes i1.localisedStyleMapFamilyName = dict( 149*e1fe3e4aSElliott Hughes de="Montserrat Halbfett", ja="モンセラート SemiBold" 150*e1fe3e4aSElliott Hughes ) 151*e1fe3e4aSElliott Hughes glyphData = dict(name="arrow", mute=True, unicodes=[0x123, 0x124, 0x125]) 152*e1fe3e4aSElliott Hughes i1.glyphs["arrow"] = glyphData 153*e1fe3e4aSElliott Hughes i1.lib["com.coolDesignspaceApp.binaryData"] = plistlib.Data(b"<binary gunk>") 154*e1fe3e4aSElliott Hughes i1.lib["com.coolDesignspaceApp.specimenText"] = "Hamburgerwhatever" 155*e1fe3e4aSElliott Hughes doc.addInstance(i1) 156*e1fe3e4aSElliott Hughes # add instance 2 157*e1fe3e4aSElliott Hughes i2 = InstanceDescriptor() 158*e1fe3e4aSElliott Hughes i2.filename = os.path.relpath(instancePath2, os.path.dirname(testDocPath)) 159*e1fe3e4aSElliott Hughes i2.familyName = "InstanceFamilyName" 160*e1fe3e4aSElliott Hughes i2.styleName = "InstanceStyleName" 161*e1fe3e4aSElliott Hughes i2.name = "instance.ufo2" 162*e1fe3e4aSElliott Hughes # anisotropic location 163*e1fe3e4aSElliott Hughes i2.location = dict(weight=500, width=(400, 300)) 164*e1fe3e4aSElliott Hughes i2.postScriptFontName = "InstancePostscriptName" 165*e1fe3e4aSElliott Hughes i2.styleMapFamilyName = "InstanceStyleMapFamilyName" 166*e1fe3e4aSElliott Hughes i2.styleMapStyleName = "InstanceStyleMapStyleName" 167*e1fe3e4aSElliott Hughes glyphMasters = [ 168*e1fe3e4aSElliott Hughes dict(font="master.ufo1", glyphName="BB", location=dict(width=20, weight=20)), 169*e1fe3e4aSElliott Hughes dict(font="master.ufo2", glyphName="CC", location=dict(width=900, weight=900)), 170*e1fe3e4aSElliott Hughes ] 171*e1fe3e4aSElliott Hughes glyphData = dict(name="arrow", unicodes=[101, 201, 301]) 172*e1fe3e4aSElliott Hughes glyphData["masters"] = glyphMasters 173*e1fe3e4aSElliott Hughes glyphData["note"] = "A note about this glyph" 174*e1fe3e4aSElliott Hughes glyphData["instanceLocation"] = dict(width=100, weight=120) 175*e1fe3e4aSElliott Hughes i2.glyphs["arrow"] = glyphData 176*e1fe3e4aSElliott Hughes i2.glyphs["arrow2"] = dict(mute=False) 177*e1fe3e4aSElliott Hughes doc.addInstance(i2) 178*e1fe3e4aSElliott Hughes 179*e1fe3e4aSElliott Hughes doc.filename = "suggestedFileName.designspace" 180*e1fe3e4aSElliott Hughes doc.lib["com.coolDesignspaceApp.previewSize"] = 30 181*e1fe3e4aSElliott Hughes 182*e1fe3e4aSElliott Hughes # write some rules 183*e1fe3e4aSElliott Hughes r1 = RuleDescriptor() 184*e1fe3e4aSElliott Hughes r1.name = "named.rule.1" 185*e1fe3e4aSElliott Hughes r1.conditionSets.append( 186*e1fe3e4aSElliott Hughes [ 187*e1fe3e4aSElliott Hughes dict(name="axisName_a", minimum=0, maximum=1), 188*e1fe3e4aSElliott Hughes dict(name="axisName_b", minimum=2, maximum=3), 189*e1fe3e4aSElliott Hughes ] 190*e1fe3e4aSElliott Hughes ) 191*e1fe3e4aSElliott Hughes r1.subs.append(("a", "a.alt")) 192*e1fe3e4aSElliott Hughes doc.addRule(r1) 193*e1fe3e4aSElliott Hughes # write the document; without an explicit format it will be 5.0 by default 194*e1fe3e4aSElliott Hughes doc.write(testDocPath5) 195*e1fe3e4aSElliott Hughes assert os.path.exists(testDocPath5) 196*e1fe3e4aSElliott Hughes assert_equals_test_file(testDocPath5, "data/test_v5_original.designspace") 197*e1fe3e4aSElliott Hughes # write again with an explicit format = 4.1 198*e1fe3e4aSElliott Hughes doc.formatVersion = "4.1" 199*e1fe3e4aSElliott Hughes doc.write(testDocPath) 200*e1fe3e4aSElliott Hughes assert os.path.exists(testDocPath) 201*e1fe3e4aSElliott Hughes assert_equals_test_file(testDocPath, "data/test_v4_original.designspace") 202*e1fe3e4aSElliott Hughes # import it again 203*e1fe3e4aSElliott Hughes new = DesignSpaceDocument() 204*e1fe3e4aSElliott Hughes new.read(testDocPath) 205*e1fe3e4aSElliott Hughes 206*e1fe3e4aSElliott Hughes assert new.default.location == {"width": 20.0, "weight": 0.0} 207*e1fe3e4aSElliott Hughes assert new.filename == "test_v4.designspace" 208*e1fe3e4aSElliott Hughes assert new.lib == doc.lib 209*e1fe3e4aSElliott Hughes assert new.instances[0].lib == doc.instances[0].lib 210*e1fe3e4aSElliott Hughes 211*e1fe3e4aSElliott Hughes # test roundtrip for the axis attributes and data 212*e1fe3e4aSElliott Hughes axes = {} 213*e1fe3e4aSElliott Hughes for axis in doc.axes: 214*e1fe3e4aSElliott Hughes if axis.tag not in axes: 215*e1fe3e4aSElliott Hughes axes[axis.tag] = [] 216*e1fe3e4aSElliott Hughes axes[axis.tag].append(axis.serialize()) 217*e1fe3e4aSElliott Hughes for axis in new.axes: 218*e1fe3e4aSElliott Hughes if axis.tag[0] == "_": 219*e1fe3e4aSElliott Hughes continue 220*e1fe3e4aSElliott Hughes if axis.tag not in axes: 221*e1fe3e4aSElliott Hughes axes[axis.tag] = [] 222*e1fe3e4aSElliott Hughes axes[axis.tag].append(axis.serialize()) 223*e1fe3e4aSElliott Hughes for v in axes.values(): 224*e1fe3e4aSElliott Hughes a, b = v 225*e1fe3e4aSElliott Hughes assert a == b 226*e1fe3e4aSElliott Hughes 227*e1fe3e4aSElliott Hughes 228*e1fe3e4aSElliott Hughesdef test_unicodes(tmpdir): 229*e1fe3e4aSElliott Hughes tmpdir = str(tmpdir) 230*e1fe3e4aSElliott Hughes testDocPath = os.path.join(tmpdir, "testUnicodes.designspace") 231*e1fe3e4aSElliott Hughes testDocPath2 = os.path.join(tmpdir, "testUnicodes_roundtrip.designspace") 232*e1fe3e4aSElliott Hughes masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo") 233*e1fe3e4aSElliott Hughes masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo") 234*e1fe3e4aSElliott Hughes instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo") 235*e1fe3e4aSElliott Hughes instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo") 236*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 237*e1fe3e4aSElliott Hughes doc.formatVersion = "4.1" # This test about instance glyphs is deprecated in v5 238*e1fe3e4aSElliott Hughes # add master 1 239*e1fe3e4aSElliott Hughes s1 = SourceDescriptor() 240*e1fe3e4aSElliott Hughes s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath)) 241*e1fe3e4aSElliott Hughes s1.name = "master.ufo1" 242*e1fe3e4aSElliott Hughes s1.copyInfo = True 243*e1fe3e4aSElliott Hughes s1.location = dict(weight=0) 244*e1fe3e4aSElliott Hughes doc.addSource(s1) 245*e1fe3e4aSElliott Hughes # add master 2 246*e1fe3e4aSElliott Hughes s2 = SourceDescriptor() 247*e1fe3e4aSElliott Hughes s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath)) 248*e1fe3e4aSElliott Hughes s2.name = "master.ufo2" 249*e1fe3e4aSElliott Hughes s2.location = dict(weight=1000) 250*e1fe3e4aSElliott Hughes doc.addSource(s2) 251*e1fe3e4aSElliott Hughes # add instance 1 252*e1fe3e4aSElliott Hughes i1 = InstanceDescriptor() 253*e1fe3e4aSElliott Hughes i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath)) 254*e1fe3e4aSElliott Hughes i1.name = "instance.ufo1" 255*e1fe3e4aSElliott Hughes i1.location = dict(weight=500) 256*e1fe3e4aSElliott Hughes glyphData = dict(name="arrow", mute=True, unicodes=[100, 200, 300]) 257*e1fe3e4aSElliott Hughes i1.glyphs["arrow"] = glyphData 258*e1fe3e4aSElliott Hughes doc.addInstance(i1) 259*e1fe3e4aSElliott Hughes # now we have sources and instances, but no axes yet. 260*e1fe3e4aSElliott Hughes doc.axes = [] # clear the axes 261*e1fe3e4aSElliott Hughes # write some axes 262*e1fe3e4aSElliott Hughes a1 = AxisDescriptor() 263*e1fe3e4aSElliott Hughes a1.minimum = 0 264*e1fe3e4aSElliott Hughes a1.maximum = 1000 265*e1fe3e4aSElliott Hughes a1.default = 0 266*e1fe3e4aSElliott Hughes a1.name = "weight" 267*e1fe3e4aSElliott Hughes a1.tag = "wght" 268*e1fe3e4aSElliott Hughes doc.addAxis(a1) 269*e1fe3e4aSElliott Hughes # write the document 270*e1fe3e4aSElliott Hughes doc.write(testDocPath) 271*e1fe3e4aSElliott Hughes assert os.path.exists(testDocPath) 272*e1fe3e4aSElliott Hughes # import it again 273*e1fe3e4aSElliott Hughes new = DesignSpaceDocument() 274*e1fe3e4aSElliott Hughes new.read(testDocPath) 275*e1fe3e4aSElliott Hughes new.write(testDocPath2) 276*e1fe3e4aSElliott Hughes # compare the file contents 277*e1fe3e4aSElliott Hughes with open(testDocPath, "r", encoding="utf-8") as f1: 278*e1fe3e4aSElliott Hughes t1 = f1.read() 279*e1fe3e4aSElliott Hughes with open(testDocPath2, "r", encoding="utf-8") as f2: 280*e1fe3e4aSElliott Hughes t2 = f2.read() 281*e1fe3e4aSElliott Hughes assert t1 == t2 282*e1fe3e4aSElliott Hughes # check the unicode values read from the document 283*e1fe3e4aSElliott Hughes assert new.instances[0].glyphs["arrow"]["unicodes"] == [100, 200, 300] 284*e1fe3e4aSElliott Hughes 285*e1fe3e4aSElliott Hughes 286*e1fe3e4aSElliott Hughesdef test_localisedNames(tmpdir): 287*e1fe3e4aSElliott Hughes tmpdir = str(tmpdir) 288*e1fe3e4aSElliott Hughes testDocPath = os.path.join(tmpdir, "testLocalisedNames.designspace") 289*e1fe3e4aSElliott Hughes testDocPath2 = os.path.join(tmpdir, "testLocalisedNames_roundtrip.designspace") 290*e1fe3e4aSElliott Hughes masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo") 291*e1fe3e4aSElliott Hughes masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo") 292*e1fe3e4aSElliott Hughes instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo") 293*e1fe3e4aSElliott Hughes instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo") 294*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 295*e1fe3e4aSElliott Hughes # add master 1 296*e1fe3e4aSElliott Hughes s1 = SourceDescriptor() 297*e1fe3e4aSElliott Hughes s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath)) 298*e1fe3e4aSElliott Hughes s1.name = "master.ufo1" 299*e1fe3e4aSElliott Hughes s1.copyInfo = True 300*e1fe3e4aSElliott Hughes s1.location = dict(weight=0) 301*e1fe3e4aSElliott Hughes doc.addSource(s1) 302*e1fe3e4aSElliott Hughes # add master 2 303*e1fe3e4aSElliott Hughes s2 = SourceDescriptor() 304*e1fe3e4aSElliott Hughes s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath)) 305*e1fe3e4aSElliott Hughes s2.name = "master.ufo2" 306*e1fe3e4aSElliott Hughes s2.location = dict(weight=1000) 307*e1fe3e4aSElliott Hughes doc.addSource(s2) 308*e1fe3e4aSElliott Hughes # add instance 1 309*e1fe3e4aSElliott Hughes i1 = InstanceDescriptor() 310*e1fe3e4aSElliott Hughes i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath)) 311*e1fe3e4aSElliott Hughes i1.familyName = "Montserrat" 312*e1fe3e4aSElliott Hughes i1.styleName = "SemiBold" 313*e1fe3e4aSElliott Hughes i1.styleMapFamilyName = "Montserrat SemiBold" 314*e1fe3e4aSElliott Hughes i1.styleMapStyleName = "Regular" 315*e1fe3e4aSElliott Hughes i1.setFamilyName("Montserrat", "fr") 316*e1fe3e4aSElliott Hughes i1.setFamilyName("モンセラート", "ja") 317*e1fe3e4aSElliott Hughes i1.setStyleName("Demigras", "fr") 318*e1fe3e4aSElliott Hughes i1.setStyleName("半ば", "ja") 319*e1fe3e4aSElliott Hughes i1.setStyleMapStyleName("Standard", "de") 320*e1fe3e4aSElliott Hughes i1.setStyleMapFamilyName("Montserrat Halbfett", "de") 321*e1fe3e4aSElliott Hughes i1.setStyleMapFamilyName("モンセラート SemiBold", "ja") 322*e1fe3e4aSElliott Hughes i1.name = "instance.ufo1" 323*e1fe3e4aSElliott Hughes i1.location = dict( 324*e1fe3e4aSElliott Hughes weight=500, spooky=666 325*e1fe3e4aSElliott Hughes ) # this adds a dimension that is not defined. 326*e1fe3e4aSElliott Hughes i1.postScriptFontName = "InstancePostscriptName" 327*e1fe3e4aSElliott Hughes glyphData = dict(name="arrow", mute=True, unicodes=[0x123]) 328*e1fe3e4aSElliott Hughes i1.glyphs["arrow"] = glyphData 329*e1fe3e4aSElliott Hughes doc.addInstance(i1) 330*e1fe3e4aSElliott Hughes # now we have sources and instances, but no axes yet. 331*e1fe3e4aSElliott Hughes doc.axes = [] # clear the axes 332*e1fe3e4aSElliott Hughes # write some axes 333*e1fe3e4aSElliott Hughes a1 = AxisDescriptor() 334*e1fe3e4aSElliott Hughes a1.minimum = 0 335*e1fe3e4aSElliott Hughes a1.maximum = 1000 336*e1fe3e4aSElliott Hughes a1.default = 0 337*e1fe3e4aSElliott Hughes a1.name = "weight" 338*e1fe3e4aSElliott Hughes a1.tag = "wght" 339*e1fe3e4aSElliott Hughes # note: just to test the element language, not an actual label name recommendations. 340*e1fe3e4aSElliott Hughes a1.labelNames["fa-IR"] = "قطر" 341*e1fe3e4aSElliott Hughes a1.labelNames["en"] = "Wéíght" 342*e1fe3e4aSElliott Hughes doc.addAxis(a1) 343*e1fe3e4aSElliott Hughes a2 = AxisDescriptor() 344*e1fe3e4aSElliott Hughes a2.minimum = 0 345*e1fe3e4aSElliott Hughes a2.maximum = 1000 346*e1fe3e4aSElliott Hughes a2.default = 0 347*e1fe3e4aSElliott Hughes a2.name = "width" 348*e1fe3e4aSElliott Hughes a2.tag = "wdth" 349*e1fe3e4aSElliott Hughes a2.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)] 350*e1fe3e4aSElliott Hughes a2.labelNames["fr"] = "Poids" 351*e1fe3e4aSElliott Hughes doc.addAxis(a2) 352*e1fe3e4aSElliott Hughes # add an axis that is not part of any location to see if that works 353*e1fe3e4aSElliott Hughes a3 = AxisDescriptor() 354*e1fe3e4aSElliott Hughes a3.minimum = 333 355*e1fe3e4aSElliott Hughes a3.maximum = 666 356*e1fe3e4aSElliott Hughes a3.default = 444 357*e1fe3e4aSElliott Hughes a3.name = "spooky" 358*e1fe3e4aSElliott Hughes a3.tag = "spok" 359*e1fe3e4aSElliott Hughes a3.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)] 360*e1fe3e4aSElliott Hughes # doc.addAxis(a3) # uncomment this line to test the effects of default axes values 361*e1fe3e4aSElliott Hughes # write some rules 362*e1fe3e4aSElliott Hughes r1 = RuleDescriptor() 363*e1fe3e4aSElliott Hughes r1.name = "named.rule.1" 364*e1fe3e4aSElliott Hughes r1.conditionSets.append( 365*e1fe3e4aSElliott Hughes [ 366*e1fe3e4aSElliott Hughes dict(name="weight", minimum=200, maximum=500), 367*e1fe3e4aSElliott Hughes dict(name="width", minimum=0, maximum=150), 368*e1fe3e4aSElliott Hughes ] 369*e1fe3e4aSElliott Hughes ) 370*e1fe3e4aSElliott Hughes r1.subs.append(("a", "a.alt")) 371*e1fe3e4aSElliott Hughes doc.addRule(r1) 372*e1fe3e4aSElliott Hughes # write the document 373*e1fe3e4aSElliott Hughes doc.write(testDocPath) 374*e1fe3e4aSElliott Hughes assert os.path.exists(testDocPath) 375*e1fe3e4aSElliott Hughes # import it again 376*e1fe3e4aSElliott Hughes new = DesignSpaceDocument() 377*e1fe3e4aSElliott Hughes new.read(testDocPath) 378*e1fe3e4aSElliott Hughes new.write(testDocPath2) 379*e1fe3e4aSElliott Hughes with open(testDocPath, "r", encoding="utf-8") as f1: 380*e1fe3e4aSElliott Hughes t1 = f1.read() 381*e1fe3e4aSElliott Hughes with open(testDocPath2, "r", encoding="utf-8") as f2: 382*e1fe3e4aSElliott Hughes t2 = f2.read() 383*e1fe3e4aSElliott Hughes assert t1 == t2 384*e1fe3e4aSElliott Hughes 385*e1fe3e4aSElliott Hughes 386*e1fe3e4aSElliott Hughesdef test_handleNoAxes(tmpdir): 387*e1fe3e4aSElliott Hughes tmpdir = str(tmpdir) 388*e1fe3e4aSElliott Hughes # test what happens if the designspacedocument has no axes element. 389*e1fe3e4aSElliott Hughes testDocPath = os.path.join(tmpdir, "testNoAxes_source.designspace") 390*e1fe3e4aSElliott Hughes testDocPath2 = os.path.join(tmpdir, "testNoAxes_recontructed.designspace") 391*e1fe3e4aSElliott Hughes masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo") 392*e1fe3e4aSElliott Hughes masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo") 393*e1fe3e4aSElliott Hughes instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo") 394*e1fe3e4aSElliott Hughes instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo") 395*e1fe3e4aSElliott Hughes 396*e1fe3e4aSElliott Hughes # Case 1: No axes element in the document, but there are sources and instances 397*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 398*e1fe3e4aSElliott Hughes 399*e1fe3e4aSElliott Hughes for name, value in [("One", 1), ("Two", 2), ("Three", 3)]: 400*e1fe3e4aSElliott Hughes a = AxisDescriptor() 401*e1fe3e4aSElliott Hughes a.minimum = 0 402*e1fe3e4aSElliott Hughes a.maximum = 1000 403*e1fe3e4aSElliott Hughes a.default = 0 404*e1fe3e4aSElliott Hughes a.name = "axisName%s" % (name) 405*e1fe3e4aSElliott Hughes a.tag = "ax_%d" % (value) 406*e1fe3e4aSElliott Hughes doc.addAxis(a) 407*e1fe3e4aSElliott Hughes 408*e1fe3e4aSElliott Hughes # add master 1 409*e1fe3e4aSElliott Hughes s1 = SourceDescriptor() 410*e1fe3e4aSElliott Hughes s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath)) 411*e1fe3e4aSElliott Hughes s1.name = "master.ufo1" 412*e1fe3e4aSElliott Hughes s1.copyLib = True 413*e1fe3e4aSElliott Hughes s1.copyInfo = True 414*e1fe3e4aSElliott Hughes s1.copyFeatures = True 415*e1fe3e4aSElliott Hughes s1.location = dict(axisNameOne=-1000, axisNameTwo=0, axisNameThree=1000) 416*e1fe3e4aSElliott Hughes s1.familyName = "MasterFamilyName" 417*e1fe3e4aSElliott Hughes s1.styleName = "MasterStyleNameOne" 418*e1fe3e4aSElliott Hughes doc.addSource(s1) 419*e1fe3e4aSElliott Hughes 420*e1fe3e4aSElliott Hughes # add master 2 421*e1fe3e4aSElliott Hughes s2 = SourceDescriptor() 422*e1fe3e4aSElliott Hughes s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath)) 423*e1fe3e4aSElliott Hughes s2.name = "master.ufo1" 424*e1fe3e4aSElliott Hughes s2.copyLib = False 425*e1fe3e4aSElliott Hughes s2.copyInfo = False 426*e1fe3e4aSElliott Hughes s2.copyFeatures = False 427*e1fe3e4aSElliott Hughes s2.location = dict(axisNameOne=1000, axisNameTwo=1000, axisNameThree=0) 428*e1fe3e4aSElliott Hughes s2.familyName = "MasterFamilyName" 429*e1fe3e4aSElliott Hughes s2.styleName = "MasterStyleNameTwo" 430*e1fe3e4aSElliott Hughes doc.addSource(s2) 431*e1fe3e4aSElliott Hughes 432*e1fe3e4aSElliott Hughes # add instance 1 433*e1fe3e4aSElliott Hughes i1 = InstanceDescriptor() 434*e1fe3e4aSElliott Hughes i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath)) 435*e1fe3e4aSElliott Hughes i1.familyName = "InstanceFamilyName" 436*e1fe3e4aSElliott Hughes i1.styleName = "InstanceStyleName" 437*e1fe3e4aSElliott Hughes i1.name = "instance.ufo1" 438*e1fe3e4aSElliott Hughes i1.location = dict(axisNameOne=(-1000, 500), axisNameTwo=100) 439*e1fe3e4aSElliott Hughes i1.postScriptFontName = "InstancePostscriptName" 440*e1fe3e4aSElliott Hughes i1.styleMapFamilyName = "InstanceStyleMapFamilyName" 441*e1fe3e4aSElliott Hughes i1.styleMapStyleName = "InstanceStyleMapStyleName" 442*e1fe3e4aSElliott Hughes doc.addInstance(i1) 443*e1fe3e4aSElliott Hughes 444*e1fe3e4aSElliott Hughes doc.write(testDocPath) 445*e1fe3e4aSElliott Hughes verify = DesignSpaceDocument() 446*e1fe3e4aSElliott Hughes verify.read(testDocPath) 447*e1fe3e4aSElliott Hughes verify.write(testDocPath2) 448*e1fe3e4aSElliott Hughes 449*e1fe3e4aSElliott Hughes 450*e1fe3e4aSElliott Hughesdef test_pathNameResolve(tmpdir): 451*e1fe3e4aSElliott Hughes tmpdir = str(tmpdir) 452*e1fe3e4aSElliott Hughes # test how descriptor.path and descriptor.filename are resolved 453*e1fe3e4aSElliott Hughes testDocPath1 = os.path.join(tmpdir, "testPathName_case1.designspace") 454*e1fe3e4aSElliott Hughes testDocPath2 = os.path.join(tmpdir, "testPathName_case2.designspace") 455*e1fe3e4aSElliott Hughes testDocPath3 = os.path.join(tmpdir, "testPathName_case3.designspace") 456*e1fe3e4aSElliott Hughes testDocPath4 = os.path.join(tmpdir, "testPathName_case4.designspace") 457*e1fe3e4aSElliott Hughes testDocPath5 = os.path.join(tmpdir, "testPathName_case5.designspace") 458*e1fe3e4aSElliott Hughes testDocPath6 = os.path.join(tmpdir, "testPathName_case6.designspace") 459*e1fe3e4aSElliott Hughes masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo") 460*e1fe3e4aSElliott Hughes masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo") 461*e1fe3e4aSElliott Hughes instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo") 462*e1fe3e4aSElliott Hughes instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo") 463*e1fe3e4aSElliott Hughes 464*e1fe3e4aSElliott Hughes a1 = AxisDescriptor() 465*e1fe3e4aSElliott Hughes a1.tag = "TAGA" 466*e1fe3e4aSElliott Hughes a1.name = "axisName_a" 467*e1fe3e4aSElliott Hughes a1.minimum = 0 468*e1fe3e4aSElliott Hughes a1.maximum = 1000 469*e1fe3e4aSElliott Hughes a1.default = 0 470*e1fe3e4aSElliott Hughes 471*e1fe3e4aSElliott Hughes # Case 1: filename and path are both empty. Nothing to calculate, nothing to put in the file. 472*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 473*e1fe3e4aSElliott Hughes doc.addAxis(a1) 474*e1fe3e4aSElliott Hughes s = SourceDescriptor() 475*e1fe3e4aSElliott Hughes s.filename = None 476*e1fe3e4aSElliott Hughes s.path = None 477*e1fe3e4aSElliott Hughes s.copyInfo = True 478*e1fe3e4aSElliott Hughes s.location = dict(weight=0) 479*e1fe3e4aSElliott Hughes s.familyName = "MasterFamilyName" 480*e1fe3e4aSElliott Hughes s.styleName = "MasterStyleNameOne" 481*e1fe3e4aSElliott Hughes doc.addSource(s) 482*e1fe3e4aSElliott Hughes doc.write(testDocPath1) 483*e1fe3e4aSElliott Hughes verify = DesignSpaceDocument() 484*e1fe3e4aSElliott Hughes verify.read(testDocPath1) 485*e1fe3e4aSElliott Hughes assert verify.sources[0].filename == None 486*e1fe3e4aSElliott Hughes assert verify.sources[0].path == None 487*e1fe3e4aSElliott Hughes 488*e1fe3e4aSElliott Hughes # Case 2: filename is empty, path points somewhere: calculate a new filename. 489*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 490*e1fe3e4aSElliott Hughes doc.addAxis(a1) 491*e1fe3e4aSElliott Hughes s = SourceDescriptor() 492*e1fe3e4aSElliott Hughes s.filename = None 493*e1fe3e4aSElliott Hughes s.path = masterPath1 494*e1fe3e4aSElliott Hughes s.copyInfo = True 495*e1fe3e4aSElliott Hughes s.location = dict(weight=0) 496*e1fe3e4aSElliott Hughes s.familyName = "MasterFamilyName" 497*e1fe3e4aSElliott Hughes s.styleName = "MasterStyleNameOne" 498*e1fe3e4aSElliott Hughes doc.addSource(s) 499*e1fe3e4aSElliott Hughes doc.write(testDocPath2) 500*e1fe3e4aSElliott Hughes verify = DesignSpaceDocument() 501*e1fe3e4aSElliott Hughes verify.read(testDocPath2) 502*e1fe3e4aSElliott Hughes assert verify.sources[0].filename == "masters/masterTest1.ufo" 503*e1fe3e4aSElliott Hughes assert verify.sources[0].path == posix(masterPath1) 504*e1fe3e4aSElliott Hughes 505*e1fe3e4aSElliott Hughes # Case 3: the filename is set, the path is None. 506*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 507*e1fe3e4aSElliott Hughes doc.addAxis(a1) 508*e1fe3e4aSElliott Hughes s = SourceDescriptor() 509*e1fe3e4aSElliott Hughes s.filename = "../somewhere/over/the/rainbow.ufo" 510*e1fe3e4aSElliott Hughes s.path = None 511*e1fe3e4aSElliott Hughes s.copyInfo = True 512*e1fe3e4aSElliott Hughes s.location = dict(weight=0) 513*e1fe3e4aSElliott Hughes s.familyName = "MasterFamilyName" 514*e1fe3e4aSElliott Hughes s.styleName = "MasterStyleNameOne" 515*e1fe3e4aSElliott Hughes doc.addSource(s) 516*e1fe3e4aSElliott Hughes doc.write(testDocPath3) 517*e1fe3e4aSElliott Hughes verify = DesignSpaceDocument() 518*e1fe3e4aSElliott Hughes verify.read(testDocPath3) 519*e1fe3e4aSElliott Hughes assert verify.sources[0].filename == "../somewhere/over/the/rainbow.ufo" 520*e1fe3e4aSElliott Hughes # make the absolute path for filename so we can see if it matches the path 521*e1fe3e4aSElliott Hughes p = os.path.abspath( 522*e1fe3e4aSElliott Hughes os.path.join(os.path.dirname(testDocPath3), verify.sources[0].filename) 523*e1fe3e4aSElliott Hughes ) 524*e1fe3e4aSElliott Hughes assert verify.sources[0].path == posix(p) 525*e1fe3e4aSElliott Hughes 526*e1fe3e4aSElliott Hughes # Case 4: the filename points to one file, the path points to another. The path takes precedence. 527*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 528*e1fe3e4aSElliott Hughes doc.addAxis(a1) 529*e1fe3e4aSElliott Hughes s = SourceDescriptor() 530*e1fe3e4aSElliott Hughes s.filename = "../somewhere/over/the/rainbow.ufo" 531*e1fe3e4aSElliott Hughes s.path = masterPath1 532*e1fe3e4aSElliott Hughes s.copyInfo = True 533*e1fe3e4aSElliott Hughes s.location = dict(weight=0) 534*e1fe3e4aSElliott Hughes s.familyName = "MasterFamilyName" 535*e1fe3e4aSElliott Hughes s.styleName = "MasterStyleNameOne" 536*e1fe3e4aSElliott Hughes doc.addSource(s) 537*e1fe3e4aSElliott Hughes doc.write(testDocPath4) 538*e1fe3e4aSElliott Hughes verify = DesignSpaceDocument() 539*e1fe3e4aSElliott Hughes verify.read(testDocPath4) 540*e1fe3e4aSElliott Hughes assert verify.sources[0].filename == "masters/masterTest1.ufo" 541*e1fe3e4aSElliott Hughes 542*e1fe3e4aSElliott Hughes # Case 5: the filename is None, path has a value, update the filename 543*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 544*e1fe3e4aSElliott Hughes doc.addAxis(a1) 545*e1fe3e4aSElliott Hughes s = SourceDescriptor() 546*e1fe3e4aSElliott Hughes s.filename = None 547*e1fe3e4aSElliott Hughes s.path = masterPath1 548*e1fe3e4aSElliott Hughes s.copyInfo = True 549*e1fe3e4aSElliott Hughes s.location = dict(weight=0) 550*e1fe3e4aSElliott Hughes s.familyName = "MasterFamilyName" 551*e1fe3e4aSElliott Hughes s.styleName = "MasterStyleNameOne" 552*e1fe3e4aSElliott Hughes doc.addSource(s) 553*e1fe3e4aSElliott Hughes doc.write(testDocPath5) # so that the document has a path 554*e1fe3e4aSElliott Hughes doc.updateFilenameFromPath() 555*e1fe3e4aSElliott Hughes assert doc.sources[0].filename == "masters/masterTest1.ufo" 556*e1fe3e4aSElliott Hughes 557*e1fe3e4aSElliott Hughes # Case 6: the filename has a value, path has a value, update the filenames with force 558*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 559*e1fe3e4aSElliott Hughes doc.addAxis(a1) 560*e1fe3e4aSElliott Hughes s = SourceDescriptor() 561*e1fe3e4aSElliott Hughes s.filename = "../somewhere/over/the/rainbow.ufo" 562*e1fe3e4aSElliott Hughes s.path = masterPath1 563*e1fe3e4aSElliott Hughes s.copyInfo = True 564*e1fe3e4aSElliott Hughes s.location = dict(weight=0) 565*e1fe3e4aSElliott Hughes s.familyName = "MasterFamilyName" 566*e1fe3e4aSElliott Hughes s.styleName = "MasterStyleNameOne" 567*e1fe3e4aSElliott Hughes doc.write(testDocPath5) # so that the document has a path 568*e1fe3e4aSElliott Hughes doc.addSource(s) 569*e1fe3e4aSElliott Hughes assert doc.sources[0].filename == "../somewhere/over/the/rainbow.ufo" 570*e1fe3e4aSElliott Hughes doc.updateFilenameFromPath(force=True) 571*e1fe3e4aSElliott Hughes assert doc.sources[0].filename == "masters/masterTest1.ufo" 572*e1fe3e4aSElliott Hughes 573*e1fe3e4aSElliott Hughes 574*e1fe3e4aSElliott Hughesdef test_normalise1(): 575*e1fe3e4aSElliott Hughes # normalisation of anisotropic locations, clipping 576*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 577*e1fe3e4aSElliott Hughes # write some axes 578*e1fe3e4aSElliott Hughes a1 = AxisDescriptor() 579*e1fe3e4aSElliott Hughes a1.minimum = -1000 580*e1fe3e4aSElliott Hughes a1.maximum = 1000 581*e1fe3e4aSElliott Hughes a1.default = 0 582*e1fe3e4aSElliott Hughes a1.name = "axisName_a" 583*e1fe3e4aSElliott Hughes a1.tag = "TAGA" 584*e1fe3e4aSElliott Hughes doc.addAxis(a1) 585*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_a=0)) == {"axisName_a": 0.0} 586*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_a=1000)) == {"axisName_a": 1.0} 587*e1fe3e4aSElliott Hughes # clipping beyond max values: 588*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_a=1001)) == {"axisName_a": 1.0} 589*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_a=500)) == {"axisName_a": 0.5} 590*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_a=-1000)) == {"axisName_a": -1.0} 591*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_a=-1001)) == {"axisName_a": -1.0} 592*e1fe3e4aSElliott Hughes # anisotropic coordinates normalise to isotropic 593*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_a=(1000, -1000))) == {"axisName_a": 1.0} 594*e1fe3e4aSElliott Hughes doc.normalize() 595*e1fe3e4aSElliott Hughes r = [] 596*e1fe3e4aSElliott Hughes for axis in doc.axes: 597*e1fe3e4aSElliott Hughes r.append((axis.name, axis.minimum, axis.default, axis.maximum)) 598*e1fe3e4aSElliott Hughes r.sort() 599*e1fe3e4aSElliott Hughes assert r == [("axisName_a", -1.0, 0.0, 1.0)] 600*e1fe3e4aSElliott Hughes 601*e1fe3e4aSElliott Hughes 602*e1fe3e4aSElliott Hughesdef test_normalise2(): 603*e1fe3e4aSElliott Hughes # normalisation with minimum > 0 604*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 605*e1fe3e4aSElliott Hughes # write some axes 606*e1fe3e4aSElliott Hughes a2 = AxisDescriptor() 607*e1fe3e4aSElliott Hughes a2.minimum = 100 608*e1fe3e4aSElliott Hughes a2.maximum = 1000 609*e1fe3e4aSElliott Hughes a2.default = 100 610*e1fe3e4aSElliott Hughes a2.name = "axisName_b" 611*e1fe3e4aSElliott Hughes doc.addAxis(a2) 612*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_b=0)) == {"axisName_b": 0.0} 613*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_b=1000)) == {"axisName_b": 1.0} 614*e1fe3e4aSElliott Hughes # clipping beyond max values: 615*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_b=1001)) == {"axisName_b": 1.0} 616*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_b=500)) == { 617*e1fe3e4aSElliott Hughes "axisName_b": 0.4444444444444444 618*e1fe3e4aSElliott Hughes } 619*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_b=-1000)) == {"axisName_b": 0.0} 620*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_b=-1001)) == {"axisName_b": 0.0} 621*e1fe3e4aSElliott Hughes # anisotropic coordinates normalise to isotropic 622*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_b=(1000, -1000))) == {"axisName_b": 1.0} 623*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(axisName_b=1001)) == {"axisName_b": 1.0} 624*e1fe3e4aSElliott Hughes doc.normalize() 625*e1fe3e4aSElliott Hughes r = [] 626*e1fe3e4aSElliott Hughes for axis in doc.axes: 627*e1fe3e4aSElliott Hughes r.append((axis.name, axis.minimum, axis.default, axis.maximum)) 628*e1fe3e4aSElliott Hughes r.sort() 629*e1fe3e4aSElliott Hughes assert r == [("axisName_b", 0.0, 0.0, 1.0)] 630*e1fe3e4aSElliott Hughes 631*e1fe3e4aSElliott Hughes 632*e1fe3e4aSElliott Hughesdef test_normalise3(): 633*e1fe3e4aSElliott Hughes # normalisation of negative values, with default == maximum 634*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 635*e1fe3e4aSElliott Hughes # write some axes 636*e1fe3e4aSElliott Hughes a3 = AxisDescriptor() 637*e1fe3e4aSElliott Hughes a3.minimum = -1000 638*e1fe3e4aSElliott Hughes a3.maximum = 0 639*e1fe3e4aSElliott Hughes a3.default = 0 640*e1fe3e4aSElliott Hughes a3.name = "ccc" 641*e1fe3e4aSElliott Hughes doc.addAxis(a3) 642*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(ccc=0)) == {"ccc": 0.0} 643*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(ccc=1)) == {"ccc": 0.0} 644*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(ccc=-1000)) == {"ccc": -1.0} 645*e1fe3e4aSElliott Hughes assert doc.normalizeLocation(dict(ccc=-1001)) == {"ccc": -1.0} 646*e1fe3e4aSElliott Hughes doc.normalize() 647*e1fe3e4aSElliott Hughes r = [] 648*e1fe3e4aSElliott Hughes for axis in doc.axes: 649*e1fe3e4aSElliott Hughes r.append((axis.name, axis.minimum, axis.default, axis.maximum)) 650*e1fe3e4aSElliott Hughes r.sort() 651*e1fe3e4aSElliott Hughes assert r == [("ccc", -1.0, 0.0, 0.0)] 652*e1fe3e4aSElliott Hughes 653*e1fe3e4aSElliott Hughes 654*e1fe3e4aSElliott Hughesdef test_normalise4(): 655*e1fe3e4aSElliott Hughes # normalisation with a map 656*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 657*e1fe3e4aSElliott Hughes # write some axes 658*e1fe3e4aSElliott Hughes a4 = AxisDescriptor() 659*e1fe3e4aSElliott Hughes a4.minimum = 0 660*e1fe3e4aSElliott Hughes a4.maximum = 1000 661*e1fe3e4aSElliott Hughes a4.default = 0 662*e1fe3e4aSElliott Hughes a4.name = "ddd" 663*e1fe3e4aSElliott Hughes a4.map = [(0, 100), (300, 500), (600, 500), (1000, 900)] 664*e1fe3e4aSElliott Hughes doc.addAxis(a4) 665*e1fe3e4aSElliott Hughes doc.normalize() 666*e1fe3e4aSElliott Hughes r = [] 667*e1fe3e4aSElliott Hughes for axis in doc.axes: 668*e1fe3e4aSElliott Hughes r.append((axis.name, axis.map)) 669*e1fe3e4aSElliott Hughes r.sort() 670*e1fe3e4aSElliott Hughes assert r == [("ddd", [(0, 0.0), (300, 0.5), (600, 0.5), (1000, 1.0)])] 671*e1fe3e4aSElliott Hughes 672*e1fe3e4aSElliott Hughes 673*e1fe3e4aSElliott Hughesdef test_axisMapping(): 674*e1fe3e4aSElliott Hughes # note: because designspance lib does not do any actual 675*e1fe3e4aSElliott Hughes # processing of the mapping data, we can only check if there data is there. 676*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 677*e1fe3e4aSElliott Hughes # write some axes 678*e1fe3e4aSElliott Hughes a4 = AxisDescriptor() 679*e1fe3e4aSElliott Hughes a4.minimum = 0 680*e1fe3e4aSElliott Hughes a4.maximum = 1000 681*e1fe3e4aSElliott Hughes a4.default = 0 682*e1fe3e4aSElliott Hughes a4.name = "ddd" 683*e1fe3e4aSElliott Hughes a4.map = [(0, 100), (300, 500), (600, 500), (1000, 900)] 684*e1fe3e4aSElliott Hughes doc.addAxis(a4) 685*e1fe3e4aSElliott Hughes doc.normalize() 686*e1fe3e4aSElliott Hughes r = [] 687*e1fe3e4aSElliott Hughes for axis in doc.axes: 688*e1fe3e4aSElliott Hughes r.append((axis.name, axis.map)) 689*e1fe3e4aSElliott Hughes r.sort() 690*e1fe3e4aSElliott Hughes assert r == [("ddd", [(0, 0.0), (300, 0.5), (600, 0.5), (1000, 1.0)])] 691*e1fe3e4aSElliott Hughes 692*e1fe3e4aSElliott Hughes 693*e1fe3e4aSElliott Hughesdef test_axisMappingsRoundtrip(tmpdir): 694*e1fe3e4aSElliott Hughes # tests of axisMappings in a document, roundtripping. 695*e1fe3e4aSElliott Hughes 696*e1fe3e4aSElliott Hughes tmpdir = str(tmpdir) 697*e1fe3e4aSElliott Hughes srcDocPath = (Path(__file__) / "../data/test_avar2.designspace").resolve() 698*e1fe3e4aSElliott Hughes testDocPath = os.path.join(tmpdir, "test_avar2.designspace") 699*e1fe3e4aSElliott Hughes shutil.copy(srcDocPath, testDocPath) 700*e1fe3e4aSElliott Hughes testDocPath2 = os.path.join(tmpdir, "test_avar2_roundtrip.designspace") 701*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 702*e1fe3e4aSElliott Hughes doc.read(testDocPath) 703*e1fe3e4aSElliott Hughes assert doc.axisMappings 704*e1fe3e4aSElliott Hughes assert len(doc.axisMappings) == 2 705*e1fe3e4aSElliott Hughes assert doc.axisMappings[0].inputLocation == {"Justify": -100.0, "Width": 100.0} 706*e1fe3e4aSElliott Hughes 707*e1fe3e4aSElliott Hughes # This is a bit of a hack, but it's the only way to make sure 708*e1fe3e4aSElliott Hughes # that the save works on Windows if the tempdir and the data 709*e1fe3e4aSElliott Hughes # dir are on different drives. 710*e1fe3e4aSElliott Hughes for descriptor in doc.sources + doc.instances: 711*e1fe3e4aSElliott Hughes descriptor.path = None 712*e1fe3e4aSElliott Hughes 713*e1fe3e4aSElliott Hughes doc.write(testDocPath2) 714*e1fe3e4aSElliott Hughes # verify these results 715*e1fe3e4aSElliott Hughes doc2 = DesignSpaceDocument() 716*e1fe3e4aSElliott Hughes doc2.read(testDocPath2) 717*e1fe3e4aSElliott Hughes assert [mapping.inputLocation for mapping in doc.axisMappings] == [ 718*e1fe3e4aSElliott Hughes mapping.inputLocation for mapping in doc2.axisMappings 719*e1fe3e4aSElliott Hughes ] 720*e1fe3e4aSElliott Hughes assert [mapping.outputLocation for mapping in doc.axisMappings] == [ 721*e1fe3e4aSElliott Hughes mapping.outputLocation for mapping in doc2.axisMappings 722*e1fe3e4aSElliott Hughes ] 723*e1fe3e4aSElliott Hughes assert [mapping.description for mapping in doc.axisMappings] == [ 724*e1fe3e4aSElliott Hughes mapping.description for mapping in doc2.axisMappings 725*e1fe3e4aSElliott Hughes ] 726*e1fe3e4aSElliott Hughes assert [mapping.groupDescription for mapping in doc.axisMappings] == [ 727*e1fe3e4aSElliott Hughes mapping.groupDescription for mapping in doc2.axisMappings 728*e1fe3e4aSElliott Hughes ] 729*e1fe3e4aSElliott Hughes 730*e1fe3e4aSElliott Hughes 731*e1fe3e4aSElliott Hughesdef test_rulesConditions(tmpdir): 732*e1fe3e4aSElliott Hughes # tests of rules, conditionsets and conditions 733*e1fe3e4aSElliott Hughes r1 = RuleDescriptor() 734*e1fe3e4aSElliott Hughes r1.name = "named.rule.1" 735*e1fe3e4aSElliott Hughes r1.conditionSets.append( 736*e1fe3e4aSElliott Hughes [ 737*e1fe3e4aSElliott Hughes dict(name="axisName_a", minimum=0, maximum=1000), 738*e1fe3e4aSElliott Hughes dict(name="axisName_b", minimum=0, maximum=3000), 739*e1fe3e4aSElliott Hughes ] 740*e1fe3e4aSElliott Hughes ) 741*e1fe3e4aSElliott Hughes r1.subs.append(("a", "a.alt")) 742*e1fe3e4aSElliott Hughes 743*e1fe3e4aSElliott Hughes assert evaluateRule(r1, dict(axisName_a=500, axisName_b=0)) == True 744*e1fe3e4aSElliott Hughes assert evaluateRule(r1, dict(axisName_a=0, axisName_b=0)) == True 745*e1fe3e4aSElliott Hughes assert evaluateRule(r1, dict(axisName_a=1000, axisName_b=0)) == True 746*e1fe3e4aSElliott Hughes assert evaluateRule(r1, dict(axisName_a=1000, axisName_b=-100)) == False 747*e1fe3e4aSElliott Hughes assert evaluateRule(r1, dict(axisName_a=1000.0001, axisName_b=0)) == False 748*e1fe3e4aSElliott Hughes assert evaluateRule(r1, dict(axisName_a=-0.0001, axisName_b=0)) == False 749*e1fe3e4aSElliott Hughes assert evaluateRule(r1, dict(axisName_a=-100, axisName_b=0)) == False 750*e1fe3e4aSElliott Hughes assert processRules([r1], dict(axisName_a=500, axisName_b=0), ["a", "b", "c"]) == [ 751*e1fe3e4aSElliott Hughes "a.alt", 752*e1fe3e4aSElliott Hughes "b", 753*e1fe3e4aSElliott Hughes "c", 754*e1fe3e4aSElliott Hughes ] 755*e1fe3e4aSElliott Hughes assert processRules( 756*e1fe3e4aSElliott Hughes [r1], dict(axisName_a=500, axisName_b=0), ["a.alt", "b", "c"] 757*e1fe3e4aSElliott Hughes ) == ["a.alt", "b", "c"] 758*e1fe3e4aSElliott Hughes assert processRules([r1], dict(axisName_a=2000, axisName_b=0), ["a", "b", "c"]) == [ 759*e1fe3e4aSElliott Hughes "a", 760*e1fe3e4aSElliott Hughes "b", 761*e1fe3e4aSElliott Hughes "c", 762*e1fe3e4aSElliott Hughes ] 763*e1fe3e4aSElliott Hughes 764*e1fe3e4aSElliott Hughes # rule with only a maximum 765*e1fe3e4aSElliott Hughes r2 = RuleDescriptor() 766*e1fe3e4aSElliott Hughes r2.name = "named.rule.2" 767*e1fe3e4aSElliott Hughes r2.conditionSets.append([dict(name="axisName_a", maximum=500)]) 768*e1fe3e4aSElliott Hughes r2.subs.append(("b", "b.alt")) 769*e1fe3e4aSElliott Hughes 770*e1fe3e4aSElliott Hughes assert evaluateRule(r2, dict(axisName_a=0)) == True 771*e1fe3e4aSElliott Hughes assert evaluateRule(r2, dict(axisName_a=-500)) == True 772*e1fe3e4aSElliott Hughes assert evaluateRule(r2, dict(axisName_a=1000)) == False 773*e1fe3e4aSElliott Hughes 774*e1fe3e4aSElliott Hughes # rule with only a minimum 775*e1fe3e4aSElliott Hughes r3 = RuleDescriptor() 776*e1fe3e4aSElliott Hughes r3.name = "named.rule.3" 777*e1fe3e4aSElliott Hughes r3.conditionSets.append([dict(name="axisName_a", minimum=500)]) 778*e1fe3e4aSElliott Hughes r3.subs.append(("c", "c.alt")) 779*e1fe3e4aSElliott Hughes 780*e1fe3e4aSElliott Hughes assert evaluateRule(r3, dict(axisName_a=0)) == False 781*e1fe3e4aSElliott Hughes assert evaluateRule(r3, dict(axisName_a=1000)) == True 782*e1fe3e4aSElliott Hughes assert evaluateRule(r3, dict(axisName_a=1000)) == True 783*e1fe3e4aSElliott Hughes 784*e1fe3e4aSElliott Hughes # rule with only a minimum, maximum in separate conditions 785*e1fe3e4aSElliott Hughes r4 = RuleDescriptor() 786*e1fe3e4aSElliott Hughes r4.name = "named.rule.4" 787*e1fe3e4aSElliott Hughes r4.conditionSets.append( 788*e1fe3e4aSElliott Hughes [dict(name="axisName_a", minimum=500), dict(name="axisName_b", maximum=500)] 789*e1fe3e4aSElliott Hughes ) 790*e1fe3e4aSElliott Hughes r4.subs.append(("c", "c.alt")) 791*e1fe3e4aSElliott Hughes 792*e1fe3e4aSElliott Hughes assert evaluateRule(r4, dict(axisName_a=1000, axisName_b=0)) == True 793*e1fe3e4aSElliott Hughes assert evaluateRule(r4, dict(axisName_a=0, axisName_b=0)) == False 794*e1fe3e4aSElliott Hughes assert evaluateRule(r4, dict(axisName_a=1000, axisName_b=1000)) == False 795*e1fe3e4aSElliott Hughes 796*e1fe3e4aSElliott Hughes 797*e1fe3e4aSElliott Hughesdef test_rulesDocument(tmpdir): 798*e1fe3e4aSElliott Hughes # tests of rules in a document, roundtripping. 799*e1fe3e4aSElliott Hughes tmpdir = str(tmpdir) 800*e1fe3e4aSElliott Hughes testDocPath = os.path.join(tmpdir, "testRules.designspace") 801*e1fe3e4aSElliott Hughes testDocPath2 = os.path.join(tmpdir, "testRules_roundtrip.designspace") 802*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 803*e1fe3e4aSElliott Hughes doc.rulesProcessingLast = True 804*e1fe3e4aSElliott Hughes a1 = AxisDescriptor() 805*e1fe3e4aSElliott Hughes a1.minimum = 0 806*e1fe3e4aSElliott Hughes a1.maximum = 1000 807*e1fe3e4aSElliott Hughes a1.default = 0 808*e1fe3e4aSElliott Hughes a1.name = "axisName_a" 809*e1fe3e4aSElliott Hughes a1.tag = "TAGA" 810*e1fe3e4aSElliott Hughes b1 = AxisDescriptor() 811*e1fe3e4aSElliott Hughes b1.minimum = 2000 812*e1fe3e4aSElliott Hughes b1.maximum = 3000 813*e1fe3e4aSElliott Hughes b1.default = 2000 814*e1fe3e4aSElliott Hughes b1.name = "axisName_b" 815*e1fe3e4aSElliott Hughes b1.tag = "TAGB" 816*e1fe3e4aSElliott Hughes doc.addAxis(a1) 817*e1fe3e4aSElliott Hughes doc.addAxis(b1) 818*e1fe3e4aSElliott Hughes r1 = RuleDescriptor() 819*e1fe3e4aSElliott Hughes r1.name = "named.rule.1" 820*e1fe3e4aSElliott Hughes r1.conditionSets.append( 821*e1fe3e4aSElliott Hughes [ 822*e1fe3e4aSElliott Hughes dict(name="axisName_a", minimum=0, maximum=1000), 823*e1fe3e4aSElliott Hughes dict(name="axisName_b", minimum=0, maximum=3000), 824*e1fe3e4aSElliott Hughes ] 825*e1fe3e4aSElliott Hughes ) 826*e1fe3e4aSElliott Hughes r1.subs.append(("a", "a.alt")) 827*e1fe3e4aSElliott Hughes # rule with minium and maximum 828*e1fe3e4aSElliott Hughes doc.addRule(r1) 829*e1fe3e4aSElliott Hughes assert len(doc.rules) == 1 830*e1fe3e4aSElliott Hughes assert len(doc.rules[0].conditionSets) == 1 831*e1fe3e4aSElliott Hughes assert len(doc.rules[0].conditionSets[0]) == 2 832*e1fe3e4aSElliott Hughes assert _axesAsDict(doc.axes) == { 833*e1fe3e4aSElliott Hughes "axisName_a": { 834*e1fe3e4aSElliott Hughes "map": [], 835*e1fe3e4aSElliott Hughes "name": "axisName_a", 836*e1fe3e4aSElliott Hughes "default": 0, 837*e1fe3e4aSElliott Hughes "minimum": 0, 838*e1fe3e4aSElliott Hughes "maximum": 1000, 839*e1fe3e4aSElliott Hughes "tag": "TAGA", 840*e1fe3e4aSElliott Hughes }, 841*e1fe3e4aSElliott Hughes "axisName_b": { 842*e1fe3e4aSElliott Hughes "map": [], 843*e1fe3e4aSElliott Hughes "name": "axisName_b", 844*e1fe3e4aSElliott Hughes "default": 2000, 845*e1fe3e4aSElliott Hughes "minimum": 2000, 846*e1fe3e4aSElliott Hughes "maximum": 3000, 847*e1fe3e4aSElliott Hughes "tag": "TAGB", 848*e1fe3e4aSElliott Hughes }, 849*e1fe3e4aSElliott Hughes } 850*e1fe3e4aSElliott Hughes assert doc.rules[0].conditionSets == [ 851*e1fe3e4aSElliott Hughes [ 852*e1fe3e4aSElliott Hughes {"minimum": 0, "maximum": 1000, "name": "axisName_a"}, 853*e1fe3e4aSElliott Hughes {"minimum": 0, "maximum": 3000, "name": "axisName_b"}, 854*e1fe3e4aSElliott Hughes ] 855*e1fe3e4aSElliott Hughes ] 856*e1fe3e4aSElliott Hughes assert doc.rules[0].subs == [("a", "a.alt")] 857*e1fe3e4aSElliott Hughes doc.normalize() 858*e1fe3e4aSElliott Hughes assert doc.rules[0].name == "named.rule.1" 859*e1fe3e4aSElliott Hughes assert doc.rules[0].conditionSets == [ 860*e1fe3e4aSElliott Hughes [ 861*e1fe3e4aSElliott Hughes {"minimum": 0.0, "maximum": 1.0, "name": "axisName_a"}, 862*e1fe3e4aSElliott Hughes {"minimum": 0.0, "maximum": 1.0, "name": "axisName_b"}, 863*e1fe3e4aSElliott Hughes ] 864*e1fe3e4aSElliott Hughes ] 865*e1fe3e4aSElliott Hughes # still one conditionset 866*e1fe3e4aSElliott Hughes assert len(doc.rules[0].conditionSets) == 1 867*e1fe3e4aSElliott Hughes doc.write(testDocPath) 868*e1fe3e4aSElliott Hughes # add a stray conditionset 869*e1fe3e4aSElliott Hughes _addUnwrappedCondition(testDocPath) 870*e1fe3e4aSElliott Hughes doc2 = DesignSpaceDocument() 871*e1fe3e4aSElliott Hughes doc2.read(testDocPath) 872*e1fe3e4aSElliott Hughes assert doc2.rulesProcessingLast 873*e1fe3e4aSElliott Hughes assert len(doc2.axes) == 2 874*e1fe3e4aSElliott Hughes assert len(doc2.rules) == 1 875*e1fe3e4aSElliott Hughes assert len(doc2.rules[0].conditionSets) == 2 876*e1fe3e4aSElliott Hughes doc2.write(testDocPath2) 877*e1fe3e4aSElliott Hughes # verify these results 878*e1fe3e4aSElliott Hughes # make sure the stray condition is now neatly wrapped in a conditionset. 879*e1fe3e4aSElliott Hughes doc3 = DesignSpaceDocument() 880*e1fe3e4aSElliott Hughes doc3.read(testDocPath2) 881*e1fe3e4aSElliott Hughes assert len(doc3.rules) == 1 882*e1fe3e4aSElliott Hughes assert len(doc3.rules[0].conditionSets) == 2 883*e1fe3e4aSElliott Hughes 884*e1fe3e4aSElliott Hughes 885*e1fe3e4aSElliott Hughesdef _addUnwrappedCondition(path): 886*e1fe3e4aSElliott Hughes # only for testing, so we can make an invalid designspace file 887*e1fe3e4aSElliott Hughes # older designspace files may have conditions that are not wrapped in a conditionset 888*e1fe3e4aSElliott Hughes # These can be read into a new conditionset. 889*e1fe3e4aSElliott Hughes with open(path, "r", encoding="utf-8") as f: 890*e1fe3e4aSElliott Hughes d = f.read() 891*e1fe3e4aSElliott Hughes print(d) 892*e1fe3e4aSElliott Hughes d = d.replace( 893*e1fe3e4aSElliott Hughes '<rule name="named.rule.1">', 894*e1fe3e4aSElliott Hughes '<rule name="named.rule.1">\n\t<condition maximum="22" minimum="33" name="axisName_a" />', 895*e1fe3e4aSElliott Hughes ) 896*e1fe3e4aSElliott Hughes with open(path, "w", encoding="utf-8") as f: 897*e1fe3e4aSElliott Hughes f.write(d) 898*e1fe3e4aSElliott Hughes 899*e1fe3e4aSElliott Hughes 900*e1fe3e4aSElliott Hughesdef test_documentLib(tmpdir): 901*e1fe3e4aSElliott Hughes # roundtrip test of the document lib with some nested data 902*e1fe3e4aSElliott Hughes tmpdir = str(tmpdir) 903*e1fe3e4aSElliott Hughes testDocPath1 = os.path.join(tmpdir, "testDocumentLibTest.designspace") 904*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 905*e1fe3e4aSElliott Hughes a1 = AxisDescriptor() 906*e1fe3e4aSElliott Hughes a1.tag = "TAGA" 907*e1fe3e4aSElliott Hughes a1.name = "axisName_a" 908*e1fe3e4aSElliott Hughes a1.minimum = 0 909*e1fe3e4aSElliott Hughes a1.maximum = 1000 910*e1fe3e4aSElliott Hughes a1.default = 0 911*e1fe3e4aSElliott Hughes doc.addAxis(a1) 912*e1fe3e4aSElliott Hughes dummyData = dict(a=123, b="äbc", c=[1, 2, 3], d={"a": 123}) 913*e1fe3e4aSElliott Hughes dummyKey = "org.fontTools.designspaceLib" 914*e1fe3e4aSElliott Hughes doc.lib = {dummyKey: dummyData} 915*e1fe3e4aSElliott Hughes doc.write(testDocPath1) 916*e1fe3e4aSElliott Hughes new = DesignSpaceDocument() 917*e1fe3e4aSElliott Hughes new.read(testDocPath1) 918*e1fe3e4aSElliott Hughes assert dummyKey in new.lib 919*e1fe3e4aSElliott Hughes assert new.lib[dummyKey] == dummyData 920*e1fe3e4aSElliott Hughes 921*e1fe3e4aSElliott Hughes 922*e1fe3e4aSElliott Hughesdef test_updatePaths(tmpdir): 923*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 924*e1fe3e4aSElliott Hughes doc.path = str(tmpdir / "foo" / "bar" / "MyDesignspace.designspace") 925*e1fe3e4aSElliott Hughes 926*e1fe3e4aSElliott Hughes s1 = SourceDescriptor() 927*e1fe3e4aSElliott Hughes doc.addSource(s1) 928*e1fe3e4aSElliott Hughes 929*e1fe3e4aSElliott Hughes doc.updatePaths() 930*e1fe3e4aSElliott Hughes 931*e1fe3e4aSElliott Hughes # expect no changes 932*e1fe3e4aSElliott Hughes assert s1.path is None 933*e1fe3e4aSElliott Hughes assert s1.filename is None 934*e1fe3e4aSElliott Hughes 935*e1fe3e4aSElliott Hughes name1 = "../masters/Source1.ufo" 936*e1fe3e4aSElliott Hughes path1 = posix(str(tmpdir / "foo" / "masters" / "Source1.ufo")) 937*e1fe3e4aSElliott Hughes 938*e1fe3e4aSElliott Hughes s1.path = path1 939*e1fe3e4aSElliott Hughes s1.filename = None 940*e1fe3e4aSElliott Hughes 941*e1fe3e4aSElliott Hughes doc.updatePaths() 942*e1fe3e4aSElliott Hughes 943*e1fe3e4aSElliott Hughes assert s1.path == path1 944*e1fe3e4aSElliott Hughes assert s1.filename == name1 # empty filename updated 945*e1fe3e4aSElliott Hughes 946*e1fe3e4aSElliott Hughes name2 = "../masters/Source2.ufo" 947*e1fe3e4aSElliott Hughes s1.filename = name2 948*e1fe3e4aSElliott Hughes 949*e1fe3e4aSElliott Hughes doc.updatePaths() 950*e1fe3e4aSElliott Hughes 951*e1fe3e4aSElliott Hughes # conflicting filename discarded, path always gets precedence 952*e1fe3e4aSElliott Hughes assert s1.path == path1 953*e1fe3e4aSElliott Hughes assert s1.filename == "../masters/Source1.ufo" 954*e1fe3e4aSElliott Hughes 955*e1fe3e4aSElliott Hughes s1.path = None 956*e1fe3e4aSElliott Hughes s1.filename = name2 957*e1fe3e4aSElliott Hughes 958*e1fe3e4aSElliott Hughes doc.updatePaths() 959*e1fe3e4aSElliott Hughes 960*e1fe3e4aSElliott Hughes # expect no changes 961*e1fe3e4aSElliott Hughes assert s1.path is None 962*e1fe3e4aSElliott Hughes assert s1.filename == name2 963*e1fe3e4aSElliott Hughes 964*e1fe3e4aSElliott Hughes 965*e1fe3e4aSElliott Hughesdef test_read_with_path_object(): 966*e1fe3e4aSElliott Hughes source = (Path(__file__) / "../data/test_v4_original.designspace").resolve() 967*e1fe3e4aSElliott Hughes assert source.exists() 968*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 969*e1fe3e4aSElliott Hughes doc.read(source) 970*e1fe3e4aSElliott Hughes 971*e1fe3e4aSElliott Hughes 972*e1fe3e4aSElliott Hughesdef test_with_with_path_object(tmpdir): 973*e1fe3e4aSElliott Hughes tmpdir = str(tmpdir) 974*e1fe3e4aSElliott Hughes dest = Path(tmpdir) / "test_v4_original.designspace" 975*e1fe3e4aSElliott Hughes doc = DesignSpaceDocument() 976*e1fe3e4aSElliott Hughes doc.write(dest) 977*e1fe3e4aSElliott Hughes assert dest.exists() 978*e1fe3e4aSElliott Hughes 979*e1fe3e4aSElliott Hughes 980*e1fe3e4aSElliott Hughesdef test_findDefault_axis_mapping(): 981*e1fe3e4aSElliott Hughes designspace_string = """\ 982*e1fe3e4aSElliott Hughes<?xml version='1.0' encoding='UTF-8'?> 983*e1fe3e4aSElliott Hughes<designspace format="4.0"> 984*e1fe3e4aSElliott Hughes <axes> 985*e1fe3e4aSElliott Hughes <axis tag="wght" name="Weight" minimum="100" maximum="800" default="400"> 986*e1fe3e4aSElliott Hughes <map input="100" output="20"/> 987*e1fe3e4aSElliott Hughes <map input="300" output="40"/> 988*e1fe3e4aSElliott Hughes <map input="400" output="80"/> 989*e1fe3e4aSElliott Hughes <map input="700" output="126"/> 990*e1fe3e4aSElliott Hughes <map input="800" output="170"/> 991*e1fe3e4aSElliott Hughes </axis> 992*e1fe3e4aSElliott Hughes <axis tag="ital" name="Italic" minimum="0" maximum="1" default="1"/> 993*e1fe3e4aSElliott Hughes </axes> 994*e1fe3e4aSElliott Hughes <sources> 995*e1fe3e4aSElliott Hughes <source filename="Font-Light.ufo"> 996*e1fe3e4aSElliott Hughes <location> 997*e1fe3e4aSElliott Hughes <dimension name="Weight" xvalue="20"/> 998*e1fe3e4aSElliott Hughes <dimension name="Italic" xvalue="0"/> 999*e1fe3e4aSElliott Hughes </location> 1000*e1fe3e4aSElliott Hughes </source> 1001*e1fe3e4aSElliott Hughes <source filename="Font-Regular.ufo"> 1002*e1fe3e4aSElliott Hughes <location> 1003*e1fe3e4aSElliott Hughes <dimension name="Weight" xvalue="80"/> 1004*e1fe3e4aSElliott Hughes <dimension name="Italic" xvalue="0"/> 1005*e1fe3e4aSElliott Hughes </location> 1006*e1fe3e4aSElliott Hughes </source> 1007*e1fe3e4aSElliott Hughes <source filename="Font-Bold.ufo"> 1008*e1fe3e4aSElliott Hughes <location> 1009*e1fe3e4aSElliott Hughes <dimension name="Weight" xvalue="170"/> 1010*e1fe3e4aSElliott Hughes <dimension name="Italic" xvalue="0"/> 1011*e1fe3e4aSElliott Hughes </location> 1012*e1fe3e4aSElliott Hughes </source> 1013*e1fe3e4aSElliott Hughes <source filename="Font-LightItalic.ufo"> 1014*e1fe3e4aSElliott Hughes <location> 1015*e1fe3e4aSElliott Hughes <dimension name="Weight" xvalue="20"/> 1016*e1fe3e4aSElliott Hughes <dimension name="Italic" xvalue="1"/> 1017*e1fe3e4aSElliott Hughes </location> 1018*e1fe3e4aSElliott Hughes </source> 1019*e1fe3e4aSElliott Hughes <source filename="Font-Italic.ufo"> 1020*e1fe3e4aSElliott Hughes <location> 1021*e1fe3e4aSElliott Hughes <dimension name="Weight" xvalue="80"/> 1022*e1fe3e4aSElliott Hughes <dimension name="Italic" xvalue="1"/> 1023*e1fe3e4aSElliott Hughes </location> 1024*e1fe3e4aSElliott Hughes </source> 1025*e1fe3e4aSElliott Hughes <source filename="Font-BoldItalic.ufo"> 1026*e1fe3e4aSElliott Hughes <location> 1027*e1fe3e4aSElliott Hughes <dimension name="Weight" xvalue="170"/> 1028*e1fe3e4aSElliott Hughes <dimension name="Italic" xvalue="1"/> 1029*e1fe3e4aSElliott Hughes </location> 1030*e1fe3e4aSElliott Hughes </source> 1031*e1fe3e4aSElliott Hughes </sources> 1032*e1fe3e4aSElliott Hughes</designspace> 1033*e1fe3e4aSElliott Hughes """ 1034*e1fe3e4aSElliott Hughes designspace = DesignSpaceDocument.fromstring(designspace_string) 1035*e1fe3e4aSElliott Hughes assert designspace.findDefault().filename == "Font-Italic.ufo" 1036*e1fe3e4aSElliott Hughes 1037*e1fe3e4aSElliott Hughes designspace.axes[1].default = 0 1038*e1fe3e4aSElliott Hughes 1039*e1fe3e4aSElliott Hughes assert designspace.findDefault().filename == "Font-Regular.ufo" 1040*e1fe3e4aSElliott Hughes 1041*e1fe3e4aSElliott Hughes 1042*e1fe3e4aSElliott Hughesdef test_loadSourceFonts(): 1043*e1fe3e4aSElliott Hughes def opener(path): 1044*e1fe3e4aSElliott Hughes font = ttLib.TTFont() 1045*e1fe3e4aSElliott Hughes font.importXML(path) 1046*e1fe3e4aSElliott Hughes return font 1047*e1fe3e4aSElliott Hughes 1048*e1fe3e4aSElliott Hughes # this designspace file contains .TTX source paths 1049*e1fe3e4aSElliott Hughes path = os.path.join( 1050*e1fe3e4aSElliott Hughes os.path.dirname(os.path.dirname(__file__)), 1051*e1fe3e4aSElliott Hughes "varLib", 1052*e1fe3e4aSElliott Hughes "data", 1053*e1fe3e4aSElliott Hughes "SparseMasters.designspace", 1054*e1fe3e4aSElliott Hughes ) 1055*e1fe3e4aSElliott Hughes designspace = DesignSpaceDocument.fromfile(path) 1056*e1fe3e4aSElliott Hughes 1057*e1fe3e4aSElliott Hughes # force two source descriptors to have the same path 1058*e1fe3e4aSElliott Hughes designspace.sources[1].path = designspace.sources[0].path 1059*e1fe3e4aSElliott Hughes 1060*e1fe3e4aSElliott Hughes fonts = designspace.loadSourceFonts(opener) 1061*e1fe3e4aSElliott Hughes 1062*e1fe3e4aSElliott Hughes assert len(fonts) == 3 1063*e1fe3e4aSElliott Hughes assert all(isinstance(font, ttLib.TTFont) for font in fonts) 1064*e1fe3e4aSElliott Hughes assert fonts[0] is fonts[1] # same path, identical font object 1065*e1fe3e4aSElliott Hughes 1066*e1fe3e4aSElliott Hughes fonts2 = designspace.loadSourceFonts(opener) 1067*e1fe3e4aSElliott Hughes 1068*e1fe3e4aSElliott Hughes for font1, font2 in zip(fonts, fonts2): 1069*e1fe3e4aSElliott Hughes assert font1 is font2 1070*e1fe3e4aSElliott Hughes 1071*e1fe3e4aSElliott Hughes 1072*e1fe3e4aSElliott Hughesdef test_loadSourceFonts_no_required_path(): 1073*e1fe3e4aSElliott Hughes designspace = DesignSpaceDocument() 1074*e1fe3e4aSElliott Hughes designspace.sources.append(SourceDescriptor()) 1075*e1fe3e4aSElliott Hughes 1076*e1fe3e4aSElliott Hughes with pytest.raises(DesignSpaceDocumentError, match="no 'path' attribute"): 1077*e1fe3e4aSElliott Hughes designspace.loadSourceFonts(lambda p: p) 1078*e1fe3e4aSElliott Hughes 1079*e1fe3e4aSElliott Hughes 1080*e1fe3e4aSElliott Hughesdef test_addAxisDescriptor(): 1081*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument() 1082*e1fe3e4aSElliott Hughes 1083*e1fe3e4aSElliott Hughes axis = ds.addAxisDescriptor( 1084*e1fe3e4aSElliott Hughes name="Weight", tag="wght", minimum=100, default=400, maximum=900 1085*e1fe3e4aSElliott Hughes ) 1086*e1fe3e4aSElliott Hughes 1087*e1fe3e4aSElliott Hughes assert ds.axes[0] is axis 1088*e1fe3e4aSElliott Hughes assert isinstance(axis, AxisDescriptor) 1089*e1fe3e4aSElliott Hughes assert axis.name == "Weight" 1090*e1fe3e4aSElliott Hughes assert axis.tag == "wght" 1091*e1fe3e4aSElliott Hughes assert axis.minimum == 100 1092*e1fe3e4aSElliott Hughes assert axis.default == 400 1093*e1fe3e4aSElliott Hughes assert axis.maximum == 900 1094*e1fe3e4aSElliott Hughes 1095*e1fe3e4aSElliott Hughes 1096*e1fe3e4aSElliott Hughesdef test_addAxisDescriptor(): 1097*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument() 1098*e1fe3e4aSElliott Hughes 1099*e1fe3e4aSElliott Hughes mapping = ds.addAxisMappingDescriptor( 1100*e1fe3e4aSElliott Hughes inputLocation={"weight": 900, "width": 150}, outputLocation={"weight": 870} 1101*e1fe3e4aSElliott Hughes ) 1102*e1fe3e4aSElliott Hughes 1103*e1fe3e4aSElliott Hughes assert ds.axisMappings[0] is mapping 1104*e1fe3e4aSElliott Hughes assert isinstance(mapping, AxisMappingDescriptor) 1105*e1fe3e4aSElliott Hughes assert mapping.inputLocation == {"weight": 900, "width": 150} 1106*e1fe3e4aSElliott Hughes assert mapping.outputLocation == {"weight": 870} 1107*e1fe3e4aSElliott Hughes 1108*e1fe3e4aSElliott Hughes 1109*e1fe3e4aSElliott Hughesdef test_addSourceDescriptor(): 1110*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument() 1111*e1fe3e4aSElliott Hughes 1112*e1fe3e4aSElliott Hughes source = ds.addSourceDescriptor(name="TestSource", location={"Weight": 400}) 1113*e1fe3e4aSElliott Hughes 1114*e1fe3e4aSElliott Hughes assert ds.sources[0] is source 1115*e1fe3e4aSElliott Hughes assert isinstance(source, SourceDescriptor) 1116*e1fe3e4aSElliott Hughes assert source.name == "TestSource" 1117*e1fe3e4aSElliott Hughes assert source.location == {"Weight": 400} 1118*e1fe3e4aSElliott Hughes 1119*e1fe3e4aSElliott Hughes 1120*e1fe3e4aSElliott Hughesdef test_addInstanceDescriptor(): 1121*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument() 1122*e1fe3e4aSElliott Hughes 1123*e1fe3e4aSElliott Hughes instance = ds.addInstanceDescriptor( 1124*e1fe3e4aSElliott Hughes name="TestInstance", 1125*e1fe3e4aSElliott Hughes location={"Weight": 400}, 1126*e1fe3e4aSElliott Hughes styleName="Regular", 1127*e1fe3e4aSElliott Hughes styleMapStyleName="regular", 1128*e1fe3e4aSElliott Hughes ) 1129*e1fe3e4aSElliott Hughes 1130*e1fe3e4aSElliott Hughes assert ds.instances[0] is instance 1131*e1fe3e4aSElliott Hughes assert isinstance(instance, InstanceDescriptor) 1132*e1fe3e4aSElliott Hughes assert instance.name == "TestInstance" 1133*e1fe3e4aSElliott Hughes assert instance.location == {"Weight": 400} 1134*e1fe3e4aSElliott Hughes assert instance.styleName == "Regular" 1135*e1fe3e4aSElliott Hughes assert instance.styleMapStyleName == "regular" 1136*e1fe3e4aSElliott Hughes 1137*e1fe3e4aSElliott Hughes 1138*e1fe3e4aSElliott Hughesdef test_addRuleDescriptor(tmp_path): 1139*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument() 1140*e1fe3e4aSElliott Hughes 1141*e1fe3e4aSElliott Hughes rule = ds.addRuleDescriptor( 1142*e1fe3e4aSElliott Hughes name="TestRule", 1143*e1fe3e4aSElliott Hughes conditionSets=[ 1144*e1fe3e4aSElliott Hughes [ 1145*e1fe3e4aSElliott Hughes dict(name="Weight", minimum=100, maximum=200), 1146*e1fe3e4aSElliott Hughes dict(name="Weight", minimum=700, maximum=900), 1147*e1fe3e4aSElliott Hughes ] 1148*e1fe3e4aSElliott Hughes ], 1149*e1fe3e4aSElliott Hughes subs=[("a", "a.alt")], 1150*e1fe3e4aSElliott Hughes ) 1151*e1fe3e4aSElliott Hughes 1152*e1fe3e4aSElliott Hughes assert ds.rules[0] is rule 1153*e1fe3e4aSElliott Hughes assert isinstance(rule, RuleDescriptor) 1154*e1fe3e4aSElliott Hughes assert rule.name == "TestRule" 1155*e1fe3e4aSElliott Hughes assert rule.conditionSets == [ 1156*e1fe3e4aSElliott Hughes [ 1157*e1fe3e4aSElliott Hughes dict(name="Weight", minimum=100, maximum=200), 1158*e1fe3e4aSElliott Hughes dict(name="Weight", minimum=700, maximum=900), 1159*e1fe3e4aSElliott Hughes ] 1160*e1fe3e4aSElliott Hughes ] 1161*e1fe3e4aSElliott Hughes assert rule.subs == [("a", "a.alt")] 1162*e1fe3e4aSElliott Hughes 1163*e1fe3e4aSElliott Hughes # Test it doesn't crash. 1164*e1fe3e4aSElliott Hughes ds.write(tmp_path / "test.designspace") 1165*e1fe3e4aSElliott Hughes 1166*e1fe3e4aSElliott Hughes 1167*e1fe3e4aSElliott Hughesdef test_deepcopyExceptFonts(): 1168*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument() 1169*e1fe3e4aSElliott Hughes ds.addSourceDescriptor(font=object()) 1170*e1fe3e4aSElliott Hughes ds.addSourceDescriptor(font=object()) 1171*e1fe3e4aSElliott Hughes 1172*e1fe3e4aSElliott Hughes ds_copy = ds.deepcopyExceptFonts() 1173*e1fe3e4aSElliott Hughes 1174*e1fe3e4aSElliott Hughes assert ds.tostring() == ds_copy.tostring() 1175*e1fe3e4aSElliott Hughes assert ds.sources[0].font is ds_copy.sources[0].font 1176*e1fe3e4aSElliott Hughes assert ds.sources[1].font is ds_copy.sources[1].font 1177*e1fe3e4aSElliott Hughes 1178*e1fe3e4aSElliott Hughes 1179*e1fe3e4aSElliott Hughesdef test_Range_post_init(): 1180*e1fe3e4aSElliott Hughes # test min and max are sorted and default is clamped to either min/max 1181*e1fe3e4aSElliott Hughes r = Range(minimum=2, maximum=-1, default=-2) 1182*e1fe3e4aSElliott Hughes assert r.minimum == -1 1183*e1fe3e4aSElliott Hughes assert r.maximum == 2 1184*e1fe3e4aSElliott Hughes assert r.default == -1 1185*e1fe3e4aSElliott Hughes 1186*e1fe3e4aSElliott Hughes 1187*e1fe3e4aSElliott Hughesdef test_get_axes(datadir: Path) -> None: 1188*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(datadir / "test_v5.designspace") 1189*e1fe3e4aSElliott Hughes 1190*e1fe3e4aSElliott Hughes assert ds.getAxis("Width") is ds.getAxisByTag("wdth") 1191*e1fe3e4aSElliott Hughes assert ds.getAxis("Italic") is ds.getAxisByTag("ital") 1192