[feaLib] Merge SingleSubst chain targets

This makes the output of feaLib more compact, using a similar technique
as seems to be used by makeotf.

After this change, feaLib generates output that more similar to makeotf:

* For the test cases in `bug512.fea` and `bug463.fea`, feaLib now
  generates the exact same output as makeotf v2.0.90.

* For the test cases in `GSUB_6.fea`, it is hard to say because makeotf
  crashes on the test file; our test contains language constructs that
  are valid according to the spec, but didn't yet get implemented by makeotf.
  When commenting out those constructs, feaLib generates the exact same
  output as makeotf v2.0.90.

* For the test cases in `feature_aalt.fea`, the output of feaLib is now
  structually the same as the output of makeotf v2.0.90.  However, two
  lookups are in different order. feaLib's ordering reflects the order
  of statements in the compiled input source; no idea why makeotf would
  want to reverse the ordering. Since this ordering difference only
  affects the _targets_ of chain substitutions, there is no semantic
  difference.

Resolves https://github.com/behdad/fonttools/issues/512.
This commit is contained in:
Sascha Brawer 2016-02-05 15:12:07 +01:00
parent 1ddfe24338
commit ec9077a566
7 changed files with 163 additions and 53 deletions

View File

@ -555,11 +555,7 @@ class Builder(object):
alts.add(to_glyph)
return
if prefix or suffix or forceChain:
chain = self.get_lookup_(location, ChainContextSubstBuilder)
sub = self.get_chained_lookup_(location, SingleSubstBuilder)
sub.mapping.update(mapping)
chain.substitutions.append(
(prefix, [mapping.keys()], suffix, [sub]))
self.add_single_subst_chained_(location, prefix, suffix, mapping)
return
lookup = self.get_lookup_(location, SingleSubstBuilder)
for (from_glyph, to_glyph) in mapping.items():
@ -570,6 +566,24 @@ class Builder(object):
location)
lookup.mapping[from_glyph] = to_glyph
def find_chainable_SingleSubst_(self, chain, glyphs):
"""Helper for add_single_subst_chained_()"""
for _, _, _, substitutions in chain.substitutions:
for sub in substitutions:
if (isinstance(sub, SingleSubstBuilder) and
not any(g in glyphs for g in sub.mapping.keys())):
return sub
return None
def add_single_subst_chained_(self, location, prefix, suffix, mapping):
# https://github.com/behdad/fonttools/issues/512
chain = self.get_lookup_(location, ChainContextSubstBuilder)
sub = self.find_chainable_SingleSubst_(chain, set(mapping.keys()))
if sub is None:
sub = self.get_chained_lookup_(location, SingleSubstBuilder)
sub.mapping.update(mapping)
chain.substitutions.append((prefix, [mapping.keys()], suffix, [sub]))
def add_cursive_pos(self, location, glyphclass, entryAnchor, exitAnchor):
lookup = self.get_lookup_(location, CursivePosBuilder)
lookup.add_attachment(

View File

@ -30,6 +30,7 @@ def makeTTFont():
"A.alt1 A.alt2 A.alt3 B.alt1 B.alt2 B.alt3 C.alt1 C.alt2 C.alt3 "
"a.alt1 a.alt2 a.alt3 b.alt c.mid d.alt d.mid e.begin e.mid "
"n.end s.end Eng Eng.alt1 Eng.alt2 Eng.alt3 "
"G.swash H.swash "
"f_l c_h c_k c_s c_t f_f f_f_i f_f_l f_i o_f_f_i s_t f_i.begin "
"a_n_d ydieresis yacute breve "
"grave acute dieresis macron circumflex cedilla umlaut ogonek caron "
@ -52,7 +53,7 @@ class BuilderTest(unittest.TestCase):
spec5f_ii_1 spec5f_ii_2 spec5f_ii_3
spec5h1 spec6b_ii spec6d2 spec6e spec6f spec6h_ii spec6h_iii_1 spec8a
spec9b spec9c1 spec9c2 spec9c3
bug463 bug501 bug502 bug505 bug506 bug509
bug463 bug501 bug502 bug505 bug506 bug509 bug512
""".split()
def __init__(self, methodName):

View File

@ -24,15 +24,15 @@
<Feature>
<!-- LookupCount=5 -->
<LookupListIndex index="0" value="0"/>
<LookupListIndex index="1" value="3"/>
<LookupListIndex index="2" value="5"/>
<LookupListIndex index="3" value="7"/>
<LookupListIndex index="4" value="9"/>
<LookupListIndex index="1" value="2"/>
<LookupListIndex index="2" value="4"/>
<LookupListIndex index="3" value="6"/>
<LookupListIndex index="4" value="8"/>
</Feature>
</FeatureRecord>
</FeatureList>
<LookupList>
<!-- LookupCount=10 -->
<!-- LookupCount=9 -->
<Lookup index="0">
<!-- LookupType=6 -->
<LookupFlag value="0"/>
@ -76,7 +76,7 @@
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="2"/>
<LookupListIndex value="1"/>
</SubstLookupRecord>
</ChainContextSubst>
</Lookup>
@ -86,19 +86,12 @@
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="A" out="A.sc"/>
</SingleSubst>
</Lookup>
<Lookup index="2">
<!-- LookupType=1 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="B" out="B.sc"/>
<Substitution in="C" out="C.sc"/>
<Substitution in="D" out="D.sc"/>
</SingleSubst>
</Lookup>
<Lookup index="3">
<Lookup index="2">
<!-- LookupType=6 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
@ -142,11 +135,11 @@
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="4"/>
<LookupListIndex value="3"/>
</SubstLookupRecord>
</ChainContextSubst>
</Lookup>
<Lookup index="4">
<Lookup index="3">
<!-- LookupType=2 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
@ -154,7 +147,7 @@
<Substitution in="c_t" out="c,t"/>
</MultipleSubst>
</Lookup>
<Lookup index="5">
<Lookup index="4">
<!-- LookupType=6 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
@ -173,11 +166,11 @@
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="6"/>
<LookupListIndex value="5"/>
</SubstLookupRecord>
</ChainContextSubst>
</Lookup>
<Lookup index="6">
<Lookup index="5">
<!-- LookupType=3 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
@ -188,7 +181,7 @@
</AlternateSet>
</AlternateSubst>
</Lookup>
<Lookup index="7">
<Lookup index="6">
<!-- LookupType=6 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
@ -213,11 +206,11 @@
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="8"/>
<LookupListIndex value="7"/>
</SubstLookupRecord>
</ChainContextSubst>
</Lookup>
<Lookup index="8">
<Lookup index="7">
<!-- LookupType=4 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
@ -232,7 +225,7 @@
</LigatureSet>
</LigatureSubst>
</Lookup>
<Lookup index="9">
<Lookup index="8">
<!-- LookupType=6 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
@ -264,7 +257,7 @@
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="3"/>
<LookupListIndex value="2"/>
</SubstLookupRecord>
</ChainContextSubst>
</Lookup>

View File

@ -28,7 +28,7 @@
</FeatureRecord>
</FeatureList>
<LookupList>
<!-- LookupCount=3 -->
<!-- LookupCount=2 -->
<Lookup index="0">
<!-- LookupType=6 -->
<LookupFlag value="0"/>
@ -82,7 +82,7 @@
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="2"/>
<LookupListIndex value="1"/>
</SubstLookupRecord>
</ChainContextSubst>
</Lookup>
@ -92,15 +92,8 @@
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="A" out="ordfeminine"/>
<Substitution in="a" out="ordfeminine"/>
</SingleSubst>
</Lookup>
<Lookup index="2">
<!-- LookupType=1 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="O" out="ordmasculine"/>
<Substitution in="a" out="ordfeminine"/>
<Substitution in="o" out="ordmasculine"/>
</SingleSubst>
</Lookup>

View File

@ -0,0 +1,6 @@
feature test {
sub G' by G.swash;
sub H' by H.swash;
sub G' by g;
sub H' by H.swash;
} test;

110
Lib/fontTools/feaLib/testdata/bug512.ttx vendored Normal file
View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="true" ttLibVersion="3.0">
<GSUB>
<Version value="1.0"/>
<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="test"/>
<Feature>
<!-- LookupCount=1 -->
<LookupListIndex index="0" value="0"/>
</Feature>
</FeatureRecord>
</FeatureList>
<LookupList>
<!-- LookupCount=3 -->
<Lookup index="0">
<!-- LookupType=6 -->
<LookupFlag value="0"/>
<!-- SubTableCount=4 -->
<ChainContextSubst index="0" Format="3">
<!-- BacktrackGlyphCount=0 -->
<!-- InputGlyphCount=1 -->
<InputCoverage index="0">
<Glyph value="G"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="1"/>
</SubstLookupRecord>
</ChainContextSubst>
<ChainContextSubst index="1" Format="3">
<!-- BacktrackGlyphCount=0 -->
<!-- InputGlyphCount=1 -->
<InputCoverage index="0">
<Glyph value="H"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="1"/>
</SubstLookupRecord>
</ChainContextSubst>
<ChainContextSubst index="2" Format="3">
<!-- BacktrackGlyphCount=0 -->
<!-- InputGlyphCount=1 -->
<InputCoverage index="0">
<Glyph value="G"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="2"/>
</SubstLookupRecord>
</ChainContextSubst>
<ChainContextSubst index="3" Format="3">
<!-- BacktrackGlyphCount=0 -->
<!-- InputGlyphCount=1 -->
<InputCoverage index="0">
<Glyph value="H"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="2"/>
</SubstLookupRecord>
</ChainContextSubst>
</Lookup>
<Lookup index="1">
<!-- LookupType=1 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="G" out="G.swash"/>
<Substitution in="H" out="H.swash"/>
</SingleSubst>
</Lookup>
<Lookup index="2">
<!-- LookupType=1 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="G" out="g"/>
<Substitution in="H" out="H.swash"/>
</SingleSubst>
</Lookup>
</LookupList>
</GSUB>
</ttFont>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>
<ttFont sfntVersion="true" ttLibVersion="3.0">
<GSUB>
<Version value="1.0"/>
@ -41,7 +41,7 @@
<FeatureTag value="liga"/>
<Feature>
<!-- LookupCount=1 -->
<LookupListIndex index="0" value="6"/>
<LookupListIndex index="0" value="5"/>
</Feature>
</FeatureRecord>
<FeatureRecord index="3">
@ -60,7 +60,7 @@
</FeatureRecord>
</FeatureList>
<LookupList>
<!-- LookupCount=7 -->
<!-- LookupCount=6 -->
<Lookup index="0">
<!-- LookupType=1 -->
<LookupFlag value="0"/>
@ -152,7 +152,7 @@
<!-- SubstCount=1 -->
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="5"/>
<LookupListIndex value="4"/>
</SubstLookupRecord>
</ChainContextSubst>
</Lookup>
@ -162,19 +162,12 @@
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="A" out="ordfeminine"/>
<Substitution in="a" out="ordfeminine"/>
</SingleSubst>
</Lookup>
<Lookup index="5">
<!-- LookupType=1 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="O" out="ordmasculine"/>
<Substitution in="a" out="ordfeminine"/>
<Substitution in="o" out="ordmasculine"/>
</SingleSubst>
</Lookup>
<Lookup index="6">
<Lookup index="5">
<!-- LookupType=4 -->
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->