Merge pull request #2016 from simoncozens/gsub5-gpos7-lookups

Generate GSUB5/GPOS7 lookups (See #1856)
This commit is contained in:
Simon Cozens 2020-07-11 21:51:47 +01:00 committed by GitHub
commit cbe6d3a3f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 446 additions and 186 deletions

View File

@ -172,6 +172,13 @@ class LookupBuilder(object):
coverage = buildCoverage(g, self.glyphMap) coverage = buildCoverage(g, self.glyphMap)
subtable.InputCoverage.append(coverage) subtable.InputCoverage.append(coverage)
def setCoverage_(self, glyphs, subtable):
subtable.GlyphCount = len(glyphs)
subtable.Coverage = []
for g in glyphs:
coverage = buildCoverage(g, self.glyphMap)
subtable.Coverage.append(coverage)
def build_subst_subtables(self, mapping, klass): def build_subst_subtables(self, mapping, klass):
substitutions = [{}] substitutions = [{}]
for key in mapping: for key in mapping:
@ -244,11 +251,71 @@ class AlternateSubstBuilder(LookupBuilder):
self.alternates[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_ self.alternates[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
class ChainContextualRuleset():
def __init__(self):
self.rules = []
def addRule(self, prefix, glyphs, suffix, lookups):
self.rules.append((prefix, glyphs, suffix, lookups))
@property
def hasPrefixOrSuffix(self):
# Do we have any prefixes/suffixes? If this is False for all
# rulesets, we can express the whole lookup as GPOS5/GSUB7.
for (prefix, glyphs, suffix, lookups) in self.rules:
if len(prefix) > 0 or len(suffix) > 0:
return True
return False
@property
def hasAnyGlyphClasses(self):
# Do we use glyph classes anywhere in the rules? If this is False
# we can express this subtable as a Format 1.
for (prefix, glyphs, suffix, lookups) in self.rules:
for coverage in (prefix, glyphs, suffix):
if any(len(x) > 1 for x in coverage):
return True
return False
def format2ClassDefs(self):
PREFIX, GLYPHS, SUFFIX = 0,1,2
classDefBuilders = []
for ix in [PREFIX, GLYPHS, SUFFIX]:
context = []
for r in self.rules:
context.append(r[ix])
classes = self._classBuilderForContext(context)
if not classes:
return None
classDefBuilders.append(classes)
return classDefBuilders
def _classBuilderForContext(self, context):
classdefbuilder = ClassDefBuilder(useClass0=False)
for position in context:
for glyphset in position:
if not classdefbuilder.canAdd(glyphset):
return None
classdefbuilder.add(glyphset)
return classdefbuilder
class ChainContextualBuilder(LookupBuilder): class ChainContextualBuilder(LookupBuilder):
def equals(self, other): def equals(self, other):
return (LookupBuilder.equals(self, other) and return (LookupBuilder.equals(self, other) and
self.rules == other.rules) self.rules == other.rules)
def rulesets(self):
# Return a list of ChainContextRuleset objects, taking explicit
# subtable breaks into account
ruleset = [ChainContextualRuleset()]
for (prefix, glyphs, suffix, lookups) in self.rules:
if prefix == self.SUBTABLE_BREAK_:
ruleset.append(ChainContextualRuleset())
continue
ruleset[-1].addRule(prefix, glyphs, suffix, lookups)
# Squish any empty subtables
return [x for x in ruleset if len(x.rules) > 0]
def build(self): def build(self):
"""Build the lookup. """Build the lookup.
@ -257,40 +324,91 @@ class ChainContextualBuilder(LookupBuilder):
contextual positioning lookup. contextual positioning lookup.
""" """
subtables = [] subtables = []
for (prefix, glyphs, suffix, lookups) in self.rules: chaining = False
if prefix == self.SUBTABLE_BREAK_: rulesets = self.rulesets()
continue chaining = any(ruleset.hasPrefixOrSuffix for ruleset in rulesets)
st = self.newSubtable_() for ruleset in rulesets:
subtables.append(st) for rule in ruleset.rules:
st.Format = 3 subtables.append(self.buildFormat3Subtable(rule, chaining))
# If we are not chaining, lookup type will be automatically fixed by
# buildLookup_
return self.buildLookup_(subtables)
def buildFormat3Subtable(self, rule, chaining=True):
(prefix, glyphs, suffix, lookups) = rule
st = self.newSubtable_(chaining=chaining)
st.Format = 3
if chaining:
self.setBacktrackCoverage_(prefix, st) self.setBacktrackCoverage_(prefix, st)
self.setLookAheadCoverage_(suffix, st) self.setLookAheadCoverage_(suffix, st)
self.setInputCoverage_(glyphs, st) self.setInputCoverage_(glyphs, st)
else:
self.setCoverage_(glyphs, st)
for sequenceIndex, lookupList in enumerate(lookups): for sequenceIndex, lookupList in enumerate(lookups):
if lookupList is not None: if lookupList is not None:
if not isinstance(lookupList, list): if not isinstance(lookupList, list):
# Can happen with synthesised lookups # Can happen with synthesised lookups
lookupList = [ lookupList ] lookupList = [lookupList]
for l in lookupList: for l in lookupList:
if l.lookup_index is None: if l.lookup_index is None:
if isinstance(self, ChainContextPosBuilder): if isinstance(self, ChainContextPosBuilder):
other = "substitution" other = "substitution"
else: else:
other = "positioning" other = "positioning"
raise OpenTypeLibError('Missing index of the specified ' raise OpenTypeLibError('Missing index of the specified '
f'lookup, might be a {other} lookup', f'lookup, might be a {other} lookup',
self.location) self.location)
rec = self.newLookupRecord_() rec = self.newLookupRecord_(st)
rec.SequenceIndex = sequenceIndex rec.SequenceIndex = sequenceIndex
rec.LookupListIndex = l.lookup_index rec.LookupListIndex = l.lookup_index
self.addLookupRecordToSubtable_(st, rec) return st
return self.buildLookup_(subtables)
def add_subtable_break(self, location): def add_subtable_break(self, location):
self.rules.append((self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_, self.rules.append((self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_,
self.SUBTABLE_BREAK_, [self.SUBTABLE_BREAK_])) self.SUBTABLE_BREAK_, [self.SUBTABLE_BREAK_]))
def newSubtable_(self, chaining=True):
subtablename = f"Context{self.subtable_type}"
if chaining:
subtablename = "Chain"+subtablename
st = getattr(ot,subtablename)() # ot.ChainContextPos()/ot.ChainSubst()/etc.
setattr(st, f"{self.subtable_type}Count", 0)
setattr(st, f"{self.subtable_type}LookupRecord", [])
return st
def attachSubtableWithCount_(self, st,
subtable_name, count_name,
existing=None,
index=None, chaining=False):
if chaining:
subtable_name = "Chain"+subtable_name
count_name = "Chain"+count_name
if not hasattr(st, count_name):
setattr(st, count_name, 0)
setattr(st, subtable_name, [])
if existing:
new_subtable = existing
else:
# Create a new, empty subtable from otTables
new_subtable = getattr(ot, subtable_name)()
setattr(st, count_name, getattr(st, count_name) + 1)
if index:
getattr(st, subtable_name).insert(index, new_subtable)
else:
getattr(st, subtable_name).append(new_subtable)
return new_subtable
def newLookupRecord_(self, st):
return self.attachSubtableWithCount_(st,
f"{self.subtable_type}LookupRecord",
f"{self.subtable_type}Count",
chaining=False) # Oddly, it isn't ChainSubstLookupRecord
class ChainContextPosBuilder(ChainContextualBuilder): class ChainContextPosBuilder(ChainContextualBuilder):
"""Builds a Chained Contextual Positioning (GPOS8) lookup. """Builds a Chained Contextual Positioning (GPOS8) lookup.
@ -321,19 +439,7 @@ class ChainContextPosBuilder(ChainContextualBuilder):
def __init__(self, font, location): def __init__(self, font, location):
LookupBuilder.__init__(self, font, location, 'GPOS', 8) LookupBuilder.__init__(self, font, location, 'GPOS', 8)
self.rules = [] # (prefix, input, suffix, lookups) self.rules = [] # (prefix, input, suffix, lookups)
self.subtable_type = "Pos"
def newSubtable_(self):
st = ot.ChainContextPos()
st.PosCount = 0
st.PosLookupRecord = []
return st
def newLookupRecord_(self):
return ot.PosLookupRecord()
def addLookupRecordToSubtable_(self, st, rec):
st.PosCount += 1
st.PosLookupRecord.append(rec)
def find_chainable_single_pos(self, lookups, glyphs, value): def find_chainable_single_pos(self, lookups, glyphs, value):
"""Helper for add_single_pos_chained_()""" """Helper for add_single_pos_chained_()"""
@ -376,19 +482,7 @@ class ChainContextSubstBuilder(ChainContextualBuilder):
def __init__(self, font, location): def __init__(self, font, location):
LookupBuilder.__init__(self, font, location, 'GSUB', 6) LookupBuilder.__init__(self, font, location, 'GSUB', 6)
self.rules = [] # (prefix, input, suffix, lookups) self.rules = [] # (prefix, input, suffix, lookups)
self.subtable_type = "Subst"
def newSubtable_(self):
st = ot.ChainContextSubst()
st.SubstCount = 0
st.SubstLookupRecord = []
return st
def newLookupRecord_(self):
return ot.SubstLookupRecord()
def addLookupRecordToSubtable_(self, st, rec):
st.SubstCount += 1
st.SubstLookupRecord.append(rec)
def getAlternateGlyphs(self): def getAlternateGlyphs(self):
result = {} result = {}

View File

@ -69,10 +69,10 @@ class BuilderTest(unittest.TestCase):
ZeroValue_SinglePos_horizontal ZeroValue_SinglePos_vertical ZeroValue_SinglePos_horizontal ZeroValue_SinglePos_vertical
ZeroValue_PairPos_horizontal ZeroValue_PairPos_vertical ZeroValue_PairPos_horizontal ZeroValue_PairPos_vertical
ZeroValue_ChainSinglePos_horizontal ZeroValue_ChainSinglePos_vertical ZeroValue_ChainSinglePos_horizontal ZeroValue_ChainSinglePos_vertical
PairPosSubtable ChainSubstSubtable ChainPosSubtable LigatureSubtable PairPosSubtable ChainSubstSubtable SubstSubtable ChainPosSubtable
AlternateSubtable MultipleSubstSubtable SingleSubstSubtable LigatureSubtable AlternateSubtable MultipleSubstSubtable
aalt_chain_contextual_subst AlternateChained MultipleLookupsPerGlyph SingleSubstSubtable aalt_chain_contextual_subst AlternateChained
MultipleLookupsPerGlyph2 MultipleLookupsPerGlyph MultipleLookupsPerGlyph2
""".split() """.split()
def __init__(self, methodName): def __init__(self, methodName):

View File

@ -1,9 +1,9 @@
feature test { feature test {
sub G' by G.swash; sub A G' by G.swash;
subtable; subtable;
sub H' by H.swash; sub A H' by H.swash;
subtable; subtable;
sub G' by g; sub A G' by g;
subtable; subtable;
sub H' by H.swash; sub A H' by H.swash;
} test; } test;

View File

@ -34,7 +34,10 @@
<LookupFlag value="0"/> <LookupFlag value="0"/>
<!-- SubTableCount=4 --> <!-- SubTableCount=4 -->
<ChainContextSubst index="0" Format="3"> <ChainContextSubst index="0" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- BacktrackGlyphCount=1 -->
<BacktrackCoverage index="0">
<Glyph value="A"/>
</BacktrackCoverage>
<!-- InputGlyphCount=1 --> <!-- InputGlyphCount=1 -->
<InputCoverage index="0"> <InputCoverage index="0">
<Glyph value="G"/> <Glyph value="G"/>
@ -47,7 +50,10 @@
</SubstLookupRecord> </SubstLookupRecord>
</ChainContextSubst> </ChainContextSubst>
<ChainContextSubst index="1" Format="3"> <ChainContextSubst index="1" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- BacktrackGlyphCount=1 -->
<BacktrackCoverage index="0">
<Glyph value="A"/>
</BacktrackCoverage>
<!-- InputGlyphCount=1 --> <!-- InputGlyphCount=1 -->
<InputCoverage index="0"> <InputCoverage index="0">
<Glyph value="H"/> <Glyph value="H"/>
@ -60,7 +66,10 @@
</SubstLookupRecord> </SubstLookupRecord>
</ChainContextSubst> </ChainContextSubst>
<ChainContextSubst index="2" Format="3"> <ChainContextSubst index="2" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- BacktrackGlyphCount=1 -->
<BacktrackCoverage index="0">
<Glyph value="A"/>
</BacktrackCoverage>
<!-- InputGlyphCount=1 --> <!-- InputGlyphCount=1 -->
<InputCoverage index="0"> <InputCoverage index="0">
<Glyph value="G"/> <Glyph value="G"/>
@ -73,7 +82,10 @@
</SubstLookupRecord> </SubstLookupRecord>
</ChainContextSubst> </ChainContextSubst>
<ChainContextSubst index="3" Format="3"> <ChainContextSubst index="3" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- BacktrackGlyphCount=1 -->
<BacktrackCoverage index="0">
<Glyph value="A"/>
</BacktrackCoverage>
<!-- InputGlyphCount=1 --> <!-- InputGlyphCount=1 -->
<InputCoverage index="0"> <InputCoverage index="0">
<Glyph value="H"/> <Glyph value="H"/>

View File

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

View File

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>
<GSUB>
<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="test"/>
<Feature>
<!-- LookupCount=1 -->
<LookupListIndex index="0" value="0"/>
</Feature>
</FeatureRecord>
</FeatureList>
<LookupList>
<!-- LookupCount=5 -->
<Lookup index="0">
<LookupType value="5"/>
<LookupFlag value="0"/>
<!-- SubTableCount=4 -->
<ContextSubst index="0" Format="3">
<!-- GlyphCount=1 -->
<!-- SubstCount=1 -->
<Coverage index="0">
<Glyph value="G"/>
</Coverage>
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="1"/>
</SubstLookupRecord>
</ContextSubst>
<ContextSubst index="1" Format="3">
<!-- GlyphCount=1 -->
<!-- SubstCount=1 -->
<Coverage index="0">
<Glyph value="H"/>
</Coverage>
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="2"/>
</SubstLookupRecord>
</ContextSubst>
<ContextSubst index="2" Format="3">
<!-- GlyphCount=1 -->
<!-- SubstCount=1 -->
<Coverage index="0">
<Glyph value="G"/>
</Coverage>
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="3"/>
</SubstLookupRecord>
</ContextSubst>
<ContextSubst index="3" Format="3">
<!-- GlyphCount=1 -->
<!-- SubstCount=1 -->
<Coverage index="0">
<Glyph value="H"/>
</Coverage>
<SubstLookupRecord index="0">
<SequenceIndex value="0"/>
<LookupListIndex value="4"/>
</SubstLookupRecord>
</ContextSubst>
</Lookup>
<Lookup index="1">
<LookupType value="1"/>
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="G" out="G.swash"/>
</SingleSubst>
</Lookup>
<Lookup index="2">
<LookupType value="1"/>
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="H" out="H.swash"/>
</SingleSubst>
</Lookup>
<Lookup index="3">
<LookupType value="1"/>
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="G" out="g"/>
</SingleSubst>
</Lookup>
<Lookup index="4">
<LookupType value="1"/>
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SingleSubst index="0">
<Substitution in="H" out="H.swash"/>
</SingleSubst>
</Lookup>
</LookupList>
</GSUB>
</ttFont>

View File

@ -30,25 +30,23 @@
<LookupList> <LookupList>
<!-- LookupCount=2 --> <!-- LookupCount=2 -->
<Lookup index="0"> <Lookup index="0">
<LookupType value="6"/> <LookupType value="5"/>
<LookupFlag value="0"/> <LookupFlag value="0"/>
<!-- SubTableCount=1 --> <!-- SubTableCount=1 -->
<ChainContextSubst index="0" Format="3"> <ContextSubst index="0" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- GlyphCount=2 -->
<!-- InputGlyphCount=2 -->
<InputCoverage index="0">
<Glyph value="f"/>
</InputCoverage>
<InputCoverage index="1">
<Glyph value="i"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=1 --> <!-- SubstCount=1 -->
<Coverage index="0">
<Glyph value="f"/>
</Coverage>
<Coverage index="1">
<Glyph value="i"/>
</Coverage>
<SubstLookupRecord index="0"> <SubstLookupRecord index="0">
<SequenceIndex value="0"/> <SequenceIndex value="0"/>
<LookupListIndex value="1"/> <LookupListIndex value="1"/>
</SubstLookupRecord> </SubstLookupRecord>
</ChainContextSubst> </ContextSubst>
</Lookup> </Lookup>
<Lookup index="1"> <Lookup index="1">
<LookupType value="4"/> <LookupType value="4"/>

View File

@ -30,33 +30,29 @@
<LookupList> <LookupList>
<!-- LookupCount=2 --> <!-- LookupCount=2 -->
<Lookup index="0"> <Lookup index="0">
<LookupType value="6"/> <LookupType value="5"/>
<LookupFlag value="0"/> <LookupFlag value="0"/>
<!-- SubTableCount=2 --> <!-- SubTableCount=2 -->
<ChainContextSubst index="0" Format="3"> <ContextSubst index="0" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- GlyphCount=1 -->
<!-- InputGlyphCount=1 -->
<InputCoverage index="0">
<Glyph value="A"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=0 --> <!-- SubstCount=0 -->
</ChainContextSubst> <Coverage index="0">
<ChainContextSubst index="1" Format="3"> <Glyph value="A"/>
<!-- BacktrackGlyphCount=0 --> </Coverage>
<!-- InputGlyphCount=1 --> </ContextSubst>
<InputCoverage index="0"> <ContextSubst index="1" Format="3">
<!-- GlyphCount=1 -->
<!-- SubstCount=1 -->
<Coverage index="0">
<Glyph value="A"/> <Glyph value="A"/>
<Glyph value="A.sc"/> <Glyph value="A.sc"/>
<Glyph value="A.alt1"/> <Glyph value="A.alt1"/>
</InputCoverage> </Coverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=1 -->
<SubstLookupRecord index="0"> <SubstLookupRecord index="0">
<SequenceIndex value="0"/> <SequenceIndex value="0"/>
<LookupListIndex value="1"/> <LookupListIndex value="1"/>
</SubstLookupRecord> </SubstLookupRecord>
</ChainContextSubst> </ContextSubst>
</Lookup> </Lookup>
<Lookup index="1"> <Lookup index="1">
<LookupType value="1"/> <LookupType value="1"/>

View File

@ -30,61 +30,53 @@
<LookupList> <LookupList>
<!-- LookupCount=3 --> <!-- LookupCount=3 -->
<Lookup index="0"> <Lookup index="0">
<LookupType value="6"/> <LookupType value="5"/>
<LookupFlag value="0"/> <LookupFlag value="0"/>
<!-- SubTableCount=4 --> <!-- SubTableCount=4 -->
<ChainContextSubst index="0" Format="3"> <ContextSubst index="0" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- GlyphCount=1 -->
<!-- InputGlyphCount=1 -->
<InputCoverage index="0">
<Glyph value="G"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=1 --> <!-- SubstCount=1 -->
<Coverage index="0">
<Glyph value="G"/>
</Coverage>
<SubstLookupRecord index="0"> <SubstLookupRecord index="0">
<SequenceIndex value="0"/> <SequenceIndex value="0"/>
<LookupListIndex value="1"/> <LookupListIndex value="1"/>
</SubstLookupRecord> </SubstLookupRecord>
</ChainContextSubst> </ContextSubst>
<ChainContextSubst index="1" Format="3"> <ContextSubst index="1" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- GlyphCount=1 -->
<!-- InputGlyphCount=1 -->
<InputCoverage index="0">
<Glyph value="H"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=1 --> <!-- SubstCount=1 -->
<Coverage index="0">
<Glyph value="H"/>
</Coverage>
<SubstLookupRecord index="0"> <SubstLookupRecord index="0">
<SequenceIndex value="0"/> <SequenceIndex value="0"/>
<LookupListIndex value="1"/> <LookupListIndex value="1"/>
</SubstLookupRecord> </SubstLookupRecord>
</ChainContextSubst> </ContextSubst>
<ChainContextSubst index="2" Format="3"> <ContextSubst index="2" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- GlyphCount=1 -->
<!-- InputGlyphCount=1 --> <!-- SubstCount=1 -->
<InputCoverage index="0"> <Coverage index="0">
<Glyph value="G"/> <Glyph value="G"/>
</InputCoverage> </Coverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=1 -->
<SubstLookupRecord index="0"> <SubstLookupRecord index="0">
<SequenceIndex value="0"/> <SequenceIndex value="0"/>
<LookupListIndex value="2"/> <LookupListIndex value="2"/>
</SubstLookupRecord> </SubstLookupRecord>
</ChainContextSubst> </ContextSubst>
<ChainContextSubst index="3" Format="3"> <ContextSubst index="3" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- GlyphCount=1 -->
<!-- InputGlyphCount=1 --> <!-- SubstCount=1 -->
<InputCoverage index="0"> <Coverage index="0">
<Glyph value="H"/> <Glyph value="H"/>
</InputCoverage> </Coverage>
<!-- LookAheadGlyphCount=0 -->
<!-- SubstCount=1 -->
<SubstLookupRecord index="0"> <SubstLookupRecord index="0">
<SequenceIndex value="0"/> <SequenceIndex value="0"/>
<LookupListIndex value="2"/> <LookupListIndex value="2"/>
</SubstLookupRecord> </SubstLookupRecord>
</ChainContextSubst> </ContextSubst>
</Lookup> </Lookup>
<Lookup index="1"> <Lookup index="1">
<LookupType value="1"/> <LookupType value="1"/>

View File

@ -112,25 +112,23 @@
</MarkBasePos> </MarkBasePos>
</Lookup> </Lookup>
<Lookup index="2"> <Lookup index="2">
<LookupType value="8"/> <LookupType value="7"/>
<LookupFlag value="0"/> <LookupFlag value="0"/>
<!-- SubTableCount=1 --> <!-- SubTableCount=1 -->
<ChainContextPos index="0" Format="3"> <ContextPos index="0" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- GlyphCount=3 -->
<!-- InputGlyphCount=3 --> <!-- PosCount=2 -->
<InputCoverage index="0"> <Coverage index="0">
<Glyph value="T"/> <Glyph value="T"/>
</InputCoverage> </Coverage>
<InputCoverage index="1"> <Coverage index="1">
<Glyph value="c"/> <Glyph value="c"/>
<Glyph value="o"/> <Glyph value="o"/>
</InputCoverage> </Coverage>
<InputCoverage index="2"> <Coverage index="2">
<Glyph value="grave"/> <Glyph value="grave"/>
<Glyph value="acute"/> <Glyph value="acute"/>
</InputCoverage> </Coverage>
<!-- LookAheadGlyphCount=0 -->
<!-- PosCount=2 -->
<PosLookupRecord index="0"> <PosLookupRecord index="0">
<SequenceIndex value="0"/> <SequenceIndex value="0"/>
<LookupListIndex value="0"/> <LookupListIndex value="0"/>
@ -139,7 +137,7 @@
<SequenceIndex value="2"/> <SequenceIndex value="2"/>
<LookupListIndex value="1"/> <LookupListIndex value="1"/>
</PosLookupRecord> </PosLookupRecord>
</ChainContextPos> </ContextPos>
</Lookup> </Lookup>
</LookupList> </LookupList>
</GPOS> </GPOS>

View File

@ -30,25 +30,23 @@
<LookupList> <LookupList>
<!-- LookupCount=2 --> <!-- LookupCount=2 -->
<Lookup index="0"> <Lookup index="0">
<LookupType value="8"/> <LookupType value="7"/>
<LookupFlag value="0"/> <LookupFlag value="0"/>
<!-- SubTableCount=1 --> <!-- SubTableCount=1 -->
<ChainContextPos index="0" Format="3"> <ContextPos index="0" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- GlyphCount=2 -->
<!-- InputGlyphCount=2 -->
<InputCoverage index="0">
<Glyph value="L"/>
</InputCoverage>
<InputCoverage index="1">
<Glyph value="quoteright"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- PosCount=1 --> <!-- PosCount=1 -->
<Coverage index="0">
<Glyph value="L"/>
</Coverage>
<Coverage index="1">
<Glyph value="quoteright"/>
</Coverage>
<PosLookupRecord index="0"> <PosLookupRecord index="0">
<SequenceIndex value="1"/> <SequenceIndex value="1"/>
<LookupListIndex value="1"/> <LookupListIndex value="1"/>
</PosLookupRecord> </PosLookupRecord>
</ChainContextPos> </ContextPos>
</Lookup> </Lookup>
<Lookup index="1"> <Lookup index="1">
<LookupType value="1"/> <LookupType value="1"/>

View File

@ -1392,6 +1392,57 @@ def test_stat_infinities():
assert struct.pack(">l", posInf) == b"\x7f\xff\xff\xff" assert struct.pack(">l", posInf) == b"\x7f\xff\xff\xff"
class ChainContextualRulesetTest(object):
def test_makeRulesets(self):
font = ttLib.TTFont()
font.setGlyphOrder(["a","b","c","d","A","B","C","D","E"])
sb = builder.ChainContextSubstBuilder(font, None)
prefix, input_, suffix, lookups = [["a"], ["b"]], [["c"]], [], [None]
sb.rules.append((prefix, input_, suffix, lookups))
prefix, input_, suffix, lookups = [["a"], ["d"]], [["c"]], [], [None]
sb.rules.append((prefix, input_, suffix, lookups))
sb.add_subtable_break(None)
# Second subtable has some glyph classes
prefix, input_, suffix, lookups = [["A"]], [["E"]], [], [None]
sb.rules.append((prefix, input_, suffix, lookups))
prefix, input_, suffix, lookups = [["A"]], [["C","D"]], [], [None]
sb.rules.append((prefix, input_, suffix, lookups))
prefix, input_, suffix, lookups = [["A", "B"]], [["E"]], [], [None]
sb.rules.append((prefix, input_, suffix, lookups))
sb.add_subtable_break(None)
# Third subtable has no pre/post context
prefix, input_, suffix, lookups = [], [["E"]], [], [None]
sb.rules.append((prefix, input_, suffix, lookups))
prefix, input_, suffix, lookups = [], [["C","D"]], [], [None]
sb.rules.append((prefix, input_, suffix, lookups))
rulesets = sb.rulesets()
assert len(rulesets) == 3
assert rulesets[0].hasPrefixOrSuffix
assert not rulesets[0].hasAnyGlyphClasses
cd = rulesets[0].format2ClassDefs()
assert set(cd[0].classes()[1:]) == set([("d",),("b",),("a",)])
assert set(cd[1].classes()[1:]) == set([("c",)])
assert set(cd[2].classes()[1:]) == set()
assert rulesets[1].hasPrefixOrSuffix
assert rulesets[1].hasAnyGlyphClasses
assert not rulesets[1].format2ClassDefs()
assert not rulesets[2].hasPrefixOrSuffix
assert rulesets[2].hasAnyGlyphClasses
assert rulesets[2].format2ClassDefs()
cd = rulesets[2].format2ClassDefs()
assert set(cd[0].classes()[1:]) == set()
assert set(cd[1].classes()[1:]) == set([("C","D"), ("E",)])
assert set(cd[2].classes()[1:]) == set()
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys

View File

@ -83,23 +83,21 @@
</MarkBasePos> </MarkBasePos>
</Lookup> </Lookup>
<Lookup index="2"> <Lookup index="2">
<LookupType value="8"/> <LookupType value="7"/>
<LookupFlag value="0"/> <LookupFlag value="0"/>
<!-- SubTableCount=1 --> <!-- SubTableCount=1 -->
<ChainContextPos index="0" Format="3"> <ContextPos index="0" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- GlyphCount=3 -->
<!-- InputGlyphCount=3 -->
<InputCoverage index="0" Format="1">
<Glyph value="A"/>
</InputCoverage>
<InputCoverage index="1" Format="1">
<Glyph value="a"/>
</InputCoverage>
<InputCoverage index="2" Format="1">
<Glyph value="uni0303"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- PosCount=2 --> <!-- PosCount=2 -->
<Coverage index="0" Format="1">
<Glyph value="A"/>
</Coverage>
<Coverage index="1" Format="1">
<Glyph value="a"/>
</Coverage>
<Coverage index="2" Format="1">
<Glyph value="uni0303"/>
</Coverage>
<PosLookupRecord index="0"> <PosLookupRecord index="0">
<SequenceIndex value="0"/> <SequenceIndex value="0"/>
<LookupListIndex value="0"/> <LookupListIndex value="0"/>
@ -108,7 +106,7 @@
<SequenceIndex value="2"/> <SequenceIndex value="2"/>
<LookupListIndex value="1"/> <LookupListIndex value="1"/>
</PosLookupRecord> </PosLookupRecord>
</ChainContextPos> </ContextPos>
</Lookup> </Lookup>
</LookupList> </LookupList>
</GPOS> </GPOS>

View File

@ -83,23 +83,21 @@
</MarkBasePos> </MarkBasePos>
</Lookup> </Lookup>
<Lookup index="2"> <Lookup index="2">
<LookupType value="8"/> <LookupType value="7"/>
<LookupFlag value="0"/> <LookupFlag value="0"/>
<!-- SubTableCount=1 --> <!-- SubTableCount=1 -->
<ChainContextPos index="0" Format="3"> <ContextPos index="0" Format="3">
<!-- BacktrackGlyphCount=0 --> <!-- GlyphCount=3 -->
<!-- InputGlyphCount=3 -->
<InputCoverage index="0" Format="1">
<Glyph value="A"/>
</InputCoverage>
<InputCoverage index="1" Format="1">
<Glyph value="a"/>
</InputCoverage>
<InputCoverage index="2" Format="1">
<Glyph value="uni0303"/>
</InputCoverage>
<!-- LookAheadGlyphCount=0 -->
<!-- PosCount=2 --> <!-- PosCount=2 -->
<Coverage index="0" Format="1">
<Glyph value="A"/>
</Coverage>
<Coverage index="1" Format="1">
<Glyph value="a"/>
</Coverage>
<Coverage index="2" Format="1">
<Glyph value="uni0303"/>
</Coverage>
<PosLookupRecord index="0"> <PosLookupRecord index="0">
<SequenceIndex value="0"/> <SequenceIndex value="0"/>
<LookupListIndex value="0"/> <LookupListIndex value="0"/>
@ -108,7 +106,7 @@
<SequenceIndex value="2"/> <SequenceIndex value="2"/>
<LookupListIndex value="1"/> <LookupListIndex value="1"/>
</PosLookupRecord> </PosLookupRecord>
</ChainContextPos> </ContextPos>
</Lookup> </Lookup>
</LookupList> </LookupList>
</GPOS> </GPOS>

View File

@ -748,8 +748,8 @@ class InterpolateLayoutTest(unittest.TestCase):
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_8_same_val_ttf(self): def test_varlib_interpolate_layout_GPOS_only_LookupType_7_same_val_ttf(self):
"""Only GPOS; LookupType 8; same values in all masters. """Only GPOS; LookupType 7; same values in all masters.
""" """
suffix = '.ttf' suffix = '.ttf'
ds_path = self.get_test_input('InterpolateLayout.designspace') ds_path = self.get_test_input('InterpolateLayout.designspace')
@ -781,13 +781,13 @@ class InterpolateLayoutTest(unittest.TestCase):
instfont = interpolate_layout(ds_path, {'weight': 500}, finder) instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
tables = ['GPOS'] tables = ['GPOS']
expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_8_same.ttx') expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_7_same.ttx')
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_8_diff_val_ttf(self): def test_varlib_interpolate_layout_GPOS_only_LookupType_7_diff_val_ttf(self):
"""Only GPOS; LookupType 8; different values in each master. """Only GPOS; LookupType 7; different values in each master.
""" """
suffix = '.ttf' suffix = '.ttf'
ds_path = self.get_test_input('InterpolateLayout.designspace') ds_path = self.get_test_input('InterpolateLayout.designspace')
@ -833,7 +833,7 @@ class InterpolateLayoutTest(unittest.TestCase):
instfont = interpolate_layout(ds_path, {'weight': 500}, finder) instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
tables = ['GPOS'] tables = ['GPOS']
expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_8_diff.ttx') expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_7_diff.ttx')
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)