Merge pull request #1860 from anthrotype/dslib-constructors

designspaceLib: allow to construct descriptors from keyword args
This commit is contained in:
Cosimo Lupo 2020-03-20 16:29:47 +00:00 committed by GitHub
commit c43c6b2d99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 194 additions and 48 deletions

View File

@ -100,14 +100,32 @@ class SourceDescriptor(SimpleDescriptor):
'mutedGlyphNames', 'mutedGlyphNames',
'familyName', 'styleName'] 'familyName', 'styleName']
def __init__(self): def __init__(
self.filename = None self,
*,
filename=None,
path=None,
font=None,
name=None,
location=None,
layerName=None,
familyName=None,
styleName=None,
copyLib=False,
copyInfo=False,
copyGroups=False,
copyFeatures=False,
muteKerning=False,
muteInfo=False,
mutedGlyphNames=None,
):
self.filename = filename
"""The original path as found in the document.""" """The original path as found in the document."""
self.path = None self.path = path
"""The absolute path, calculated from filename.""" """The absolute path, calculated from filename."""
self.font = None self.font = font
"""Any Python object. Optional. Points to a representation of this """Any Python object. Optional. Points to a representation of this
source font that is loaded in memory, as a Python object (e.g. a source font that is loaded in memory, as a Python object (e.g. a
``defcon.Font`` or a ``fontTools.ttFont.TTFont``). ``defcon.Font`` or a ``fontTools.ttFont.TTFont``).
@ -119,18 +137,19 @@ class SourceDescriptor(SimpleDescriptor):
this field to the disk and make ```filename`` point to that. this field to the disk and make ```filename`` point to that.
""" """
self.name = None self.name = name
self.location = None self.location = location
self.layerName = None self.layerName = layerName
self.copyLib = False self.familyName = familyName
self.copyInfo = False self.styleName = styleName
self.copyGroups = False
self.copyFeatures = False self.copyLib = copyLib
self.muteKerning = False self.copyInfo = copyInfo
self.muteInfo = False self.copyGroups = copyGroups
self.mutedGlyphNames = [] self.copyFeatures = copyFeatures
self.familyName = None self.muteKerning = muteKerning
self.styleName = None self.muteInfo = muteInfo
self.mutedGlyphNames = mutedGlyphNames or []
path = posixpath_property("_path") path = posixpath_property("_path")
filename = posixpath_property("_filename") filename = posixpath_property("_filename")
@ -152,10 +171,12 @@ class RuleDescriptor(SimpleDescriptor):
""" """
_attrs = ['name', 'conditionSets', 'subs'] # what do we need here _attrs = ['name', 'conditionSets', 'subs'] # what do we need here
def __init__(self): def __init__(self, *, name=None, conditionSets=None, subs=None):
self.name = None self.name = name
self.conditionSets = [] # list of list of dict(name='aaaa', minimum=0, maximum=1000) # list of lists of dict(name='aaaa', minimum=0, maximum=1000)
self.subs = [] # list of substitutions stored as tuples of glyphnames ("a", "a.alt") self.conditionSets = conditionSets or []
# list of substitutions stored as tuples of glyphnames ("a", "a.alt")
self.subs = subs or []
def evaluateRule(rule, location): def evaluateRule(rule, location):
@ -219,26 +240,50 @@ class InstanceDescriptor(SimpleDescriptor):
'info', 'info',
'lib'] 'lib']
def __init__(self): def __init__(
self.filename = None # the original path as found in the document self,
self.path = None # the absolute path, calculated from filename *,
self.font = None # Same as in SourceDescriptor. filename=None,
self.name = None path=None,
self.location = None font=None,
self.familyName = None name=None,
self.styleName = None location=None,
self.postScriptFontName = None familyName=None,
self.styleMapFamilyName = None styleName=None,
self.styleMapStyleName = None postScriptFontName=None,
self.localisedStyleName = {} styleMapFamilyName=None,
self.localisedFamilyName = {} styleMapStyleName=None,
self.localisedStyleMapStyleName = {} localisedFamilyName=None,
self.localisedStyleMapFamilyName = {} localisedStyleName=None,
self.glyphs = {} localisedStyleMapFamilyName=None,
self.kerning = True localisedStyleMapStyleName=None,
self.info = True glyphs=None,
kerning=True,
info=True,
lib=None,
):
# the original path as found in the document
self.filename = filename
# the absolute path, calculated from filename
self.path = path
# Same as in SourceDescriptor.
self.font = font
self.name = name
self.location = location
self.familyName = familyName
self.styleName = styleName
self.postScriptFontName = postScriptFontName
self.styleMapFamilyName = styleMapFamilyName
self.styleMapStyleName = styleMapStyleName
self.localisedFamilyName = localisedFamilyName or {}
self.localisedStyleName = localisedStyleName or {}
self.localisedStyleMapFamilyName = localisedStyleMapFamilyName or {}
self.localisedStyleMapStyleName = localisedStyleMapStyleName or {}
self.glyphs = glyphs or {}
self.kerning = kerning
self.info = info
self.lib = {} self.lib = lib or {}
"""Custom data associated with this instance.""" """Custom data associated with this instance."""
path = posixpath_property("_path") path = posixpath_property("_path")
@ -294,15 +339,29 @@ class AxisDescriptor(SimpleDescriptor):
flavor = "axis" flavor = "axis"
_attrs = ['tag', 'name', 'maximum', 'minimum', 'default', 'map'] _attrs = ['tag', 'name', 'maximum', 'minimum', 'default', 'map']
def __init__(self): def __init__(
self.tag = None # opentype tag for this axis self,
self.name = None # name of the axis used in locations *,
self.labelNames = {} # names for UI purposes, if this is not a standard axis, tag=None,
self.minimum = None name=None,
self.maximum = None labelNames=None,
self.default = None minimum=None,
self.hidden = False default=None,
self.map = [] maximum=None,
hidden=False,
map=None,
):
# opentype tag for this axis
self.tag = tag
# name of the axis used in locations
self.name = name
# names for UI purposes, if this is not a standard axis,
self.labelNames = labelNames or {}
self.minimum = minimum
self.maximum = maximum
self.default = default
self.hidden = hidden
self.map = map or []
def serialize(self): def serialize(self):
# output to a dict, used in testing # output to a dict, used in testing
@ -1129,15 +1188,35 @@ class DesignSpaceDocument(LogMixin, AsDictMixin):
def addSource(self, sourceDescriptor): def addSource(self, sourceDescriptor):
self.sources.append(sourceDescriptor) self.sources.append(sourceDescriptor)
def addSourceDescriptor(self, **kwargs):
source = self.writerClass.sourceDescriptorClass(**kwargs)
self.addSource(source)
return source
def addInstance(self, instanceDescriptor): def addInstance(self, instanceDescriptor):
self.instances.append(instanceDescriptor) self.instances.append(instanceDescriptor)
def addInstanceDescriptor(self, **kwargs):
instance = self.writerClass.instanceDescriptorClass(**kwargs)
self.addInstance(instance)
return instance
def addAxis(self, axisDescriptor): def addAxis(self, axisDescriptor):
self.axes.append(axisDescriptor) self.axes.append(axisDescriptor)
def addAxisDescriptor(self, **kwargs):
axis = self.writerClass.axisDescriptorClass(**kwargs)
self.addAxis(axis)
return axis
def addRule(self, ruleDescriptor): def addRule(self, ruleDescriptor):
self.rules.append(ruleDescriptor) self.rules.append(ruleDescriptor)
def addRuleDescriptor(self, **kwargs):
rule = self.writerClass.ruleDescriptorClass(**kwargs)
self.addRule(rule)
return rule
def newDefaultLocation(self): def newDefaultLocation(self):
"""Return default location in design space.""" """Return default location in design space."""
# Without OrderedDict, output XML would be non-deterministic. # Without OrderedDict, output XML would be non-deterministic.

