designspaceLib: add loadSourceFonts method with custom opener
Allows to load the SourceDescriptor.font attribute from its path, using a custom callable (e.g. defcon.Font or ttLib.TTFont, etc.).
This commit is contained in:
parent
85c3f85526
commit
44f74dc8bb
@ -1262,3 +1262,41 @@ class DesignSpaceDocument(LogMixin, AsDictMixin):
|
|||||||
newConditions.append(dict(name=cond['name'], minimum=minimum, maximum=maximum))
|
newConditions.append(dict(name=cond['name'], minimum=minimum, maximum=maximum))
|
||||||
newConditionSets.append(newConditions)
|
newConditionSets.append(newConditions)
|
||||||
rule.conditionSets = newConditionSets
|
rule.conditionSets = newConditionSets
|
||||||
|
|
||||||
|
def loadSourceFonts(self, opener, **kwargs):
|
||||||
|
"""Ensure SourceDescriptor.font attributes are loaded, and return list of fonts.
|
||||||
|
|
||||||
|
Takes a callable which initializes a new font object (e.g. TTFont, or
|
||||||
|
defcon.Font, etc.) from the SourceDescriptor.path, and sets the
|
||||||
|
SourceDescriptor.font attribute.
|
||||||
|
If the font attribute is already not None, it is not loaded again.
|
||||||
|
Fonts with the same path are only loaded once and shared among SourceDescriptors.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
opener (Callable): takes one required positional argument, the source.path,
|
||||||
|
and an optional list of keyword arguments, and returns a new font object
|
||||||
|
loaded from the path.
|
||||||
|
**kwargs: extra options passed on to the opener function.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of font objects in the order they appear in the sources list.
|
||||||
|
"""
|
||||||
|
# we load fonts with the same source.path only once
|
||||||
|
loaded = {}
|
||||||
|
fonts = []
|
||||||
|
for source in self.sources:
|
||||||
|
if source.font is not None: # font already loaded
|
||||||
|
fonts.append(source.font)
|
||||||
|
continue
|
||||||
|
if source.path in loaded:
|
||||||
|
source.font = loaded[source.path]
|
||||||
|
else:
|
||||||
|
if source.path is None:
|
||||||
|
raise DesignSpaceDocumentError(
|
||||||
|
"Designspace source '%s' has no 'path' attribute"
|
||||||
|
% (source.name or "<Unknown>")
|
||||||
|
)
|
||||||
|
source.font = opener(source.path, **kwargs)
|
||||||
|
loaded[source.path] = source.font
|
||||||
|
fonts.append(source.font)
|
||||||
|
return fonts
|
||||||
|
@ -13,6 +13,7 @@ from fontTools.misc import plistlib
|
|||||||
from fontTools.designspaceLib import (
|
from fontTools.designspaceLib import (
|
||||||
DesignSpaceDocument, SourceDescriptor, AxisDescriptor, RuleDescriptor,
|
DesignSpaceDocument, SourceDescriptor, AxisDescriptor, RuleDescriptor,
|
||||||
InstanceDescriptor, evaluateRule, processRules, posix, DesignSpaceDocumentError)
|
InstanceDescriptor, evaluateRule, processRules, posix, DesignSpaceDocumentError)
|
||||||
|
from fontTools import ttLib
|
||||||
|
|
||||||
def _axesAsDict(axes):
|
def _axesAsDict(axes):
|
||||||
"""
|
"""
|
||||||
@ -909,3 +910,42 @@ def test_findDefault_axis_mapping():
|
|||||||
designspace.axes[1].default = 0
|
designspace.axes[1].default = 0
|
||||||
|
|
||||||
assert designspace.findDefault().filename == "Font-Regular.ufo"
|
assert designspace.findDefault().filename == "Font-Regular.ufo"
|
||||||
|
|
||||||
|
|
||||||
|
def test_loadSourceFonts():
|
||||||
|
|
||||||
|
def opener(path):
|
||||||
|
font = ttLib.TTFont()
|
||||||
|
font.importXML(path)
|
||||||
|
return font
|
||||||
|
|
||||||
|
# this designspace file contains .TTX source paths
|
||||||
|
path = os.path.join(
|
||||||
|
os.path.dirname(os.path.dirname(__file__)),
|
||||||
|
"varLib",
|
||||||
|
"data",
|
||||||
|
"SparseMasters.designspace"
|
||||||
|
)
|
||||||
|
designspace = DesignSpaceDocument.fromfile(path)
|
||||||
|
|
||||||
|
# force two source descriptors to have the same path
|
||||||
|
designspace.sources[1].path = designspace.sources[0].path
|
||||||
|
|
||||||
|
fonts = designspace.loadSourceFonts(opener)
|
||||||
|
|
||||||
|
assert len(fonts) == 3
|
||||||
|
assert all(isinstance(font, ttLib.TTFont) for font in fonts)
|
||||||
|
assert fonts[0] is fonts[1] # same path, identical font object
|
||||||
|
|
||||||
|
fonts2 = designspace.loadSourceFonts(opener)
|
||||||
|
|
||||||
|
for font1, font2 in zip(fonts, fonts2):
|
||||||
|
assert font1 is font2
|
||||||
|
|
||||||
|
|
||||||
|
def test_loadSourceFonts_no_required_path():
|
||||||
|
designspace = DesignSpaceDocument()
|
||||||
|
designspace.sources.append(SourceDescriptor())
|
||||||
|
|
||||||
|
with pytest.raises(DesignSpaceDocumentError, match="no 'path' attribute"):
|
||||||
|
designspace.loadSourceFonts(lambda p: p)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user