+
Skip to content

[feaLib] Correctly handle <NULL> in single pos lookups #3803

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Lib/fontTools/feaLib/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -1583,15 +1583,20 @@ def asFea(self, indent=""):
res += " ".join(map(asFea, self.prefix)) + " "
res += " ".join(
[
asFea(x[0]) + "'" + ((" " + x[1].asFea()) if x[1] else "")
asFea(x[0])
+ "'"
+ ((" " + x[1].asFea()) if x[1] is not None else "")
for x in self.pos
]
)
if len(self.suffix):
res += " " + " ".join(map(asFea, self.suffix))
else:
res += " ".join(
[asFea(x[0]) + " " + (x[1].asFea() if x[1] else "") for x in self.pos]
[
asFea(x[0]) + " " + (x[1].asFea() if x[1] is not None else "")
for x in self.pos
]
)
res += ";"
return res
Expand Down
2 changes: 2 additions & 0 deletions Lib/fontTools/otlLib/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,8 @@ def add_pos(self, location, glyph, otValueRecord):
otValueRection: A ``otTables.ValueRecord`` used to position the
glyph.
"""
if otValueRecord is None:
otValueRecord = ValueRecord()
if not self.can_add(glyph, otValueRecord):
otherLoc = self.locations[glyph]
raise OpenTypeLibError(
Expand Down
22 changes: 22 additions & 0 deletions Tests/feaLib/ast_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,34 @@ def test_valuerecord_none(self):
statement = ast.ValueRecord(xPlacement=10, xAdvance=20)
self.assertEqual(statement.asFea(), "<10 0 20 0>")

def test_valuerecord_empty(self):
statement = ast.ValueRecord()
self.assertEqual(statement.asFea(), "<NULL>")

def test_non_object_location(self):
el = ast.Element(location=("file.fea", 1, 2))
self.assertEqual(el.location.file, "file.fea")
self.assertEqual(el.location.line, 1)
self.assertEqual(el.location.column, 2)

def test_single_pos_statement_empty_valuerecord(self):
statement = ast.SinglePosStatement(
pos=[(ast.GlyphName("a"), ast.ValueRecord())],
prefix=[],
suffix=[],
forceChain=False,
)
self.assertEqual(statement.asFea(), "pos a <NULL>;")

def test_single_pos_statement_empty_valuerecord_chain(self):
statement = ast.SinglePosStatement(
pos=[(ast.GlyphName("a"), ast.ValueRecord())],
prefix=[],
suffix=[],
forceChain=True,
)
self.assertEqual(statement.asFea(), "pos a' <NULL>;")


if __name__ == "__main__":
import sys
Expand Down
1 change: 1 addition & 0 deletions Tests/feaLib/builder_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class BuilderTest(unittest.TestCase):
MarkBasePosSubtable
MarkLigPosSubtable
MarkMarkPosSubtable
single_pos_NULL
""".split()

VARFONT_AXES = [
Expand Down
8 changes: 8 additions & 0 deletions Tests/feaLib/data/single_pos_NULL.fea
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Both lookups should result in SinglePos lookup with a ValueFormat 0
lookup test1 {
pos A <NULL>;
} test1;

lookup test2 {
pos B' <NULL>;
} test2;
57 changes: 57 additions & 0 deletions Tests/feaLib/data/single_pos_NULL.ttx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>

<GPOS>
<Version value="0x00010000"/>
<ScriptList>
<!-- ScriptCount=0 -->
</ScriptList>
<FeatureList>
<!-- FeatureCount=0 -->
</FeatureList>
<LookupList>
<!-- LookupCount=3 -->
<Lookup index="0">
<LookupType value="1"/>
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SinglePos index="0" Format="1">
<Coverage>
<Glyph value="A"/>
</Coverage>
<ValueFormat value="0"/>
</SinglePos>
</Lookup>
<Lookup index="1">
<LookupType value="8"/>
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<ChainContextPos index="0" Format="3">
<!-- BacktrackGlyphCount=0 -->
<!-- InputGlyphCount=1 -->
<InputCoverage index="0">
<Glyph value="B"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- PosCount=1 -->
<PosLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="2"/>
</PosLookupRecord>
</ChainContextPos>
</Lookup>
<Lookup index="2">
<LookupType value="1"/>
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SinglePos index="0" Format="1">
<Coverage>
<Glyph value="B"/>
</Coverage>
<ValueFormat value="0"/>
</SinglePos>
</Lookup>
</LookupList>
</GPOS>

</ttFont>
10 changes: 10 additions & 0 deletions Tests/feaLib/parser_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,16 @@ def test_gpos_type_1_chained_exception4(self):
with self.assertRaisesRegex(FeatureLibError, "Positioning values are allowed"):
doc = self.parse("feature kern {" " pos a' b c 123 d;" "} kern;")

def test_gpos_type_1_null(self):
doc = self.parse("feature test {pos a <NULL>;} test;")
pos = doc.statements[0].statements[0]
self.assertEqual(pos.asFea(), "pos a <NULL>;")

def test_gpos_type_1_null_chained(self):
doc = self.parse("feature test {pos a' <NULL>;} test;")
pos = doc.statements[0].statements[0]
self.assertEqual(pos.asFea(), "pos a' <NULL>;")

def test_gpos_type_2_format_a(self):
doc = self.parse(
"feature kern {" " pos [T V] -60 [a b c] <1 2 3 4>;" "} kern;"
Expand Down
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载