[otlLib] Move building of MarkBasePos from feaLib to otlLib

This commit is contained in:
Sascha Brawer 2016-01-22 11:53:34 +01:00
parent 50cbd5ca78
commit cf7c670e80
3 changed files with 140 additions and 21 deletions

View File

@ -955,16 +955,6 @@ class MarkBasePosBuilder(LookupBuilder):
return result
def build(self):
# TODO: Consider emitting multiple subtables to save space.
# Partition the marks and bases into disjoint subsets, so that
# MarkBasePos rules would only access glyphs from a single
# subset. This would likely lead to smaller mark/base
# matrices, so we might be able to omit many of the empty
# anchor tables that we currently produce. Of course, this
# would only work if the MarkBasePos rules of real-world fonts
# allow partitioning into multiple subsets. We should find out
# whether this is the case; if so, implement the optimization.
markClasses = self.buildMarkClasses_(self.marks)
marks = {mark: (markClasses[mc], anchor)
for mark, (mc, anchor) in self.marks.items()}
@ -972,15 +962,8 @@ class MarkBasePosBuilder(LookupBuilder):
for glyph, anchors in self.bases.items():
bases[glyph] = {markClasses[mc]: anchor
for (mc, anchor) in anchors.items()}
st = otTables.MarkBasePos()
st.Format = 1
st.MarkCoverage = otl.buildCoverage(marks, self.glyphMap)
st.MarkArray = otl.buildMarkArray(marks, self.glyphMap)
st.ClassCount = len(markClasses)
st.BaseCoverage = otl.buildCoverage(bases, self.glyphMap)
st.BaseArray = otl.buildBaseArray(bases, st.ClassCount, self.glyphMap)
return self.buildLookup_([st])
subtables = otl.buildMarkBasePos(marks, bases, self.glyphMap)
return self.buildLookup_(subtables)
class MarkLigPosBuilder(LookupBuilder):

View File

@ -186,6 +186,44 @@ def buildMarkArray(marks, glyphMap):
return self
def buildMarkBasePos(marks, bases, glyphMap):
"""Build a list of MarkBasePos subtables.
a1, a2, a3, a4 = buildAnchor(500, 100), ...
marks = {"acute": (0, a1), "grave": (0, a1), "cedilla": (1, a2)}
bases = {"a": {0: a3, 1: a5}, "b": {0: a4, 1: a5}}
"""
# TODO: Consider emitting multiple subtables to save space.
# Partition the marks and bases into disjoint subsets, so that
# MarkBasePos rules would only access glyphs from a single
# subset. This would likely lead to smaller mark/base
# matrices, so we might be able to omit many of the empty
# anchor tables that we currently produce. Of course, this
# would only work if the MarkBasePos rules of real-world fonts
# allow partitioning into multiple subsets. We should find out
# whether this is the case; if so, implement the optimization.
# On the other hand, a very large number of subtables could
# slow down layout engines; so this would need profiling.
return [buildMarkBasePosSubtable(marks, bases, glyphMap)]
def buildMarkBasePosSubtable(marks, bases, glyphMap):
"""Build a single MarkBasePos subtable.
a1, a2, a3, a4 = buildAnchor(500, 100), ...
marks = {"acute": (0, a1), "grave": (0, a1), "cedilla": (1, a2)}
bases = {"a": {0: a3, 1: a5}, "b": {0: a4, 1: a5}}
"""
self = ot.MarkBasePos()
self.Format = 1
self.MarkCoverage = buildCoverage(marks, glyphMap)
self.MarkArray = buildMarkArray(marks, glyphMap)
self.ClassCount = max([mc for mc, _ in marks.values()]) + 1
self.BaseCoverage = buildCoverage(bases, glyphMap)
self.BaseArray = buildBaseArray(bases, self.ClassCount, glyphMap)
return self
def buildMarkRecord(classID, anchor):
assert isinstance(classID, int)
assert isinstance(anchor, ot.Anchor)

View File

