[varLib] Fix interpolate_layout for non-similar SinglePos
Has to be ported to varfont merger as well.
This commit is contained in:
parent
b9c38af1ff
commit
b39772b256
@ -868,6 +868,12 @@ class ValueRecord(object):
|
||||
|
||||
# see ValueRecordFactory
|
||||
|
||||
def __init__(self, valueFormat=None):
|
||||
if valueFormat is not None:
|
||||
for mask, name, isDevice, signed in valueRecordFormat:
|
||||
if valueFormat & mask:
|
||||
setattr(self, name, None if isDevice else 0)
|
||||
|
||||
def getFormat(self):
|
||||
format = 0
|
||||
for name in self.__dict__.keys():
|
||||
|
@ -248,7 +248,8 @@ def buildVarDevTable(store_builder, master_values):
|
||||
|
||||
class VariationMerger(Merger):
|
||||
|
||||
def __init__(self, model, axisTags):
|
||||
def __init__(self, model, axisTags, font):
|
||||
Merger.__init__(self, font)
|
||||
self.model = model
|
||||
self.store_builder = builder.OnlineVarStoreBuilder(axisTags)
|
||||
self.store_builder.setModel(model)
|
||||
@ -302,7 +303,7 @@ def merge(merger, self, lst):
|
||||
|
||||
if hasattr(self, name):
|
||||
deviceTable = buildVarDevTable(merger.store_builder,
|
||||
[getattr(a, name) for a in lst])
|
||||
[getattr(a, name, 0) for a in lst])
|
||||
if deviceTable:
|
||||
setattr(self, tableName, deviceTable)
|
||||
|
||||
@ -310,7 +311,7 @@ def merge(merger, self, lst):
|
||||
def _merge_OTL(font, model, master_fonts, axisTags, base_idx):
|
||||
|
||||
print("Merging OpenType Layout tables")
|
||||
merger = VariationMerger(model, axisTags)
|
||||
merger = VariationMerger(model, axisTags, font)
|
||||
|
||||
merge_tables(font, merger, master_fonts, axisTags, base_idx, ['GPOS'])
|
||||
store = merger.store_builder.finish()
|
||||
|
@ -9,11 +9,13 @@ from fontTools.ttLib.tables import otBase as otBase
|
||||
from fontTools.ttLib.tables.DefaultTable import DefaultTable
|
||||
from fontTools.varLib import designspace, models, builder
|
||||
from fontTools.varLib.merger import merge_tables, Merger
|
||||
from functools import reduce
|
||||
import os.path
|
||||
|
||||
class InstancerMerger(Merger):
|
||||
|
||||
def __init__(self, model, location):
|
||||
def __init__(self, font, model, location):
|
||||
Merger.__init__(self, font)
|
||||
self.model = model
|
||||
self.location = location
|
||||
|
||||
@ -30,6 +32,7 @@ def merge(merger, self, lst):
|
||||
def merge(merger, self, lst):
|
||||
model = merger.model
|
||||
location = merger.location
|
||||
# TODO Handle differing valueformats
|
||||
for name, tableName in [('XAdvance','XAdvDevice'),
|
||||
('YAdvance','YAdvDevice'),
|
||||
('XPlacement','XPlaDevice'),
|
||||
@ -38,10 +41,115 @@ def merge(merger, self, lst):
|
||||
assert not hasattr(self, tableName)
|
||||
|
||||
if hasattr(self, name):
|
||||
values = [getattr(a, name) for a in lst]
|
||||
values = [getattr(a, name, 0) for a in lst]
|
||||
value = round(model.interpolateFromMasters(location, values))
|
||||
setattr(self, name, value)
|
||||
|
||||
def _SinglePosUpgradeToFormat2(self):
|
||||
if self.Format == 2: return self
|
||||
|
||||
ret = ot.SinglePos()
|
||||
ret.Format = 2
|
||||
ret.Coverage = self.Coverage
|
||||
ret.ValueFormat = self.ValueFormat
|
||||
ret.Value = [self.Value for g in ret.Coverage.glyphs]
|
||||
ret.ValueCount = len(ret.Value)
|
||||
|
||||
return ret
|
||||
|
||||
def _merge_GlyphOrders(font, lst, values_lst=None):
|
||||
"""Takes font and list of glyph lists (must be sorted by glyph id), and returns
|
||||
two things:
|
||||
- Combined glyph list,
|
||||
- If values_lst is None, return input glyph lists, but padded with None when a glyph
|
||||
was missing in a list. Otherwise, return values_lst list-of-list, padded with None
|
||||
to match combined glyph lists.
|
||||
"""
|
||||
dict_sets = [{g:i for i,g in enumerate(l)} for l in lst]
|
||||
combined = set()
|
||||
combined.update(*dict_sets)
|
||||
order = font.getGlyphOrder()
|
||||
order = [glyph for glyph in order if glyph in combined]
|
||||
paddedValues = None
|
||||
if values_lst is None:
|
||||
padded = [[glyph if glyph in dict_set else None
|
||||
for glyph in combined]
|
||||
for dict_set in dict_sets]
|
||||
else:
|
||||
assert len(lst) == len(values_lst)
|
||||
padded = [[values[dict_set[glyph]] if glyph in dict_set else None
|
||||
for glyph in combined]
|
||||
for dict_set, values in zip(dict_sets, values_lst)]
|
||||
return order, padded
|
||||
|
||||
def _Lookup_SinglePos_get_effective_value(self, glyph):
|
||||
if self is None: return None
|
||||
subtables = self.SubTable
|
||||
for self in subtables:
|
||||
if self is None or \
|
||||
type(self) != ot.SinglePos or \
|
||||
self.Coverage is None or \
|
||||
glyph not in self.Coverage.glyphs:
|
||||
continue
|
||||
if self.Format == 1:
|
||||
return self.Value
|
||||
elif self.Format == 2:
|
||||
return self.Value[self.Coverage.glyphs.index(glyph)]
|
||||
else:
|
||||
assert 0
|
||||
return None
|
||||
|
||||
@InstancerMerger.merger(ot.SinglePos)
|
||||
def merge(merger, self, lst):
|
||||
self.ValueFormat = valueFormat = reduce(int.__or__, [l.ValueFormat for l in lst])
|
||||
assert valueFormat & ~0xF == 0, valueFormat
|
||||
|
||||
# If all have same coverage table and all are format 1,
|
||||
if all(v.Format == 1 for v in lst) and all(self.Coverage.glyphs == v.Coverage.glyphs for v in lst):
|
||||
self.Value = otBase.ValueRecord(valueFormat)
|
||||
merger.mergeThings(self.Value, [v.Value for v in lst])
|
||||
return
|
||||
|
||||
# Upgrade everything to Format=2
|
||||
self.Format = 2
|
||||
lst = [_SinglePosUpgradeToFormat2(v) for v in lst]
|
||||
|
||||
# Align them
|
||||
glyphs, padded = _merge_GlyphOrders(merger.font,
|
||||
[v.Coverage.glyphs for v in lst],
|
||||
[v.Value for v in lst])
|
||||
|
||||
self.Coverage.glyphs = glyphs
|
||||
self.Value = [otBase.ValueRecord(valueFormat) for g in glyphs]
|
||||
self.ValueCount = len(self.Value)
|
||||
|
||||
for glyph,values in zip(glyphs, padded):
|
||||
for i in range(len(values)):
|
||||
if values[i] is not None: continue
|
||||
# Fill in value from k
|
||||
v = _Lookup_SinglePos_get_effective_value(merger.lookups[i], glyph)
|
||||
if v is None:
|
||||
v = otBase.ValueRecord(valueFormat)
|
||||
values[i] = v
|
||||
|
||||
merger.mergeThings(self.Value, padded)
|
||||
# Merge everything else; though, there shouldn't be anything else. :)
|
||||
merger.mergeObjects(self, lst,
|
||||
exclude=('Format', 'Coverage', 'ValueRecord', 'Value', 'ValueCount'))
|
||||
|
||||
#@InstancerMerger.merger(ot.PairPos)
|
||||
#def merge(merger, self, lst):
|
||||
# #return #XXX
|
||||
# # Merge everything else; though, there shouldn't be anything else. :)
|
||||
# merger.mergeObjects(self, lst,
|
||||
# exclude=('Coverage', 'PairSet', 'PairSetCount'))
|
||||
|
||||
@InstancerMerger.merger(ot.Lookup)
|
||||
def merge(merger, self, lst):
|
||||
merger.lookups = lst
|
||||
merger.mergeObjects(self, lst)
|
||||
del merger.lookups
|
||||
|
||||
|
||||
def interpolate_layout(designspace_filename, loc, finder):
|
||||
|
||||
@ -96,7 +204,7 @@ def interpolate_layout(designspace_filename, loc, finder):
|
||||
model = models.VariationModel(master_locs)
|
||||
assert 0 == model.mapping[base_idx]
|
||||
|
||||
merger = InstancerMerger(model, loc)
|
||||
merger = InstancerMerger(font, model, loc)
|
||||
|
||||
print("Building variations tables")
|
||||
merge_tables(font, merger, master_fonts, axes, base_idx, ['GPOS'])
|
||||
|
@ -10,6 +10,9 @@ class Merger(object):
|
||||
|
||||
mergers = None
|
||||
|
||||
def __init__(self, font=None):
|
||||
self.font = font
|
||||
|
||||
@classmethod
|
||||
def merger(celf, clazzes, attrs=(None,)):
|
||||
assert celf != Merger, 'Subclass Merger instead.'
|
||||
@ -33,13 +36,15 @@ class Merger(object):
|
||||
return None
|
||||
return wrapper
|
||||
|
||||
def mergeObjects(self, out, lst, _default={}):
|
||||
keys = vars(out).keys()
|
||||
assert all(vars(table).keys() == keys for table in lst)
|
||||
def mergeObjects(self, out, lst, exclude=(), _default={}):
|
||||
keys = sorted(vars(out).keys())
|
||||
assert all(keys == sorted(vars(v).keys()) for v in lst), \
|
||||
(keys, [sorted(vars(v).keys()) for v in lst])
|
||||
mergers = self.mergers.get(type(out), _default)
|
||||
defaultMerger = mergers.get('*', self.__class__.mergeThings)
|
||||
try:
|
||||
for key in keys:
|
||||
if key in exclude: continue
|
||||
value = getattr(out, key)
|
||||
values = [getattr(table, key) for table in lst]
|
||||
mergerFunc = mergers.get(key, defaultMerger)
|
||||
|
Loading…
x
Reference in New Issue
Block a user