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))
|
||||
newConditionSets.append(newConditions)
|
||||
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 (
|
||||
DesignSpaceDocument, SourceDescriptor, AxisDescriptor, RuleDescriptor,
|
||||
InstanceDescriptor, evaluateRule, processRules, posix, DesignSpaceDocumentError)
|
||||
from fontTools import ttLib
|
||||
|
||||
def _axesAsDict(axes):
|
||||
"""
|
||||
@ -909,3 +910,42 @@ def test_findDefault_axis_mapping():
|
||||
designspace.axes[1].default = 0
|
||||
|
||||
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