Merge pull request #887 from fonttools/class-kern-merge

[varLib.merger] Fix interpolating PairPos Format 2
This commit is contained in:
Behdad Esfahbod 2017-03-16 16:03:34 -07:00 committed by GitHub
commit a2b23811a2
6 changed files with 334 additions and 13 deletions

View File

@ -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

View File

@ -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)

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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.
"""