def test_normalise(): doc = DesignSpaceDocument() # write some axes a1 = AxisDescriptor() a1.minimum = -1000 a1.maximum = 1000 a1.default = 0 a1.name = "aaa" a1.tag = "aaaa" doc.addAxis(a1) assert doc.normalizeLocation(dict(aaa=0)) == {'aaa': 0.0} assert doc.normalizeLocation(dict(aaa=1000)) == {'aaa': 1.0} # clipping beyond max values: assert doc.normalizeLocation(dict(aaa=1001)) == {'aaa': 1.0} assert doc.normalizeLocation(dict(aaa=500)) == {'aaa': 0.5} assert doc.normalizeLocation(dict(aaa=-1000)) == {'aaa': -1.0} assert doc.normalizeLocation(dict(aaa=-1001)) == {'aaa': -1.0} # anisotropic coordinates normalise to isotropic assert doc.normalizeLocation(dict(aaa=(1000, -1000))) == {'aaa': 1.0} doc.normalize() r = [] for axis in doc.axes: r.append((axis.name, axis.minimum, axis.default, axis.maximum)) r.sort() assert r == [('aaa', -1.0, 0.0, 1.0)] doc = DesignSpaceDocument() # write some axes a2 = AxisDescriptor() a2.minimum = 100 a2.maximum = 1000 a2.default = 100 a2.name = "bbb" doc.addAxis(a2) assert doc.normalizeLocation(dict(bbb=0)) == {'bbb': 0.0} assert doc.normalizeLocation(dict(bbb=1000)) == {'bbb': 1.0} # clipping beyond max values: assert doc.normalizeLocation(dict(bbb=1001)) == {'bbb': 1.0} assert doc.normalizeLocation(dict(bbb=500)) == {'bbb': 0.4444444444444444} assert doc.normalizeLocation(dict(bbb=-1000)) == {'bbb': 0.0} assert doc.normalizeLocation(dict(bbb=-1001)) == {'bbb': 0.0} # anisotropic coordinates normalise to isotropic assert doc.normalizeLocation(dict(bbb=(1000, -1000))) == {'bbb': 1.0} assert doc.normalizeLocation(dict(bbb=1001)) == {'bbb': 1.0} doc.normalize() r = [] for axis in doc.axes: r.append((axis.name, axis.minimum, axis.default, axis.maximum)) r.sort() assert r == [('bbb', 0.0, 0.0, 1.0)] doc = DesignSpaceDocument() # write some axes a3 = AxisDescriptor() a3.minimum = -1000 a3.maximum = 0 a3.default = 0 a3.name = "ccc" doc.addAxis(a3) assert doc.normalizeLocation(dict(ccc=0)) == {'ccc': 0.0} assert doc.normalizeLocation(dict(ccc=1)) == {'ccc': 0.0} assert doc.normalizeLocation(dict(ccc=-1000)) == {'ccc': -1.0} assert doc.normalizeLocation(dict(ccc=-1001)) == {'ccc': -1.0} doc.normalize() r = [] for axis in doc.axes: r.append((axis.name, axis.minimum, axis.default, axis.maximum)) r.sort() assert r == [('ccc', -1.0, 0.0, 0.0)] doc = DesignSpaceDocument() # write some axes a3 = AxisDescriptor() a3.minimum = 2000 a3.maximum = 3000 a3.default = 2000 a3.name = "ccc" doc.addAxis(a3) assert doc.normalizeLocation(dict(ccc=0)) == {'ccc': 0.0} assert doc.normalizeLocation(dict(ccc=1)) == {'ccc': 0.0} assert doc.normalizeLocation(dict(ccc=-1000)) == {'ccc': 0.0} assert doc.normalizeLocation(dict(ccc=-1001)) == {'ccc': 0.0} doc.normalize() r = [] for axis in doc.axes: r.append((axis.name, axis.minimum, axis.default, axis.maximum)) r.sort() assert r == [('ccc', 0.0, 0.0, 1.0)] doc = DesignSpaceDocument() # write some axes a4 = AxisDescriptor() a4.minimum = 0 a4.maximum = 1000 a4.default = 0 a4.name = "ddd" a4.map = [(0, 100), (300, 500), (600, 500), (1000, 900)] doc.addAxis(a4) doc.normalize() r = [] for axis in doc.axes: r.append((axis.name, axis.map)) r.sort() assert r == [('ddd', [(0, 0.1), (300, 0.5), (600, 0.5), (1000, 0.9)])]
def test_rules(tmpdir): tmpdir = str(tmpdir) testDocPath = os.path.join(tmpdir, "testRules.designspace") testDocPath2 = os.path.join(tmpdir, "testRules_roundtrip.designspace") doc = DesignSpaceDocument() # write some axes a1 = AxisDescriptor() a1.tag = "taga" a1.name = "aaaa" a1.minimum = 0 a1.maximum = 1000 a1.default = 0 doc.addAxis(a1) a2 = AxisDescriptor() a2.tag = "tagb" a2.name = "bbbb" a2.minimum = 0 a2.maximum = 3000 a2.default = 0 doc.addAxis(a2) r1 = RuleDescriptor() r1.name = "named.rule.1" r1.conditions.append(dict(name='aaaa', minimum=0, maximum=1000)) r1.conditions.append(dict(name='bbbb', 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].conditions) == 2 assert evaluateRule(r1, dict(aaaa=500, bbbb=0)) == True assert evaluateRule(r1, dict(aaaa=0, bbbb=0)) == True assert evaluateRule(r1, dict(aaaa=1000, bbbb=0)) == True assert evaluateRule(r1, dict(aaaa=1000, bbbb=-100)) == False assert evaluateRule(r1, dict(aaaa=1000.0001, bbbb=0)) == False assert evaluateRule(r1, dict(aaaa=-0.0001, bbbb=0)) == False assert evaluateRule(r1, dict(aaaa=-100, bbbb=0)) == False assert processRules([r1], dict(aaaa=500), ["a", "b", "c"]) == ['a.alt', 'b', 'c'] assert processRules([r1], dict(aaaa=500), ["a.alt", "b", "c"]) == ['a.alt', 'b', 'c'] assert processRules([r1], dict(aaaa=2000), ["a", "b", "c"]) == ['a', 'b', 'c'] # rule with only a maximum r2 = RuleDescriptor() r2.name = "named.rule.2" r2.conditions.append(dict(name='aaaa', maximum=500)) r2.subs.append(("b", "b.alt")) assert evaluateRule(r2, dict(aaaa=0)) == True assert evaluateRule(r2, dict(aaaa=-500)) == True assert evaluateRule(r2, dict(aaaa=1000)) == False # rule with only a minimum r3 = RuleDescriptor() r3.name = "named.rule.3" r3.conditions.append(dict(name='aaaa', minimum=500)) r3.subs.append(("c", "c.alt")) assert evaluateRule(r3, dict(aaaa=0)) == False assert evaluateRule(r3, dict(aaaa=1000)) == True assert evaluateRule(r3, dict(bbbb=1000)) == True # rule with only a minimum, maximum in separate conditions r4 = RuleDescriptor() r4.name = "named.rule.4" r4.conditions.append(dict(name='aaaa', minimum=500)) r4.conditions.append(dict(name='bbbb', maximum=500)) r4.subs.append(("c", "c.alt")) assert evaluateRule(r4, dict()) == True # is this what we expect though? assert evaluateRule(r4, dict(aaaa=1000, bbbb=0)) == True assert evaluateRule(r4, dict(aaaa=0, bbbb=0)) == False assert evaluateRule(r4, dict(aaaa=1000, bbbb=1000)) == False a1 = AxisDescriptor() a1.minimum = 0 a1.maximum = 1000 a1.default = 0 a1.name = "aaaa" a1.tag = "aaaa" b1 = AxisDescriptor() b1.minimum = 2000 b1.maximum = 3000 b1.default = 2000 b1.name = "bbbb" b1.tag = "bbbb" doc.addAxis(a1) doc.addAxis(b1) assert doc._prepAxesForBender() == { 'aaaa': { 'map': [], 'name': 'aaaa', 'default': 0, 'minimum': 0, 'maximum': 1000, 'tag': 'aaaa' }, 'bbbb': { 'map': [], 'name': 'bbbb', 'default': 2000, 'minimum': 2000, 'maximum': 3000, 'tag': 'bbbb' } } assert doc.rules[0].conditions == [{ 'minimum': 0, 'maximum': 1000, 'name': 'aaaa' }, { 'minimum': 0, 'maximum': 3000, 'name': 'bbbb' }] assert doc.rules[0].subs == [('a', 'a.alt')] doc.normalize() assert doc.rules[0].name == 'named.rule.1' assert doc.rules[0].conditions == [{ 'minimum': 0.0, 'maximum': 1.0, 'name': 'aaaa' }, { 'minimum': 0.0, 'maximum': 1.0, 'name': 'bbbb' }] doc.write(testDocPath) new = DesignSpaceDocument() new.read(testDocPath) assert len(new.axes) == 4 assert len(new.rules) == 1 new.write(testDocPath2)
def test_handleNoAxes(tmpdir): tmpdir = str(tmpdir) # test what happens if the designspacedocument has no axes element. testDocPath = os.path.join(tmpdir, "testNoAxes_source.designspace") testDocPath2 = os.path.join(tmpdir, "testNoAxes_recontructed.designspace") masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo") masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo") instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo") instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo") # Case 1: No axes element in the document, but there are sources and instances doc = DesignSpaceDocument() for name, value in [('One', 1), ('Two', 2), ('Three', 3)]: a = AxisDescriptor() a.minimum = 0 a.maximum = 1000 a.default = 0 a.name = "axisName%s" % (name) a.tag = "ax_%d" % (value) doc.addAxis(a) # add master 1 s1 = SourceDescriptor() s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath)) s1.name = "master.ufo1" s1.copyLib = True s1.copyInfo = True s1.copyFeatures = True s1.location = dict(axisNameOne=-1000, axisNameTwo=0, axisNameThree=1000) s1.familyName = "MasterFamilyName" s1.styleName = "MasterStyleNameOne" doc.addSource(s1) # add master 2 s2 = SourceDescriptor() s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath)) s2.name = "master.ufo1" s2.copyLib = False s2.copyInfo = False s2.copyFeatures = False s2.location = dict(axisNameOne=1000, axisNameTwo=1000, axisNameThree=0) s2.familyName = "MasterFamilyName" s2.styleName = "MasterStyleNameTwo" doc.addSource(s2) # add instance 1 i1 = InstanceDescriptor() i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath)) i1.familyName = "InstanceFamilyName" i1.styleName = "InstanceStyleName" i1.name = "instance.ufo1" i1.location = dict(axisNameOne=(-1000, 500), axisNameTwo=100) i1.postScriptFontName = "InstancePostscriptName" i1.styleMapFamilyName = "InstanceStyleMapFamilyName" i1.styleMapStyleName = "InstanceStyleMapStyleName" doc.addInstance(i1) doc.write(testDocPath) __removeAxesFromDesignSpace(testDocPath) verify = DesignSpaceDocument() verify.read(testDocPath) verify.write(testDocPath2)
def test_pathNameResolve(tmpdir): tmpdir = str(tmpdir) # test how descriptor.path and descriptor.filename are resolved testDocPath1 = os.path.join(tmpdir, "testPathName_case1.designspace") testDocPath2 = os.path.join(tmpdir, "testPathName_case2.designspace") testDocPath3 = os.path.join(tmpdir, "testPathName_case3.designspace") testDocPath4 = os.path.join(tmpdir, "testPathName_case4.designspace") testDocPath5 = os.path.join(tmpdir, "testPathName_case5.designspace") testDocPath6 = os.path.join(tmpdir, "testPathName_case6.designspace") masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo") masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo") instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo") instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo") # Case 1: filename and path are both empty. Nothing to calculate, nothing to put in the file. doc = DesignSpaceDocument() 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() 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() s = SourceDescriptor() s.filename = "../somewhere/over/the/rainbow.ufo" s.path = None s.copyInfo = True s.location = dict(weight=0) s.familyName = "MasterFamilyName" s.styleName = "MasterStyleNameOne" doc.addSource(s) doc.write(testDocPath3) verify = DesignSpaceDocument() verify.read(testDocPath3) assert verify.sources[0].filename == "../somewhere/over/the/rainbow.ufo" # make the absolute path for filename so we can see if it matches the path p = os.path.abspath( os.path.join(os.path.dirname(testDocPath3), verify.sources[0].filename)) assert verify.sources[0].path == posix(p) # Case 4: the filename points to one file, the path points to another. The path takes precedence. doc = DesignSpaceDocument() 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() 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() s = SourceDescriptor() s.filename = "../somewhere/over/the/rainbow.ufo" s.path = masterPath1 s.copyInfo = True s.location = dict(weight=0) s.familyName = "MasterFamilyName" s.styleName = "MasterStyleNameOne" doc.write(testDocPath5) # so that the document has a path doc.addSource(s) assert doc.sources[0].filename == "../somewhere/over/the/rainbow.ufo" doc.updateFilenameFromPath(force=True) assert doc.sources[0].filename == "masters/masterTest1.ufo"
def test_fill_document(tmpdir): tmpdir = str(tmpdir) testDocPath = os.path.join(tmpdir, "test.designspace") masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo") masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo") instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo") instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo") doc = DesignSpaceDocument() # add master 1 s1 = SourceDescriptor() s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath)) assert s1.font is None s1.name = "master.ufo1" s1.copyLib = True s1.copyInfo = True s1.copyFeatures = True s1.location = dict(weight=0) s1.familyName = "MasterFamilyName" s1.styleName = "MasterStyleNameOne" s1.mutedGlyphNames.append("A") s1.mutedGlyphNames.append("Z") doc.addSource(s1) # add master 2 s2 = SourceDescriptor() s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath)) s2.name = "master.ufo2" s2.copyLib = False s2.copyInfo = False s2.copyFeatures = False s2.muteKerning = True s2.location = dict(weight=1000) s2.familyName = "MasterFamilyName" s2.styleName = "MasterStyleNameTwo" doc.addSource(s2) # add instance 1 i1 = InstanceDescriptor() i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath)) i1.familyName = "InstanceFamilyName" i1.styleName = "InstanceStyleName" i1.name = "instance.ufo1" i1.location = dict( weight=500, spooky=666) # this adds a dimension that is not defined. i1.postScriptFontName = "InstancePostscriptName" i1.styleMapFamilyName = "InstanceStyleMapFamilyName" i1.styleMapStyleName = "InstanceStyleMapStyleName" glyphData = dict(name="arrow", mute=True, unicodes=[0x123, 0x124, 0x125]) i1.glyphs['arrow'] = glyphData i1.lib['com.coolDesignspaceApp.specimenText'] = "Hamburgerwhatever" doc.addInstance(i1) # add instance 2 i2 = InstanceDescriptor() i2.filename = os.path.relpath(instancePath2, os.path.dirname(testDocPath)) i2.familyName = "InstanceFamilyName" i2.styleName = "InstanceStyleName" i2.name = "instance.ufo2" # anisotropic location i2.location = dict(weight=500, width=(400, 300)) i2.postScriptFontName = "InstancePostscriptName" i2.styleMapFamilyName = "InstanceStyleMapFamilyName" i2.styleMapStyleName = "InstanceStyleMapStyleName" glyphMasters = [ dict(font="master.ufo1", glyphName="BB", location=dict(width=20, weight=20)), dict(font="master.ufo2", glyphName="CC", location=dict(width=900, weight=900)) ] glyphData = dict(name="arrow", unicodes=[101, 201, 301]) glyphData['masters'] = glyphMasters glyphData['note'] = "A note about this glyph" glyphData['instanceLocation'] = dict(width=100, weight=120) i2.glyphs['arrow'] = glyphData i2.glyphs['arrow2'] = dict(mute=False) doc.addInstance(i2) doc.filename = "suggestedFileName.designspace" doc.lib['com.coolDesignspaceApp.previewSize'] = 30 # now we have sources and instances, but no axes yet. doc.check() # Here, since the axes are not defined in the document, but instead are # infered from the locations of the instances, we cannot guarantee the # order in which they will be created by the `check()` method. assert set(doc.getAxisOrder()) == set(['spooky', 'weight', 'width']) doc.axes = [] # clear the axes # write some axes a1 = AxisDescriptor() a1.minimum = 0 a1.maximum = 1000 a1.default = 0 a1.name = "weight" a1.tag = "wght" # note: just to test the element language, not an actual label name recommendations. a1.labelNames[u'fa-IR'] = u"قطر" a1.labelNames[u'en'] = u"Wéíght" doc.addAxis(a1) a2 = AxisDescriptor() a2.minimum = 0 a2.maximum = 1000 a2.default = 20 a2.name = "width" a2.tag = "wdth" a2.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)] a2.hidden = True a2.labelNames[u'fr'] = u"Chasse" doc.addAxis(a2) # add an axis that is not part of any location to see if that works a3 = AxisDescriptor() a3.minimum = 333 a3.maximum = 666 a3.default = 444 a3.name = "spooky" a3.tag = "spok" a3.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)] #doc.addAxis(a3) # uncomment this line to test the effects of default axes values # write some rules r1 = RuleDescriptor() r1.name = "named.rule.1" r1.conditions.append(dict(name='aaaa', minimum=0, maximum=1)) r1.conditions.append(dict(name='bbbb', minimum=2, maximum=3)) r1.subs.append(("a", "a.alt")) doc.addRule(r1) # write the document doc.write(testDocPath) assert os.path.exists(testDocPath) assert_equals_test_file(testDocPath, 'data/test.designspace') # import it again new = DesignSpaceDocument() new.read(testDocPath) new.check() assert new.default.location == {'width': 20.0, 'weight': 0.0} assert new.filename == 'test.designspace' assert new.lib == doc.lib assert new.instances[0].lib == doc.instances[0].lib # >>> for a, b in zip(doc.instances, new.instances): # ... a.compare(b) # >>> for a, b in zip(doc.sources, new.sources): # ... a.compare(b) # >>> for a, b in zip(doc.axes, new.axes): # ... a.compare(b) # >>> [n.mutedGlyphNames for n in new.sources] # [['A', 'Z'], []] # >>> doc.getFonts() # [] # 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_localisedNames(tmpdir): tmpdir = str(tmpdir) testDocPath = os.path.join(tmpdir, "testLocalisedNames.designspace") testDocPath2 = os.path.join(tmpdir, "testLocalisedNames_roundtrip.designspace") masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo") masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo") instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo") instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo") doc = DesignSpaceDocument() # add master 1 s1 = SourceDescriptor() s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath)) s1.name = "master.ufo1" s1.copyInfo = True s1.location = dict(weight=0) doc.addSource(s1) # add master 2 s2 = SourceDescriptor() s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath)) s2.name = "master.ufo2" s2.location = dict(weight=1000) doc.addSource(s2) # add instance 1 i1 = InstanceDescriptor() i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath)) i1.familyName = "Montserrat" i1.styleName = "SemiBold" i1.styleMapFamilyName = "Montserrat SemiBold" i1.styleMapStyleName = "Regular" i1.setFamilyName("Montserrat", "fr") i1.setFamilyName(u"モンセラート", "ja") i1.setStyleName("Demigras", "fr") i1.setStyleName(u"半ば", "ja") i1.setStyleMapStyleName(u"Standard", "de") i1.setStyleMapFamilyName("Montserrat Halbfett", "de") i1.setStyleMapFamilyName(u"モンセラート SemiBold", "ja") i1.name = "instance.ufo1" i1.location = dict( weight=500, spooky=666) # this adds a dimension that is not defined. i1.postScriptFontName = "InstancePostscriptName" glyphData = dict(name="arrow", mute=True, unicodes=[0x123]) i1.glyphs['arrow'] = glyphData doc.addInstance(i1) # now we have sources and instances, but no axes yet. doc.axes = [] # clear the axes # write some axes a1 = AxisDescriptor() a1.minimum = 0 a1.maximum = 1000 a1.default = 0 a1.name = "weight" a1.tag = "wght" # note: just to test the element language, not an actual label name recommendations. a1.labelNames[u'fa-IR'] = u"قطر" a1.labelNames[u'en'] = u"Wéíght" doc.addAxis(a1) a2 = AxisDescriptor() a2.minimum = 0 a2.maximum = 1000 a2.default = 0 a2.name = "width" a2.tag = "wdth" a2.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)] a2.labelNames[u'fr'] = u"Poids" doc.addAxis(a2) # add an axis that is not part of any location to see if that works a3 = AxisDescriptor() a3.minimum = 333 a3.maximum = 666 a3.default = 444 a3.name = "spooky" a3.tag = "spok" a3.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)] #doc.addAxis(a3) # uncomment this line to test the effects of default axes values # write some rules r1 = RuleDescriptor() r1.name = "named.rule.1" r1.conditions.append(dict(name='aaaa', minimum=0, maximum=1)) r1.conditions.append(dict(name='bbbb', minimum=2, maximum=3)) r1.subs.append(("a", "a.alt")) doc.addRule(r1) # write the document doc.write(testDocPath) assert os.path.exists(testDocPath) # import it again new = DesignSpaceDocument() new.read(testDocPath) new.write(testDocPath2) f1 = open(testDocPath, 'r', encoding='utf-8') t1 = f1.read() f1.close() f2 = open(testDocPath2, 'r', encoding='utf-8') t2 = f2.read() f2.close() assert t1 == t2
def test_read_with_path_object(): import pathlib source = (pathlib.Path(__file__) / "../data/test.designspace").resolve() assert source.exists() doc = DesignSpaceDocument() doc.read(source)
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)
def buildDesignSpace(masterFont=None, destPath=None, glyphNames=[], compositionType="rotate", outlineAmount=None, zOffset=None, shadowLengthFactor=1, doForceSmooth=False, doMakeSubSources=False, familyName=None, alwaysConnect=False, cap="RoundSimple", connection="Round", layerName=None, styleName=None): # Open the master UFO if type(masterFont) == str: masterFont = OpenFont(masterFont, showInterface=False) # Get the master file name, if it's saved basePath = None masterFileName = "Font" if masterFont.path: basePath, masterFileName = os.path.split(masterFont.path) # Try to make a dest path, if there isn't one if destPath == None: if basePath: destPath = os.path.join(basePath, "Rotated") # Make new folders for the destPath if not os.path.exists(destPath): os.makedirs(destPath) # Use all glyphs, if no names are called for if glyphNames == []: glyphNames = list(masterFont.keys()) glyphNames.sort() # Default names if not familyName: familyName = masterFont.info.familyName if not styleName: styleName = "Regular" """ Collect glyph data """ # Organize the point data out of the glyph lib # and check to see which glyphs need to be present in a SubSource glyphPointData = {} needSubHROT = [ ] # Glyphs that need to be included in a SubSource when HROT is default needSubVROT = [] for gName in glyphNames: if gName in masterFont: g = masterFont[gName] if layerName: # Copy point data to the layer if ZPOSITIONLIBKEY in g.lib.keys(): libdata = copy.deepcopy(g.lib[ZPOSITIONLIBKEY]) else: libdata = {} g = g.getLayer(layerName) g.lib[ZPOSITIONLIBKEY] = libdata pointData = readGlyphPointData(g) glyphPointData[gName] = pointData # Test for self-overlapping contours if doMakeSubSources == True: overlapResult = checkCurveOverlap(g) if "x" in overlapResult: needSubHROT.append(gName) elif "y" in overlapResult: needSubVROT.append(gName) """ Organize Source combinations """ # Collect source info, based on the layout type # Organize file names, designspace locations, glyph lists, etc. sourceCombinations = [] for locHROT, tagHROT in [(-45, "HROTn"), (0, "HROTd"), (45, "HROTx")]: for locVROT, tagVROT in [(-45, "VROTn"), (0, "VROTd"), (45, "VROTx")]: if "shadow" in compositionType: for locSLEN, tagSLEN in [(0, "SLENn"), (100, "SLENx")]: for locSANG, tagSANG in [(-45, "SANGn"), (45, "SANGx")]: # Rotate and Shadow fileName = "Source-%s_%s_%s_%s.ufo" % ( tagHROT, tagVROT, tagSLEN, tagSANG) loc = dict(HROT=locHROT, VROT=locVROT, SLEN=locSLEN, SANG=locSANG) sourceInfo = dict(glyphNames=glyphNames, loc=loc, fileName=fileName, nudgeLoc=[0, 0]) sourceCombinations.append(sourceInfo) elif "depth" in compositionType: for locDPTH, tagDPTH in [(0, "DPTHn"), (100, "DPTHx")]: # Rotate and depth fileName = "Source-%s_%s_%s.ufo" % (tagHROT, tagVROT, tagDPTH) loc = dict(HROT=locHROT, VROT=locVROT, DPTH=locDPTH) sourceInfo = dict(glyphNames=glyphNames, loc=loc, fileName=fileName, nudgeLoc=[0, 0]) sourceCombinations.append(sourceInfo) else: # Rotate only fileName = "Source-%s_%s.ufo" % (tagHROT, tagVROT) loc = dict(HROT=locHROT, VROT=locVROT) sourceInfo = dict(glyphNames=glyphNames, loc=loc, fileName=fileName, nudgeLoc=[0, 0]) sourceCombinations.append(sourceInfo) # Process the sourceCombinations and make SubSources if necessary #print("needSubHROT", needSubHROT) #print("needSubVROT", needSubVROT) # @@@ Temporarily force all glyphs to be in all submasters needSubHROT = glyphNames needSubVROT = glyphNames if doMakeSubSources: doSubHROT = len(needSubHROT) doSubVROT = len(needSubVROT) else: doSubHROT = False doSubVROT = False # Loop through once to add new HROT SubSources newSourceCombos = [] for sourceInfo in sourceCombinations: if sourceInfo["loc"]["HROT"] == 0: if doSubHROT: subSourceInfo = copy.deepcopy(sourceInfo) subSourceInfo["nudgeLoc"][ 0] = 0 # Don't nudge, move the location instead subSourceInfo["loc"]["HROT"] += SLIGHTLYOFFAXIS subSourceInfo["glyphNames"] = needSubHROT subSourceInfo[ "fileName"] = "Sub" + subSourceInfo["fileName"].replace( "HROTd", "HROTdd") newSourceCombos.append(subSourceInfo) # Nudge the default source sourceInfo["nudgeLoc"][0] -= SLIGHTLYOFFAXIS sourceCombinations += newSourceCombos # Looping back through to add VROT SubSources and to catch all of the new HROT SubSources newSourceCombos = [] for sourceInfo in sourceCombinations: if sourceInfo["loc"]["VROT"] == 0: if doSubVROT: subSourceInfo = copy.deepcopy(sourceInfo) subSourceInfo["nudgeLoc"][ 1] = 0 # Don't nudge, move the location instead subSourceInfo["loc"]["VROT"] -= SLIGHTLYOFFAXIS # Append the glyph list if this was the HROT=SLIGHTLYOFFAXIS if not subSourceInfo["loc"]["HROT"] == SLIGHTLYOFFAXIS: subSourceInfo["glyphNames"] = [] subSourceInfo["glyphNames"] += needSubVROT subSourceInfo["fileName"] = subSourceInfo["fileName"].replace( "VROTd", "VROTdd") if not "Sub" in subSourceInfo["fileName"]: subSourceInfo[ "fileName"] = "Sub" + subSourceInfo["fileName"] newSourceCombos.append(subSourceInfo) # Nudge the default source sourceInfo["nudgeLoc"][1] += SLIGHTLYOFFAXIS sourceCombinations += newSourceCombos """ Make the source UFOs """ for sourceInfo in sourceCombinations: sourceUfoPath = os.path.join(destPath, sourceInfo["fileName"]) if not os.path.exists(sourceUfoPath): sourceFont = NewFont(showInterface=False) sourceFont.save(sourceUfoPath) sourceFont.info.familyName = familyName sourceFont.info.styleName = styleName sourceFont.save() sourceFont.close() """ Process Glyphs into Source UFOs """ # Process each UFO source, one at a time for sourceInfo in sourceCombinations: # Combine the nudgeLoc and loc, use this value when rotating rotateLoc = copy.deepcopy(sourceInfo["loc"]) rotateLoc["HROT"] += sourceInfo["nudgeLoc"][0] rotateLoc["VROT"] += sourceInfo["nudgeLoc"][1] sourceUfoPath = os.path.join(destPath, sourceInfo["fileName"]) sourceFont = OpenFont(sourceUfoPath, showInterface=False) for gName in sourceInfo["glyphNames"]: if gName in glyphPointData: pointData = copy.deepcopy(glyphPointData[gName]) else: pointData = {} # Get the glyph started g = masterFont[gName] if layerName: g = g.getLayer(layerName) # Remove the glyph if it already existed and make a new one if gName in sourceFont: for layer in sourceFont.layers: if gName in layer: layer.removeGlyph(gName) sourceFont.newGlyph(gName) gDest = sourceFont[gName] gDest.appendGlyph(g) gDest.width = g.width gDest.unicode = g.unicode # Add anchors to the pointData so that they shift correctly # Use anchor + str(idx) as the ident for aIdx, anc in enumerate(gDest.anchors): ident = "anchor%s" % aIdx pos = list(anc.position) pointData[ident] = dict(x=pos[0], y=pos[1], z=0) # Decompose components in glyphs that were not bulit with Glyph Builder # If the glyph has components, and if it's not marked with the "Glyph Builder Gray", # it will need to be decomposed at this stage (but leave overlaps of course) # Otherwise, glyphs that are gray will have their components reapplied after rotating with Glyph Builder. isComponent = False if not g.markColor == COMPOSITEGRAY: if len(gDest.components): for c in gDest.components: baseName = c.baseGlyph masterBaseGlyph = masterFont[baseName] # Decomposing from the master font because the base glyph might not be in the source font yet for mc in masterBaseGlyph.contours: gDest.appendContour(mc, offset=c.offset) gDest.removeComponent(c) # ...and copy over point data, taking into account the component offset if baseName in glyphPointData: basePointData = copy.deepcopy( glyphPointData[baseName]) for ident in basePointData: pointData[ident] = dict( x=basePointData[ident]["x"] + c.offset[0], y=basePointData[ident]["y"] + c.offset[1], z=basePointData[ident]["z"]) isComponent = True # Flatten the depth if "DPTH" in sourceInfo["loc"].keys(): for ident in pointData: pointData[ident]["z"] *= (sourceInfo["loc"]["DPTH"] * 0.01 ) # Scale by the depth value # Shift the "z" value by an offset if not zOffset == None: for ident in pointData: if not "anchor" in ident: # ...but don't shift the anchors, the components are already shifted pointData[ident]["z"] += zOffset # Extend the shadow if "SANG" in sourceInfo["loc"].keys(): if sourceInfo["loc"]["SANG"] == -45: shadowDirection = "left" else: shadowDirection = "right" finalShadowLengthFactor = (sourceInfo["loc"]["SLEN"] * 0.01) * shadowLengthFactor pointData = flattenShadow(gDest, pointData, shadowDirection, finalShadowLengthFactor) # Rotate the glyph # Merge the location and the nudgeLoc, if there is one marginChange, pointData = rotateGlyphPointData( gDest, rotateLoc, pointData) # Move the contour points into the correct position for c in gDest.contours: for pt in c.points: ident = getIdent(pt) if ident in pointData: pt.x = pointData[ident]["x"] pt.y = pointData[ident]["y"] # Move the anchors into the correct position for aIdx, anc in enumerate(gDest.anchors): ident = "anchor%s" % aIdx if ident in pointData: anc.position = (int(round(pointData[ident]["x"])), int(round(pointData[ident]["y"]))) # Shift the glyph to take in the sidebearings gDest.moveBy((-marginChange[0], 0)) gDest.width -= marginChange[0] * 2 if doForceSmooth and not isComponent: # If a bPoint was a smooth curve point in the original glyph, # force the related bPoint in the rotated glyph to be smooth for cIdx, c in enumerate(gDest.contours): for bptIdx, thisBPt in enumerate(c.bPoints): sourceBPt = g.contours[cIdx].bPoints[bptIdx] if sourceBPt.type == "curve": forceSmooth(thisBPt) # Round the point coordinates before outlining gDest.round() gDest.changed() # Outline the glyph if outlineAmount: outlineGlyph(sourceFont, gDest, outlineAmount, alwaysConnect=alwaysConnect, cap=cap, connection=connection) # Round the point coordinates again, now that it's outlined gDest.round() gDest.changed() # Update #gDest.changed() # Resort the font sourceFont.glyphOrder = masterFont.glyphOrder # Copy the kerning sourceFont.groups.update(copy.deepcopy(masterFont.groups)) sourceFont.kerning.update(copy.deepcopy(masterFont.kerning)) # Copy the features sourceFont.features.text = masterFont.features.text # Done, save sourceFont.changed() sourceFont.save() """ New DesignSpaceDocument """ designSpace = DesignSpaceDocument() designSpaceDocFilename = os.path.splitext( masterFileName)[0] + ".designspace" designSpaceDocPath = os.path.join(destPath, designSpaceDocFilename) """ Axis Descriptors """ for tag in sourceCombinations[0]["loc"].keys(): a = AxisDescriptor() a.minimum = AXISINFO[tag]["minimum"] a.maximum = AXISINFO[tag]["maximum"] a.default = AXISINFO[tag]["default"] a.name = AXISINFO[tag]["name"] a.tag = tag a.labelNames[u'en'] = AXISINFO[tag]["name"] designSpace.addAxis(a) """ Source Descriptors """ for sourceInfo in sourceCombinations: sourceUfoPath = os.path.join(destPath, sourceInfo["fileName"]) # Make a source description s = SourceDescriptor() s.path = sourceUfoPath s.name = os.path.splitext(sourceInfo["fileName"])[0] #s.font = defcon.Font(s.name) s.copyLib = True s.copyInfo = True s.copyInfoures = True s.familyName = masterFont.info.familyName s.styleName = s.name # Convert the loc from tags to names loc = {} for tag, value in sourceInfo["loc"].items(): axisName = AXISINFO[tag]["name"] loc[axisName] = value s.location = loc designSpace.addSource(s) designSpace.write(designSpaceDocPath)
def test_fill_document(tmpdir): tmpdir = str(tmpdir) testDocPath = os.path.join(tmpdir, "test.designspace") masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo") masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo") instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo") instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo") doc = DesignSpaceDocument() # write some axes a1 = AxisDescriptor() a1.minimum = 0 a1.maximum = 1000 a1.default = 0 a1.name = "weight" a1.tag = "wght" # note: just to test the element language, not an actual label name recommendations. a1.labelNames[u'fa-IR'] = u"قطر" a1.labelNames[u'en'] = u"Wéíght" doc.addAxis(a1) a2 = AxisDescriptor() a2.minimum = 0 a2.maximum = 1000 a2.default = 15 a2.name = "width" a2.tag = "wdth" a2.map = [(0.0, 10.0), (15.0, 20.0), (401.0, 66.0), (1000.0, 990.0)] a2.hidden = True a2.labelNames[u'fr'] = u"Chasse" doc.addAxis(a2) # add master 1 s1 = SourceDescriptor() s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath)) assert s1.font is None s1.name = "master.ufo1" s1.copyLib = True s1.copyInfo = True s1.copyFeatures = True s1.location = dict(weight=0) s1.familyName = "MasterFamilyName" s1.styleName = "MasterStyleNameOne" s1.mutedGlyphNames.append("A") s1.mutedGlyphNames.append("Z") doc.addSource(s1) # add master 2 s2 = SourceDescriptor() s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath)) s2.name = "master.ufo2" s2.copyLib = False s2.copyInfo = False s2.copyFeatures = False s2.muteKerning = True s2.location = dict(weight=1000) s2.familyName = "MasterFamilyName" s2.styleName = "MasterStyleNameTwo" doc.addSource(s2) # add master 3 from a different layer s3 = SourceDescriptor() s3.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath)) s3.name = "master.ufo2" s3.copyLib = False s3.copyInfo = False s3.copyFeatures = False s3.muteKerning = False s3.layerName = "supports" s3.location = dict(weight=1000) s3.familyName = "MasterFamilyName" s3.styleName = "Supports" doc.addSource(s3) # add instance 1 i1 = InstanceDescriptor() i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath)) i1.familyName = "InstanceFamilyName" i1.styleName = "InstanceStyleName" i1.name = "instance.ufo1" i1.location = dict(weight=500, spooky=666) # this adds a dimension that is not defined. i1.postScriptFontName = "InstancePostscriptName" i1.styleMapFamilyName = "InstanceStyleMapFamilyName" i1.styleMapStyleName = "InstanceStyleMapStyleName" glyphData = dict(name="arrow", mute=True, unicodes=[0x123, 0x124, 0x125]) i1.glyphs['arrow'] = glyphData i1.lib['com.coolDesignspaceApp.binaryData'] = plistlib.Data(b'<binary gunk>') i1.lib['com.coolDesignspaceApp.specimenText'] = "Hamburgerwhatever" doc.addInstance(i1) # add instance 2 i2 = InstanceDescriptor() i2.filename = os.path.relpath(instancePath2, os.path.dirname(testDocPath)) i2.familyName = "InstanceFamilyName" i2.styleName = "InstanceStyleName" i2.name = "instance.ufo2" # anisotropic location i2.location = dict(weight=500, width=(400,300)) i2.postScriptFontName = "InstancePostscriptName" i2.styleMapFamilyName = "InstanceStyleMapFamilyName" i2.styleMapStyleName = "InstanceStyleMapStyleName" glyphMasters = [dict(font="master.ufo1", glyphName="BB", location=dict(width=20,weight=20)), dict(font="master.ufo2", glyphName="CC", location=dict(width=900,weight=900))] glyphData = dict(name="arrow", unicodes=[101, 201, 301]) glyphData['masters'] = glyphMasters glyphData['note'] = "A note about this glyph" glyphData['instanceLocation'] = dict(width=100, weight=120) i2.glyphs['arrow'] = glyphData i2.glyphs['arrow2'] = dict(mute=False) doc.addInstance(i2) doc.filename = "suggestedFileName.designspace" doc.lib['com.coolDesignspaceApp.previewSize'] = 30 # write some rules r1 = RuleDescriptor() r1.name = "named.rule.1" r1.conditionSets.append([ dict(name='axisName_a', minimum=0, maximum=1), dict(name='axisName_b', minimum=2, maximum=3) ]) r1.subs.append(("a", "a.alt")) doc.addRule(r1) # write the document doc.write(testDocPath) assert os.path.exists(testDocPath) assert_equals_test_file(testDocPath, 'data/test.designspace') # import it again new = DesignSpaceDocument() new.read(testDocPath) assert new.default.location == {'width': 20.0, 'weight': 0.0} assert new.filename == 'test.designspace' assert new.lib == doc.lib assert new.instances[0].lib == doc.instances[0].lib # test roundtrip for the axis attributes and data axes = {} for axis in doc.axes: if axis.tag not in axes: axes[axis.tag] = [] axes[axis.tag].append(axis.serialize()) for axis in new.axes: if axis.tag[0] == "_": continue if axis.tag not in axes: axes[axis.tag] = [] axes[axis.tag].append(axis.serialize()) for v in axes.values(): a, b = v assert a == b
from mojo.UI import AskString from fontTools.designspaceLib import DesignSpaceDocument """ Remove glyphs from designspace rules 2019_11_28 Benedikt Bramboeck, Alphabet heavily based on https://github.com/arrowtype/varfont-prep/blob/master/remove-list-of-glyphs.py """ glyphsToDelete = AskString( 'Input glyphnames to remove, then select designspace').replace( "'", "").replace(",", "").split(" ") instruction = f"select designspace to remove {glyphsToDelete} from" docPath = getFile(instruction, allowsMultipleSelection=False, fileTypes=["designspace"]) doc = DesignSpaceDocument() doc.read(*docPath) for gName in glyphsToDelete: for rule in doc.rules: newSubs = [] for sub in rule.subs: if gName not in sub: newSubs.append(sub) rule.subs = newSubs doc.write(*docPath)
def test_rulesDocument(tmpdir): # tests of rules in a document, roundtripping. tmpdir = str(tmpdir) testDocPath = os.path.join(tmpdir, "testRules.designspace") testDocPath2 = os.path.join(tmpdir, "testRules_roundtrip.designspace") doc = DesignSpaceDocument() a1 = AxisDescriptor() a1.minimum = 0 a1.maximum = 1000 a1.default = 0 a1.name = "axisName_a" a1.tag = "TAGA" b1 = AxisDescriptor() b1.minimum = 2000 b1.maximum = 3000 b1.default = 2000 b1.name = "axisName_b" b1.tag = "TAGB" doc.addAxis(a1) doc.addAxis(b1) r1 = RuleDescriptor() r1.name = "named.rule.1" r1.conditionSets.append([ dict(name='axisName_a', minimum=0, maximum=1000), dict(name='axisName_b', minimum=0, maximum=3000) ]) r1.subs.append(("a", "a.alt")) # rule with minium and maximum doc.addRule(r1) assert len(doc.rules) == 1 assert len(doc.rules[0].conditionSets) == 1 assert len(doc.rules[0].conditionSets[0]) == 2 assert _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' } } assert doc.rules[0].conditionSets == [[{ 'minimum': 0, 'maximum': 1000, 'name': 'axisName_a' }, { 'minimum': 0, 'maximum': 3000, 'name': 'axisName_b' }]] assert doc.rules[0].subs == [('a', 'a.alt')] doc.normalize() assert doc.rules[0].name == 'named.rule.1' assert doc.rules[0].conditionSets == [[{ 'minimum': 0.0, 'maximum': 1.0, 'name': 'axisName_a' }, { 'minimum': 0.0, 'maximum': 1.0, 'name': 'axisName_b' }]] # still one conditionset assert len(doc.rules[0].conditionSets) == 1 doc.write(testDocPath) # add a stray conditionset _addUnwrappedCondition(testDocPath) doc2 = DesignSpaceDocument() doc2.read(testDocPath) 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
def test_superpolator_testdoc1(): # read superpolator_testdoc1.sp3 # and test all the values testDoc = DesignSpaceDocument() testPath = "../../Tests/spReader_testdocs/superpolator_testdoc1.sp3" reader = SuperpolatorReader(testPath, testDoc) reader.read() # check the axes names = [a.name for a in reader.documentObject.axes] names.sort() assert names == ['grade', 'space', 'weight', 'width'] tags = [a.tag for a in reader.documentObject.axes] tags.sort() assert tags == ['SPCE', 'grad', 'wdth', 'wght'] # check the data items assert superpolatorDataLibKey in reader.documentObject.lib items = list(reader.documentObject.lib[superpolatorDataLibKey].items()) items.sort() assert items == [('expandRules', False), ('horizontalPreviewAxis', 'width'), ('includeLegacyRules', False), ('instancefolder', 'instances'), ('keepWorkFiles', True), ('lineInverted', True), ('lineStacked', 'lined'), ('lineViewFilled', True), ('outputFormatUFO', 3.0), ('previewtext', 'VA'), ('roundGeometry', False), ('verticalPreviewAxis', 'weight')] # check the sources for sd in reader.documentObject.sources: assert sd.familyName == "MutatorMathTest_SourceFamilyName" if sd.styleName == "Default": assert sd.location == { 'width': 0.0, 'weight': 0.0, 'space': 0.0, 'grade': -0.5 } assert sd.copyLib == True assert sd.copyGroups == True assert sd.copyInfo == True assert sd.copyFeatures == True elif sd.styleName == "TheOther": assert sd.location == { 'width': 0.0, 'weight': 1000.0, 'space': 0.0, 'grade': -0.5 } assert sd.copyLib == False assert sd.copyGroups == False assert sd.copyInfo == False assert sd.copyFeatures == False # check the instances for nd in reader.documentObject.instances: assert nd.familyName == "MutatorMathTest_InstanceFamilyName" if nd.styleName == "AWeightThatILike": assert nd.location == { 'width': 133.152174, 'weight': 723.981097, 'space': 0.0, 'grade': -0.5 } assert nd.filename == "instances/MutatorMathTest_InstanceFamilyName-AWeightThatILike.ufo" assert nd.styleMapFamilyName == None assert nd.styleMapStyleName == None if nd.styleName == "wdth759.79_SPCE0.00_wght260.72": # note the converted anisotropic location in the width axis. assert nd.location == { 'grade': -0.5, 'width': 500.0, 'weight': 260.7217, 'space': 0.0 } assert nd.filename == "instances/MutatorMathTest_InstanceFamilyName-wdth759.79_SPCE0.00_wght260.72.ufo" assert nd.styleMapFamilyName == "StyleMappedFamily" assert nd.styleMapStyleName == "bold" # check the rules for rd in reader.documentObject.rules: assert rd.name == "width: < 500.0" assert len(rd.conditionSets) == 1 assert rd.subs == [('I', 'I.narrow')] for conditionSet in rd.conditionSets: for cd in conditionSet: if cd['name'] == "width": assert cd == { 'minimum': None, 'maximum': 500.0, 'name': 'width' } if cd['name'] == "grade": assert cd == { 'minimum': 0.0, 'maximum': 500.0, 'name': 'grade' } testDoc.write( testPath.replace(".sp3", "_output_roundtripped.designspace"))
def designSpace(self): designspace = DesignSpaceDocument() designspace.read(self.designspace_path) return designspace
def test_method(self, filename=filename): doc = DesignSpaceDocument(writerClass=InMemoryDocWriter) doc.read(filename) self.assertDesignspaceRoundtrip(doc)