[varLib] Allow sparse masters in GPOS MarkBasePos lookups

Part of https://github.com/googlei18n/fontmake/issues/88
Part of https://github.com/fonttools/fonttools/issues/1355
This commit is contained in:
Behdad Esfahbod 2018-11-09 00:41:25 -05:00
parent 0d85fc57d0
commit 11a679df21

View File

@ -9,7 +9,7 @@ from fontTools.ttLib.tables import otTables as ot
from fontTools.ttLib.tables import otBase as otBase
from fontTools.ttLib.tables.DefaultTable import DefaultTable
from fontTools.varLib import builder, models, varStore
from fontTools.varLib.models import allSame, nonNone
from fontTools.varLib.models import nonNone, allSame, allSameAs
from fontTools.varLib.varStore import VarStoreInstancer
from functools import reduce
@ -519,6 +519,85 @@ def merge(merger, self, lst):
self.ValueFormat1 = vf1
self.ValueFormat2 = vf2
def _MarkBasePosFormat1_merge(self, lst, merger):
self.ClassCount = max(l.ClassCount for l in lst)
self.MarkCoverage = ot.Coverage()
self.MarkCoverage.glyphs, MarkRecords = \
_merge_GlyphOrders(merger.font,
[l.MarkCoverage.glyphs for l in lst],
[l.MarkArray.MarkRecord for l in lst])
self.BaseCoverage = ot.Coverage()
self.BaseCoverage.glyphs, BaseRecords = \
_merge_GlyphOrders(merger.font,
[l.BaseCoverage.glyphs for l in lst],
[l.BaseArray.BaseRecord for l in lst])
# MarkArray
self.MarkArray = ot.MarkArray()
self.MarkArray.MarkRecord = records = []
for g,glyphRecords in zip(self.MarkCoverage.glyphs, zip(*MarkRecords)):
allClasses = [r.Class for r in glyphRecords if r is not None]
# TODO Right now we require that all marks have same class in
# all masters that cover them. This is not required.
#
# We can relax that by just requiring that all marks that have
# the same class in a master, have the same class in every other
# master. Indeed, if, say, a sparse master only covers one mark,
# that mark probably will get class 0, which would possibly be
# different from its class in other masters.
#
# We can even go further and reclassify marks to support any
# input. But, since, it's unlikely that two marks being both,
# say, "top" in one master, and one being "top" and other being
# "top-right" in another master, we shouldn't do that, as any
# failures in that case will probably signify mistakes in the
# input masters.
assert allSame(allClasses), allClasses
if not allClasses:
rec = None
else:
rec = ot.MarkRecord()
rec.Class = allClasses[0]
rec.MarkAnchor = anchor = ot.Anchor()
anchor.Format = 1
merger.mergeThings(anchor,
[None if r is None else r.MarkAnchor for r in glyphRecords])
records.append(rec)
# BaseArray
self.BaseArray = ot.BaseArray()
self.BaseArray.BaseRecord = records = []
for g,glyphRecords in zip(self.BaseCoverage.glyphs, zip(*BaseRecords)):
if allSameAs(None, glyphRecords):
rec = None
else:
rec = ot.BaseRecord()
rec.BaseAnchor = anchors = []
glyphAnchors = [[] if r is None else r.BaseAnchor for r in glyphRecords]
for l in glyphAnchors:
l.extend([None] * (self.ClassCount - len(l)))
for allAnchors in zip(*glyphAnchors):
if allSameAs(None, allAnchors):
anchor = None
else:
anchor = ot.Anchor()
anchor.Format = 1
merger.mergeThings(anchor, allAnchors)
anchors.append(anchor)
records.append(rec)
@AligningMerger.merger(ot.MarkBasePos)
def merge(merger, self, lst):
assert allSameAs(self.Format, (l.Format for l in lst))
if self.Format == 1:
_MarkBasePosFormat1_merge(self, lst, merger)
else:
assert False
def _PairSet_flatten(lst, font):
self = ot.PairSet()
@ -846,7 +925,7 @@ class VariationMerger(AligningMerger):
masterModel = None
if None in lst:
if allSame(lst):
assert out is None
assert out is None, (out, lst)
return
masterModel = self.model
model, lst = masterModel.getSubModel(lst)