@ -101,7 +101,6 @@ class BuilderTest(unittest.TestCase):
"a": {2: anchor(300, 80)},
"c": {1: anchor(300, 80), 2: anchor(300, -20)}
}, numMarkClasses=4, glyphMap=self.GLYPHMAP)
self.maxDiff = None
self.assertEqual(getXML(baseArray.toXML),
'<BaseArray>'
' <!-- BaseCount=2 -->'
@ -412,6 +411,106 @@ class BuilderTest(unittest.TestCase):
' </MarkRecord>'
'</MarkArray>')
def test_buildMarkBasePosSubtable(self):
anchor = builder.buildAnchor
marks = {
"acute": (0, anchor(300, 700)),
"cedilla": (1, anchor(300, -100)),
"grave": (0, anchor(300, 700))
}
bases = {
# Make sure we can handle missing entries.
"A": {}, # no entry for any markClass
"B": {0: anchor(500, 900)}, # only markClass 0 specified
"C": {1: anchor(500, -10)}, # only markClass 1 specified
"a": {0: anchor(500, 400), 1: anchor(500, -20)},
"b": {0: anchor(500, 800), 1: anchor(500, -20)}
}
table = builder.buildMarkBasePosSubtable(marks, bases, self.GLYPHMAP)
self.maxDiff = None
self.assertEqual(getXML(table.toXML),
'<MarkBasePos Format="1">'
' <MarkCoverage>'
' <Glyph value="grave"/>'
' <Glyph value="acute"/>'
' <Glyph value="cedilla"/>'
' </MarkCoverage>'
' <BaseCoverage>'
' <Glyph value="A"/>'
' <Glyph value="B"/>'
' <Glyph value="C"/>'
' <Glyph value="a"/>'
' <Glyph value="b"/>'
' </BaseCoverage>'
' <!-- ClassCount=2 -->'
' <MarkArray>'
' <!-- MarkCount=3 -->'
' <MarkRecord index="0">' # grave
' <Class value="0"/>'
' <MarkAnchor Format="1">'
' <XCoordinate value="300"/>'
' <YCoordinate value="700"/>'
' </MarkAnchor>'
' </MarkRecord>'
' <MarkRecord index="1">' # acute
' <Class value="0"/>'
' <MarkAnchor Format="1">'
' <XCoordinate value="300"/>'
' <YCoordinate value="700"/>'
' </MarkAnchor>'
' </MarkRecord>'
' <MarkRecord index="2">' # cedilla
' <Class value="1"/>'
' <MarkAnchor Format="1">'
' <XCoordinate value="300"/>'
' <YCoordinate value="-100"/>'
' </MarkAnchor>'
' </MarkRecord>'
' </MarkArray>'
' <BaseArray>'
' <!-- BaseCount=5 -->'
' <BaseRecord index="0">' # A
' <BaseAnchor index="0" empty="1"/>'
' <BaseAnchor index="1" empty="1"/>'
' </BaseRecord>'
' <BaseRecord index="1">' # B
' <BaseAnchor index="0" Format="1">'
' <XCoordinate value="500"/>'
' <YCoordinate value="900"/>'
' </BaseAnchor>'
' <BaseAnchor index="1" empty="1"/>'
' </BaseRecord>'
' <BaseRecord index="2">' # C
' <BaseAnchor index="0" empty="1"/>'
' <BaseAnchor index="1" Format="1">'
' <XCoordinate value="500"/>'
' <YCoordinate value="-10"/>'
' </BaseAnchor>'
' </BaseRecord>'
' <BaseRecord index="3">' # a
' <BaseAnchor index="0" Format="1">'
' <XCoordinate value="500"/>'
' <YCoordinate value="400"/>'
' </BaseAnchor>'
' <BaseAnchor index="1" Format="1">'
' <XCoordinate value="500"/>'
' <YCoordinate value="-20"/>'
' </BaseAnchor>'
' </BaseRecord>'
' <BaseRecord index="4">' # b
' <BaseAnchor index="0" Format="1">'
' <XCoordinate value="500"/>'
' <YCoordinate value="800"/>'
' </BaseAnchor>'
' <BaseAnchor index="1" Format="1">'
' <XCoordinate value="500"/>'
' <YCoordinate value="-20"/>'
' </BaseAnchor>'
' </BaseRecord>'
' </BaseArray>'
'</MarkBasePos>')
def test_buildMarkGlyphSetsDef(self):
marksets = builder.buildMarkGlyphSetsDef(
[{"acute", "grave"}, {"cedilla", "grave"}], self.GLYPHMAP)
@ -531,7 +630,6 @@ class BuilderTest(unittest.TestCase):
"one": builder.buildValue({"XPlacement": 777}),
"two": builder.buildValue({"YPlacement": -888}),
}, self.GLYPHMAP)
self.maxDiff = None
self.assertEqual(getXML(subtable.toXML),
'<SinglePos Format="2">'
' <Coverage>'