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. st = otTables.MarkBasePos() st.Format = 1 st.MarkCoverage = \ self.buildCoverage_(self.marks, otTables.MarkCoverage) markClasses = self.buildMarkClasses_(self.marks) st.ClassCount = len(markClasses) self.setMarkArray_(self.marks, markClasses, st) st.BaseCoverage = \ self.buildCoverage_(self.bases, otTables.BaseCoverage) st.BaseArray = otTables.BaseArray() st.BaseArray.BaseCount = len(st.BaseCoverage.glyphs) st.BaseArray.BaseRecord = [] for base in st.BaseCoverage.glyphs: baserec = otTables.BaseRecord() st.BaseArray.BaseRecord.append(baserec) baserec.BaseAnchor = [] for markClass in sorted(markClasses.keys(), key=markClasses.get): baserec.BaseAnchor.append(self.bases[base].get(markClass)) return self.buildLookup_([st])
def buildMarkBasePosSubtable(marks, bases, glyphMap): """Build a single MarkBasePos subtable. a1, a2, a3, a4, a5 = 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 test_splitMarkBasePos(): from fontTools.otlLib.builder import buildAnchor, buildMarkBasePosSubtable marks = { "acutecomb": (0, buildAnchor(0, 600)), "gravecomb": (0, buildAnchor(0, 590)), "cedillacomb": (1, buildAnchor(0, 0)), } bases = { "a": { 0: buildAnchor(350, 500), 1: None, }, "c": { 0: buildAnchor(300, 700), 1: buildAnchor(300, 0), }, } glyphOrder = ["a", "c", "acutecomb", "gravecomb", "cedillacomb"] glyphMap = {g: i for i, g in enumerate(glyphOrder)} oldSubTable = buildMarkBasePosSubtable(marks, bases, glyphMap) oldSubTable.MarkCoverage.Format = oldSubTable.BaseCoverage.Format = 1 newSubTable = otTables.MarkBasePos() ok = otTables.splitMarkBasePos(oldSubTable, newSubTable, overflowRecord=None) assert ok assert oldSubTable.Format == newSubTable.Format assert oldSubTable.MarkCoverage.glyphs == ["acutecomb", "gravecomb"] assert newSubTable.MarkCoverage.glyphs == ["cedillacomb"] assert newSubTable.MarkCoverage.Format == 1 assert oldSubTable.BaseCoverage.glyphs == newSubTable.BaseCoverage.glyphs assert newSubTable.BaseCoverage.Format == 1 assert oldSubTable.ClassCount == newSubTable.ClassCount == 1 assert oldSubTable.MarkArray.MarkCount == 2 assert newSubTable.MarkArray.MarkCount == 1 assert oldSubTable.BaseArray.BaseCount == newSubTable.BaseArray.BaseCount assert newSubTable.BaseArray.BaseRecord[0].BaseAnchor[0] is None assert newSubTable.BaseArray.BaseRecord[1].BaseAnchor[0] == buildAnchor( 300, 0)
def test_splitMarkBasePos(): from fontTools.otlLib.builder import buildAnchor, buildMarkBasePosSubtable marks = { "acutecomb": (0, buildAnchor(0, 600)), "gravecomb": (0, buildAnchor(0, 590)), "cedillacomb": (1, buildAnchor(0, 0)), } bases = { "a": { 0: buildAnchor(350, 500), 1: None, }, "c": { 0: buildAnchor(300, 700), 1: buildAnchor(300, 0), }, } glyphOrder = ["a", "c", "acutecomb", "gravecomb", "cedillacomb"] glyphMap = {g: i for i, g in enumerate(glyphOrder)} oldSubTable = buildMarkBasePosSubtable(marks, bases, glyphMap) newSubTable = otTables.MarkBasePos() ok = otTables.splitMarkBasePos(oldSubTable, newSubTable, overflowRecord=None) assert ok assert getXML(oldSubTable.toXML) == [ '<MarkBasePos Format="1">', ' <MarkCoverage>', ' <Glyph value="acutecomb"/>', ' <Glyph value="gravecomb"/>', ' </MarkCoverage>', ' <BaseCoverage>', ' <Glyph value="a"/>', ' <Glyph value="c"/>', ' </BaseCoverage>', ' <!-- ClassCount=1 -->', ' <MarkArray>', ' <!-- MarkCount=2 -->', ' <MarkRecord index="0">', ' <Class value="0"/>', ' <MarkAnchor Format="1">', ' <XCoordinate value="0"/>', ' <YCoordinate value="600"/>', ' </MarkAnchor>', ' </MarkRecord>', ' <MarkRecord index="1">', ' <Class value="0"/>', ' <MarkAnchor Format="1">', ' <XCoordinate value="0"/>', ' <YCoordinate value="590"/>', ' </MarkAnchor>', ' </MarkRecord>', ' </MarkArray>', ' <BaseArray>', ' <!-- BaseCount=2 -->', ' <BaseRecord index="0">', ' <BaseAnchor index="0" Format="1">', ' <XCoordinate value="350"/>', ' <YCoordinate value="500"/>', ' </BaseAnchor>', ' </BaseRecord>', ' <BaseRecord index="1">', ' <BaseAnchor index="0" Format="1">', ' <XCoordinate value="300"/>', ' <YCoordinate value="700"/>', ' </BaseAnchor>', ' </BaseRecord>', ' </BaseArray>', '</MarkBasePos>', ] assert getXML(newSubTable.toXML) == [ '<MarkBasePos Format="1">', ' <MarkCoverage>', ' <Glyph value="cedillacomb"/>', ' </MarkCoverage>', ' <BaseCoverage>', ' <Glyph value="a"/>', ' <Glyph value="c"/>', ' </BaseCoverage>', ' <!-- ClassCount=1 -->', ' <MarkArray>', ' <!-- MarkCount=1 -->', ' <MarkRecord index="0">', ' <Class value="0"/>', ' <MarkAnchor Format="1">', ' <XCoordinate value="0"/>', ' <YCoordinate value="0"/>', ' </MarkAnchor>', ' </MarkRecord>', ' </MarkArray>', ' <BaseArray>', ' <!-- BaseCount=2 -->', ' <BaseRecord index="0">', ' <BaseAnchor index="0" empty="1"/>', ' </BaseRecord>', ' <BaseRecord index="1">', ' <BaseAnchor index="0" Format="1">', ' <XCoordinate value="300"/>', ' <YCoordinate value="0"/>', ' </BaseAnchor>', ' </BaseRecord>', ' </BaseArray>', '</MarkBasePos>', ]