Merge pull request #3130 from fonttools/fealib-ligcaret-variable
[feaLib] Support variable ligature caret position
This commit is contained in:
commit
9ad4a36309
@ -1553,7 +1553,16 @@ class Builder(object):
|
|||||||
if glyph not in self.ligCaretPoints_:
|
if glyph not in self.ligCaretPoints_:
|
||||||
self.ligCaretPoints_[glyph] = carets
|
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):
|
def add_ligatureCaretByPos_(self, location, glyphs, carets):
|
||||||
|
carets = [self.makeLigCaret(location, caret) for caret in carets]
|
||||||
for glyph in glyphs:
|
for glyph in glyphs:
|
||||||
if glyph not in self.ligCaretCoords_:
|
if glyph not in self.ligCaretCoords_:
|
||||||
self.ligCaretCoords_[glyph] = carets
|
self.ligCaretCoords_[glyph] = carets
|
||||||
|
@ -603,9 +603,9 @@ class Parser(object):
|
|||||||
assert self.is_cur_keyword_("LigatureCaretByPos")
|
assert self.is_cur_keyword_("LigatureCaretByPos")
|
||||||
location = self.cur_token_location_
|
location = self.cur_token_location_
|
||||||
glyphs = self.parse_glyphclass_(accept_glyphname=True)
|
glyphs = self.parse_glyphclass_(accept_glyphname=True)
|
||||||
carets = [self.expect_number_()]
|
carets = [self.expect_number_(variable=True)]
|
||||||
while self.next_token_ != ";":
|
while self.next_token_ != ";":
|
||||||
carets.append(self.expect_number_())
|
carets.append(self.expect_number_(variable=True))
|
||||||
self.expect_symbol_(";")
|
self.expect_symbol_(";")
|
||||||
return self.ast.LigatureCaretByPosStatement(glyphs, carets, location=location)
|
return self.ast.LigatureCaretByPosStatement(glyphs, carets, location=location)
|
||||||
|
|
||||||
|
@ -2509,9 +2509,14 @@ def buildAttachPoint(points):
|
|||||||
|
|
||||||
def buildCaretValueForCoord(coord):
|
def buildCaretValueForCoord(coord):
|
||||||
# 500 --> otTables.CaretValue, format 1
|
# 500 --> otTables.CaretValue, format 1
|
||||||
|
# (500, DeviceTable) --> otTables.CaretValue, format 3
|
||||||
self = ot.CaretValue()
|
self = ot.CaretValue()
|
||||||
self.Format = 1
|
if isinstance(coord, tuple):
|
||||||
self.Coordinate = coord
|
self.Format = 3
|
||||||
|
self.Coordinate, self.DeviceTable = coord
|
||||||
|
else:
|
||||||
|
self.Format = 1
|
||||||
|
self.Coordinate = coord
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
@ -2573,7 +2578,8 @@ def buildLigGlyph(coords, points):
|
|||||||
# ([500], [4]) --> otTables.LigGlyph; None for empty coords/points
|
# ([500], [4]) --> otTables.LigGlyph; None for empty coords/points
|
||||||
carets = []
|
carets = []
|
||||||
if coords:
|
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:
|
if points:
|
||||||
carets.extend([buildCaretValueForPoint(p) for p in sorted(points)])
|
carets.extend([buildCaretValueForPoint(p) for p in sorted(points)])
|
||||||
if not carets:
|
if not carets:
|
||||||
|
@ -226,6 +226,21 @@ class BuilderTest(unittest.TestCase):
|
|||||||
output.append(l)
|
output.append(l)
|
||||||
return output
|
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):
|
def test_alternateSubst_multipleSubstitutionsForSameGlyph(self):
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
FeatureLibError,
|
FeatureLibError,
|
||||||
@ -1046,37 +1061,23 @@ class BuilderTest(unittest.TestCase):
|
|||||||
} kern;
|
} 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):
|
# Without `avar` (wght=200, wdth=100 is the default location):
|
||||||
font = make_mock_vf()
|
font = self.make_mock_vf()
|
||||||
addOpenTypeFeaturesFromString(font, features)
|
addOpenTypeFeaturesFromString(font, features)
|
||||||
|
|
||||||
var_region_list = font.tables["GDEF"].table.VarStore.VarRegionList
|
var_region_list = font.tables["GDEF"].table.VarStore.VarRegionList
|
||||||
var_region_axis_wght = var_region_list.Region[0].VarRegionAxis[0]
|
var_region_axis_wght = var_region_list.Region[0].VarRegionAxis[0]
|
||||||
var_region_axis_wdth = var_region_list.Region[0].VarRegionAxis[1]
|
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 self.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_wdth) == (0.0, 0.0, 0.0)
|
||||||
var_region_axis_wght = var_region_list.Region[1].VarRegionAxis[0]
|
var_region_axis_wght = var_region_list.Region[1].VarRegionAxis[0]
|
||||||
var_region_axis_wdth = var_region_list.Region[1].VarRegionAxis[1]
|
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 self.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_wdth) == (0.0, 0.5, 0.5)
|
||||||
|
|
||||||
# With `avar`, shifting the wght axis' positive midpoint 0.5 a bit to
|
# With `avar`, shifting the wght axis' positive midpoint 0.5 a bit to
|
||||||
# the right, but leaving the wdth axis alone:
|
# the right, but leaving the wdth axis alone:
|
||||||
font = make_mock_vf()
|
font = self.make_mock_vf()
|
||||||
font["avar"] = newTable("avar")
|
font["avar"] = newTable("avar")
|
||||||
font["avar"].segments = {"wght": {-1.0: -1.0, 0.0: 0.0, 0.5: 0.625, 1.0: 1.0}}
|
font["avar"].segments = {"wght": {-1.0: -1.0, 0.0: 0.0, 0.5: 0.625, 1.0: 1.0}}
|
||||||
addOpenTypeFeaturesFromString(font, features)
|
addOpenTypeFeaturesFromString(font, features)
|
||||||
@ -1084,12 +1085,36 @@ class BuilderTest(unittest.TestCase):
|
|||||||
var_region_list = font.tables["GDEF"].table.VarStore.VarRegionList
|
var_region_list = font.tables["GDEF"].table.VarStore.VarRegionList
|
||||||
var_region_axis_wght = var_region_list.Region[0].VarRegionAxis[0]
|
var_region_axis_wght = var_region_list.Region[0].VarRegionAxis[0]
|
||||||
var_region_axis_wdth = var_region_list.Region[0].VarRegionAxis[1]
|
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 self.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_wdth) == (0.0, 0.0, 0.0)
|
||||||
var_region_axis_wght = var_region_list.Region[1].VarRegionAxis[0]
|
var_region_axis_wght = var_region_list.Region[1].VarRegionAxis[0]
|
||||||
var_region_axis_wdth = var_region_list.Region[1].VarRegionAxis[1]
|
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 self.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_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):
|
def generate_feature_file_test(name):
|
||||||
|
@ -707,6 +707,17 @@ class ParserTest(unittest.TestCase):
|
|||||||
self.assertEqual(glyphstr([s.glyphs]), "f_i")
|
self.assertEqual(glyphstr([s.glyphs]), "f_i")
|
||||||
self.assertEqual(s.carets, [400, 380])
|
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):
|
def test_lookup_block(self):
|
||||||
[lookup] = self.parse("lookup Ligatures {} Ligatures;").statements
|
[lookup] = self.parse("lookup Ligatures {} Ligatures;").statements
|
||||||
self.assertEqual(lookup.name, "Ligatures")
|
self.assertEqual(lookup.name, "Ligatures")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user