[feaLib] Support variable ligature caret position
Allow variable scaler in ligature caret position and build CaretValueFormat3 with DeviceTable. Does not support non-variable device table, but can be added if someone really really wants it.
This commit is contained in:
parent
96bede39e7
commit
a43f824af9
@ -1553,7 +1553,16 @@ class Builder(object):
|
||||
if glyph not in self.ligCaretPoints_:
|
||||
self.ligCaretPoints_[glyph] = carets
|
||||
|
||||
def makeLigCaret(self, location, caret):
|
||||
if not isinstance(caret, VariableScalar):
|
||||
return caret
|
||||
default, device = self.makeVariablePos(location, caret)
|
||||
if device is not None:
|
||||
return (default, device)
|
||||
return default
|
||||
|
||||
def add_ligatureCaretByPos_(self, location, glyphs, carets):
|
||||
carets = [self.makeLigCaret(location, caret) for caret in carets]
|
||||
for glyph in glyphs:
|
||||
if glyph not in self.ligCaretCoords_:
|
||||
self.ligCaretCoords_[glyph] = carets
|
||||
|
@ -603,9 +603,9 @@ class Parser(object):
|
||||
assert self.is_cur_keyword_("LigatureCaretByPos")
|
||||
location = self.cur_token_location_
|
||||
glyphs = self.parse_glyphclass_(accept_glyphname=True)
|
||||
carets = [self.expect_number_()]
|
||||
carets = [self.expect_number_(variable=True)]
|
||||
while self.next_token_ != ";":
|
||||
carets.append(self.expect_number_())
|
||||
carets.append(self.expect_number_(variable=True))
|
||||
self.expect_symbol_(";")
|
||||
return self.ast.LigatureCaretByPosStatement(glyphs, carets, location=location)
|
||||
|
||||
|
@ -2509,7 +2509,12 @@ def buildAttachPoint(points):
|
||||
|
||||
def buildCaretValueForCoord(coord):
|
||||
# 500 --> otTables.CaretValue, format 1
|
||||
# (500, DeviceTable) --> otTables.CaretValue, format 3
|
||||
self = ot.CaretValue()
|
||||
if isinstance(coord, tuple):
|
||||
self.Format = 3
|
||||
self.Coordinate, self.DeviceTable = coord
|
||||
else:
|
||||
self.Format = 1
|
||||
self.Coordinate = coord
|
||||
return self
|
||||
@ -2573,7 +2578,8 @@ def buildLigGlyph(coords, points):
|
||||
# ([500], [4]) --> otTables.LigGlyph; None for empty coords/points
|
||||
carets = []
|
||||
if coords:
|
||||
carets.extend([buildCaretValueForCoord(c) for c in sorted(coords)])
|
||||
coords = sorted(coords, key=lambda c: c[0] if isinstance(c, tuple) else c)
|
||||
carets.extend([buildCaretValueForCoord(c) for c in coords])
|
||||
if points:
|
||||
carets.extend([buildCaretValueForPoint(p) for p in sorted(points)])
|
||||
if not carets:
|
||||
|
@ -226,6 +226,21 @@ class BuilderTest(unittest.TestCase):
|
||||
output.append(l)
|
||||
return output
|
||||
|
||||
def make_mock_vf(self):
|
||||
font = makeTTFont()
|
||||
font["name"] = newTable("name")
|
||||
addFvar(font, self.VARFONT_AXES, [])
|
||||
del font["name"]
|
||||
return font
|
||||
|
||||
@staticmethod
|
||||
def get_region(var_region_axis):
|
||||
return (
|
||||
var_region_axis.StartCoord,
|
||||
var_region_axis.PeakCoord,
|
||||
var_region_axis.EndCoord,
|
||||
)
|
||||
|
||||
def test_alternateSubst_multipleSubstitutionsForSameGlyph(self):
|
||||
self.assertRaisesRegex(
|
||||
FeatureLibError,
|
||||
@ -1046,37 +1061,23 @@ class BuilderTest(unittest.TestCase):
|
||||
} kern;
|
||||
"""
|
||||
|
||||
def make_mock_vf():
|
||||
font = makeTTFont()
|
||||
font["name"] = newTable("name")
|
||||
addFvar(font, self.VARFONT_AXES, [])
|
||||
del font["name"]
|
||||
return font
|
||||
|
||||
def get_region(var_region_axis):
|
||||
return (
|
||||
var_region_axis.StartCoord,
|
||||
var_region_axis.PeakCoord,
|
||||
var_region_axis.EndCoord,
|
||||
)
|
||||
|
||||
# Without `avar` (wght=200, wdth=100 is the default location):
|
||||
font = make_mock_vf()
|
||||
font = self.make_mock_vf()
|
||||
addOpenTypeFeaturesFromString(font, features)
|
||||
|
||||
var_region_list = font.tables["GDEF"].table.VarStore.VarRegionList
|
||||
var_region_axis_wght = var_region_list.Region[0].VarRegionAxis[0]
|
||||
var_region_axis_wdth = var_region_list.Region[0].VarRegionAxis[1]
|
||||
assert get_region(var_region_axis_wght) == (0.0, 0.875, 0.875)
|
||||
assert get_region(var_region_axis_wdth) == (0.0, 0.0, 0.0)
|
||||
assert self.get_region(var_region_axis_wght) == (0.0, 0.875, 0.875)
|
||||
assert self.get_region(var_region_axis_wdth) == (0.0, 0.0, 0.0)
|
||||
var_region_axis_wght = var_region_list.Region[1].VarRegionAxis[0]
|
||||
var_region_axis_wdth = var_region_list.Region[1].VarRegionAxis[1]
|
||||
assert get_region(var_region_axis_wght) == (0.0, 0.875, 0.875)
|
||||
assert get_region(var_region_axis_wdth) == (0.0, 0.5, 0.5)
|
||||
assert self.get_region(var_region_axis_wght) == (0.0, 0.875, 0.875)
|
||||
assert self.get_region(var_region_axis_wdth) == (0.0, 0.5, 0.5)
|
||||
|
||||
# With `avar`, shifting the wght axis' positive midpoint 0.5 a bit to
|
||||
# the right, but leaving the wdth axis alone:
|
||||
font = make_mock_vf()
|
||||
font = self.make_mock_vf()
|
||||
font["avar"] = newTable("avar")
|
||||
font["avar"].segments = {"wght": {-1.0: -1.0, 0.0: 0.0, 0.5: 0.625, 1.0: 1.0}}
|
||||
addOpenTypeFeaturesFromString(font, features)
|
||||
@ -1084,12 +1085,36 @@ class BuilderTest(unittest.TestCase):
|
||||
var_region_list = font.tables["GDEF"].table.VarStore.VarRegionList
|
||||
var_region_axis_wght = var_region_list.Region[0].VarRegionAxis[0]
|
||||
var_region_axis_wdth = var_region_list.Region[0].VarRegionAxis[1]
|
||||
assert get_region(var_region_axis_wght) == (0.0, 0.90625, 0.90625)
|
||||
assert get_region(var_region_axis_wdth) == (0.0, 0.0, 0.0)
|
||||
assert self.get_region(var_region_axis_wght) == (0.0, 0.90625, 0.90625)
|
||||
assert self.get_region(var_region_axis_wdth) == (0.0, 0.0, 0.0)
|
||||
var_region_axis_wght = var_region_list.Region[1].VarRegionAxis[0]
|
||||
var_region_axis_wdth = var_region_list.Region[1].VarRegionAxis[1]
|
||||
assert get_region(var_region_axis_wght) == (0.0, 0.90625, 0.90625)
|
||||
assert get_region(var_region_axis_wdth) == (0.0, 0.5, 0.5)
|
||||
assert self.get_region(var_region_axis_wght) == (0.0, 0.90625, 0.90625)
|
||||
assert self.get_region(var_region_axis_wdth) == (0.0, 0.5, 0.5)
|
||||
|
||||
def test_ligatureCaretByPos_variable_scalar(self):
|
||||
"""Test that the `avar` table is consulted when normalizing user-space
|
||||
values."""
|
||||
|
||||
features = """
|
||||
table GDEF {
|
||||
LigatureCaretByPos f_i (wght=200:400 wght=900:1000) 380;
|
||||
} GDEF;
|
||||
"""
|
||||
|
||||
font = self.make_mock_vf()
|
||||
addOpenTypeFeaturesFromString(font, features)
|
||||
|
||||
table = font["GDEF"].table
|
||||
lig_glyph = table.LigCaretList.LigGlyph[0]
|
||||
assert lig_glyph.CaretValue[0].Format == 1
|
||||
assert lig_glyph.CaretValue[0].Coordinate == 380
|
||||
assert lig_glyph.CaretValue[1].Format == 3
|
||||
assert lig_glyph.CaretValue[1].Coordinate == 400
|
||||
|
||||
var_region_list = table.VarStore.VarRegionList
|
||||
var_region_axis = var_region_list.Region[0].VarRegionAxis[0]
|
||||
assert self.get_region(var_region_axis) == (0.0, 0.875, 0.875)
|
||||
|
||||
|
||||
def generate_feature_file_test(name):
|
||||
|
@ -707,6 +707,17 @@ class ParserTest(unittest.TestCase):
|
||||
self.assertEqual(glyphstr([s.glyphs]), "f_i")
|
||||
self.assertEqual(s.carets, [400, 380])
|
||||
|
||||
def test_ligatureCaretByPos_variable_scalar(self):
|
||||
doc = self.parse(
|
||||
"table GDEF {LigatureCaretByPos f_i (wght=200:400 wght=900:1000) 380;} GDEF;"
|
||||
)
|
||||
s = doc.statements[0].statements[0]
|
||||
self.assertIsInstance(s, ast.LigatureCaretByPosStatement)
|
||||
self.assertEqual(glyphstr([s.glyphs]), "f_i")
|
||||
self.assertEqual(len(s.carets), 2)
|
||||
self.assertEqual(str(s.carets[0]), "(wght=200:400 wght=900:1000)")
|
||||
self.assertEqual(s.carets[1], 380)
|
||||
|
||||
def test_lookup_block(self):
|
||||
[lookup] = self.parse("lookup Ligatures {} Ligatures;").statements
|
||||
self.assertEqual(lookup.name, "Ligatures")
|
||||
|
Loading…
x
Reference in New Issue
Block a user