View File

@ -949,3 +949,70 @@ def test_loadSourceFonts_no_required_path():
with pytest.raises(DesignSpaceDocumentError, match="no 'path' attribute"): with pytest.raises(DesignSpaceDocumentError, match="no 'path' attribute"):
designspace.loadSourceFonts(lambda p: p) designspace.loadSourceFonts(lambda p: p)
def test_addAxisDescriptor():
ds = DesignSpaceDocument()
axis = ds.addAxisDescriptor(
name="Weight", tag="wght", minimum=100, default=400, maximum=900
)
assert ds.axes[0] is axis
assert isinstance(axis, AxisDescriptor)
assert axis.name == "Weight"
assert axis.tag == "wght"
assert axis.minimum == 100
assert axis.default == 400
assert axis.maximum == 900
def test_addSourceDescriptor():
ds = DesignSpaceDocument()
source = ds.addSourceDescriptor(name="TestSource", location={"Weight": 400})
assert ds.sources[0] is source
assert isinstance(source, SourceDescriptor)
assert source.name == "TestSource"
assert source.location == {"Weight": 400}
def test_addInstanceDescriptor():
ds = DesignSpaceDocument()
instance = ds.addInstanceDescriptor(
name="TestInstance",
location={"Weight": 400},
styleName="Regular",
styleMapStyleName="regular",
)
assert ds.instances[0] is instance
assert isinstance(instance, InstanceDescriptor)
assert instance.name == "TestInstance"
assert instance.location == {"Weight": 400}
assert instance.styleName == "Regular"
assert instance.styleMapStyleName == "regular"
def test_addRuleDescriptor():
ds = DesignSpaceDocument()
rule = ds.addRuleDescriptor(
name="TestRule",
conditionSets=[
dict(name='Weight', minimum=100, maximum=200),
dict(name='Weight', minimum=700, maximum=900),
],
subs=[("a", "a.alt")],
)
assert ds.rules[0] is rule
assert isinstance(rule, RuleDescriptor)
assert rule.name == "TestRule"
assert rule.conditionSets == [
dict(name='Weight', minimum=100, maximum=200),
dict(name='Weight', minimum=700, maximum=900),
]
assert rule.subs == [("a", "a.alt")]