diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
index 56d9c0ef5..b9d358564 100644
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -636,10 +636,16 @@ def prune_post_subset(self, font, options):
self.Value.prune_hints()
self.ValueFormat = self.Value.getEffectiveFormat()
elif self.Format == 2:
- if not options.hinting:
- for v in self.Value:
- v.prune_hints()
- self.ValueFormat = reduce(int.__or__, [v.getEffectiveFormat() for v in self.Value], 0)
+ if None in self.Value:
+ assert self.ValueFormat == 0
+ assert all(v is None for v in self.Value)
+ else:
+ if not options.hinting:
+ for v in self.Value:
+ v.prune_hints()
+ self.ValueFormat = reduce(
+ int.__or__, [v.getEffectiveFormat() for v in self.Value], 0
+ )
# Downgrade to Format 1 if all ValueRecords are the same
if self.Format == 2 and all(v == self.Value[0] for v in self.Value):
diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py
index facafb2a7..2c51a5a9e 100644
--- a/Tests/subset/subset_test.py
+++ b/Tests/subset/subset_test.py
@@ -953,7 +953,8 @@ def test_subset_feature_variations_drop_all(featureVarsTestFont):
# https://github.com/fonttools/fonttools/issues/1881#issuecomment-619415044
-def test_subset_single_pos_format():
+@pytest.fixture
+def singlepos2_font():
fb = FontBuilder(unitsPerEm=1000)
fb.setupGlyphOrder([".notdef", "a", "b", "c"])
fb.setupCharacterMap({ord("a"): "a", ord("b"): "b", ord("c"): "c"})
@@ -971,8 +972,11 @@ def test_subset_single_pos_format():
fb.save(buf)
buf.seek(0)
- font = TTFont(buf)
+ return TTFont(buf)
+
+def test_subset_single_pos_format(singlepos2_font):
+ font = singlepos2_font
# The input font has a SinglePos Format 2 subtable where each glyph has
# different ValueRecords
assert getXML(font["GPOS"].table.LookupList.Lookup[0].toXML, font) == [
@@ -1018,6 +1022,46 @@ def test_subset_single_pos_format():
'',
]
+def test_subset_single_pos_format2_all_None(singlepos2_font):
+ # https://github.com/fonttools/fonttools/issues/2602
+ font = singlepos2_font
+ gpos = font["GPOS"].table
+ subtable = gpos.LookupList.Lookup[0].SubTable[0]
+ assert subtable.Format == 2
+ # Hack a SinglePosFormat2 with ValueFormat = 0; our own buildSinglePos
+ # never makes these as a SinglePosFormat1 is more compact, but they can
+ # be found in the wild.
+ subtable.Value = [None] * subtable.ValueCount
+ subtable.ValueFormat = 0
+
+ assert getXML(subtable.toXML, font) == [
+ '',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ '',
+ ]
+
+ options = subset.Options()
+ subsetter = subset.Subsetter(options)
+ subsetter.populate(unicodes=[ord("a"), ord("c")])
+ subsetter.subset(font)
+
+ # Check it was downgraded to Format1 after subsetting
+ assert getXML(font["GPOS"].table.LookupList.Lookup[0].SubTable[0].toXML, font) == [
+ '',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ '',
+ ]
+
@pytest.fixture
def ttf_path(tmp_path):