[otlib] Add a config option to write GPOS 7 lookups
See the inline comment and option documentation
This commit is contained in:
parent
580e3b3b50
commit
fd6c81585f
@ -57,3 +57,18 @@ Config.register_option(
|
|||||||
parse=Option.parse_optional_bool,
|
parse=Option.parse_optional_bool,
|
||||||
validate=Option.validate_optional_bool,
|
validate=Option.validate_optional_bool,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Config.register_option(
|
||||||
|
name="fontTools.otlLib.builder:WRITE_GPOS7",
|
||||||
|
help=dedent(
|
||||||
|
"""\
|
||||||
|
macOS before 13.2 didn’t support GPOS LookupType 7 (none-chaining
|
||||||
|
ContextPos lookups), so FontTools.otLib.builder disables a file size
|
||||||
|
optimisation that would use LookupType 7 instead of 8 when there is no
|
||||||
|
chaining (no prefix or suffix). Set to True to enable the optimization.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
default=False,
|
||||||
|
parse=Option.parse_optional_bool,
|
||||||
|
validate=Option.validate_optional_bool,
|
||||||
|
)
|
||||||
|
@ -369,10 +369,19 @@ class ChainContextualBuilder(LookupBuilder):
|
|||||||
|
|
||||||
rulesets = self.rulesets()
|
rulesets = self.rulesets()
|
||||||
chaining = any(ruleset.hasPrefixOrSuffix for ruleset in rulesets)
|
chaining = any(ruleset.hasPrefixOrSuffix for ruleset in rulesets)
|
||||||
|
|
||||||
|
# https://github.com/fonttools/fonttools/issues/2539
|
||||||
|
#
|
||||||
# Unfortunately, as of 2022-03-07, Apple's CoreText renderer does not
|
# Unfortunately, as of 2022-03-07, Apple's CoreText renderer does not
|
||||||
# correctly process GPOS7 lookups, so for now we force contextual
|
# correctly process GPOS7 lookups, so for now we force contextual
|
||||||
# positioning lookups to be chaining (GPOS8).
|
# positioning lookups to be chaining (GPOS8).
|
||||||
if self.subtable_type == "Pos": # horrible separation of concerns breach
|
#
|
||||||
|
# This seems to be fixed as of macOS 13.2, but we keep disabling this
|
||||||
|
# for now until we are no longer concerned about old macOS versions.
|
||||||
|
# But we allow people to opt-out of this with the config key below.
|
||||||
|
write_gpos7 = self.font.cfg.get("fontTools.otlLib.builder:WRITE_GPOS7")
|
||||||
|
# horrible separation of concerns breach
|
||||||
|
if not write_gpos7 and self.subtable_type == "Pos":
|
||||||
chaining = True
|
chaining = True
|
||||||
|
|
||||||
for ruleset in rulesets:
|
for ruleset in rulesets:
|
||||||
|
116
Tests/varLib/data/test_results/InterpolateLayoutGPOS_7_diff.ttx
Normal file
116
Tests/varLib/data/test_results/InterpolateLayoutGPOS_7_diff.ttx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont>
|
||||||
|
|
||||||
|
<GPOS>
|
||||||
|
<Version value="0x00010000"/>
|
||||||
|
<ScriptList>
|
||||||
|
<!-- ScriptCount=1 -->
|
||||||
|
<ScriptRecord index="0">
|
||||||
|
<ScriptTag value="DFLT"/>
|
||||||
|
<Script>
|
||||||
|
<DefaultLangSys>
|
||||||
|
<ReqFeatureIndex value="65535"/>
|
||||||
|
<!-- FeatureCount=1 -->
|
||||||
|
<FeatureIndex index="0" value="0"/>
|
||||||
|
</DefaultLangSys>
|
||||||
|
<!-- LangSysCount=0 -->
|
||||||
|
</Script>
|
||||||
|
</ScriptRecord>
|
||||||
|
</ScriptList>
|
||||||
|
<FeatureList>
|
||||||
|
<!-- FeatureCount=1 -->
|
||||||
|
<FeatureRecord index="0">
|
||||||
|
<FeatureTag value="xxxx"/>
|
||||||
|
<Feature>
|
||||||
|
<!-- LookupCount=1 -->
|
||||||
|
<LookupListIndex index="0" value="2"/>
|
||||||
|
</Feature>
|
||||||
|
</FeatureRecord>
|
||||||
|
</FeatureList>
|
||||||
|
<LookupList>
|
||||||
|
<!-- LookupCount=3 -->
|
||||||
|
<Lookup index="0">
|
||||||
|
<LookupType value="2"/>
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<PairPos index="0" Format="1">
|
||||||
|
<Coverage>
|
||||||
|
<Glyph value="A"/>
|
||||||
|
</Coverage>
|
||||||
|
<ValueFormat1 value="4"/>
|
||||||
|
<ValueFormat2 value="0"/>
|
||||||
|
<!-- PairSetCount=1 -->
|
||||||
|
<PairSet index="0">
|
||||||
|
<!-- PairValueCount=1 -->
|
||||||
|
<PairValueRecord index="0">
|
||||||
|
<SecondGlyph value="a"/>
|
||||||
|
<Value1 XAdvance="17"/>
|
||||||
|
</PairValueRecord>
|
||||||
|
</PairSet>
|
||||||
|
</PairPos>
|
||||||
|
</Lookup>
|
||||||
|
<Lookup index="1">
|
||||||
|
<LookupType value="4"/>
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<MarkBasePos index="0" Format="1">
|
||||||
|
<MarkCoverage>
|
||||||
|
<Glyph value="uni0303"/>
|
||||||
|
</MarkCoverage>
|
||||||
|
<BaseCoverage>
|
||||||
|
<Glyph value="a"/>
|
||||||
|
</BaseCoverage>
|
||||||
|
<!-- ClassCount=1 -->
|
||||||
|
<MarkArray>
|
||||||
|
<!-- MarkCount=1 -->
|
||||||
|
<MarkRecord index="0">
|
||||||
|
<Class value="0"/>
|
||||||
|
<MarkAnchor Format="1">
|
||||||
|
<XCoordinate value="0"/>
|
||||||
|
<YCoordinate value="510"/>
|
||||||
|
</MarkAnchor>
|
||||||
|
</MarkRecord>
|
||||||
|
</MarkArray>
|
||||||
|
<BaseArray>
|
||||||
|
<!-- BaseCount=1 -->
|
||||||
|
<BaseRecord index="0">
|
||||||
|
<BaseAnchor index="0" Format="1">
|
||||||
|
<XCoordinate value="273"/>
|
||||||
|
<YCoordinate value="510"/>
|
||||||
|
</BaseAnchor>
|
||||||
|
</BaseRecord>
|
||||||
|
</BaseArray>
|
||||||
|
</MarkBasePos>
|
||||||
|
</Lookup>
|
||||||
|
<Lookup index="2">
|
||||||
|
<LookupType value="7"/>
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<ContextPos index="0" Format="1">
|
||||||
|
<Coverage>
|
||||||
|
<Glyph value="A"/>
|
||||||
|
</Coverage>
|
||||||
|
<!-- PosRuleSetCount=1 -->
|
||||||
|
<PosRuleSet index="0">
|
||||||
|
<!-- PosRuleCount=1 -->
|
||||||
|
<PosRule index="0">
|
||||||
|
<!-- GlyphCount=3 -->
|
||||||
|
<!-- PosCount=2 -->
|
||||||
|
<Input index="0" value="a"/>
|
||||||
|
<Input index="1" value="uni0303"/>
|
||||||
|
<PosLookupRecord index="0">
|
||||||
|
<SequenceIndex value="0"/>
|
||||||
|
<LookupListIndex value="0"/>
|
||||||
|
</PosLookupRecord>
|
||||||
|
<PosLookupRecord index="1">
|
||||||
|
<SequenceIndex value="2"/>
|
||||||
|
<LookupListIndex value="1"/>
|
||||||
|
</PosLookupRecord>
|
||||||
|
</PosRule>
|
||||||
|
</PosRuleSet>
|
||||||
|
</ContextPos>
|
||||||
|
</Lookup>
|
||||||
|
</LookupList>
|
||||||
|
</GPOS>
|
||||||
|
|
||||||
|
</ttFont>
|
116
Tests/varLib/data/test_results/InterpolateLayoutGPOS_7_same.ttx
Normal file
116
Tests/varLib/data/test_results/InterpolateLayoutGPOS_7_same.ttx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont>
|
||||||
|
|
||||||
|
<GPOS>
|
||||||
|
<Version value="0x00010000"/>
|
||||||
|
<ScriptList>
|
||||||
|
<!-- ScriptCount=1 -->
|
||||||
|
<ScriptRecord index="0">
|
||||||
|
<ScriptTag value="DFLT"/>
|
||||||
|
<Script>
|
||||||
|
<DefaultLangSys>
|
||||||
|
<ReqFeatureIndex value="65535"/>
|
||||||
|
<!-- FeatureCount=1 -->
|
||||||
|
<FeatureIndex index="0" value="0"/>
|
||||||
|
</DefaultLangSys>
|
||||||
|
<!-- LangSysCount=0 -->
|
||||||
|
</Script>
|
||||||
|
</ScriptRecord>
|
||||||
|
</ScriptList>
|
||||||
|
<FeatureList>
|
||||||
|
<!-- FeatureCount=1 -->
|
||||||
|
<FeatureRecord index="0">
|
||||||
|
<FeatureTag value="xxxx"/>
|
||||||
|
<Feature>
|
||||||
|
<!-- LookupCount=1 -->
|
||||||
|
<LookupListIndex index="0" value="2"/>
|
||||||
|
</Feature>
|
||||||
|
</FeatureRecord>
|
||||||
|
</FeatureList>
|
||||||
|
<LookupList>
|
||||||
|
<!-- LookupCount=3 -->
|
||||||
|
<Lookup index="0">
|
||||||
|
<LookupType value="2"/>
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<PairPos index="0" Format="1">
|
||||||
|
<Coverage>
|
||||||
|
<Glyph value="A"/>
|
||||||
|
</Coverage>
|
||||||
|
<ValueFormat1 value="4"/>
|
||||||
|
<ValueFormat2 value="0"/>
|
||||||
|
<!-- PairSetCount=1 -->
|
||||||
|
<PairSet index="0">
|
||||||
|
<!-- PairValueCount=1 -->
|
||||||
|
<PairValueRecord index="0">
|
||||||
|
<SecondGlyph value="a"/>
|
||||||
|
<Value1 XAdvance="-23"/>
|
||||||
|
</PairValueRecord>
|
||||||
|
</PairSet>
|
||||||
|
</PairPos>
|
||||||
|
</Lookup>
|
||||||
|
<Lookup index="1">
|
||||||
|
<LookupType value="4"/>
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<MarkBasePos index="0" Format="1">
|
||||||
|
<MarkCoverage>
|
||||||
|
<Glyph value="uni0303"/>
|
||||||
|
</MarkCoverage>
|
||||||
|
<BaseCoverage>
|
||||||
|
<Glyph value="a"/>
|
||||||
|
</BaseCoverage>
|
||||||
|
<!-- ClassCount=1 -->
|
||||||
|
<MarkArray>
|
||||||
|
<!-- MarkCount=1 -->
|
||||||
|
<MarkRecord index="0">
|
||||||
|
<Class value="0"/>
|
||||||
|
<MarkAnchor Format="1">
|
||||||
|
<XCoordinate value="0"/>
|
||||||
|
<YCoordinate value="500"/>
|
||||||
|
</MarkAnchor>
|
||||||
|
</MarkRecord>
|
||||||
|
</MarkArray>
|
||||||
|
<BaseArray>
|
||||||
|
<!-- BaseCount=1 -->
|
||||||
|
<BaseRecord index="0">
|
||||||
|
<BaseAnchor index="0" Format="1">
|
||||||
|
<XCoordinate value="260"/>
|
||||||
|
<YCoordinate value="500"/>
|
||||||
|
</BaseAnchor>
|
||||||
|
</BaseRecord>
|
||||||
|
</BaseArray>
|
||||||
|
</MarkBasePos>
|
||||||
|
</Lookup>
|
||||||
|
<Lookup index="2">
|
||||||
|
<LookupType value="7"/>
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<ContextPos index="0" Format="1">
|
||||||
|
<Coverage>
|
||||||
|
<Glyph value="A"/>
|
||||||
|
</Coverage>
|
||||||
|
<!-- PosRuleSetCount=1 -->
|
||||||
|
<PosRuleSet index="0">
|
||||||
|
<!-- PosRuleCount=1 -->
|
||||||
|
<PosRule index="0">
|
||||||
|
<!-- GlyphCount=3 -->
|
||||||
|
<!-- PosCount=2 -->
|
||||||
|
<Input index="0" value="a"/>
|
||||||
|
<Input index="1" value="uni0303"/>
|
||||||
|
<PosLookupRecord index="0">
|
||||||
|
<SequenceIndex value="0"/>
|
||||||
|
<LookupListIndex value="0"/>
|
||||||
|
</PosLookupRecord>
|
||||||
|
<PosLookupRecord index="1">
|
||||||
|
<SequenceIndex value="2"/>
|
||||||
|
<LookupListIndex value="1"/>
|
||||||
|
</PosLookupRecord>
|
||||||
|
</PosRule>
|
||||||
|
</PosRuleSet>
|
||||||
|
</ContextPos>
|
||||||
|
</Lookup>
|
||||||
|
</LookupList>
|
||||||
|
</GPOS>
|
||||||
|
|
||||||
|
</ttFont>
|
@ -85,10 +85,12 @@ class InterpolateLayoutTest(unittest.TestCase):
|
|||||||
font.save(path)
|
font.save(path)
|
||||||
self.expect_ttx(TTFont(path), expected_ttx, tables)
|
self.expect_ttx(TTFont(path), expected_ttx, tables)
|
||||||
|
|
||||||
def compile_font(self, path, suffix, temp_dir, features=None):
|
def compile_font(self, path, suffix, temp_dir, features=None, cfg=None):
|
||||||
ttx_filename = os.path.basename(path)
|
ttx_filename = os.path.basename(path)
|
||||||
savepath = os.path.join(temp_dir, ttx_filename.replace(".ttx", suffix))
|
savepath = os.path.join(temp_dir, ttx_filename.replace(".ttx", suffix))
|
||||||
font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
|
font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
|
||||||
|
if cfg:
|
||||||
|
font.cfg.update(cfg)
|
||||||
font.importXML(path)
|
font.importXML(path)
|
||||||
if features:
|
if features:
|
||||||
addOpenTypeFeaturesFromString(font, features)
|
addOpenTypeFeaturesFromString(font, features)
|
||||||
@ -734,6 +736,94 @@ class InterpolateLayoutTest(unittest.TestCase):
|
|||||||
self.expect_ttx(instfont, expected_ttx_path, tables)
|
self.expect_ttx(instfont, expected_ttx_path, tables)
|
||||||
self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
||||||
|
|
||||||
|
def test_varlib_interpolate_layout_GPOS_only_LookupType_7_same_val_ttf(self):
|
||||||
|
"""Only GPOS; LookupType 7; same values in all masters."""
|
||||||
|
suffix = ".ttf"
|
||||||
|
ds_path = self.get_test_input("InterpolateLayout.designspace")
|
||||||
|
ufo_dir = self.get_test_input("master_ufo")
|
||||||
|
ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
|
||||||
|
|
||||||
|
fea_str = """
|
||||||
|
markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
|
||||||
|
lookup CNTXT_PAIR_POS {
|
||||||
|
pos A a -23;
|
||||||
|
} CNTXT_PAIR_POS;
|
||||||
|
|
||||||
|
lookup CNTXT_MARK_TO_BASE {
|
||||||
|
pos base a <anchor 260 500> mark @MARKS_ABOVE;
|
||||||
|
} CNTXT_MARK_TO_BASE;
|
||||||
|
|
||||||
|
feature xxxx {
|
||||||
|
pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE;
|
||||||
|
} xxxx;
|
||||||
|
"""
|
||||||
|
features = [fea_str] * 2
|
||||||
|
|
||||||
|
self.temp_dir()
|
||||||
|
ttx_paths = self.get_file_list(ttx_dir, ".ttx", "TestFamily2-")
|
||||||
|
cfg = {"fontTools.otlLib.builder:WRITE_GPOS7": True}
|
||||||
|
for i, path in enumerate(ttx_paths):
|
||||||
|
self.compile_font(path, suffix, self.tempdir, features[i], cfg)
|
||||||
|
|
||||||
|
finder = lambda s: s.replace(ufo_dir, self.tempdir).replace(".ufo", suffix)
|
||||||
|
instfont = interpolate_layout(ds_path, {"weight": 500}, finder)
|
||||||
|
|
||||||
|
tables = ["GPOS"]
|
||||||
|
expected_ttx_path = self.get_test_output("InterpolateLayoutGPOS_7_same.ttx")
|
||||||
|
self.expect_ttx(instfont, expected_ttx_path, tables)
|
||||||
|
self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
||||||
|
|
||||||
|
def test_varlib_interpolate_layout_GPOS_only_LookupType_7_diff_val_ttf(self):
|
||||||
|
"""Only GPOS; LookupType 7; different values in each master."""
|
||||||
|
suffix = ".ttf"
|
||||||
|
ds_path = self.get_test_input("InterpolateLayout.designspace")
|
||||||
|
ufo_dir = self.get_test_input("master_ufo")
|
||||||
|
ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
|
||||||
|
|
||||||
|
fea_str_0 = """
|
||||||
|
markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
|
||||||
|
lookup CNTXT_PAIR_POS {
|
||||||
|
pos A a -23;
|
||||||
|
} CNTXT_PAIR_POS;
|
||||||
|
|
||||||
|
lookup CNTXT_MARK_TO_BASE {
|
||||||
|
pos base a <anchor 260 500> mark @MARKS_ABOVE;
|
||||||
|
} CNTXT_MARK_TO_BASE;
|
||||||
|
|
||||||
|
feature xxxx {
|
||||||
|
pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE;
|
||||||
|
} xxxx;
|
||||||
|
"""
|
||||||
|
fea_str_1 = """
|
||||||
|
markClass uni0303 <anchor 0 520> @MARKS_ABOVE;
|
||||||
|
lookup CNTXT_PAIR_POS {
|
||||||
|
pos A a 57;
|
||||||
|
} CNTXT_PAIR_POS;
|
||||||
|
|
||||||
|
lookup CNTXT_MARK_TO_BASE {
|
||||||
|
pos base a <anchor 285 520> mark @MARKS_ABOVE;
|
||||||
|
} CNTXT_MARK_TO_BASE;
|
||||||
|
|
||||||
|
feature xxxx {
|
||||||
|
pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE;
|
||||||
|
} xxxx;
|
||||||
|
"""
|
||||||
|
features = [fea_str_0, fea_str_1]
|
||||||
|
|
||||||
|
self.temp_dir()
|
||||||
|
ttx_paths = self.get_file_list(ttx_dir, ".ttx", "TestFamily2-")
|
||||||
|
cfg = {"fontTools.otlLib.builder:WRITE_GPOS7": True}
|
||||||
|
for i, path in enumerate(ttx_paths):
|
||||||
|
self.compile_font(path, suffix, self.tempdir, features[i], cfg)
|
||||||
|
|
||||||
|
finder = lambda s: s.replace(ufo_dir, self.tempdir).replace(".ufo", suffix)
|
||||||
|
instfont = interpolate_layout(ds_path, {"weight": 500}, finder)
|
||||||
|
|
||||||
|
tables = ["GPOS"]
|
||||||
|
expected_ttx_path = self.get_test_output("InterpolateLayoutGPOS_7_diff.ttx")
|
||||||
|
self.expect_ttx(instfont, expected_ttx_path, tables)
|
||||||
|
self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
||||||
|
|
||||||
def test_varlib_interpolate_layout_GPOS_only_LookupType_8_same_val_ttf(self):
|
def test_varlib_interpolate_layout_GPOS_only_LookupType_8_same_val_ttf(self):
|
||||||
"""Only GPOS; LookupType 8; same values in all masters."""
|
"""Only GPOS; LookupType 8; same values in all masters."""
|
||||||
suffix = ".ttf"
|
suffix = ".ttf"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user