Skip to content

Commit 2855c77

Browse files
committed
initial implementation of L3 sub-space slicing support
1 parent 30dc537 commit 2855c77

File tree

2 files changed

+44
-22
lines changed

2 files changed

+44
-22
lines changed

src/slice/instanceworker.py

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,7 @@
2020
from fontTools.misc.textTools import num2binary
2121
from fontTools.ttLib import sfnt
2222
from fontTools.ttLib.ttFont import TTFont
23-
from fontTools.varLib.instancer import (
24-
instantiateVariableFont as partial_instantiateVariableFont,
25-
)
26-
from fontTools.varLib.mutator import (
27-
instantiateVariableFont as static_instantiateVariableFont,
28-
)
23+
from fontTools.varLib.instancer import instantiateVariableFont
2924
from PyQt5.QtCore import QObject, QRunnable, pyqtSignal, pyqtSlot
3025

3126

@@ -85,24 +80,11 @@ def instantiate_ttfont(self):
8580

8681
def instantiate_variable_font(self):
8782
axis_instance_data = self.axis_model.get_instance_data()
88-
axis_tag_number = len(self.ttfont["fvar"].axes)
89-
# this is a partial instance request if the number of axis_instance_data
90-
# definitions is less than the number of axes in the font
91-
is_partial_instance = axis_tag_number > len(axis_instance_data)
92-
# Execute the full static or partial instantiation.
93-
# The static vs. partial approach is defined by the
94-
# axis values contained in the axis_instance_data dict.
95-
# When an axis tag is not present, that axis remains variable
96-
# in the new font and uses the variable instantiator method
97-
if is_partial_instance:
98-
partial_instantiateVariableFont(
99-
self.ttfont, axis_instance_data, inplace=True, optimize=True
100-
)
101-
else:
102-
static_instantiateVariableFont(self.ttfont, axis_instance_data, inplace=True)
83+
instantiateVariableFont(
84+
self.ttfont, axis_instance_data, inplace=True, optimize=True
85+
)
10386
print("\nAXIS INSTANCE VALUES")
10487
print(f"Instantiated variable font with axis definitions:\n{axis_instance_data}")
105-
print(f"Partial instance: {is_partial_instance}")
10688

10789
def edit_name_table(self):
10890
# string, nameID, platformID, platEncID, langID

src/slice/models.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,8 @@ def get_instance_data(self):
281281
# if user did not define the axis value, then
282282
# it remains a variable axis
283283
pass
284+
elif ":" in axis_value:
285+
instance_data[axistag] = self.parse_subspace_range(axis_value, axistag)
284286
else:
285287
# else use the numeric value set in the editor
286288
try:
@@ -293,6 +295,44 @@ def get_instance_data(self):
293295

294296
return instance_data
295297

298+
def parse_subspace_range(self, range_string, axistag):
299+
range_list = range_string.split(":")
300+
# expect 2 values: min and max range sub-space values
301+
# formatted as `value1:value2`
302+
if len(range_list) != 2:
303+
raise ValueError(
304+
f"{range_string} is not a valid axis range. "
305+
f"Use the format `min_value:max_value`"
306+
)
307+
# remove any extraneous whitespace (e.g., "100 : 200" entry)
308+
# before attempt to cast to a float
309+
try:
310+
range_list[0] = float(range_list[0].strip())
311+
range_list[1] = float(range_list[1].strip())
312+
except ValueError as e:
313+
raise ValueError(f"{range_string} is not a valid axis value range. {e}")
314+
# order the values in case they were entered in reverse
315+
# e.g., 800:400, not 400:800
316+
sorted_range_list = sorted(range_list)
317+
# We only support Level 3 sub-spacing due to the support
318+
# that is available in fontTools lib. Let's check that user
319+
# included the default axis value in the range request
320+
self.subspace_data_validates_includes_default_value(sorted_range_list, axistag)
321+
# all seems well, return data formatted as tuple
322+
# this is the data format required by fonttools lib
323+
return (sorted_range_list[0], sorted_range_list[1])
324+
325+
def subspace_data_validates_includes_default_value(self, range_list, axistag):
326+
"""Validates Level 3 sub-space requirement that restricted axis range
327+
includes the default axis value."""
328+
default = self.get_default_axis_value(axistag)
329+
if default < range_list[0] or default > range_list[1]:
330+
raise ValueError(
331+
f"{axistag} range {range_list[0]}:{range_list[1]} does not "
332+
f"include the default axis value. This is currently a "
333+
f"requirement."
334+
)
335+
296336
def instance_data_validates_missing_data(self):
297337
# validator that returns True if there is at least one
298338
# axis tag with a defined instance, and False if all

0 commit comments

Comments
 (0)