[otlLib] Move building of MarkBasePos from feaLib to otlLib
This commit is contained in:
parent
50cbd5ca78
commit
cf7c670e80
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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>'
|
||||
|
Loading…
x
Reference in New Issue
Block a user