Merge pull request #887 from fonttools/class-kern-merge
[varLib.merger] Fix interpolating PairPos Format 2
This commit is contained in:
commit
a2b23811a2
@ -418,19 +418,19 @@ class ClassDef(FormatSwitchingBaseTable):
|
||||
assert 0, "unknown format: %s" % self.Format
|
||||
self.classDefs = classDefs
|
||||
|
||||
def preWrite(self, font):
|
||||
def _getClassRanges(self, font):
|
||||
classDefs = getattr(self, "classDefs", None)
|
||||
if classDefs is None:
|
||||
classDefs = self.classDefs = {}
|
||||
format = 2
|
||||
rawTable = {"ClassRangeRecord": []}
|
||||
self.classDefs = {}
|
||||
return
|
||||
getGlyphID = font.getGlyphID
|
||||
items = []
|
||||
for glyphName, cls in classDefs.items():
|
||||
if not cls: continue
|
||||
if not cls:
|
||||
continue
|
||||
items.append((getGlyphID(glyphName), glyphName, cls))
|
||||
items.sort()
|
||||
if items:
|
||||
items.sort()
|
||||
last, lastName, lastCls = items[0]
|
||||
ranges = [[lastCls, last, lastName]]
|
||||
for glyphID, glyphName, cls in items[1:]:
|
||||
@ -441,7 +441,13 @@ class ClassDef(FormatSwitchingBaseTable):
|
||||
lastName = glyphName
|
||||
lastCls = cls
|
||||
ranges[-1].extend([last, lastName])
|
||||
return ranges
|
||||
|
||||
def preWrite(self, font):
|
||||
format = 2
|
||||
rawTable = {"ClassRangeRecord": []}
|
||||
ranges = self._getClassRanges(font)
|
||||
if ranges:
|
||||
startGlyph = ranges[0][1]
|
||||
endGlyph = ranges[-1][3]
|
||||
glyphCount = endGlyph - startGlyph + 1
|
||||
|
@ -322,10 +322,12 @@ def _ClassDef_merge_classify(lst, allGlyphs=None):
|
||||
sets = _ClassDef_invert(l)
|
||||
if allGlyphs is None:
|
||||
sets = sets[1:]
|
||||
else:
|
||||
elif sets:
|
||||
sets[0] = set(allGlyphs)
|
||||
for s in sets[1:]:
|
||||
sets[0].difference_update(s)
|
||||
else:
|
||||
sets = [set(allGlyphs)]
|
||||
classifier.update(sets)
|
||||
classes = classifier.getClasses()
|
||||
|
||||
@ -340,6 +342,18 @@ def _ClassDef_merge_classify(lst, allGlyphs=None):
|
||||
|
||||
return self, classes
|
||||
|
||||
def _ClassDef_calculate_Format(self, font):
|
||||
fmt = 2
|
||||
ranges = self._getClassRanges(font)
|
||||
if ranges:
|
||||
startGlyph = ranges[0][1]
|
||||
endGlyph = ranges[-1][3]
|
||||
glyphCount = endGlyph - startGlyph + 1
|
||||
if len(ranges) * 3 >= glyphCount + 1:
|
||||
# Format 1 is more compact
|
||||
fmt = 1
|
||||
self.Format = fmt
|
||||
|
||||
def _PairPosFormat2_merge(self, lst, merger):
|
||||
merger.mergeObjects(self, lst,
|
||||
exclude=('Coverage',
|
||||
@ -366,6 +380,7 @@ def _PairPosFormat2_merge(self, lst, merger):
|
||||
|
||||
# Align first classes
|
||||
self.ClassDef1, classes = _ClassDef_merge_classify([l.ClassDef1 for l in lst], allGlyphs=glyphSet)
|
||||
_ClassDef_calculate_Format(self.ClassDef1, merger.font)
|
||||
self.Class1Count = len(classes)
|
||||
new_matrices = []
|
||||
for l,matrix in zip(lst, matrices):
|
||||
@ -377,11 +392,13 @@ def _PairPosFormat2_merge(self, lst, merger):
|
||||
exemplarGlyph = next(iter(classSet))
|
||||
if exemplarGlyph not in coverage:
|
||||
if nullRow is None:
|
||||
rec2 = ot.Class2Record()
|
||||
rec2.Value1 = otBase.ValueRecord(l.ValueFormat1) if l.ValueFormat1 else None
|
||||
rec2.Value2 = otBase.ValueRecord(l.ValueFormat2) if l.ValueFormat2 else None
|
||||
nullRow = ot.Class1Record()
|
||||
nullRow.Class2Record = [rec2] * l.Class2Count
|
||||
class2records = nullRow.Class2Record = []
|
||||
for _ in range(l.Class2Count):
|
||||
rec2 = ot.Class2Record()
|
||||
rec2.Value1 = otBase.ValueRecord(l.ValueFormat1) if l.ValueFormat1 else None
|
||||
rec2.Value2 = otBase.ValueRecord(l.ValueFormat2) if l.ValueFormat2 else None
|
||||
class2records.append(rec2)
|
||||
rec1 = nullRow
|
||||
else:
|
||||
klass = classDef1.get(exemplarGlyph, 0)
|
||||
@ -393,6 +410,7 @@ def _PairPosFormat2_merge(self, lst, merger):
|
||||
|
||||
# Align second classes
|
||||
self.ClassDef2, classes = _ClassDef_merge_classify([l.ClassDef2 for l in lst])
|
||||
_ClassDef_calculate_Format(self.ClassDef2, merger.font)
|
||||
self.Class2Count = len(classes)
|
||||
new_matrices = []
|
||||
for l,matrix in zip(lst, matrices):
|
||||
@ -506,15 +524,18 @@ def _Lookup_PairPos_subtables_canonicalize(lst, font):
|
||||
head = []
|
||||
tail = []
|
||||
it = iter(lst)
|
||||
has_Format1 = False
|
||||
for subtable in it:
|
||||
if subtable.Format == 1:
|
||||
head.append(subtable)
|
||||
has_Format1 = True
|
||||
continue
|
||||
tail.append(subtable)
|
||||
break
|
||||
tail.extend(it)
|
||||
# TODO Only do this if at least one font has a Format1.
|
||||
tail.insert(0, _Lookup_PairPosFormat1_subtables_merge_overlay(head, font))
|
||||
if has_Format1:
|
||||
# only insert if at least one font has a Format1
|
||||
tail.insert(0, _Lookup_PairPosFormat1_subtables_merge_overlay(head, font))
|
||||
return tail
|
||||
|
||||
@AligningMerger.merger(ot.Lookup)
|
||||
|
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
|
||||
|
||||
<GPOS>
|
||||
<Version value="0x00010000"/>
|
||||
<ScriptList>
|
||||
<!-- ScriptCount=1 -->
|
||||
<ScriptRecord index="0">
|
||||
<ScriptTag value="DFLT"/>
|
||||
<Script>
|
||||
<DefaultLangSys>
|
||||
<ReqFeatureIndex value="65535"/>
|
||||
<!-- FeatureCount=1 -->
|
||||
<FeatureIndex index="0" value="0"/>
|
||||
</DefaultLangSys>
|
||||
<!-- LangSysCount=0 -->
|
||||
</Script>
|
||||
</ScriptRecord>
|
||||
</ScriptList>
|
||||
<FeatureList>
|
||||
<!-- FeatureCount=1 -->
|
||||
<FeatureRecord index="0">
|
||||
<FeatureTag value="xxxx"/>
|
||||
<Feature>
|
||||
<!-- LookupCount=1 -->
|
||||
<LookupListIndex index="0" value="0"/>
|
||||
</Feature>
|
||||
</FeatureRecord>
|
||||
</FeatureList>
|
||||
<LookupList>
|
||||
<!-- LookupCount=1 -->
|
||||
<Lookup index="0">
|
||||
<LookupType value="2"/>
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=1 -->
|
||||
<PairPos index="0" Format="2">
|
||||
<Coverage Format="1">
|
||||
<Glyph value="A"/>
|
||||
</Coverage>
|
||||
<ValueFormat1 value="4"/>
|
||||
<ValueFormat2 value="0"/>
|
||||
<ClassDef1 Format="2">
|
||||
</ClassDef1>
|
||||
<ClassDef2 Format="1">
|
||||
<ClassDef glyph="a" class="1"/>
|
||||
</ClassDef2>
|
||||
<!-- Class1Count=1 -->
|
||||
<!-- Class2Count=2 -->
|
||||
<Class1Record index="0">
|
||||
<Class2Record index="0">
|
||||
<Value1 XAdvance="0"/>
|
||||
</Class2Record>
|
||||
<Class2Record index="1">
|
||||
<Value1 XAdvance="-40"/>
|
||||
</Class2Record>
|
||||
</Class1Record>
|
||||
</PairPos>
|
||||
</Lookup>
|
||||
</LookupList>
|
||||
</GPOS>
|
||||
|
||||
</ttFont>
|
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
|
||||
|
||||
<GPOS>
|
||||
<Version value="0x00010000"/>
|
||||
<ScriptList>
|
||||
<!-- ScriptCount=1 -->
|
||||
<ScriptRecord index="0">
|
||||
<ScriptTag value="DFLT"/>
|
||||
<Script>
|
||||
<DefaultLangSys>
|
||||
<ReqFeatureIndex value="65535"/>
|
||||
<!-- FeatureCount=1 -->
|
||||
<FeatureIndex index="0" value="0"/>
|
||||
</DefaultLangSys>
|
||||
<!-- LangSysCount=0 -->
|
||||
</Script>
|
||||
</ScriptRecord>
|
||||
</ScriptList>
|
||||
<FeatureList>
|
||||
<!-- FeatureCount=1 -->
|
||||
<FeatureRecord index="0">
|
||||
<FeatureTag value="xxxx"/>
|
||||
<Feature>
|
||||
<!-- LookupCount=1 -->
|
||||
<LookupListIndex index="0" value="0"/>
|
||||
</Feature>
|
||||
</FeatureRecord>
|
||||
</FeatureList>
|
||||
<LookupList>
|
||||
<!-- LookupCount=1 -->
|
||||
<Lookup index="0">
|
||||
<LookupType value="2"/>
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=1 -->
|
||||
<PairPos index="0" Format="2">
|
||||
<Coverage Format="1">
|
||||
<Glyph value="A"/>
|
||||
<Glyph value="a"/>
|
||||
</Coverage>
|
||||
<ValueFormat1 value="4"/>
|
||||
<ValueFormat2 value="0"/>
|
||||
<ClassDef1 Format="1">
|
||||
<ClassDef glyph="A" class="1"/>
|
||||
</ClassDef1>
|
||||
<ClassDef2 Format="1">
|
||||
<ClassDef glyph="a" class="1"/>
|
||||
</ClassDef2>
|
||||
<!-- Class1Count=2 -->
|
||||
<!-- Class2Count=2 -->
|
||||
<Class1Record index="0">
|
||||
<Class2Record index="0">
|
||||
<Value1 XAdvance="0"/>
|
||||
</Class2Record>
|
||||
<Class2Record index="1">
|
||||
<Value1 XAdvance="10"/>
|
||||
</Class2Record>
|
||||
</Class1Record>
|
||||
<Class1Record index="1">
|
||||
<Class2Record index="0">
|
||||
<Value1 XAdvance="0"/>
|
||||
</Class2Record>
|
||||
<Class2Record index="1">
|
||||
<Value1 XAdvance="-40"/>
|
||||
</Class2Record>
|
||||
</Class1Record>
|
||||
</PairPos>
|
||||
</Lookup>
|
||||
</LookupList>
|
||||
</GPOS>
|
||||
|
||||
</ttFont>
|
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
|
||||
|
||||
<GPOS>
|
||||
<Version value="0x00010000"/>
|
||||
<ScriptList>
|
||||
<!-- ScriptCount=1 -->
|
||||
<ScriptRecord index="0">
|
||||
<ScriptTag value="DFLT"/>
|
||||
<Script>
|
||||
<DefaultLangSys>
|
||||
<ReqFeatureIndex value="65535"/>
|
||||
<!-- FeatureCount=1 -->
|
||||
<FeatureIndex index="0" value="0"/>
|
||||
</DefaultLangSys>
|
||||
<!-- LangSysCount=0 -->
|
||||
</Script>
|
||||
</ScriptRecord>
|
||||
</ScriptList>
|
||||
<FeatureList>
|
||||
<!-- FeatureCount=1 -->
|
||||
<FeatureRecord index="0">
|
||||
<FeatureTag value="xxxx"/>
|
||||
<Feature>
|
||||
<!-- LookupCount=1 -->
|
||||
<LookupListIndex index="0" value="0"/>
|
||||
</Feature>
|
||||
</FeatureRecord>
|
||||
</FeatureList>
|
||||
<LookupList>
|
||||
<!-- LookupCount=1 -->
|
||||
<Lookup index="0">
|
||||
<LookupType value="2"/>
|
||||
<LookupFlag value="0"/>
|
||||
<!-- SubTableCount=1 -->
|
||||
<PairPos index="0" Format="2">
|
||||
<Coverage Format="1">
|
||||
<Glyph value="A"/>
|
||||
</Coverage>
|
||||
<ValueFormat1 value="4"/>
|
||||
<ValueFormat2 value="0"/>
|
||||
<ClassDef1 Format="2">
|
||||
</ClassDef1>
|
||||
<ClassDef2 Format="1">
|
||||
<ClassDef glyph="a" class="1"/>
|
||||
</ClassDef2>
|
||||
<!-- Class1Count=1 -->
|
||||
<!-- Class2Count=2 -->
|
||||
<Class1Record index="0">
|
||||
<Class2Record index="0">
|
||||
<Value1 XAdvance="0"/>
|
||||
</Class2Record>
|
||||
<Class2Record index="1">
|
||||
<Value1 XAdvance="-53"/>
|
||||
</Class2Record>
|
||||
</Class1Record>
|
||||
</PairPos>
|
||||
</Lookup>
|
||||
</LookupList>
|
||||
</GPOS>
|
||||
|
||||
</ttFont>
|
@ -401,6 +401,104 @@ class InterpolateLayoutTest(unittest.TestCase):
|
||||
self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
||||
|
||||
|
||||
def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_same_val_ttf(self):
|
||||
"""Only GPOS; LookupType 2 class pairs; same values in all masters.
|
||||
"""
|
||||
suffix = '.ttf'
|
||||
ds_path = self.get_test_input('InterpolateLayout.designspace')
|
||||
ufo_dir = self.get_test_input('master_ufo')
|
||||
ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
|
||||
|
||||
fea_str = """
|
||||
feature xxxx {
|
||||
pos [A] [a] -53;
|
||||
} xxxx;
|
||||
"""
|
||||
features = [fea_str] * 2
|
||||
|
||||
self.temp_dir()
|
||||
ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
|
||||
for i, path in enumerate(ttx_paths):
|
||||
self.compile_font(path, suffix, self.tempdir, features[i])
|
||||
|
||||
finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
|
||||
instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
|
||||
|
||||
tables = ['GPOS']
|
||||
expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_same.ttx')
|
||||
self.expect_ttx(instfont, expected_ttx_path, tables)
|
||||
self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
||||
|
||||
|
||||
def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_diff_val_ttf(self):
|
||||
"""Only GPOS; LookupType 2 class pairs; different values in each master.
|
||||
"""
|
||||
suffix = '.ttf'
|
||||
ds_path = self.get_test_input('InterpolateLayout.designspace')
|
||||
ufo_dir = self.get_test_input('master_ufo')
|
||||
ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
|
||||
|
||||
fea_str_0 = """
|
||||
feature xxxx {
|
||||
pos [A] [a] -53;
|
||||
} xxxx;
|
||||
"""
|
||||
fea_str_1 = """
|
||||
feature xxxx {
|
||||
pos [A] [a] -27;
|
||||
} xxxx;
|
||||
"""
|
||||
features = [fea_str_0, fea_str_1]
|
||||
|
||||
self.temp_dir()
|
||||
ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
|
||||
for i, path in enumerate(ttx_paths):
|
||||
self.compile_font(path, suffix, self.tempdir, features[i])
|
||||
|
||||
finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
|
||||
instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
|
||||
|
||||
tables = ['GPOS']
|
||||
expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_diff.ttx')
|
||||
self.expect_ttx(instfont, expected_ttx_path, tables)
|
||||
self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
||||
|
||||
|
||||
def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_diff2_val_ttf(self):
|
||||
"""Only GPOS; LookupType 2 class pairs; different values and items in each master.
|
||||
"""
|
||||
suffix = '.ttf'
|
||||
ds_path = self.get_test_input('InterpolateLayout.designspace')
|
||||
ufo_dir = self.get_test_input('master_ufo')
|
||||
ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
|
||||
|
||||
fea_str_0 = """
|
||||
feature xxxx {
|
||||
pos [A] [a] -53;
|
||||
} xxxx;
|
||||
"""
|
||||
fea_str_1 = """
|
||||
feature xxxx {
|
||||
pos [A] [a] -27;
|
||||
pos [a] [a] 19;
|
||||
} xxxx;
|
||||
"""
|
||||
features = [fea_str_0, fea_str_1]
|
||||
|
||||
self.temp_dir()
|
||||
ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
|
||||
for i, path in enumerate(ttx_paths):
|
||||
self.compile_font(path, suffix, self.tempdir, features[i])
|
||||
|
||||
finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
|
||||
instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
|
||||
|
||||
tables = ['GPOS']
|
||||
expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_diff2.ttx')
|
||||
self.expect_ttx(instfont, expected_ttx_path, tables)
|
||||
self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
|
||||
|
||||
|
||||
def test_varlib_interpolate_layout_GPOS_only_LookupType_3_same_val_ttf(self):
|
||||
"""Only GPOS; LookupType 3; same values in all masters.
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user