Merge pull request #2016 from simoncozens/gsub5-gpos7-lookups
Generate GSUB5/GPOS7 lookups (See #1856)
This commit is contained in:
commit
cbe6d3a3f4
@ -172,6 +172,13 @@ class LookupBuilder(object):
|
||||
coverage = buildCoverage(g, self.glyphMap)
|
||||
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):
|
||||
substitutions = [{}]
|
||||
for key in mapping:
|
||||
@ -244,11 +251,71 @@ class AlternateSubstBuilder(LookupBuilder):
|
||||
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):
|
||||
def equals(self, other):
|
||||
return (LookupBuilder.equals(self, other) and
|
||||
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):
|
||||
"""Build the lookup.
|
||||
|
||||
@ -257,40 +324,91 @@ class ChainContextualBuilder(LookupBuilder):
|
||||
contextual positioning lookup.
|
||||
"""
|
||||
subtables = []
|
||||
for (prefix, glyphs, suffix, lookups) in self.rules:
|
||||
if prefix == self.SUBTABLE_BREAK_:
|
||||
continue
|
||||
st = self.newSubtable_()
|
||||
subtables.append(st)
|
||||
st.Format = 3
|
||||
chaining = False
|
||||
rulesets = self.rulesets()
|
||||
chaining = any(ruleset.hasPrefixOrSuffix for ruleset in rulesets)
|
||||
for ruleset in rulesets:
|
||||
for rule in ruleset.rules:
|
||||
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.setLookAheadCoverage_(suffix, st)
|
||||
self.setInputCoverage_(glyphs, st)
|
||||
else:
|
||||
self.setCoverage_(glyphs, st)
|
||||
|
||||
for sequenceIndex, lookupList in enumerate(lookups):
|
||||
if lookupList is not None:
|
||||
if not isinstance(lookupList, list):
|
||||
# Can happen with synthesised lookups
|
||||
lookupList = [ lookupList ]
|
||||
for l in lookupList:
|
||||
if l.lookup_index is None:
|
||||
if isinstance(self, ChainContextPosBuilder):
|
||||
other = "substitution"
|
||||
else:
|
||||
other = "positioning"
|
||||
raise OpenTypeLibError('Missing index of the specified '
|
||||
f'lookup, might be a {other} lookup',
|
||||
self.location)
|
||||
rec = self.newLookupRecord_()
|
||||
rec.SequenceIndex = sequenceIndex
|
||||
rec.LookupListIndex = l.lookup_index
|
||||
self.addLookupRecordToSubtable_(st, rec)
|
||||
return self.buildLookup_(subtables)
|
||||
for sequenceIndex, lookupList in enumerate(lookups):
|
||||
if lookupList is not None:
|
||||
if not isinstance(lookupList, list):
|
||||
# Can happen with synthesised lookups
|
||||
lookupList = [lookupList]
|
||||
for l in lookupList:
|
||||
if l.lookup_index is None:
|
||||
if isinstance(self, ChainContextPosBuilder):
|
||||
other = "substitution"
|
||||
else:
|
||||
other = "positioning"
|
||||
raise OpenTypeLibError('Missing index of the specified '
|
||||
f'lookup, might be a {other} lookup',
|
||||
self.location)
|
||||
rec = self.newLookupRecord_(st)
|
||||
rec.SequenceIndex = sequenceIndex
|
||||
rec.LookupListIndex = l.lookup_index
|
||||
return st
|
||||
|
||||
def add_subtable_break(self, location):
|
||||
self.rules.append((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):
|
||||
"""Builds a Chained Contextual Positioning (GPOS8) lookup.
|
||||
@ -321,19 +439,7 @@ class ChainContextPosBuilder(ChainContextualBuilder):
|
||||
def __init__(self, font, location):
|
||||
LookupBuilder.__init__(self, font, location, 'GPOS', 8)
|
||||
self.rules = [] # (prefix, input, suffix, lookups)
|
||||
|
||||
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)
|
||||
self.subtable_type = "Pos"
|
||||
|
||||
def find_chainable_single_pos(self, lookups, glyphs, value):
|
||||
"""Helper for add_single_pos_chained_()"""
|
||||
@ -376,19 +482,7 @@ class ChainContextSubstBuilder(ChainContextualBuilder):
|
||||
def __init__(self, font, location):
|
||||
LookupBuilder.__init__(self, font, location, 'GSUB', 6)
|
||||
self.rules = [] # (prefix, input, suffix, lookups)
|
||||
|
||||
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)
|
||||
self.subtable_type = "Subst"
|
||||
|
||||
def getAlternateGlyphs(self):
|
||||
result = {}
|
||||
|
@ -69,10 +69,10 @@ class BuilderTest(unittest.TestCase):
|
||||
ZeroValue_SinglePos_horizontal ZeroValue_SinglePos_vertical
|
||||
ZeroValue_PairPos_horizontal ZeroValue_PairPos_vertical
|
||||
ZeroValue_ChainSinglePos_horizontal ZeroValue_ChainSinglePos_vertical
|
||||
PairPosSubtable ChainSubstSubtable ChainPosSubtable LigatureSubtable
|
||||
AlternateSubtable MultipleSubstSubtable SingleSubstSubtable
|
||||
aalt_chain_contextual_subst AlternateChained MultipleLookupsPerGlyph
|
||||
MultipleLookupsPerGlyph2
|
||||
PairPosSubtable ChainSubstSubtable SubstSubtable ChainPosSubtable
|
||||
LigatureSubtable AlternateSubtable MultipleSubstSubtable
|
||||
SingleSubstSubtable aalt_chain_contextual_subst AlternateChained
|
||||
MultipleLookupsPerGlyph MultipleLookupsPerGlyph2
|
||||
""".split()
|
||||
|
||||
def __init__(self, methodName):
|
||||
|
@ -1,9 +1,9 @@
|
||||
feature test {
|
||||
sub G' by G.swash;
|
||||
sub A G' by G.swash;
|
||||
subtable;
|
||||
sub H' by H.swash;
|
||||
sub A H' by H.swash;
|
||||
subtable;
|
||||
sub G' by g;
|
||||
sub A G' by g;
|
||||
subtable;
|
||||
sub H' by H.swash;
|
||||
sub A H' by H.swash;
|
||||
} test;
|
||||
|
@ -34,7 +34,10 @@
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=4 -->
|
||||
<ChainContextSubst index="0" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- BacktrackGlyphCount=1 -->
|
||||
<BacktrackCoverage index="0">
|
||||
<Glyph value="A"/>
|
||||
</BacktrackCoverage>
|
||||
<!-- InputGlyphCount=1 -->
|
||||
<InputCoverage index="0">
|
||||
<Glyph value="G"/>
|
||||
@ -47,7 +50,10 @@
|
||||
</SubstLookupRecord>
|
||||
</ChainContextSubst>
|
||||
<ChainContextSubst index="1" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- BacktrackGlyphCount=1 -->
|
||||
<BacktrackCoverage index="0">
|
||||
<Glyph value="A"/>
|
||||
</BacktrackCoverage>
|
||||
<!-- InputGlyphCount=1 -->
|
||||
<InputCoverage index="0">
|
||||
<Glyph value="H"/>
|
||||
@ -60,7 +66,10 @@
|
||||
</SubstLookupRecord>
|
||||
</ChainContextSubst>
|
||||
<ChainContextSubst index="2" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- BacktrackGlyphCount=1 -->
|
||||
<BacktrackCoverage index="0">
|
||||
<Glyph value="A"/>
|
||||
</BacktrackCoverage>
|
||||
<!-- InputGlyphCount=1 -->
|
||||
<InputCoverage index="0">
|
||||
<Glyph value="G"/>
|
||||
@ -73,7 +82,10 @@
|
||||
</SubstLookupRecord>
|
||||
</ChainContextSubst>
|
||||
<ChainContextSubst index="3" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- BacktrackGlyphCount=1 -->
|
||||
<BacktrackCoverage index="0">
|
||||
<Glyph value="A"/>
|
||||
</BacktrackCoverage>
|
||||
<!-- InputGlyphCount=1 -->
|
||||
<InputCoverage index="0">
|
||||
<Glyph value="H"/>
|
||||
|
9
Tests/feaLib/data/SubstSubtable.fea
Normal file
9
Tests/feaLib/data/SubstSubtable.fea
Normal 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;
|
116
Tests/feaLib/data/SubstSubtable.ttx
Normal file
116
Tests/feaLib/data/SubstSubtable.ttx
Normal 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>
|
@ -30,25 +30,23 @@
|
||||
<LookupList>
|
||||
<!-- LookupCount=2 -->
|
||||
<Lookup index="0">
|
||||
<LookupType value="6"/>
|
||||
<LookupType value="5"/>
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=1 -->
|
||||
<ChainContextSubst index="0" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- InputGlyphCount=2 -->
|
||||
<InputCoverage index="0">
|
||||
<Glyph value="f"/>
|
||||
</InputCoverage>
|
||||
<InputCoverage index="1">
|
||||
<Glyph value="i"/>
|
||||
</InputCoverage>
|
||||
<!-- LookAheadGlyphCount=0 -->
|
||||
<ContextSubst index="0" Format="3">
|
||||
<!-- GlyphCount=2 -->
|
||||
<!-- SubstCount=1 -->
|
||||
<Coverage index="0">
|
||||
<Glyph value="f"/>
|
||||
</Coverage>
|
||||
<Coverage index="1">
|
||||
<Glyph value="i"/>
|
||||
</Coverage>
|
||||
<SubstLookupRecord index="0">
|
||||
<SequenceIndex value="0"/>
|
||||
<LookupListIndex value="1"/>
|
||||
</SubstLookupRecord>
|
||||
</ChainContextSubst>
|
||||
</ContextSubst>
|
||||
</Lookup>
|
||||
<Lookup index="1">
|
||||
<LookupType value="4"/>
|
||||
|
@ -30,33 +30,29 @@
|
||||
<LookupList>
|
||||
<!-- LookupCount=2 -->
|
||||
<Lookup index="0">
|
||||
<LookupType value="6"/>
|
||||
<LookupType value="5"/>
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=2 -->
|
||||
<ChainContextSubst index="0" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- InputGlyphCount=1 -->
|
||||
<InputCoverage index="0">
|
||||
<Glyph value="A"/>
|
||||
</InputCoverage>
|
||||
<!-- LookAheadGlyphCount=0 -->
|
||||
<ContextSubst index="0" Format="3">
|
||||
<!-- GlyphCount=1 -->
|
||||
<!-- SubstCount=0 -->
|
||||
</ChainContextSubst>
|
||||
<ChainContextSubst index="1" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- InputGlyphCount=1 -->
|
||||
<InputCoverage index="0">
|
||||
<Coverage index="0">
|
||||
<Glyph value="A"/>
|
||||
</Coverage>
|
||||
</ContextSubst>
|
||||
<ContextSubst index="1" Format="3">
|
||||
<!-- GlyphCount=1 -->
|
||||
<!-- SubstCount=1 -->
|
||||
<Coverage index="0">
|
||||
<Glyph value="A"/>
|
||||
<Glyph value="A.sc"/>
|
||||
<Glyph value="A.alt1"/>
|
||||
</InputCoverage>
|
||||
<!-- LookAheadGlyphCount=0 -->
|
||||
<!-- SubstCount=1 -->
|
||||
</Coverage>
|
||||
<SubstLookupRecord index="0">
|
||||
<SequenceIndex value="0"/>
|
||||
<LookupListIndex value="1"/>
|
||||
</SubstLookupRecord>
|
||||
</ChainContextSubst>
|
||||
</ContextSubst>
|
||||
</Lookup>
|
||||
<Lookup index="1">
|
||||
<LookupType value="1"/>
|
||||
|
@ -30,61 +30,53 @@
|
||||
<LookupList>
|
||||
<!-- LookupCount=3 -->
|
||||
<Lookup index="0">
|
||||
<LookupType value="6"/>
|
||||
<LookupType value="5"/>
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=4 -->
|
||||
<ChainContextSubst index="0" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- InputGlyphCount=1 -->
|
||||
<InputCoverage index="0">
|
||||
<Glyph value="G"/>
|
||||
</InputCoverage>
|
||||
<!-- LookAheadGlyphCount=0 -->
|
||||
<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>
|
||||
</ChainContextSubst>
|
||||
<ChainContextSubst index="1" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- InputGlyphCount=1 -->
|
||||
<InputCoverage index="0">
|
||||
<Glyph value="H"/>
|
||||
</InputCoverage>
|
||||
<!-- LookAheadGlyphCount=0 -->
|
||||
</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="1"/>
|
||||
</SubstLookupRecord>
|
||||
</ChainContextSubst>
|
||||
<ChainContextSubst index="2" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- InputGlyphCount=1 -->
|
||||
<InputCoverage index="0">
|
||||
</ContextSubst>
|
||||
<ContextSubst index="2" Format="3">
|
||||
<!-- GlyphCount=1 -->
|
||||
<!-- SubstCount=1 -->
|
||||
<Coverage index="0">
|
||||
<Glyph value="G"/>
|
||||
</InputCoverage>
|
||||
<!-- LookAheadGlyphCount=0 -->
|
||||
<!-- SubstCount=1 -->
|
||||
</Coverage>
|
||||
<SubstLookupRecord index="0">
|
||||
<SequenceIndex value="0"/>
|
||||
<LookupListIndex value="2"/>
|
||||
</SubstLookupRecord>
|
||||
</ChainContextSubst>
|
||||
<ChainContextSubst index="3" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- InputGlyphCount=1 -->
|
||||
<InputCoverage index="0">
|
||||
</ContextSubst>
|
||||
<ContextSubst index="3" Format="3">
|
||||
<!-- GlyphCount=1 -->
|
||||
<!-- SubstCount=1 -->
|
||||
<Coverage index="0">
|
||||
<Glyph value="H"/>
|
||||
</InputCoverage>
|
||||
<!-- LookAheadGlyphCount=0 -->
|
||||
<!-- SubstCount=1 -->
|
||||
</Coverage>
|
||||
<SubstLookupRecord index="0">
|
||||
<SequenceIndex value="0"/>
|
||||
<LookupListIndex value="2"/>
|
||||
</SubstLookupRecord>
|
||||
</ChainContextSubst>
|
||||
</ContextSubst>
|
||||
</Lookup>
|
||||
<Lookup index="1">
|
||||
<LookupType value="1"/>
|
||||
|
@ -112,25 +112,23 @@
|
||||
</MarkBasePos>
|
||||
</Lookup>
|
||||
<Lookup index="2">
|
||||
<LookupType value="8"/>
|
||||
<LookupType value="7"/>
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=1 -->
|
||||
<ChainContextPos index="0" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- InputGlyphCount=3 -->
|
||||
<InputCoverage index="0">
|
||||
<ContextPos index="0" Format="3">
|
||||
<!-- GlyphCount=3 -->
|
||||
<!-- PosCount=2 -->
|
||||
<Coverage index="0">
|
||||
<Glyph value="T"/>
|
||||
</InputCoverage>
|
||||
<InputCoverage index="1">
|
||||
</Coverage>
|
||||
<Coverage index="1">
|
||||
<Glyph value="c"/>
|
||||
<Glyph value="o"/>
|
||||
</InputCoverage>
|
||||
<InputCoverage index="2">
|
||||
</Coverage>
|
||||
<Coverage index="2">
|
||||
<Glyph value="grave"/>
|
||||
<Glyph value="acute"/>
|
||||
</InputCoverage>
|
||||
<!-- LookAheadGlyphCount=0 -->
|
||||
<!-- PosCount=2 -->
|
||||
</Coverage>
|
||||
<PosLookupRecord index="0">
|
||||
<SequenceIndex value="0"/>
|
||||
<LookupListIndex value="0"/>
|
||||
@ -139,7 +137,7 @@
|
||||
<SequenceIndex value="2"/>
|
||||
<LookupListIndex value="1"/>
|
||||
</PosLookupRecord>
|
||||
</ChainContextPos>
|
||||
</ContextPos>
|
||||
</Lookup>
|
||||
</LookupList>
|
||||
</GPOS>
|
||||
|
@ -30,25 +30,23 @@
|
||||
<LookupList>
|
||||
<!-- LookupCount=2 -->
|
||||
<Lookup index="0">
|
||||
<LookupType value="8"/>
|
||||
<LookupType value="7"/>
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=1 -->
|
||||
<ChainContextPos index="0" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- InputGlyphCount=2 -->
|
||||
<InputCoverage index="0">
|
||||
<Glyph value="L"/>
|
||||
</InputCoverage>
|
||||
<InputCoverage index="1">
|
||||
<Glyph value="quoteright"/>
|
||||
</InputCoverage>
|
||||
<!-- LookAheadGlyphCount=0 -->
|
||||
<ContextPos index="0" Format="3">
|
||||
<!-- GlyphCount=2 -->
|
||||
<!-- PosCount=1 -->
|
||||
<Coverage index="0">
|
||||
<Glyph value="L"/>
|
||||
</Coverage>
|
||||
<Coverage index="1">
|
||||
<Glyph value="quoteright"/>
|
||||
</Coverage>
|
||||
<PosLookupRecord index="0">
|
||||
<SequenceIndex value="1"/>
|
||||
<LookupListIndex value="1"/>
|
||||
</PosLookupRecord>
|
||||
</ChainContextPos>
|
||||
</ContextPos>
|
||||
</Lookup>
|
||||
<Lookup index="1">
|
||||
<LookupType value="1"/>
|
||||
|
@ -1392,6 +1392,57 @@ def test_stat_infinities():
|
||||
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__":
|
||||
import sys
|
||||
|
||||
|
@ -83,23 +83,21 @@
|
||||
</MarkBasePos>
|
||||
</Lookup>
|
||||
<Lookup index="2">
|
||||
<LookupType value="8"/>
|
||||
<LookupType value="7"/>
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=1 -->
|
||||
<ChainContextPos index="0" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- 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 -->
|
||||
<ContextPos index="0" Format="3">
|
||||
<!-- GlyphCount=3 -->
|
||||
<!-- 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">
|
||||
<SequenceIndex value="0"/>
|
||||
<LookupListIndex value="0"/>
|
||||
@ -108,7 +106,7 @@
|
||||
<SequenceIndex value="2"/>
|
||||
<LookupListIndex value="1"/>
|
||||
</PosLookupRecord>
|
||||
</ChainContextPos>
|
||||
</ContextPos>
|
||||
</Lookup>
|
||||
</LookupList>
|
||||
</GPOS>
|
@ -83,23 +83,21 @@
|
||||
</MarkBasePos>
|
||||
</Lookup>
|
||||
<Lookup index="2">
|
||||
<LookupType value="8"/>
|
||||
<LookupType value="7"/>
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=1 -->
|
||||
<ChainContextPos index="0" Format="3">
|
||||
<!-- BacktrackGlyphCount=0 -->
|
||||
<!-- 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 -->
|
||||
<ContextPos index="0" Format="3">
|
||||
<!-- GlyphCount=3 -->
|
||||
<!-- 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">
|
||||
<SequenceIndex value="0"/>
|
||||
<LookupListIndex value="0"/>
|
||||
@ -108,7 +106,7 @@
|
||||
<SequenceIndex value="2"/>
|
||||
<LookupListIndex value="1"/>
|
||||
</PosLookupRecord>
|
||||
</ChainContextPos>
|
||||
</ContextPos>
|
||||
</Lookup>
|
||||
</LookupList>
|
||||
</GPOS>
|
@ -748,8 +748,8 @@ class InterpolateLayoutTest(unittest.TestCase):
|
||||
self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
||||
|
||||
|
||||
def test_varlib_interpolate_layout_GPOS_only_LookupType_8_same_val_ttf(self):
|
||||
"""Only GPOS; LookupType 8; same values in all masters.
|
||||
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')
|
||||
@ -781,13 +781,13 @@ class InterpolateLayoutTest(unittest.TestCase):
|
||||
instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
|
||||
|
||||
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.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
||||
|
||||
|
||||
def test_varlib_interpolate_layout_GPOS_only_LookupType_8_diff_val_ttf(self):
|
||||
"""Only GPOS; LookupType 8; different values in each master.
|
||||
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')
|
||||
@ -833,7 +833,7 @@ class InterpolateLayoutTest(unittest.TestCase):
|
||||
instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
|
||||
|
||||
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.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user