Introduce the concept of a ClassContextualRuleSet
Currently unused but will be super helpful for optimizing the formats of contextual lookups. * Splits the rules of a class contextual lookup based on explicit subtable breaks * Returns various properties on the ruleset to help determine appropriate layout format. * (More properties, such as "touched glyphs", planned - will be added when needed.)
This commit is contained in:
parent
26072943c3
commit
9f4cc2f1cb
@ -244,11 +244,70 @@ 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 format1Classdefs(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.
|
||||||
|
|
||||||
|
@ -1392,6 +1392,63 @@ 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 = [ ["a"], ["b"] ]
|
||||||
|
input_ = [ ["c"] ]
|
||||||
|
suffix = [ ]
|
||||||
|
lookups = [ None ]
|
||||||
|
sb.rules.append((prefix, input_, suffix, lookups))
|
||||||
|
|
||||||
|
prefix = [ ["a"], ["d"] ]
|
||||||
|
sb.rules.append((prefix, input_, suffix, lookups))
|
||||||
|
|
||||||
|
sb.add_subtable_break(None)
|
||||||
|
|
||||||
|
# Second subtable has some glyph classes
|
||||||
|
prefix = [ ["A"] ]
|
||||||
|
input_ = [ ["E"] ]
|
||||||
|
sb.rules.append((prefix, input_, suffix, lookups))
|
||||||
|
input_ = [ ["C","D"] ]
|
||||||
|
sb.rules.append((prefix, input_, suffix, lookups))
|
||||||
|
prefix = [ ["A", "B"] ]
|
||||||
|
input_ = [ ["E"] ]
|
||||||
|
sb.rules.append((prefix, input_, suffix, lookups))
|
||||||
|
|
||||||
|
sb.add_subtable_break(None)
|
||||||
|
|
||||||
|
# Third subtable has no pre/post context
|
||||||
|
prefix = []
|
||||||
|
suffix = []
|
||||||
|
sb.rules.append((prefix, input_, suffix, lookups))
|
||||||
|
input_ = [ ["C","D"] ]
|
||||||
|
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].format1Classdefs()
|
||||||
|
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].format1Classdefs()
|
||||||
|
|
||||||
|
assert not rulesets[2].hasPrefixOrSuffix
|
||||||
|
assert rulesets[2].hasAnyGlyphClasses
|
||||||
|
assert rulesets[2].format1Classdefs()
|
||||||
|
cd = rulesets[2].format1Classdefs()
|
||||||
|
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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user